41 Commits

Author SHA1 Message Date
3267ee1eb9 Merge branch 'release/v0.0.8' 2011-01-12 16:13:13 +00:00
6309e4b217 Version bump to 0.0.8 2011-01-12 16:12:55 +00:00
776ee8ac97 make version available in code via
Redistat::VERSION
2011-01-12 16:11:19 +00:00
3b346e88e0 moved DateHelper module to it's own files for the
sake of transparency
2011-01-12 16:10:38 +00:00
c3fe861b10 connection handling was so thread-safe that it
stopped working in newly created threads
2011-01-12 16:04:42 +00:00
bc5034b6bb Merge branch 'release/v0.0.7' into dev 2010-12-29 17:29:20 +00:00
66510fe344 Merge branch 'release/v0.0.7' 2010-12-29 17:29:13 +00:00
745473862f added note about system_timer gem to readme 2010-12-29 17:28:56 +00:00
a5c8fc6fbf Version bump to 0.0.7 2010-12-29 17:27:19 +00:00
115b223d7c added class_name option to Model warpper for
customizing the scope used in Redis keys
2010-12-29 17:26:22 +00:00
0597b587fd Merge branch 'feature/ruby19' into dev 2010-12-29 17:14:40 +00:00
89932759ef Merge branch 'master' of https://github.com/JamesHarrison/redistat into feature/ruby19 2010-12-29 16:42:50 +00:00
55e0687837 Merge branch 'dev' into feature/ruby19 2010-12-29 16:42:36 +00:00
James Harrison
93360dbeb9 Specs pass again - problem with Time.now resolution surpassing that provided by values stored in a Redistat::Date, truncated to seconds by using to_s, which is accurate enough for testing purposes 2010-12-28 23:55:39 +00:00
James Harrison
6a66605e0b Adds Ruby 1.9.2 compat (references to TimeExt#round collide with the new real Time#now, changed to TimeExt#beginning_of_closest), 3 specs failing relating to time equality testing 2010-12-28 23:46:25 +00:00
James Harrison
d9ce0daade Removes SystemTimer for Ruby 1.9.2 compat 2010-12-28 22:11:33 +00:00
f6ec2e97b2 Merge branch 'dev' of github.com:jimeh/redistat into dev 2010-12-09 22:42:55 +00:00
67dc9433c7 fixed typo in deprecation warning 2010-12-09 22:42:27 +00:00
f0fcd2110d Merge branch 'release/v0.0.6' into dev 2010-12-01 13:41:48 +00:00
24112e4705 Merge branch 'release/v0.0.6' 2010-12-01 13:41:44 +00:00
b9752ff92f Version bump to 0.0.6 2010-12-01 13:41:30 +00:00
14a093d79b updated gem dependencies to less specific versions
as older versions should work
2010-12-01 13:40:55 +00:00
84a05363dd Merge branch 'release/v0.0.5' into dev 2010-11-28 11:56:23 +00:00
690d1d9407 Merge branch 'release/v0.0.5' 2010-11-28 11:56:15 +00:00
2aedd4eee3 Version bump to 0.0.5 2010-11-28 11:55:50 +00:00
f906cf068e added a spec for Collection#total 2010-11-28 11:52:23 +00:00
cbb9050c80 fixed a typo 2010-11-28 11:51:50 +00:00
58a2fb560c Merge branch 'feature/multi-conf' into dev 2010-11-28 11:48:27 +00:00
6bae8ce2bc Updated Connection spec so the specs actually pass
without a local Redis server running on the
default port of 6379.
2010-11-28 11:48:10 +00:00
18e6125c6a Added support for connection_ref's down throughout
the code, so models can connect to specific Redis
servers.

I believe a lot of the code needs some
restructuring at some point down the line to
handle multiple connections in a cleaner way, but
for now it'll do.
2010-11-28 11:47:26 +00:00
dc162e0c89 initial work to being able to use per-model redis
configurations
2010-11-28 10:10:58 +00:00
5338676a5f moved all dependencies to gemspec 2010-11-25 22:48:57 +00:00
02fe41082a Merge branch 'release/v0.0.4' into dev 2010-11-24 13:51:13 +00:00
65e7745419 Merge branch 'release/v0.0.4' 2010-11-24 13:51:03 +00:00
490356ee96 Bumped version to 0.0.4 2010-11-24 13:50:54 +00:00
a6c4600aa5 Merge branch 'feature/new_gemspec' into dev 2010-11-24 13:49:51 +00:00
85ba61b2cc removed jeweler's VERSION file 2010-11-24 13:48:48 +00:00
8f6a4a6820 fixed a typo 2010-11-24 13:47:51 +00:00
81ee2ec0b6 added console rake task 2010-11-24 13:46:11 +00:00
20280f2c5d switched to using bundle's own gemspec format
instead of jeweler
2010-11-24 13:46:03 +00:00
bf29696c46 Merge branch 'release/v0.0.3' into dev 2010-11-24 00:38:38 +00:00
31 changed files with 450 additions and 193 deletions

8
.gitignore vendored
View File

@@ -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
View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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

View File

@@ -1 +0,0 @@
0.0.3

View File

@@ -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

View File

@@ -12,5 +12,9 @@ module Redistat
@depth = options[:depth] ||= nil @depth = options[:depth] ||= nil
end end
def total
@total ||= {}
end
end end
end end

View 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

View File

@@ -0,0 +1,8 @@
class Bignum
include Redistat::DateHelper
def to_time
Time.at(self)
end
end

View File

@@ -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

View File

@@ -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

View 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

View File

@@ -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}",

View File

@@ -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

View File

@@ -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|

View File

@@ -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

View File

@@ -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

View File

@@ -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
View File

@@ -0,0 +1,3 @@
module Redistat
VERSION = "0.0.8"
end

29
redistat.gemspec Normal file
View 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

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View 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