mirror of
https://github.com/jimeh/redistat.git
synced 2026-02-19 13:26:39 +00:00
Compare commits
41 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3267ee1eb9 | |||
| 6309e4b217 | |||
| 776ee8ac97 | |||
| 3b346e88e0 | |||
| c3fe861b10 | |||
| bc5034b6bb | |||
| 66510fe344 | |||
| 745473862f | |||
| a5c8fc6fbf | |||
| 115b223d7c | |||
| 0597b587fd | |||
| 89932759ef | |||
| 55e0687837 | |||
|
|
93360dbeb9 | ||
|
|
6a66605e0b | ||
|
|
d9ce0daade | ||
| f6ec2e97b2 | |||
| 67dc9433c7 | |||
| f0fcd2110d | |||
| 24112e4705 | |||
| b9752ff92f | |||
| 14a093d79b | |||
| 84a05363dd | |||
| 690d1d9407 | |||
| 2aedd4eee3 | |||
| f906cf068e | |||
| cbb9050c80 | |||
| 58a2fb560c | |||
| 6bae8ce2bc | |||
| 18e6125c6a | |||
| dc162e0c89 | |||
| 5338676a5f | |||
| 02fe41082a | |||
| 65e7745419 | |||
| 490356ee96 | |||
| a6c4600aa5 | |||
| 85ba61b2cc | |||
| 8f6a4a6820 | |||
| 81ee2ec0b6 | |||
| 20280f2c5d | |||
| bf29696c46 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -16,11 +16,11 @@ tmtags
|
|||||||
## PROJECT::GENERAL
|
## PROJECT::GENERAL
|
||||||
coverage
|
coverage
|
||||||
rdoc
|
rdoc
|
||||||
pkg
|
pkg/*
|
||||||
|
*.gem
|
||||||
|
.bundle
|
||||||
|
|
||||||
## PROJECT::SPECIFIC
|
## PROJECT::SPECIFIC
|
||||||
.bundle/*
|
|
||||||
.yardoc/*
|
.yardoc/*
|
||||||
spec/db/*
|
spec/db/*
|
||||||
doc
|
doc/*
|
||||||
redistat.gemspec
|
|
||||||
|
|||||||
14
Gemfile
14
Gemfile
@@ -1,14 +1,4 @@
|
|||||||
source 'http://rubygems.org/'
|
source 'http://rubygems.org/'
|
||||||
|
|
||||||
gem 'activesupport', '>= 2.3.0'
|
# Specify your gem's dependencies in redistat.gemspec
|
||||||
gem 'json', '>= 1.4.6'
|
gemspec
|
||||||
gem 'redis', '>= 2.1.1'
|
|
||||||
gem 'system_timer', '>= 1.0.0'
|
|
||||||
gem 'time_ext', '>= 0.2.6'
|
|
||||||
|
|
||||||
group :development do
|
|
||||||
gem 'jeweler', '>= 1.5.1'
|
|
||||||
gem 'rspec', '>= 2.1.0'
|
|
||||||
gem 'yard', '>= 0.6.3'
|
|
||||||
gem 'i18n'
|
|
||||||
end
|
|
||||||
|
|||||||
29
Gemfile.lock
29
Gemfile.lock
@@ -1,16 +1,19 @@
|
|||||||
|
PATH
|
||||||
|
remote: .
|
||||||
|
specs:
|
||||||
|
redistat (0.0.8)
|
||||||
|
activesupport (>= 2.3.0)
|
||||||
|
json (>= 1.4.0)
|
||||||
|
redis (>= 2.1.0)
|
||||||
|
time_ext (>= 0.2.8)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: http://rubygems.org/
|
remote: http://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
activesupport (3.0.3)
|
activesupport (3.0.3)
|
||||||
diff-lcs (1.1.2)
|
diff-lcs (1.1.2)
|
||||||
git (1.2.5)
|
|
||||||
i18n (0.4.2)
|
i18n (0.4.2)
|
||||||
jeweler (1.5.1)
|
|
||||||
bundler (~> 1.0.0)
|
|
||||||
git (>= 1.2.5)
|
|
||||||
rake
|
|
||||||
json (1.4.6)
|
json (1.4.6)
|
||||||
rake (0.8.7)
|
|
||||||
redis (2.1.1)
|
redis (2.1.1)
|
||||||
rspec (2.1.0)
|
rspec (2.1.0)
|
||||||
rspec-core (~> 2.1.0)
|
rspec-core (~> 2.1.0)
|
||||||
@@ -20,9 +23,9 @@ GEM
|
|||||||
rspec-expectations (2.1.0)
|
rspec-expectations (2.1.0)
|
||||||
diff-lcs (~> 1.1.2)
|
diff-lcs (~> 1.1.2)
|
||||||
rspec-mocks (2.1.0)
|
rspec-mocks (2.1.0)
|
||||||
system_timer (1.0)
|
time_ext (0.2.8)
|
||||||
time_ext (0.2.6)
|
|
||||||
activesupport (>= 2.3.0)
|
activesupport (>= 2.3.0)
|
||||||
|
i18n (>= 0.4.2)
|
||||||
yard (0.6.3)
|
yard (0.6.3)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
@@ -30,11 +33,9 @@ PLATFORMS
|
|||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
activesupport (>= 2.3.0)
|
activesupport (>= 2.3.0)
|
||||||
i18n
|
json (>= 1.4.0)
|
||||||
jeweler (>= 1.5.1)
|
redis (>= 2.1.0)
|
||||||
json (>= 1.4.6)
|
redistat!
|
||||||
redis (>= 2.1.1)
|
|
||||||
rspec (>= 2.1.0)
|
rspec (>= 2.1.0)
|
||||||
system_timer (>= 1.0.0)
|
time_ext (>= 0.2.8)
|
||||||
time_ext (>= 0.2.6)
|
|
||||||
yard (>= 0.6.3)
|
yard (>= 0.6.3)
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ Redis fits perfectly with all of these requirements. It has atomic operations li
|
|||||||
|
|
||||||
gem install redistat
|
gem install redistat
|
||||||
|
|
||||||
|
If you are using Ruby 1.8.x, it's recommended you also install the `system_timer` gem, as the Redis gem will otherwise complain.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
The simplest way to use Redistat is through the model wrapper.
|
The simplest way to use Redistat is through the model wrapper.
|
||||||
|
|||||||
51
Rakefile
51
Rakefile
@@ -1,31 +1,11 @@
|
|||||||
require 'rubygems'
|
require 'bundler'
|
||||||
require 'rake'
|
Bundler::GemHelper.install_tasks
|
||||||
|
|
||||||
begin
|
|
||||||
require 'jeweler'
|
|
||||||
Jeweler::Tasks.new do |gem|
|
|
||||||
gem.name = 'redistat'
|
|
||||||
gem.summary = 'A Redis-backed statistics storage and querying library written in Ruby.'
|
|
||||||
gem.description = 'A Redis-backed statistics storage and querying library written in Ruby.'
|
|
||||||
gem.email = 'contact@jimeh.me'
|
|
||||||
gem.homepage = 'http://github.com/jimeh/redistat'
|
|
||||||
gem.authors = ['Jim Myhrberg']
|
|
||||||
gem.add_dependency 'activesupport', '>= 2.3.0'
|
|
||||||
gem.add_dependency 'json', '>= 1.4.6'
|
|
||||||
gem.add_dependency 'redis', '>= 2.1.1'
|
|
||||||
gem.add_dependency 'system_timer', '>= 1.0.0'
|
|
||||||
gem.add_dependency 'time_ext', '>= 0.2.6'
|
|
||||||
gem.add_development_dependency 'jeweler', '>= 1.5.1'
|
|
||||||
gem.add_development_dependency 'rspec', '>= 2.1.0'
|
|
||||||
gem.add_development_dependency 'yard', '>= 0.6.1'
|
|
||||||
end
|
|
||||||
Jeweler::GemcutterTasks.new
|
|
||||||
rescue LoadError
|
|
||||||
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
# Rspec
|
# Rspec
|
||||||
|
#
|
||||||
|
|
||||||
require 'rspec/core/rake_task'
|
require 'rspec/core/rake_task'
|
||||||
RSpec::Core::RakeTask.new(:spec) do |spec|
|
RSpec::Core::RakeTask.new(:spec) do |spec|
|
||||||
spec.pattern = 'spec/**/*_spec.rb'
|
spec.pattern = 'spec/**/*_spec.rb'
|
||||||
@@ -36,12 +16,13 @@ RSpec::Core::RakeTask.new(:rcov) do |spec|
|
|||||||
spec.rcov = true
|
spec.rcov = true
|
||||||
end
|
end
|
||||||
|
|
||||||
task :spec => :check_dependencies
|
|
||||||
|
|
||||||
task :default => [:start, :spec, :stop]
|
task :default => [:start, :spec, :stop]
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
# Start/stop Redis test server
|
# Start/stop Redis test server
|
||||||
|
#
|
||||||
|
|
||||||
REDIS_DIR = File.expand_path(File.join("..", "spec"), __FILE__)
|
REDIS_DIR = File.expand_path(File.join("..", "spec"), __FILE__)
|
||||||
REDIS_CNF = File.join(REDIS_DIR, "redis-test.conf")
|
REDIS_CNF = File.join(REDIS_DIR, "redis-test.conf")
|
||||||
REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
|
REDIS_PID = File.join(REDIS_DIR, "db", "redis.pid")
|
||||||
@@ -62,7 +43,10 @@ task :stop do
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# YARD Documentation
|
#
|
||||||
|
# Yard
|
||||||
|
#
|
||||||
|
|
||||||
begin
|
begin
|
||||||
require 'yard'
|
require 'yard'
|
||||||
YARD::Rake::YardocTask.new
|
YARD::Rake::YardocTask.new
|
||||||
@@ -71,3 +55,14 @@ rescue LoadError
|
|||||||
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Misc.
|
||||||
|
#
|
||||||
|
|
||||||
|
desc "Start an irb console with Redistat pre-loaded."
|
||||||
|
task :console do
|
||||||
|
exec "irb -r spec/spec_helper"
|
||||||
|
end
|
||||||
|
task :c => :console
|
||||||
|
|||||||
@@ -10,8 +10,10 @@ require 'json'
|
|||||||
require 'digest/sha1'
|
require 'digest/sha1'
|
||||||
|
|
||||||
require 'redistat/collection'
|
require 'redistat/collection'
|
||||||
|
require 'redistat/connection'
|
||||||
require 'redistat/database'
|
require 'redistat/database'
|
||||||
require 'redistat/date'
|
require 'redistat/date'
|
||||||
|
require 'redistat/date_helper'
|
||||||
require 'redistat/event'
|
require 'redistat/event'
|
||||||
require 'redistat/finder'
|
require 'redistat/finder'
|
||||||
require 'redistat/finder/date_set'
|
require 'redistat/finder/date_set'
|
||||||
@@ -21,10 +23,12 @@ require 'redistat/model'
|
|||||||
require 'redistat/result'
|
require 'redistat/result'
|
||||||
require 'redistat/scope'
|
require 'redistat/scope'
|
||||||
require 'redistat/summary'
|
require 'redistat/summary'
|
||||||
|
require 'redistat/version'
|
||||||
|
|
||||||
require 'redistat/core_ext/date'
|
require 'redistat/core_ext/date'
|
||||||
require 'redistat/core_ext/time'
|
require 'redistat/core_ext/time'
|
||||||
require 'redistat/core_ext/fixnum'
|
require 'redistat/core_ext/fixnum'
|
||||||
|
require 'redistat/core_ext/bignum'
|
||||||
|
|
||||||
module Redistat
|
module Redistat
|
||||||
|
|
||||||
@@ -34,51 +38,28 @@ module Redistat
|
|||||||
KEY_EVENT_IDS = ".event_ids"
|
KEY_EVENT_IDS = ".event_ids"
|
||||||
|
|
||||||
class InvalidOptions < ArgumentError; end
|
class InvalidOptions < ArgumentError; end
|
||||||
|
class RedisServerIsTooOld < Exception; 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
|
|
||||||
|
|
||||||
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.flushdb instead."
|
||||||
|
connection.flushdb
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -12,5 +12,9 @@ module Redistat
|
|||||||
@depth = options[:depth] ||= nil
|
@depth = options[:depth] ||= nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def total
|
||||||
|
@total ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
64
lib/redistat/connection.rb
Normal file
64
lib/redistat/connection.rb
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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 = {})
|
||||||
|
#TODO clean/remove all ref-less connections
|
||||||
|
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
|
||||||
|
@connections ||= {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def references
|
||||||
|
@references ||= {}
|
||||||
|
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
|
||||||
8
lib/redistat/core_ext/bignum.rb
Executable file
8
lib/redistat/core_ext/bignum.rb
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
class Bignum
|
||||||
|
include Redistat::DateHelper
|
||||||
|
|
||||||
|
def to_time
|
||||||
|
Time.at(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
@@ -3,8 +3,8 @@ module Redistat
|
|||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.extend(Database)
|
base.extend(Database)
|
||||||
end
|
end
|
||||||
def db
|
def db(ref = nil)
|
||||||
Redistat.redis
|
Redistat.connection(ref)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -22,6 +22,8 @@ module Redistat
|
|||||||
from_string(input)
|
from_string(input)
|
||||||
elsif input.is_a?(::Fixnum)
|
elsif input.is_a?(::Fixnum)
|
||||||
from_integer(input)
|
from_integer(input)
|
||||||
|
elsif input.is_a?(::Bignum)
|
||||||
|
from_integer(input)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -83,11 +85,4 @@ module Redistat
|
|||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
module DateHelper
|
|
||||||
def to_redistat(depth = nil)
|
|
||||||
Redistat::Date.new(self, depth)
|
|
||||||
end
|
|
||||||
alias :to_rs :to_redistat
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|||||||
8
lib/redistat/date_helper.rb
Normal file
8
lib/redistat/date_helper.rb
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module Redistat
|
||||||
|
module DateHelper
|
||||||
|
def to_redistat(depth = nil)
|
||||||
|
Redistat::Date.new(self, depth)
|
||||||
|
end
|
||||||
|
alias :to_rs :to_redistat
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -4,21 +4,34 @@ module Redistat
|
|||||||
|
|
||||||
attr_reader :id
|
attr_reader :id
|
||||||
attr_reader :key
|
attr_reader :key
|
||||||
|
attr_reader :connection_ref
|
||||||
|
|
||||||
attr_accessor :stats
|
attr_accessor :stats
|
||||||
attr_accessor :meta
|
attr_accessor :meta
|
||||||
attr_accessor :options
|
attr_accessor :options
|
||||||
|
|
||||||
def initialize(scope, label = nil, date = nil, stats = {}, options = {}, meta = {}, is_new = true)
|
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)
|
@key = Key.new(scope, label, date, @options)
|
||||||
@stats = stats ||= {}
|
@stats = stats ||= {}
|
||||||
@meta = meta ||= {}
|
@meta = meta ||= {}
|
||||||
@new = is_new
|
@new = is_new
|
||||||
end
|
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
|
def default_options
|
||||||
{ :depth => :hour, :store_event => false }
|
{ :depth => :hour, :store_event => false, :connection_ref => nil }
|
||||||
end
|
end
|
||||||
|
|
||||||
def new?
|
def new?
|
||||||
@@ -59,7 +72,7 @@ module Redistat
|
|||||||
|
|
||||||
def save
|
def save
|
||||||
return false if !self.new?
|
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]
|
if @options[:store_event]
|
||||||
@id = self.next_id
|
@id = self.next_id
|
||||||
db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
|
db.hmset("#{self.scope}#{KEY_EVENT}#{@id}",
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ module Redistat
|
|||||||
@options = options
|
@options = options
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def db
|
||||||
|
super(@options[:connection_ref])
|
||||||
|
end
|
||||||
|
|
||||||
def valid_options?
|
def valid_options?
|
||||||
return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
|
return true if !@options[:scope].blank? && !@options[:label].blank? && !@options[:from].blank? && !@options[:till].blank?
|
||||||
false
|
false
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ module Redistat
|
|||||||
return find_start_year_for(start_date, end_date, lowest_depth) if unit == :year
|
return find_start_year_for(start_date, end_date, lowest_depth) if unit == :year
|
||||||
index = Date::DEPTHS.index(unit)
|
index = Date::DEPTHS.index(unit)
|
||||||
nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
|
nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
|
||||||
if start_date < start_date.round(nunit) || start_date.next(nunit).beginning_of(nunit) > end_date.beginning_of(nunit)
|
if start_date < start_date.beginning_of_closest(nunit) || start_date.next(nunit).beginning_of(nunit) > end_date.beginning_of(nunit)
|
||||||
add = []
|
add = []
|
||||||
start_date.beginning_of_each(unit, :include_start => lowest_depth).until(start_date.end_of(nunit)) do |t|
|
start_date.beginning_of_each(unit, :include_start => lowest_depth).until(start_date.end_of(nunit)) do |t|
|
||||||
add << t.to_rs.to_s(unit) if t < end_date.beginning_of(unit)
|
add << t.to_rs.to_s(unit) if t < end_date.beginning_of(unit)
|
||||||
@@ -59,7 +59,7 @@ module Redistat
|
|||||||
index = Date::DEPTHS.index(unit)
|
index = Date::DEPTHS.index(unit)
|
||||||
nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
|
nunit = Date::DEPTHS[(index > 0) ? index-1 : index]
|
||||||
has_nunit = end_date.prev(nunit).beginning_of(nunit) >= start_date.beginning_of(nunit)
|
has_nunit = end_date.prev(nunit).beginning_of(nunit) >= start_date.beginning_of(nunit)
|
||||||
nearest_nunit = end_date.round(nunit)
|
nearest_nunit = end_date.beginning_of_closest(nunit)
|
||||||
if end_date >= nearest_nunit && has_nunit
|
if end_date >= nearest_nunit && has_nunit
|
||||||
add = []
|
add = []
|
||||||
end_date.beginning_of(nunit).beginning_of_each(unit, :include_start => true, :include_end => lowest_depth).until(end_date) do |t|
|
end_date.beginning_of(nunit).beginning_of_each(unit, :include_start => true, :include_end => lowest_depth).until(end_date) do |t|
|
||||||
|
|||||||
@@ -3,11 +3,16 @@ module Redistat
|
|||||||
include Database
|
include Database
|
||||||
|
|
||||||
attr_reader :raw
|
attr_reader :raw
|
||||||
|
attr_reader :connection_ref
|
||||||
|
|
||||||
def initialize(str, options = {})
|
def initialize(str, options = {})
|
||||||
@options = options
|
@options = options
|
||||||
@raw = str.to_s
|
@raw = str.to_s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def db
|
||||||
|
super(@options[:connection_ref])
|
||||||
|
end
|
||||||
|
|
||||||
def name
|
def name
|
||||||
@options[:hashed_label] ? hash : @raw
|
@options[:hashed_label] ? hash : @raw
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
module Redistat
|
module Redistat
|
||||||
module Model
|
module Model
|
||||||
|
include Redistat::Database
|
||||||
|
|
||||||
def self.included(base)
|
def self.included(base)
|
||||||
base.extend(self)
|
base.extend(self)
|
||||||
@@ -10,6 +11,16 @@ module Redistat
|
|||||||
end
|
end
|
||||||
alias :event :store
|
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 = {})
|
def fetch(label, from, till, opts = {})
|
||||||
Finder.find({
|
Finder.find({
|
||||||
:scope => name,
|
:scope => name,
|
||||||
@@ -28,6 +39,15 @@ module Redistat
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def class_name(class_name = nil)
|
||||||
|
if !class_name.nil?
|
||||||
|
options[:class_name] = class_name
|
||||||
|
else
|
||||||
|
options[:class_name] || nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
alias :scope :class_name
|
||||||
|
|
||||||
def depth(depth = nil)
|
def depth(depth = nil)
|
||||||
if !depth.nil?
|
if !depth.nil?
|
||||||
options[:depth] = depth
|
options[:depth] = depth
|
||||||
@@ -51,7 +71,7 @@ module Redistat
|
|||||||
private
|
private
|
||||||
|
|
||||||
def name
|
def name
|
||||||
@name ||= self.to_s
|
options[:class_name] || (@name ||= self.to_s)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -2,21 +2,21 @@ module Redistat
|
|||||||
class Summary
|
class Summary
|
||||||
include Database
|
include Database
|
||||||
|
|
||||||
def self.update_all(key, stats = {}, depth_limit = nil)
|
def self.update_all(key, stats = {}, depth_limit = nil, connection_ref = nil)
|
||||||
stats ||= {}
|
stats ||= {}
|
||||||
depth_limit ||= key.depth
|
depth_limit ||= key.depth
|
||||||
return nil if stats.size == 0
|
return nil if stats.size == 0
|
||||||
Date::DEPTHS.each do |depth|
|
Date::DEPTHS.each do |depth|
|
||||||
update(key, stats, depth)
|
update(key, stats, depth, connection_ref)
|
||||||
break if depth == depth_limit
|
break if depth == depth_limit
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def self.update(key, stats, depth)
|
def self.update(key, stats, depth, connection_ref = nil)
|
||||||
stats.each do |field, value|
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
3
lib/redistat/version.rb
Normal file
3
lib/redistat/version.rb
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module Redistat
|
||||||
|
VERSION = "0.0.8"
|
||||||
|
end
|
||||||
29
redistat.gemspec
Normal file
29
redistat.gemspec
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
# -*- encoding: utf-8 -*-
|
||||||
|
$:.push File.expand_path("../lib", __FILE__)
|
||||||
|
require "redistat/version"
|
||||||
|
|
||||||
|
Gem::Specification.new do |s|
|
||||||
|
s.name = "redistat"
|
||||||
|
s.version = Redistat::VERSION
|
||||||
|
s.platform = Gem::Platform::RUBY
|
||||||
|
s.authors = ["Jim Myhrberg"]
|
||||||
|
s.email = ["contact@jimeh.me"]
|
||||||
|
s.homepage = "http://github.com/jimeh/redistat"
|
||||||
|
s.summary = %q{A Redis-backed statistics storage and querying library written in Ruby.}
|
||||||
|
s.description = %q{A Redis-backed statistics storage and querying library written in Ruby.}
|
||||||
|
|
||||||
|
s.rubyforge_project = "redistat"
|
||||||
|
|
||||||
|
s.files = `git ls-files`.split("\n")
|
||||||
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
||||||
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
||||||
|
s.require_paths = ["lib"]
|
||||||
|
|
||||||
|
s.add_runtime_dependency 'activesupport', '>= 2.3.0'
|
||||||
|
s.add_runtime_dependency 'json', '>= 1.4.0'
|
||||||
|
s.add_runtime_dependency 'redis', '>= 2.1.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
|
describe Redistat::Collection do
|
||||||
|
|
||||||
it "should should initialize properly" do
|
it "should initialize properly" do
|
||||||
options = {:from => "from", :till => "till", :depth => "depth"}
|
options = {:from => "from", :till => "till", :depth => "depth"}
|
||||||
result = Redistat::Collection.new(options)
|
result = Redistat::Collection.new(options)
|
||||||
result.from.should == options[:from]
|
result.from.should == options[:from]
|
||||||
@@ -10,4 +10,11 @@ describe Redistat::Collection do
|
|||||||
result.depth.should == options[:depth]
|
result.depth.should == options[:depth]
|
||||||
end
|
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
|
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
|
||||||
@@ -20,7 +20,7 @@ describe Redistat::Event do
|
|||||||
@event.scope.should == @scope
|
@event.scope.should == @scope
|
||||||
@event.label.should == @label
|
@event.label.should == @label
|
||||||
@event.label_hash.should == @label_hash
|
@event.label_hash.should == @label_hash
|
||||||
@event.date.to_time.should == @date
|
@event.date.to_time.to_s.should == @date.to_s
|
||||||
@event.stats.should == @stats
|
@event.stats.should == @stats
|
||||||
@event.meta.should == @meta
|
@event.meta.should == @meta
|
||||||
@event.options.should == @event.default_options.merge(@options)
|
@event.options.should == @event.default_options.merge(@options)
|
||||||
@@ -28,10 +28,10 @@ describe Redistat::Event do
|
|||||||
|
|
||||||
it "should allow changing attributes" do
|
it "should allow changing attributes" do
|
||||||
# date
|
# date
|
||||||
@event.date.to_time.should == @date
|
@event.date.to_time.to_s.should == @date.to_s
|
||||||
@date = Time.now
|
@date = Time.now
|
||||||
@event.date = @date
|
@event.date = @date
|
||||||
@event.date.to_time.should == @date
|
@event.date.to_time.to_s.should == @date.to_s
|
||||||
# label
|
# label
|
||||||
@event.label.should == @label
|
@event.label.should == @label
|
||||||
@event.label_hash.should == @label_hash
|
@event.label_hash.should == @label_hash
|
||||||
|
|||||||
@@ -46,10 +46,10 @@ describe Redistat::Key do
|
|||||||
@key.scope = @scope
|
@key.scope = @scope
|
||||||
@key.scope.should == @scope
|
@key.scope.should == @scope
|
||||||
# date
|
# date
|
||||||
@key.date.to_time.should == @date
|
@key.date.to_time.to_s.should == @date.to_s
|
||||||
@date = Time.now
|
@date = Time.now
|
||||||
@key.date = @date
|
@key.date = @date
|
||||||
@key.date.to_time.should == @date
|
@key.date.to_time.to_s.should == @date.to_s
|
||||||
# label
|
# label
|
||||||
@key.label.should == @label
|
@key.label.should == @label
|
||||||
@key.label_hash == @label_hash
|
@key.label_hash == @label_hash
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
require "redistat"
|
require "redistat"
|
||||||
|
|
||||||
class ModelHelper
|
class ModelHelper1
|
||||||
include Redistat::Model
|
include Redistat::Model
|
||||||
|
|
||||||
|
|
||||||
@@ -13,4 +13,18 @@ class ModelHelper2
|
|||||||
store_event true
|
store_event true
|
||||||
hashed_label true
|
hashed_label true
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class ModelHelper3
|
||||||
|
include Redistat::Model
|
||||||
|
|
||||||
|
connect_to :port => 8379, :db => 14
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
class ModelHelper4
|
||||||
|
include Redistat::Model
|
||||||
|
|
||||||
|
class_name "FancyHelper"
|
||||||
|
|
||||||
end
|
end
|
||||||
@@ -5,11 +5,14 @@ describe Redistat::Model do
|
|||||||
include Redistat::Database
|
include Redistat::Database
|
||||||
|
|
||||||
before(:each) do
|
before(:each) do
|
||||||
db.flushdb
|
ModelHelper1.redis.flushdb
|
||||||
|
ModelHelper2.redis.flushdb
|
||||||
|
ModelHelper3.redis.flushdb
|
||||||
|
ModelHelper4.redis.flushdb
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should should name itself correctly" do
|
it "should should name itself correctly" do
|
||||||
ModelHelper.send(:name).should == "ModelHelper"
|
ModelHelper1.send(:name).should == "ModelHelper1"
|
||||||
ModelHelper2.send(:name).should == "ModelHelper2"
|
ModelHelper2.send(:name).should == "ModelHelper2"
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -17,50 +20,86 @@ describe Redistat::Model do
|
|||||||
ModelHelper2.depth.should == :day
|
ModelHelper2.depth.should == :day
|
||||||
ModelHelper2.store_event.should == true
|
ModelHelper2.store_event.should == true
|
||||||
ModelHelper2.hashed_label.should == true
|
ModelHelper2.hashed_label.should == true
|
||||||
|
ModelHelper2.class_name.should be_nil
|
||||||
|
|
||||||
ModelHelper.depth.should == nil
|
ModelHelper1.depth.should == nil
|
||||||
ModelHelper.store_event.should == nil
|
ModelHelper1.store_event.should == nil
|
||||||
ModelHelper.hashed_label.should == nil
|
ModelHelper1.hashed_label.should == nil
|
||||||
ModelHelper.depth(:hour)
|
ModelHelper1.depth(:hour)
|
||||||
ModelHelper.depth.should == :hour
|
ModelHelper1.depth.should == :hour
|
||||||
ModelHelper.store_event(true)
|
ModelHelper1.store_event(true)
|
||||||
ModelHelper.store_event.should == true
|
ModelHelper1.store_event.should == true
|
||||||
ModelHelper.hashed_label(true)
|
ModelHelper1.hashed_label(true)
|
||||||
ModelHelper.hashed_label.should == true
|
ModelHelper1.hashed_label.should == true
|
||||||
ModelHelper.options[:depth] = nil
|
ModelHelper1.options[:depth] = nil
|
||||||
ModelHelper.options[:store_event] = nil
|
ModelHelper1.options[:store_event] = nil
|
||||||
ModelHelper.options[:hashed_label] = nil
|
ModelHelper1.options[:hashed_label] = nil
|
||||||
ModelHelper.depth.should == nil
|
ModelHelper1.depth.should == nil
|
||||||
ModelHelper.store_event.should == nil
|
ModelHelper1.store_event.should == nil
|
||||||
ModelHelper.hashed_label.should == nil
|
ModelHelper1.hashed_label.should == nil
|
||||||
|
|
||||||
|
ModelHelper4.class_name.should == "FancyHelper"
|
||||||
|
ModelHelper4.send(:name).should == "FancyHelper"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "should store and fetch stats" do
|
it "should store and fetch stats" do
|
||||||
ModelHelper.store("sheep.black", {:count => 6, :weight => 461}, 4.hours.ago)
|
ModelHelper1.store("sheep.black", {:count => 6, :weight => 461}, 4.hours.ago)
|
||||||
ModelHelper.store("sheep.black", {:count => 2, :weight => 156})
|
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["count"].should == 2
|
||||||
stats.total["weight"].should == 156
|
stats.total["weight"].should == 156
|
||||||
stats.first.should == stats.total
|
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[:count].should == 8
|
||||||
stats.total[:weight].should == 617
|
stats.total[:weight].should == 617
|
||||||
stats.first.should == stats.total
|
stats.first.should == stats.total
|
||||||
|
|
||||||
ModelHelper.store("sheep.white", {:count => 5, :weight => 393}, 4.hours.ago)
|
ModelHelper1.store("sheep.white", {:count => 5, :weight => 393}, 4.hours.ago)
|
||||||
ModelHelper.store("sheep.white", {:count => 4, :weight => 316})
|
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[:count].should == 4
|
||||||
stats.total[:weight].should == 316
|
stats.total[:weight].should == 316
|
||||||
stats.first.should == stats.total
|
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[:count].should == 9
|
||||||
stats.total[:weight].should == 709
|
stats.total[:weight].should == 709
|
||||||
stats.first.should == stats.total
|
stats.first.should == stats.total
|
||||||
end
|
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'
|
require 'rspec/autorun'
|
||||||
|
|
||||||
# use the test Redistat instance
|
# use the test Redistat instance
|
||||||
Redistat.connect({:port => 8379, :db => 15})
|
Redistat.connect(:port => 8379, :db => 15, :thread_safe => true)
|
||||||
Redistat.flush
|
Redistat.redis.flushdb
|
||||||
|
|||||||
39
spec/thread_safety_spec.rb
Normal file
39
spec/thread_safety_spec.rb
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
require "spec_helper"
|
||||||
|
|
||||||
|
describe "Thread-Safety" do
|
||||||
|
include Redistat::Database
|
||||||
|
|
||||||
|
before(:each) do
|
||||||
|
db.flushdb
|
||||||
|
end
|
||||||
|
|
||||||
|
#TODO should have more comprehensive thread-safe tests
|
||||||
|
|
||||||
|
it "should incr in multiple threads" do
|
||||||
|
threads = []
|
||||||
|
50.times do
|
||||||
|
threads << Thread.new {
|
||||||
|
db.incr("spec:incr")
|
||||||
|
}
|
||||||
|
end
|
||||||
|
threads.each { |t| t.join }
|
||||||
|
db.get("spec:incr").should == "50"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should store event in multiple threads" do
|
||||||
|
class ThreadSafetySpec
|
||||||
|
include Redistat::Model
|
||||||
|
end
|
||||||
|
threads = []
|
||||||
|
50.times do
|
||||||
|
threads << Thread.new {
|
||||||
|
ThreadSafetySpec.store("spec:threadsafe", {:count => 1, :rand => rand(5)})
|
||||||
|
}
|
||||||
|
end
|
||||||
|
threads.each { |t| t.join }
|
||||||
|
result = ThreadSafetySpec.fetch("spec:threadsafe", 5.hours.ago, 5.hours.from_now)
|
||||||
|
result.total[:count].should == 50
|
||||||
|
result.total[:rand].should <= 250
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user