redistat 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. data/.travis.yml +8 -0
  2. data/README.md +219 -97
  3. data/lib/redistat.rb +13 -13
  4. data/lib/redistat/buffer.rb +27 -24
  5. data/lib/redistat/collection.rb +5 -5
  6. data/lib/redistat/connection.rb +23 -18
  7. data/lib/redistat/core_ext.rb +1 -1
  8. data/lib/redistat/core_ext/bignum.rb +2 -2
  9. data/lib/redistat/core_ext/date.rb +2 -2
  10. data/lib/redistat/core_ext/fixnum.rb +2 -2
  11. data/lib/redistat/core_ext/hash.rb +4 -4
  12. data/lib/redistat/date.rb +11 -11
  13. data/lib/redistat/event.rb +18 -18
  14. data/lib/redistat/finder.rb +39 -39
  15. data/lib/redistat/finder/date_set.rb +4 -4
  16. data/lib/redistat/key.rb +16 -16
  17. data/lib/redistat/label.rb +14 -14
  18. data/lib/redistat/mixins/database.rb +1 -1
  19. data/lib/redistat/mixins/date_helper.rb +1 -1
  20. data/lib/redistat/mixins/options.rb +8 -8
  21. data/lib/redistat/mixins/synchronize.rb +12 -11
  22. data/lib/redistat/model.rb +25 -17
  23. data/lib/redistat/result.rb +4 -4
  24. data/lib/redistat/scope.rb +5 -5
  25. data/lib/redistat/summary.rb +33 -26
  26. data/lib/redistat/version.rb +1 -1
  27. data/redistat.gemspec +4 -3
  28. data/spec/buffer_spec.rb +27 -25
  29. data/spec/collection_spec.rb +4 -4
  30. data/spec/connection_spec.rb +12 -12
  31. data/spec/core_ext/hash_spec.rb +5 -5
  32. data/spec/database_spec.rb +3 -3
  33. data/spec/date_spec.rb +15 -15
  34. data/spec/event_spec.rb +8 -8
  35. data/spec/finder/date_set_spec.rb +134 -134
  36. data/spec/finder_spec.rb +36 -36
  37. data/spec/key_spec.rb +19 -19
  38. data/spec/label_spec.rb +10 -10
  39. data/spec/model_helper.rb +10 -9
  40. data/spec/model_spec.rb +38 -41
  41. data/spec/options_spec.rb +9 -9
  42. data/spec/result_spec.rb +4 -4
  43. data/spec/scope_spec.rb +6 -6
  44. data/spec/spec_helper.rb +6 -0
  45. data/spec/summary_spec.rb +31 -24
  46. data/spec/synchronize_spec.rb +118 -57
  47. data/spec/thread_safety_spec.rb +6 -6
  48. metadata +88 -126
  49. data/.rvmrc +0 -1
@@ -5,4 +5,4 @@ module Redistat
5
5
  end
6
6
  alias :to_rs :to_redistat
7
7
  end
8
- end
8
+ end
@@ -1,10 +1,10 @@
1
1
  module Redistat
2
2
  module Options
3
-
3
+
4
4
  def self.included(base)
5
5
  base.extend(ClassMethods)
6
6
  end
7
-
7
+
8
8
  module ClassMethods
9
9
  def option_accessor(*opts)
10
10
  opts.each do |option|
@@ -18,24 +18,24 @@ module Redistat
18
18
  end
19
19
  end
20
20
  end
21
-
21
+
22
22
  def parse_options(opts)
23
23
  opts ||= {}
24
24
  @raw_options = opts
25
25
  @options = default_options.merge(opts.reject { |k,v| v.nil? })
26
26
  end
27
-
27
+
28
28
  def default_options
29
29
  {}
30
30
  end
31
-
31
+
32
32
  def options
33
33
  @options ||= {}
34
34
  end
35
-
35
+
36
36
  def raw_options
37
37
  @raw_options ||= {}
38
38
  end
39
-
39
+
40
40
  end
41
- end
41
+ end
@@ -2,42 +2,43 @@ require 'monitor'
2
2
 
3
3
  module Redistat
4
4
  module Synchronize
5
-
5
+
6
6
  class << self
7
7
  def included(base)
8
8
  base.send(:include, InstanceMethods)
9
9
  end
10
-
10
+
11
11
  def monitor
12
12
  @monitor ||= Monitor.new
13
13
  end
14
-
14
+
15
15
  def thread_safe
16
16
  monitor.synchronize do
17
- @thread_safe ||= false
17
+ return @thread_safe unless @thread_safe.nil?
18
+ @thread_safe = false
18
19
  end
19
20
  end
20
-
21
+
21
22
  def thread_safe=(value)
22
23
  monitor.synchronize do
23
24
  @thread_safe = value
24
25
  end
25
26
  end
26
27
  end # << self
27
-
28
+
28
29
  module InstanceMethods
29
30
  def thread_safe
30
31
  Synchronize.thread_safe
31
32
  end
32
-
33
+
33
34
  def thread_safe=(value)
34
35
  Synchronize.thread_safe = value
35
36
  end
36
-
37
+
37
38
  def monitor
38
39
  Synchronize.monitor
39
40
  end
40
-
41
+
41
42
  def synchronize(&block)
42
43
  if thread_safe
43
44
  monitor.synchronize(&block)
@@ -46,6 +47,6 @@ module Redistat
46
47
  end
47
48
  end
48
49
  end # InstanceMethods
49
-
50
+
50
51
  end
51
- end
52
+ end
@@ -2,16 +2,16 @@ module Redistat
2
2
  module Model
3
3
  include Database
4
4
  include Options
5
-
5
+
6
6
  def self.included(base)
7
7
  base.extend(self)
8
8
  end
9
-
10
-
9
+
10
+
11
11
  #
12
12
  # statistics store/fetch methods
13
- #
14
-
13
+ #
14
+
15
15
  def store(label, stats = {}, date = nil, opts = {}, meta = {})
16
16
  Event.new(self.name, label, date, stats, options.merge(opts), meta).save
17
17
  end
@@ -28,42 +28,50 @@ module Redistat
28
28
  :from => from,
29
29
  :till => till }.merge(options.merge(opts)) )
30
30
  end
31
-
31
+
32
32
  def find_event(event_id)
33
33
  Event.find(self.name, event_id)
34
34
  end
35
-
36
-
35
+
36
+
37
37
  #
38
38
  # options methods
39
39
  #
40
-
40
+
41
41
  option_accessor :depth
42
42
  option_accessor :scope
43
43
  option_accessor :store_event
44
44
  option_accessor :hashed_label
45
45
  option_accessor :label_indexing
46
-
46
+
47
47
  alias :class_name :scope
48
-
48
+
49
+ def expire(exp = nil)
50
+ if !exp.nil?
51
+ options[:expire] = exp.is_a?(Hash) ? exp : Hash.new(exp)
52
+ else
53
+ options[:expire]
54
+ end
55
+ end
56
+
49
57
  def connect_to(opts = {})
50
58
  Connection.create(opts.merge(:ref => name))
51
59
  options[:connection_ref] = name
52
60
  end
53
-
54
-
61
+
62
+
55
63
  #
56
64
  # resource access methods
57
65
  #
58
-
66
+
59
67
  def connection
60
68
  db(options[:connection_ref])
61
69
  end
62
70
  alias :redis :connection
63
-
71
+
64
72
  def name
65
73
  options[:scope] || (@name ||= self.to_s)
66
74
  end
67
-
75
+
68
76
  end
69
- end
77
+ end
@@ -2,17 +2,17 @@ require 'active_support/core_ext/hash/indifferent_access'
2
2
 
3
3
  module Redistat
4
4
  class Result < HashWithIndifferentAccess
5
-
5
+
6
6
  attr_accessor :from
7
7
  attr_accessor :till
8
-
8
+
9
9
  alias :date :from
10
10
  alias :date= :from=
11
-
11
+
12
12
  def initialize(options = {})
13
13
  @from = options[:from] ||= nil
14
14
  @till = options[:till] ||= nil
15
15
  end
16
-
16
+
17
17
  end
18
18
  end
@@ -1,18 +1,18 @@
1
1
  module Redistat
2
2
  class Scope
3
3
  include Database
4
-
4
+
5
5
  def initialize(name)
6
6
  @name = name.to_s
7
7
  end
8
-
8
+
9
9
  def to_s
10
10
  @name
11
11
  end
12
-
12
+
13
13
  def next_id
14
14
  db.incr("#{@name}#{KEY_NEXT_ID}")
15
15
  end
16
-
16
+
17
17
  end
18
- end
18
+ end
@@ -1,61 +1,68 @@
1
1
  module Redistat
2
2
  class Summary
3
3
  include Database
4
-
4
+
5
5
  class << self
6
-
6
+
7
7
  def default_options
8
- { :enable_grouping => true,
9
- :label_indexing => true,
10
- :connection_ref => nil }
8
+ {
9
+ :enable_grouping => true,
10
+ :label_indexing => true,
11
+ :connection_ref => nil,
12
+ :expire => {}
13
+ }
11
14
  end
12
-
15
+
13
16
  def buffer
14
17
  Redistat.buffer
15
18
  end
16
-
19
+
17
20
  def update_all(key, stats = {}, depth_limit = nil, opts = {})
18
21
  stats ||= {}
19
22
  return if stats.empty?
20
-
23
+
21
24
  options = default_options.merge((opts || {}).reject { |k,v| v.nil? })
22
-
25
+
23
26
  depth_limit ||= key.depth
24
-
27
+
25
28
  update_through_buffer(key, stats, depth_limit, options)
26
29
  end
27
-
30
+
28
31
  def update_through_buffer(*args)
29
32
  update(*args) unless buffer.store(*args)
30
33
  end
31
-
32
- def update(key, stats, depth_limit, opts)
34
+
35
+ def update(key, stats, depth_limit, opts = {})
33
36
  if opts[:enable_grouping]
34
37
  stats = inject_group_summaries(stats)
35
38
  key.groups.each do |k|
36
- update_key(k, stats, depth_limit, opts[:connection_ref])
39
+ update_key(k, stats, depth_limit, opts)
37
40
  k.update_index if opts[:label_indexing]
38
41
  end
39
42
  else
40
- update_key(key, stats, depth_limit, opts[:connection_ref])
43
+ update_key(key, stats, depth_limit, opts)
41
44
  end
42
45
  end
43
-
46
+
44
47
  private
45
-
46
- def update_key(key, stats, depth_limit, connection_ref)
48
+
49
+ def update_key(key, stats, depth_limit, opts = {})
47
50
  Date::DEPTHS.each do |depth|
48
- update_fields(key, stats, depth, connection_ref)
51
+ update_fields(key, stats, depth, opts)
49
52
  break if depth == depth_limit
50
53
  end
51
54
  end
52
-
53
- def update_fields(key, stats, depth, connection_ref = nil)
55
+
56
+ def update_fields(key, stats, depth, opts = {})
54
57
  stats.each do |field, value|
55
- db(connection_ref).hincrby key.to_s(depth), field, value
58
+ db(opts[:connection_ref]).hincrby key.to_s(depth), field, value
59
+ end
60
+
61
+ if opts[:expire] && !opts[:expire][depth].nil?
62
+ db(opts[:connection_ref]).expire key.to_s(depth), opts[:expire][depth]
56
63
  end
57
64
  end
58
-
65
+
59
66
  def inject_group_summaries!(stats)
60
67
  summaries = {}
61
68
  stats.each do |key, value|
@@ -72,11 +79,11 @@ module Redistat
72
79
  end
73
80
  stats.merge_and_incr!(summaries)
74
81
  end
75
-
82
+
76
83
  def inject_group_summaries(stats)
77
84
  inject_group_summaries!(stats.clone)
78
85
  end
79
-
86
+
80
87
  end
81
88
  end
82
- end
89
+ end
@@ -1,3 +1,3 @@
1
1
  module Redistat
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -18,13 +18,14 @@ Gem::Specification.new do |s|
18
18
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
19
  s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
20
  s.require_paths = ["lib"]
21
-
21
+
22
22
  s.add_runtime_dependency 'activesupport', '>= 2.3.6'
23
23
  s.add_runtime_dependency 'json', '>= 1.4.0'
24
24
  s.add_runtime_dependency 'redis', '>= 2.1.0'
25
25
  s.add_runtime_dependency 'time_ext', '>= 0.2.9'
26
-
26
+
27
+ s.add_development_dependency 'rake', '>= 0.8.7'
27
28
  s.add_development_dependency 'rspec', '>= 2.1.0'
28
- s.add_development_dependency 'rcov', '>= 0.9.9'
29
29
  s.add_development_dependency 'yard', '>= 0.6.3'
30
+ s.add_development_dependency 'simplecov', '>= 0.6.1'
30
31
  end
@@ -1,7 +1,7 @@
1
1
  require "spec_helper"
2
2
 
3
3
  describe Redistat::Buffer do
4
-
4
+
5
5
  before(:each) do
6
6
  @class = Redistat::Buffer
7
7
  @buffer = Redistat::Buffer.instance
@@ -10,17 +10,17 @@ describe Redistat::Buffer do
10
10
  @depth_limit = :hour
11
11
  @opts = {:enable_grouping => true}
12
12
  end
13
-
13
+
14
14
  # let's cleanup after ourselves for the other specs
15
15
  after(:each) do
16
16
  @class.instance_variable_set("@instance", nil)
17
17
  @buffer.size = 0
18
18
  end
19
-
19
+
20
20
  it "should provide instance of itself" do
21
21
  @buffer.should be_a(@class)
22
22
  end
23
-
23
+
24
24
  it "should only buffer if buffer size setting is greater than 1" do
25
25
  @buffer.size.should == 0
26
26
  @buffer.send(:should_buffer?).should be_false
@@ -31,7 +31,7 @@ describe Redistat::Buffer do
31
31
  @buffer.size.should == 2
32
32
  @buffer.send(:should_buffer?).should be_true
33
33
  end
34
-
34
+
35
35
  it "should only flush buffer if buffer size is greater than or equal to buffer size setting" do
36
36
  @buffer.size.should == 0
37
37
  @buffer.send(:queue).size.should == 0
@@ -50,7 +50,7 @@ describe Redistat::Buffer do
50
50
  @buffer.send(:incr_count)
51
51
  @buffer.send(:should_flush?).should be_true
52
52
  end
53
-
53
+
54
54
  it "should force flush queue irregardless of result of #should_flush? when #reset_queue is called with true" do
55
55
  @buffer.send(:queue)[:hello] = 'world'
56
56
  @buffer.send(:incr_count)
@@ -62,7 +62,7 @@ describe Redistat::Buffer do
62
62
  @buffer.send(:reset_queue, true).should == {:hello => 'world'}
63
63
  @buffer.instance_variable_get("@count").should == 0
64
64
  end
65
-
65
+
66
66
  it "should #flush_data into Summary.update properly" do
67
67
  # the root level key value doesn't actually matter, but it's something like this...
68
68
  data = {'ScopeName/label/goes/here:2011::true:true' => {
@@ -75,14 +75,16 @@ describe Redistat::Buffer do
75
75
  Redistat::Summary.should_receive(:update).with(@key, @stats, @depth_limit, @opts)
76
76
  @buffer.send(:flush_data, data)
77
77
  end
78
-
78
+
79
79
  it "should build #buffer_key correctly" do
80
80
  opts = {:enable_grouping => true, :label_indexing => false, :connection_ref => nil}
81
- @buffer.send(:buffer_key, @key, opts).should == "#{@key.to_s}::true:false"
81
+ @buffer.send(:buffer_key, @key, opts).should ==
82
+ "#{@key.to_s}:connection_ref::enable_grouping:true:label_indexing:false"
82
83
  opts = {:enable_grouping => false, :label_indexing => true, :connection_ref => :omg}
83
- @buffer.send(:buffer_key, @key, opts).should == "#{@key.to_s}:omg:false:true"
84
+ @buffer.send(:buffer_key, @key, opts).should ==
85
+ "#{@key.to_s}:connection_ref:omg:enable_grouping:false:label_indexing:true"
84
86
  end
85
-
87
+
86
88
  describe "Buffering" do
87
89
  it "should store items on buffer queue" do
88
90
  @buffer.store(@key, @stats, @depth_limit, @opts).should be_false
@@ -96,7 +98,7 @@ describe Redistat::Buffer do
96
98
  @buffer.send(:queue)[@buffer.send(:queue).keys.first][:stats][:count].should == 2
97
99
  @buffer.send(:queue)[@buffer.send(:queue).keys.first][:stats][:views].should == 6
98
100
  end
99
-
101
+
100
102
  it "should flush buffer queue when size is reached" do
101
103
  key = mock('Key', :to_s => "Scope/labelx:2011")
102
104
  @buffer.size = 10
@@ -115,18 +117,18 @@ describe Redistat::Buffer do
115
117
  4.times { @buffer.store(key, @stats, @depth_limit, @opts).should be_true }
116
118
  end
117
119
  end
118
-
120
+
119
121
  describe "Thread-Safety" do
120
122
  it "should read/write to buffer queue in a thread-safe manner" do
121
-
122
- # This spec passes wether thread safety is enabled or not. In short I need
123
- # better specs for thread-safety, and personally a better understanding of
124
- # thread-safety in general.
123
+
124
+ # Setting thread_safe to false only makes the spec fail with
125
+ # JRuby. 1.8.x and 1.9.x both pass fine for some reason
126
+ # regardless of what the thread_safe option is set to.
125
127
  Redistat.thread_safe = true
126
-
128
+
127
129
  key = mock('Key', :to_s => "Scope/labelx:2011")
128
130
  @buffer.size = 100
129
-
131
+
130
132
  Redistat::Summary.should_receive(:update).exactly(2).times.and_return do |k, stats, depth_limit, opts|
131
133
  depth_limit.should == @depth_limit
132
134
  opts.should == @opts
@@ -138,7 +140,7 @@ describe Redistat::Buffer do
138
140
  stats[:views].should == 120
139
141
  end
140
142
  end
141
-
143
+
142
144
  threads = []
143
145
  10.times do
144
146
  threads << Thread.new {
@@ -146,12 +148,12 @@ describe Redistat::Buffer do
146
148
  4.times { @buffer.store(key, @stats, @depth_limit, @opts).should be_true }
147
149
  }
148
150
  end
149
-
151
+
150
152
  threads.each { |t| t.join }
151
153
  end
152
-
153
- it "should have better specs that actually fail when thread-safety is off"
154
-
154
+
155
+ it "should have specs that fail on 1.8.x/1.9.x when thread_safe is disabled"
156
+
155
157
  end
156
-
158
+
157
159
  end