mirror of
https://github.com/jimeh/redistat.git
synced 2026-02-19 13:26:39 +00:00
Merge branch 'release/v0.0.5'
This commit is contained in:
5
Gemfile
5
Gemfile
@@ -2,8 +2,3 @@ source 'http://rubygems.org/'
|
||||
|
||||
# Specify your gem's dependencies in redistat.gemspec
|
||||
gemspec
|
||||
|
||||
group :development do
|
||||
gem 'rspec', '>= 2.1.0'
|
||||
gem 'yard', '>= 0.6.3'
|
||||
end
|
||||
@@ -10,6 +10,7 @@ require 'json'
|
||||
require 'digest/sha1'
|
||||
|
||||
require 'redistat/collection'
|
||||
require 'redistat/connection'
|
||||
require 'redistat/database'
|
||||
require 'redistat/date'
|
||||
require 'redistat/event'
|
||||
@@ -34,51 +35,28 @@ module Redistat
|
||||
KEY_EVENT_IDS = ".event_ids"
|
||||
|
||||
class InvalidOptions < ArgumentError; end
|
||||
|
||||
# Provides access to the Redis database. This is shared accross all models and instances.
|
||||
def redis
|
||||
threaded[:redis] ||= connection(*options)
|
||||
end
|
||||
|
||||
def redis=(connection)
|
||||
threaded[:redis] = connection
|
||||
end
|
||||
|
||||
def threaded
|
||||
Thread.current[:redistat] ||= {}
|
||||
end
|
||||
|
||||
# Connect to a redis database.
|
||||
#
|
||||
# @param options [Hash] options to create a message with.
|
||||
# @option options [#to_s] :host ('127.0.0.1') Host of the redis database.
|
||||
# @option options [#to_s] :port (6379) Port number.
|
||||
# @option options [#to_s] :db (0) Database number.
|
||||
# @option options [#to_s] :timeout (0) Database timeout in seconds.
|
||||
# @example Connect to a database in port 6380.
|
||||
# Redistat.connect(:port => 6380)
|
||||
def connect(*options)
|
||||
self.redis = nil
|
||||
@options = options
|
||||
end
|
||||
|
||||
# Return a connection to Redis.
|
||||
#
|
||||
# This is a wapper around Redis.new(options)
|
||||
def connection(*options)
|
||||
Redis.new(*options)
|
||||
end
|
||||
|
||||
def options
|
||||
@options = [] unless defined? @options
|
||||
@options
|
||||
end
|
||||
|
||||
# Clear the database.
|
||||
def flush
|
||||
redis.flushdb
|
||||
end
|
||||
class RedisServerIsTooOld < Exception; end
|
||||
|
||||
module_function :connect, :connection, :flush, :redis, :redis=, :options, :threaded
|
||||
|
||||
class << self
|
||||
|
||||
def connection(ref = nil)
|
||||
Connection.get(ref)
|
||||
end
|
||||
alias :redis :connection
|
||||
|
||||
def connection=(connection)
|
||||
Connection.add(connection)
|
||||
end
|
||||
alias :redis= :connection=
|
||||
|
||||
def connect(options)
|
||||
Connection.create(options)
|
||||
end
|
||||
|
||||
def flush
|
||||
puts "WARNING: Redistat.flush is deprecated. Use Redistat.redis.flush instead."
|
||||
connection.flushdb
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,5 +12,9 @@ module Redistat
|
||||
@depth = options[:depth] ||= nil
|
||||
end
|
||||
|
||||
def total
|
||||
@total ||= {}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
67
lib/redistat/connection.rb
Normal file
67
lib/redistat/connection.rb
Normal file
@@ -0,0 +1,67 @@
|
||||
module Redistat
|
||||
module Connection
|
||||
|
||||
REQUIRED_SERVER_VERSION = "1.3.10"
|
||||
|
||||
class << self
|
||||
|
||||
def get(ref = nil)
|
||||
ref ||= :default
|
||||
connections[references[ref]] || create
|
||||
end
|
||||
|
||||
def add(conn, ref = nil)
|
||||
ref ||= :default
|
||||
check_redis_version(conn)
|
||||
references[ref] = conn.client.id
|
||||
connections[conn.client.id] = conn
|
||||
end
|
||||
|
||||
def create(options = {})
|
||||
ref = options.delete(:ref) || :default
|
||||
options.reverse_merge!(default_options)
|
||||
conn = (connections[connection_id(options)] ||= connection(options))
|
||||
references[ref] = conn.client.id
|
||||
conn
|
||||
end
|
||||
|
||||
def connections
|
||||
threaded[:connections] ||= {}
|
||||
end
|
||||
|
||||
def references
|
||||
threaded[:references] ||= {}
|
||||
end
|
||||
|
||||
def threaded
|
||||
Thread.current[:redistat] ||= {}
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def check_redis_version(conn)
|
||||
raise RedisServerIsTooOld if conn.info["redis_version"] < REQUIRED_SERVER_VERSION
|
||||
conn
|
||||
end
|
||||
|
||||
def connection(options)
|
||||
check_redis_version(Redis.new(options))
|
||||
end
|
||||
|
||||
def connection_id(options = {})
|
||||
options.reverse_merge!(default_options)
|
||||
"redis://#{options[:host]}:#{options[:port]}/#{options[:db]}"
|
||||
end
|
||||
|
||||
def default_options
|
||||
{
|
||||
:host => '127.0.0.1',
|
||||
:port => 6379,
|
||||
:db => 0,
|
||||
:timeout => 5
|
||||
}
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -3,8 +3,8 @@ module Redistat
|
||||
def self.included(base)
|
||||
base.extend(Database)
|
||||
end
|
||||
def db
|
||||
Redistat.redis
|
||||
def db(ref = nil)
|
||||
Redistat.connection(ref)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -4,21 +4,34 @@ module Redistat
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :key
|
||||
attr_reader :connection_ref
|
||||
|
||||
attr_accessor :stats
|
||||
attr_accessor :meta
|
||||
attr_accessor :options
|
||||
|
||||
def initialize(scope, label = nil, date = nil, stats = {}, options = {}, meta = {}, is_new = true)
|
||||
@options = default_options.merge(options)
|
||||
@options = parse_options(options)
|
||||
@connection_ref = @options[:connection_ref]
|
||||
@key = Key.new(scope, label, date, @options)
|
||||
@stats = stats ||= {}
|
||||
@meta = meta ||= {}
|
||||
@new = is_new
|
||||
end
|
||||
|
||||
def db
|
||||
super(@connection_ref)
|
||||
end
|
||||
|
||||
def parse_options(options)
|
||||
default_options.each do |opt, val|
|
||||
options[opt] = val if options[opt].nil?
|
||||
end
|
||||
options
|
||||
end
|
||||
|
||||
def default_options
|
||||
{ :depth => :hour, :store_event => false }
|
||||
{ :depth => :hour, :store_event => false, :connection_ref => nil }
|
||||
end
|
||||
|
||||
def new?
|
||||
@@ -59,7 +72,7 @@ module Redistat
|
||||
|
||||
def save
|
||||
return false if !self.new?
|
||||
Summary.update_all(@key, @stats, depth_limit)
|
||||
Summary.update_all(@key, @stats, depth_limit, @connection_ref)
|
||||
if @options[:store_event]
|
||||
@id = self.next_id
|
||||
db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
|
||||
|
||||
@@ -8,6 +8,10 @@ module Redistat
|
||||
@options = options
|
||||
end
|
||||
|
||||
def db
|
||||
super(@options[:connection_ref])
|
||||
end
|
||||
|
||||
def valid_options?
|
||||
return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
|
||||
false
|
||||
|
||||
@@ -3,11 +3,16 @@ module Redistat
|
||||
include Database
|
||||
|
||||
attr_reader :raw
|
||||
attr_reader :connection_ref
|
||||
|
||||
def initialize(str, options = {})
|
||||
@options = options
|
||||
@raw = str.to_s
|
||||
end
|
||||
|
||||
def db
|
||||
super(@options[:connection_ref])
|
||||
end
|
||||
|
||||
def name
|
||||
@options[:hashed_label] ? hash : @raw
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
module Redistat
|
||||
module Model
|
||||
include Redistat::Database
|
||||
|
||||
def self.included(base)
|
||||
base.extend(self)
|
||||
@@ -10,6 +11,16 @@ module Redistat
|
||||
end
|
||||
alias :event :store
|
||||
|
||||
def connect_to(opts = {})
|
||||
Connection.create(opts.merge(:ref => name))
|
||||
options[:connection_ref] = name
|
||||
end
|
||||
|
||||
def connection
|
||||
db(options[:connection_ref])
|
||||
end
|
||||
alias :redis :connection
|
||||
|
||||
def fetch(label, from, till, opts = {})
|
||||
Finder.find({
|
||||
:scope => name,
|
||||
|
||||
@@ -2,21 +2,21 @@ module Redistat
|
||||
class Summary
|
||||
include Database
|
||||
|
||||
def self.update_all(key, stats = {}, depth_limit = nil)
|
||||
def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil)
|
||||
stats ||= {}
|
||||
depth_limit ||= key.depth
|
||||
return nil if stats.size == 0
|
||||
Date::DEPTHS.each do |depth|
|
||||
update(key, stats, depth)
|
||||
update(key, stats, depth, connection_ref)
|
||||
break if depth == depth_limit
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def self.update(key, stats, depth)
|
||||
def self.update(key, stats, depth, connection_ref = nil)
|
||||
stats.each do |field, value|
|
||||
db.hincrby key.to_s(depth), field, value
|
||||
db(connection_ref).hincrby key.to_s(depth), field, value
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
module Redistat
|
||||
VERSION = "0.0.4"
|
||||
end
|
||||
VERSION = "0.0.5"
|
||||
end
|
||||
@@ -24,4 +24,7 @@ Gem::Specification.new do |s|
|
||||
s.add_runtime_dependency 'redis', '>= 2.1.1'
|
||||
s.add_runtime_dependency 'system_timer', '>= 1.0.0'
|
||||
s.add_runtime_dependency 'time_ext', '>= 0.2.8'
|
||||
|
||||
s.add_development_dependency 'rspec', '>= 2.1.0'
|
||||
s.add_development_dependency 'yard', '>= 0.6.3'
|
||||
end
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe Redistat do
|
||||
include Redistat::Database
|
||||
|
||||
before(:each) do
|
||||
db.flushdb
|
||||
end
|
||||
|
||||
it "should have a valid Redis client instance" do
|
||||
db.should_not be_nil
|
||||
end
|
||||
|
||||
it "should be connected to the testing server" do
|
||||
db.client.port.should == 8379
|
||||
db.client.host.should == "127.0.0.1"
|
||||
end
|
||||
|
||||
it "should be able to set and get data" do
|
||||
db.set("hello", "world")
|
||||
db.get("hello").should == "world"
|
||||
db.del("hello").should be_true
|
||||
end
|
||||
|
||||
it "should be able to store hashes to Redis" do
|
||||
db.hset("key", "field", "1")
|
||||
db.hget("key", "field").should == "1"
|
||||
db.hincrby("key", "field", 1)
|
||||
db.hget("key", "field").should == "2"
|
||||
db.hincrby("key", "field", -1)
|
||||
db.hget("key", "field").should == "1"
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,7 +2,7 @@ require "spec_helper"
|
||||
|
||||
describe Redistat::Collection do
|
||||
|
||||
it "should should initialize properly" do
|
||||
it "should initialize properly" do
|
||||
options = {:from => "from", :till => "till", :depth => "depth"}
|
||||
result = Redistat::Collection.new(options)
|
||||
result.from.should == options[:from]
|
||||
@@ -10,4 +10,11 @@ describe Redistat::Collection do
|
||||
result.depth.should == options[:depth]
|
||||
end
|
||||
|
||||
it "should have a total property" do
|
||||
col = Redistat::Collection.new()
|
||||
col.total.should == {}
|
||||
col.total = {:foo => "bar"}
|
||||
col.total.should == {:foo => "bar"}
|
||||
end
|
||||
|
||||
end
|
||||
61
spec/connection_spec.rb
Normal file
61
spec/connection_spec.rb
Normal file
@@ -0,0 +1,61 @@
|
||||
require "spec_helper"
|
||||
include Redistat
|
||||
|
||||
describe Redistat::Connection do
|
||||
|
||||
it "should have a valid Redis client instance" do
|
||||
Redistat.redis.should_not be_nil
|
||||
end
|
||||
|
||||
it "should have initialized custom testing connection" do
|
||||
redis = Redistat.redis
|
||||
redis.client.host.should == '127.0.0.1'
|
||||
redis.client.port.should == 8379
|
||||
redis.client.db.should == 15
|
||||
end
|
||||
|
||||
it "should be able to set and get data" do
|
||||
redis = Redistat.redis
|
||||
redis.set("hello", "world")
|
||||
redis.get("hello").should == "world"
|
||||
redis.del("hello").should be_true
|
||||
end
|
||||
|
||||
it "should be able to store hashes to Redis" do
|
||||
redis = Redistat.redis
|
||||
redis.hset("hash", "field", "1")
|
||||
redis.hget("hash", "field").should == "1"
|
||||
redis.hincrby("hash", "field", 1)
|
||||
redis.hget("hash", "field").should == "2"
|
||||
redis.hincrby("hash", "field", -1)
|
||||
redis.hget("hash", "field").should == "1"
|
||||
redis.del("hash")
|
||||
end
|
||||
|
||||
it "should be accessible from Redistat module" do
|
||||
Redistat.redis.should == Connection.get
|
||||
Redistat.redis.should == Redistat.connection
|
||||
end
|
||||
|
||||
it "should handle multiple connections with refs" do
|
||||
Redistat.redis.client.db.should == 15
|
||||
Redistat.connect(:port => 8379, :db => 14, :ref => "Custom")
|
||||
Redistat.redis.client.db.should == 15
|
||||
Redistat.redis("Custom").client.db.should == 14
|
||||
end
|
||||
|
||||
it "should be able to overwrite default and custom refs" do
|
||||
Redistat.redis.client.db.should == 15
|
||||
Redistat.connect(:port => 8379, :db => 14)
|
||||
Redistat.redis.client.db.should == 14
|
||||
|
||||
Redistat.redis("Custom").client.db.should == 14
|
||||
Redistat.connect(:port => 8379, :db => 15, :ref => "Custom")
|
||||
Redistat.redis("Custom").client.db.should == 15
|
||||
|
||||
# Reset the default connection to the testing server or all hell
|
||||
# might brake loose from the rest of the specs
|
||||
Redistat.connect(:port => 8379, :db => 15)
|
||||
end
|
||||
|
||||
end
|
||||
10
spec/database_spec.rb
Normal file
10
spec/database_spec.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
require "spec_helper"
|
||||
|
||||
describe Redistat::Database do
|
||||
include Redistat::Database
|
||||
|
||||
it "should make #db method available when included" do
|
||||
db.should == Redistat.redis
|
||||
end
|
||||
|
||||
end
|
||||
@@ -1,6 +1,6 @@
|
||||
require "redistat"
|
||||
|
||||
class ModelHelper
|
||||
class ModelHelper1
|
||||
include Redistat::Model
|
||||
|
||||
|
||||
@@ -13,4 +13,11 @@ class ModelHelper2
|
||||
store_event true
|
||||
hashed_label true
|
||||
|
||||
end
|
||||
|
||||
class ModelHelper3
|
||||
include Redistat::Model
|
||||
|
||||
connect_to :port => 8379, :db => 14
|
||||
|
||||
end
|
||||
@@ -5,11 +5,13 @@ describe Redistat::Model do
|
||||
include Redistat::Database
|
||||
|
||||
before(:each) do
|
||||
db.flushdb
|
||||
ModelHelper1.redis.flushdb
|
||||
ModelHelper2.redis.flushdb
|
||||
ModelHelper3.redis.flushdb
|
||||
end
|
||||
|
||||
it "should should name itself correctly" do
|
||||
ModelHelper.send(:name).should == "ModelHelper"
|
||||
ModelHelper1.send(:name).should == "ModelHelper1"
|
||||
ModelHelper2.send(:name).should == "ModelHelper2"
|
||||
end
|
||||
|
||||
@@ -18,49 +20,81 @@ describe Redistat::Model do
|
||||
ModelHelper2.store_event.should == true
|
||||
ModelHelper2.hashed_label.should == true
|
||||
|
||||
ModelHelper.depth.should == nil
|
||||
ModelHelper.store_event.should == nil
|
||||
ModelHelper.hashed_label.should == nil
|
||||
ModelHelper.depth(:hour)
|
||||
ModelHelper.depth.should == :hour
|
||||
ModelHelper.store_event(true)
|
||||
ModelHelper.store_event.should == true
|
||||
ModelHelper.hashed_label(true)
|
||||
ModelHelper.hashed_label.should == true
|
||||
ModelHelper.options[:depth] = nil
|
||||
ModelHelper.options[:store_event] = nil
|
||||
ModelHelper.options[:hashed_label] = nil
|
||||
ModelHelper.depth.should == nil
|
||||
ModelHelper.store_event.should == nil
|
||||
ModelHelper.hashed_label.should == nil
|
||||
ModelHelper1.depth.should == nil
|
||||
ModelHelper1.store_event.should == nil
|
||||
ModelHelper1.hashed_label.should == nil
|
||||
ModelHelper1.depth(:hour)
|
||||
ModelHelper1.depth.should == :hour
|
||||
ModelHelper1.store_event(true)
|
||||
ModelHelper1.store_event.should == true
|
||||
ModelHelper1.hashed_label(true)
|
||||
ModelHelper1.hashed_label.should == true
|
||||
ModelHelper1.options[:depth] = nil
|
||||
ModelHelper1.options[:store_event] = nil
|
||||
ModelHelper1.options[:hashed_label] = nil
|
||||
ModelHelper1.depth.should == nil
|
||||
ModelHelper1.store_event.should == nil
|
||||
ModelHelper1.hashed_label.should == nil
|
||||
end
|
||||
|
||||
it "should store and fetch stats" do
|
||||
ModelHelper.store("sheep.black", {:count => 6, :weight => 461}, 4.hours.ago)
|
||||
ModelHelper.store("sheep.black", {:count => 2, :weight => 156})
|
||||
ModelHelper1.store("sheep.black", {:count => 6, :weight => 461}, 4.hours.ago)
|
||||
ModelHelper1.store("sheep.black", {:count => 2, :weight => 156})
|
||||
|
||||
stats = ModelHelper.fetch("sheep.black", 2.hours.ago, 1.hour.from_now)
|
||||
stats = ModelHelper1.fetch("sheep.black", 2.hours.ago, 1.hour.from_now)
|
||||
stats.total["count"].should == 2
|
||||
stats.total["weight"].should == 156
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper.fetch("sheep.black", 5.hours.ago, 1.hour.from_now)
|
||||
stats = ModelHelper1.fetch("sheep.black", 5.hours.ago, 1.hour.from_now)
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
stats.first.should == stats.total
|
||||
|
||||
ModelHelper.store("sheep.white", {:count => 5, :weight => 393}, 4.hours.ago)
|
||||
ModelHelper.store("sheep.white", {:count => 4, :weight => 316})
|
||||
ModelHelper1.store("sheep.white", {:count => 5, :weight => 393}, 4.hours.ago)
|
||||
ModelHelper1.store("sheep.white", {:count => 4, :weight => 316})
|
||||
|
||||
stats = ModelHelper.fetch("sheep.white", 2.hours.ago, 1.hour.from_now)
|
||||
stats = ModelHelper1.fetch("sheep.white", 2.hours.ago, 1.hour.from_now)
|
||||
stats.total[:count].should == 4
|
||||
stats.total[:weight].should == 316
|
||||
stats.first.should == stats.total
|
||||
|
||||
stats = ModelHelper.fetch("sheep.white", 5.hours.ago, 1.hour.from_now)
|
||||
stats = ModelHelper1.fetch("sheep.white", 5.hours.ago, 1.hour.from_now)
|
||||
stats.total[:count].should == 9
|
||||
stats.total[:weight].should == 709
|
||||
stats.first.should == stats.total
|
||||
end
|
||||
|
||||
end
|
||||
it "should connect to different Redis servers on a per-model basis" do
|
||||
ModelHelper3.redis.client.db.should == 14
|
||||
|
||||
ModelHelper3.store("sheep.black", {:count => 6, :weight => 461}, 4.hours.ago)
|
||||
ModelHelper3.store("sheep.black", {:count => 2, :weight => 156})
|
||||
|
||||
db.keys("*").should be_empty
|
||||
ModelHelper1.redis.keys("*").should be_empty
|
||||
db("ModelHelper3").keys("*").should have(5).items
|
||||
ModelHelper3.redis.keys("*").should have(5).items
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", 2.hours.ago, 1.hour.from_now)
|
||||
stats.total["count"].should == 2
|
||||
stats.total["weight"].should == 156
|
||||
stats = ModelHelper3.fetch("sheep.black", 5.hours.ago, 1.hour.from_now)
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
|
||||
ModelHelper3.connect_to(:port => 8379, :db => 13)
|
||||
ModelHelper3.redis.client.db.should == 13
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", 5.hours.ago, 1.hour.from_now)
|
||||
stats.total.should == {}
|
||||
|
||||
ModelHelper3.connect_to(:port => 8379, :db => 14)
|
||||
ModelHelper3.redis.client.db.should == 14
|
||||
|
||||
stats = ModelHelper3.fetch("sheep.black", 5.hours.ago, 1.hour.from_now)
|
||||
stats.total[:count].should == 8
|
||||
stats.total[:weight].should == 617
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -8,5 +8,5 @@ require 'rspec'
|
||||
require 'rspec/autorun'
|
||||
|
||||
# use the test Redistat instance
|
||||
Redistat.connect({:port => 8379, :db => 15})
|
||||
Redistat.flush
|
||||
Redistat.connect(:port => 8379, :db => 15)
|
||||
Redistat.redis.flushdb
|
||||
|
||||
Reference in New Issue
Block a user