nightfury 0.5 → 0.6

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.
data/lib/nightfury.rb CHANGED
@@ -4,8 +4,22 @@ require 'json'
4
4
  require 'active_support/core_ext/object/blank'
5
5
  require 'active_support/core_ext/class/attribute'
6
6
  require 'active_support/core_ext/string/inflections'
7
+ require 'active_support/core_ext/time/calculations'
8
+ require 'active_support/core_ext/date/calculations'
9
+ require 'active_support/core_ext/date_time/calculations'
10
+ require 'active_support/core_ext/numeric/time'
7
11
  require 'active_support/concern'
8
12
 
13
+ class Time
14
+ def round(seconds = 60)
15
+ Time.at((self.to_f / seconds).round * seconds)
16
+ end
17
+
18
+ def floor(seconds = 60)
19
+ Time.at((self.to_f / seconds).floor * seconds)
20
+ end
21
+ end
22
+
9
23
  module Nightfury
10
24
  class << self
11
25
  attr_accessor :redis, :namespace
@@ -36,8 +36,10 @@ module Nightfury
36
36
  end
37
37
 
38
38
  attr_accessor :id, :tags
39
+ attr_reader :redis
39
40
 
40
41
  def initialize(id, options={})
42
+ @redis = Nightfury.redis
41
43
  @id = id
42
44
  @tags = options[:tags]
43
45
  end
@@ -49,6 +51,10 @@ module Nightfury
49
51
  "#{store_name}.#{id}#{tag_ids}"
50
52
  end
51
53
 
54
+ def new_record?
55
+ redis.keys("#{key_prefix}*").empty?
56
+ end
57
+
52
58
  private
53
59
 
54
60
  def generate_tag_ids
@@ -1,13 +1,17 @@
1
1
  module Nightfury
2
2
  module Metric
3
3
  class Base
4
- attr_reader :name, :redis, :redis_key_prefix, :store_as
4
+
5
+ ALLOWED_STEPS = [:minute, :hour, :week, :month, :year]
6
+
7
+ attr_reader :name, :redis, :redis_key_prefix, :store_as, :step
5
8
 
6
9
  def initialize(name, options={})
7
10
  @name = name
8
11
  @redis = Nightfury.redis
9
12
  @redis_key_prefix = options[:redis_key_prefix]
10
13
  @store_as = options[:store_as]
14
+ @step = ALLOWED_STEPS.include?(options[:step]) ? options[:step] : :minute
11
15
  end
12
16
 
13
17
  def redis_key
@@ -20,7 +20,7 @@ module Nightfury
20
20
  return nil unless redis.exists(redis_key)
21
21
  data_point = ''
22
22
  if timestamp
23
- timestamp = timestamp.to_i
23
+ timestamp = get_step_time(timestamp).to_i
24
24
  data_point = redis.zrangebyscore(redis_key, 0, timestamp, withscores: true)
25
25
  data_point = data_point.each_slice(2).map {|pair| pair }.last
26
26
  else
@@ -37,8 +37,8 @@ module Nightfury
37
37
 
38
38
  def get_range(start_time, end_time)
39
39
  return nil unless redis.exists(redis_key)
40
- start_time = start_time.to_i
41
- end_time = end_time.to_i
40
+ start_time = get_step_time(start_time).to_i
41
+ end_time = get_step_time(end_time).to_i
42
42
  data_points = redis.zrangebyscore(redis_key, start_time, end_time, withscores: true)
43
43
  decode_many_data_points(data_points)
44
44
  end
@@ -75,9 +75,8 @@ module Nightfury
75
75
  private
76
76
 
77
77
  def add_value_to_timeline(value, time)
78
- time = time.to_i
79
- value = "#{time}:#{value}"
80
- redis.zadd redis_key, time, value
78
+ timestamp = get_step_time(time).to_i
79
+ redis.zadd redis_key, timestamp, value
81
80
  end
82
81
 
83
82
  def decode_many_data_points(data_points)
@@ -91,13 +90,7 @@ module Nightfury
91
90
  end
92
91
 
93
92
  def decode_data_point(data_point)
94
- data_point = data_point.first
95
- colon_index = data_point.index(':')
96
-
97
- [
98
- data_point[0...colon_index],
99
- data_point[colon_index+1..-1]
100
- ]
93
+ [data_point[1], data_point[0]]
101
94
  end
102
95
 
103
96
  def save_meta
@@ -108,6 +101,15 @@ module Nightfury
108
101
  def init_time_series
109
102
  redis.zadd redis_key, 0, default_meta.to_json
110
103
  end
104
+
105
+ def get_step_time(time)
106
+ case step
107
+ when :minute then time.round(60)
108
+ when :hour then time.round(1.hour)
109
+ when :week then time.round(1.week)
110
+ when :month then time.round(Time.days_in_month(time.month, time.year))
111
+ end
112
+ end
111
113
  end
112
114
  end
113
115
  end
@@ -1,3 +1,3 @@
1
1
  module Nightfury
2
- VERSION = "0.5"
2
+ VERSION = "0.6"
3
3
  end
@@ -41,6 +41,11 @@ describe Nightfury::Identity::Base do
41
41
  d.id.should == 1
42
42
  end
43
43
 
44
+ it "should respond to redis" do
45
+ d = Dummy.new(1)
46
+ d.redis.should == Nightfury.redis
47
+ end
48
+
44
49
  it "should generates a key prefix" do
45
50
  d = Dummy.new(1)
46
51
  d.key_prefix.should == 'dummy.1'
@@ -52,6 +57,16 @@ describe Nightfury::Identity::Base do
52
57
  d.key_prefix.should == 'd.1'
53
58
  end
54
59
 
60
+ it "should be able tell if self is a new record" do
61
+ Dummy.metric(:new_metric)
62
+
63
+ d = Dummy.new(1)
64
+ d.should be_new_record
65
+
66
+ d.new_metric.set(1)
67
+ d.should_not be_new_record
68
+ end
69
+
55
70
  it "should include tags in the key prefix" do
56
71
  DummyTwo = Class.new(Dummy)
57
72
  DummyTwo.store_as = :d
@@ -14,7 +14,7 @@ describe Nightfury::Metric::CountTimeSeries do
14
14
  end
15
15
  Timecop.return
16
16
 
17
- count_series.get.should == { time_now.to_i.to_s => "2"}
17
+ count_series.get.values.first.should == "2"
18
18
  end
19
19
 
20
20
  it "should be able to increment value by a given step" do
@@ -28,18 +28,18 @@ describe Nightfury::Metric::CountTimeSeries do
28
28
  end
29
29
  Timecop.return
30
30
 
31
- count_series.get.should == { time_now.to_i.to_s => "3"}
31
+ count_series.get.values.first.should == "3"
32
32
  end
33
33
 
34
34
 
35
- it "should be able to increment value at a given timestamp" do
35
+ it "should be able to increment value at a step near the given timestamp" do
36
36
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
37
37
  time_now = Time.now
38
- time_later = time_now + 10
38
+ time_later = time_now + 61
39
39
  # Add a data point
40
40
  count_series.set(1, time_now - 10)
41
41
  count_series.incr(1,time_later)
42
- count_series.get.should == { time_later.to_i.to_s => "2"}
42
+ count_series.get.should == { time_later.round(60).to_i.to_s => "2"}
43
43
  end
44
44
  end
45
45
 
@@ -48,50 +48,50 @@ describe Nightfury::Metric::CountTimeSeries do
48
48
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
49
49
  time_now = Time.now
50
50
  count_series.incr(2, time_now)
51
- count_series.get.should == { time_now.to_i.to_s => "2"}
51
+ count_series.get.should == { time_now.round(60).to_i.to_s => "2"}
52
52
  end
53
53
  end
54
54
  end
55
55
 
56
56
  describe "Decr" do
57
57
  context "Has data points" do
58
- it "should be able to decrement value by 1 at the current timestamp by default" do
58
+ it "should be able to decrement value by 1 at a step near the current timestamp by default" do
59
59
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
60
60
  time_now = Time.now
61
61
  # Add a data point
62
- count_series.set(1, time_now - 10)
62
+ count_series.set(1, time_now - 61)
63
63
 
64
64
  Timecop.freeze(time_now) do
65
65
  count_series.decr
66
66
  end
67
67
  Timecop.return
68
68
 
69
- count_series.get.should == { time_now.to_i.to_s => "0"}
69
+ count_series.get.values.first.should == "0"
70
70
  end
71
71
 
72
72
  it "should be able to decrement value by a given step" do
73
73
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
74
74
  time_now = Time.now
75
75
  # Add a data point
76
- count_series.set(2, time_now - 10)
76
+ count_series.set(2, time_now - 61)
77
77
 
78
78
  Timecop.freeze(time_now) do
79
79
  count_series.decr(2)
80
80
  end
81
81
  Timecop.return
82
82
 
83
- count_series.get.should == { time_now.to_i.to_s => "0"}
83
+ count_series.get.values.first.should == "0"
84
84
  end
85
85
 
86
86
 
87
- it "should be able to decrement value at a given timestamp" do
87
+ it "should be able to decrement value at nearest step of a given timestamp" do
88
88
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
89
89
  time_now = Time.now
90
- time_later = time_now + 10
90
+ time_later = time_now + 61
91
91
  # Add a data point
92
92
  count_series.set(1, time_now - 10)
93
93
  count_series.decr(1,time_later)
94
- count_series.get.should == { time_later.to_i.to_s => "0"}
94
+ count_series.get.should == { time_later.round(60).to_i.to_s => "0"}
95
95
  end
96
96
  end
97
97
 
@@ -100,7 +100,7 @@ describe Nightfury::Metric::CountTimeSeries do
100
100
  count_series = Nightfury::Metric::CountTimeSeries.new(1)
101
101
  time_now = Time.now
102
102
  count_series.decr(2, time_now)
103
- count_series.get.should == { time_now.to_i.to_s => "-2"}
103
+ count_series.get.should == { time_now.round(60).to_i.to_s => "-2"}
104
104
  end
105
105
  end
106
106
  end
@@ -31,11 +31,11 @@ describe Nightfury::Metric::TimeSeries do
31
31
  it "should get the most recent data point" do
32
32
  ts_metric = Nightfury::Metric::TimeSeries.new(:time)
33
33
  time_now = Time.now
34
- time_later = time_now + 10
34
+ time_later = time_now + 60
35
35
  ts_metric.set(1, time_now)
36
36
  ts_metric.set(2, time_later)
37
37
  result = ts_metric.get
38
- result[time_later.to_i.to_s].should == '2'
38
+ result.values.first.should == '2'
39
39
  end
40
40
 
41
41
  it "should return nil if there are no data points" do
@@ -45,14 +45,14 @@ describe Nightfury::Metric::TimeSeries do
45
45
  end
46
46
 
47
47
  context "with timestamp" do
48
- it "should get the data point at the time stamp" do
48
+ it "should get the data point at the nearest time step" do
49
49
  ts_metric = Nightfury::Metric::TimeSeries.new(:time)
50
50
  time_now = Time.now
51
- time_later = time_now + 10
51
+ time_later = time_now + 60
52
52
  ts_metric.set(1, time_now)
53
53
  ts_metric.set(2, time_later)
54
54
  result = ts_metric.get(time_now)
55
- result[time_now.to_i.to_s].should == '1'
55
+ result.values.first.should == '1'
56
56
  end
57
57
 
58
58
  context "no data point at the timestamp" do
@@ -66,7 +66,7 @@ describe Nightfury::Metric::TimeSeries do
66
66
  set_time = Time.now - 60
67
67
  ts_metric.set(1, set_time)
68
68
  result = ts_metric.get(Time.now)
69
- result[set_time.to_i.to_s].should == '1'
69
+ result.values.first.should == '1'
70
70
  end
71
71
  end
72
72
  end
@@ -92,16 +92,14 @@ describe Nightfury::Metric::TimeSeries do
92
92
 
93
93
  10.times do |i|
94
94
  ts_metric.set(i, loop_time)
95
- loop_time = loop_time + 1
95
+ loop_time = loop_time + 61
96
96
  end
97
97
 
98
- start_time = time + 3
99
- end_time = time + 5
98
+ start_time = time + (3*60)
99
+ end_time = time + (5*60)
100
100
 
101
101
  result = ts_metric.get_range(start_time, end_time)
102
- result[start_time.to_i.to_s].should == '3'
103
- result[(start_time.to_i + 1).to_s].should == '4'
104
- result[end_time.to_i.to_s].should == '5'
102
+ result.values.should == ['3','4','5']
105
103
  end
106
104
  end
107
105
 
@@ -125,7 +123,7 @@ describe Nightfury::Metric::TimeSeries do
125
123
 
126
124
  10.times do |i|
127
125
  ts_metric.set(i, loop_time)
128
- loop_time = loop_time + 1
126
+ loop_time = loop_time + 61
129
127
  end
130
128
 
131
129
  result = ts_metric.get_all
@@ -150,13 +148,13 @@ describe Nightfury::Metric::TimeSeries do
150
148
  end
151
149
 
152
150
  describe "add the value to timeline" do
153
- it "should default time to current time" do
151
+ it "should default time to the step near the current time" do
154
152
  time_now = Time.now
155
153
  ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
156
154
 
157
155
  flexmock(ts_metric.redis).should_receive(:zadd)
158
156
  .with(ts_metric.redis_key,
159
- time_now.to_i,
157
+ time_now.round(60).to_i,
160
158
  FlexMock.any)
161
159
  .once
162
160
 
@@ -166,30 +164,18 @@ describe Nightfury::Metric::TimeSeries do
166
164
  Timecop.return
167
165
  end
168
166
 
169
- it "should add at specified time" do
167
+ it "should add at the step near to specified time" do
170
168
  time = Time.now - 60
171
169
  ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
172
170
 
173
171
  flexmock(ts_metric.redis).should_receive(:zadd)
174
172
  .with(ts_metric.redis_key,
175
- time.to_i,
173
+ time.round(60).to_i,
176
174
  FlexMock.any)
177
175
  .once
178
176
 
179
177
  ts_metric.set(1, time)
180
178
  end
181
-
182
- it "should add the time to the value to avoid duplicate value in the set" do
183
- time = Time.now
184
- ts_metric = Nightfury::Metric::TimeSeries.new(:avg_time)
185
-
186
- flexmock(ts_metric.redis).should_receive(:zadd)
187
- .with(FlexMock.any,
188
- FlexMock.any,
189
- "#{time.to_i}:1")
190
- .once
191
- ts_metric.set(1, time)
192
- end
193
179
  end
194
180
  end
195
181
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nightfury
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.5'
4
+ version: '0.6'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-01-30 00:00:00.000000000 Z
13
+ date: 2013-02-21 00:00:00.000000000 Z
14
14
  dependencies: []
15
15
  description: Nightfury is a reporting/analytics backend written on Redis
16
16
  email: