pulse_meter_core 0.4.13 → 0.5.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.
data/README.md CHANGED
@@ -38,3 +38,4 @@ Or install it yourself as:
38
38
  3. Commit your changes (`git commit -am 'Added some feature'`)
39
39
  4. Push to the branch (`git push origin my-new-feature`)
40
40
  5. Create new Pull Request
41
+
@@ -1,4 +1,5 @@
1
1
  require 'securerandom'
2
+ require 'pulse_meter/time_converter'
2
3
 
3
4
  module PulseMeter
4
5
  module Sensor
@@ -18,7 +19,9 @@ module PulseMeter
18
19
  # @return [Fixnum] How long unsummarized raw data will be stored before expiration
19
20
  # @!attribute [r] reduce_delay
20
21
  # @return [Fixnum] Delay between end of interval and summarization
21
- attr_reader :interval, :ttl, :raw_data_ttl, :reduce_delay
22
+ # @!attribute [r] timezone
23
+ # @return [String] TimeZone to which sensor data is related
24
+ attr_reader :interval, :ttl, :raw_data_ttl, :reduce_delay, :timezone
22
25
 
23
26
  # Default values for some sensor parameters
24
27
  DEFAULTS = {
@@ -32,11 +35,13 @@ module PulseMeter
32
35
  # @option options [Fixnum] :ttl How long summarized data will be stored before expiration
33
36
  # @option options [Fixnum] :raw_data_ttl How long unsummarized raw data will be stored before expiration
34
37
  # @option options [Fixnum] :reduce_delay Delay between end of interval and summarization
38
+ # @option options [String] :timezone TimeZone to which sensor data is related
35
39
  def initialize(name, options)
36
40
  @interval = assert_positive_integer!(options, :interval)
37
41
  @ttl = assert_positive_integer!(options, :ttl)
38
42
  @raw_data_ttl = assert_positive_integer!(options, :raw_data_ttl, DEFAULTS[:raw_data_ttl])
39
43
  @reduce_delay = assert_positive_integer!(options, :reduce_delay, DEFAULTS[:reduce_delay])
44
+ @timezone = options[:timezone]
40
45
  super
41
46
  end
42
47
 
@@ -54,7 +59,7 @@ module PulseMeter
54
59
  # @param value event value
55
60
  def event_at(time, value = nil)
56
61
  multi do
57
- interval_id = get_interval_id(time)
62
+ interval_id = get_interval_id(time_to_redis(time))
58
63
  key = raw_data_key(interval_id)
59
64
  aggregate_event(key, value)
60
65
  command_aggregator.expire(key, raw_data_ttl)
@@ -82,7 +87,7 @@ module PulseMeter
82
87
  # @raise ArgumentError if argumets are not valid time objects
83
88
  def timeline_within(from, till, skip_optimization = false)
84
89
  raise ArgumentError unless from.kind_of?(Time) && till.kind_of?(Time)
85
- start_time, end_time = from.to_i, till.to_i
90
+ start_time, end_time = time_to_redis(from.to_i), time_to_redis(till.to_i)
86
91
  actual_interval = optimized_interval(start_time, end_time, skip_optimization)
87
92
  start_interval_id = get_interval_id(start_time) + actual_interval
88
93
  ids, values = fetch_reduced_interval_data(start_interval_id, actual_interval, end_time)
@@ -108,7 +113,7 @@ module PulseMeter
108
113
  # @raise ArgumentError if argumets are not valid time objects
109
114
  def drop_within(from, till)
110
115
  raise ArgumentError unless from.kind_of?(Time) && till.kind_of?(Time)
111
- start_time, end_time = from.to_i, till.to_i
116
+ start_time, end_time = time_to_redis(from.to_i), time_to_redis(till.to_i)
112
117
  current_interval_id = get_interval_id(start_time) + interval
113
118
  keys = []
114
119
  while current_interval_id < end_time
@@ -145,7 +150,7 @@ module PulseMeter
145
150
  # Returns current interval id
146
151
  # @return [Fixnum]
147
152
  def current_interval_id
148
- get_interval_id(Time.now)
153
+ get_interval_id(time_to_redis(Time.now))
149
154
  end
150
155
 
151
156
  # @abstract Registeres event for current interval identified by key
@@ -180,7 +185,7 @@ module PulseMeter
180
185
 
181
186
  def sensor_data(interval_id, value)
182
187
  value = deflate(value) unless value.nil?
183
- SensorData.new(Time.at(interval_id), value)
188
+ SensorData.new(Time.at(time_from_redis(interval_id)), value)
184
189
  end
185
190
 
186
191
  # Processes event
@@ -231,6 +236,18 @@ module PulseMeter
231
236
  end
232
237
  res
233
238
  end
239
+
240
+ def time_converter
241
+ @time_converter ||= PulseMeter::TimeConverter.new(@timezone)
242
+ end
243
+
244
+ def time_to_redis(time)
245
+ time_converter.to_redis(time)
246
+ end
247
+
248
+ def time_from_redis(time)
249
+ time_converter.from_redis(time)
250
+ end
234
251
  end
235
252
  end
236
253
  end
@@ -30,7 +30,7 @@ module PulseMeter
30
30
 
31
31
  # Reduces data in all raw intervals
32
32
  def reduce_all_raw
33
- time = Time.now
33
+ time = time_to_redis(Time.now)
34
34
  min_time = time - reduce_delay - interval
35
35
  max_depth = time - reduce_delay - interval * MAX_INTERVALS
36
36
  ids = collect_ids_to_reduce(time, max_depth, min_time)
@@ -42,7 +42,7 @@ module PulseMeter
42
42
  while (time > time_from) # go backwards
43
43
  time -= interval
44
44
  interval_id = get_interval_id(time)
45
- next if Time.at(interval_id) > time_to
45
+ next if interval_id > time_to
46
46
 
47
47
  reduced_key = data_key(interval_id)
48
48
  raw_key = raw_data_key(interval_id)
@@ -0,0 +1,26 @@
1
+ require 'tzinfo'
2
+
3
+ module PulseMeter
4
+ class TimeConverter
5
+ def initialize(timezone_name)
6
+ @tz = TZInfo::Timezone.get(timezone_name)
7
+ rescue TZInfo::InvalidTimezoneIdentifier
8
+ @tz = TZInfo::Timezone.get('UTC')
9
+ end
10
+
11
+ def to_redis(time)
12
+ tz_period.to_local(time.to_i).to_i
13
+ end
14
+
15
+ def from_redis(time)
16
+ tz_period.to_utc(time.to_i).to_i
17
+ end
18
+
19
+ private
20
+
21
+ def tz_period
22
+ @tz.period_for_utc(Time.now.utc)
23
+ end
24
+
25
+ end
26
+ end
@@ -15,10 +15,11 @@ Gem::Specification.new do |gem|
15
15
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
16
  gem.name = "pulse_meter_core"
17
17
  gem.require_paths = ["lib"]
18
- gem.version = "0.4.13"
18
+ gem.version = "0.5.0"
19
19
 
20
- gem.add_runtime_dependency('redis')
21
20
  gem.add_runtime_dependency('json')
21
+ gem.add_runtime_dependency('redis')
22
+ gem.add_runtime_dependency('tzinfo')
22
23
 
23
24
  gem.add_development_dependency('aquarium')
24
25
  gem.add_development_dependency('hashie')
@@ -10,7 +10,8 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
10
10
  let(:raw_data_ttl){ 3000 }
11
11
  let(:interval){ 5 }
12
12
  let(:reduce_delay){ 3 }
13
- let(:good_init_values){ {:ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay}.merge(extra_init_values || {}) }
13
+ let(:timezone){'Europe/Moscow'}
14
+ let(:good_init_values){ {:timezone => timezone, :ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay}.merge(extra_init_values || {}) }
14
15
  let!(:sensor){ described_class.new(name, good_init_values) }
15
16
  let(:dummy) {Dummy.new}
16
17
  let(:base_class){ PulseMeter::Sensor::Base }
@@ -18,16 +19,22 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
18
19
  let(:sample_event) {default_event || 123}
19
20
 
20
21
  before(:each) do
21
- @interval_id = (Time.now.to_i / interval) * interval
22
- @prev_interval_id = (Time.now.to_i / interval) * interval - interval
22
+ @now = Time.now
23
+ tc = PulseMeter::TimeConverter.new(timezone)
24
+
25
+ @redis_now = tc.to_redis(@now).to_i
26
+
27
+ @interval_id = (@redis_now / interval) * interval
28
+ @prev_interval_id = (@redis_now / interval) * interval - interval
23
29
 
24
30
  @raw_data_key = sensor.raw_data_key(@interval_id)
31
+
25
32
  @prev_raw_data_key = sensor.raw_data_key(@prev_interval_id)
26
33
 
27
34
  @next_raw_data_key = sensor.raw_data_key(@interval_id + interval)
28
35
 
29
- @start_of_interval = Time.at(@interval_id)
30
- @start_of_prev_interval = Time.at(@prev_interval_id)
36
+ @start_of_interval = Time.at(tc.from_redis(@interval_id))
37
+ @start_of_prev_interval = Time.at(tc.from_redis(@prev_interval_id))
31
38
  end
32
39
 
33
40
  describe "#dump" do
@@ -66,7 +73,7 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
66
73
  it "should write data so that it totally expires after :raw_data_ttl" do
67
74
  key_count = redis.keys('*').count
68
75
  sensor.event(sample_event)
69
- Timecop.freeze(Time.now + raw_data_ttl + 1) do
76
+ Timecop.freeze(@now + raw_data_ttl + 1) do
70
77
  redis.keys('*').count.should == key_count
71
78
  end
72
79
  end
@@ -90,7 +97,7 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
90
97
  end
91
98
 
92
99
  describe "#event_at" do
93
- let(:now) {Time.now}
100
+ let(:now) {@now}
94
101
  it "should write events to redis" do
95
102
  expect{
96
103
  sensor.event_at(now, sample_event)
@@ -230,14 +237,14 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
230
237
  describe "#timeline_within" do
231
238
  it "should raise exception unless both arguments are Time objects" do
232
239
  [:q, nil, -1].each do |bad_value|
233
- expect{ sensor.timeline_within(Time.now, bad_value) }.to raise_exception(ArgumentError)
234
- expect{ sensor.timeline_within(bad_value, Time.now) }.to raise_exception(ArgumentError)
240
+ expect{ sensor.timeline_within(@now, bad_value) }.to raise_exception(ArgumentError)
241
+ expect{ sensor.timeline_within(bad_value, @now) }.to raise_exception(ArgumentError)
235
242
  end
236
243
  end
237
244
 
238
245
  it "should return an array of SensorData objects corresponding to stored data for passed interval" do
239
246
  sensor.event(sample_event)
240
- now = Time.now
247
+ now = @now
241
248
  timeline = sensor.timeline_within(now - 1, now)
242
249
  timeline.should be_kind_of(Array)
243
250
  timeline.each{|i| i.should be_kind_of(SensorData) }
@@ -306,8 +313,8 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
306
313
  end
307
314
 
308
315
  it "should request timeline within interval from given number of seconds ago till now" do
309
- Timecop.freeze do
310
- now = Time.now
316
+ Timecop.freeze(@now) do
317
+ now = @now
311
318
  ago = interval * 100
312
319
  sensor.timeline(ago).should == sensor.timeline_within(now - ago, now)
313
320
  end
@@ -332,8 +339,8 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
332
339
  describe "#drop_within" do
333
340
  it "should raise exception unless both arguments are Time objects" do
334
341
  [:q, nil, -1].each do |bad_value|
335
- expect{ sensor.drop_within(Time.now, bad_value) }.to raise_exception(ArgumentError)
336
- expect{ sensor.drop_within(bad_value, Time.now) }.to raise_exception(ArgumentError)
342
+ expect{ sensor.drop_within(@now, bad_value) }.to raise_exception(ArgumentError)
343
+ expect{ sensor.drop_within(bad_value, @now) }.to raise_exception(ArgumentError)
337
344
  end
338
345
  end
339
346
 
@@ -399,7 +406,7 @@ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
399
406
  def check_sensor_data(sensor, value)
400
407
  data = sensor.timeline(2).first
401
408
  data.value.should be_generally_equal(sensor.deflate_safe(value))
402
- data.start_time.to_i.should == @interval_id
409
+ data.start_time.to_i.should == @start_of_interval.to_i
403
410
  end
404
411
 
405
412
  it "should contain summarized value stored by data_key for reduced intervals" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pulse_meter_core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.13
4
+ version: 0.5.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,8 +10,24 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-06-16 00:00:00.000000000 Z
13
+ date: 2014-02-09 00:00:00.000000000 Z
14
14
  dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: json
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
15
31
  - !ruby/object:Gem::Dependency
16
32
  name: redis
17
33
  requirement: !ruby/object:Gem::Requirement
@@ -29,7 +45,7 @@ dependencies:
29
45
  - !ruby/object:Gem::Version
30
46
  version: '0'
31
47
  - !ruby/object:Gem::Dependency
32
- name: json
48
+ name: tzinfo
33
49
  requirement: !ruby/object:Gem::Requirement
34
50
  none: false
35
51
  requirements:
@@ -239,6 +255,7 @@ files:
239
255
  - lib/pulse_meter/server/command_line_options.rb
240
256
  - lib/pulse_meter/server/config_options.rb
241
257
  - lib/pulse_meter/server/sensors.rb
258
+ - lib/pulse_meter/time_converter.rb
242
259
  - lib/pulse_meter/udp_server.rb
243
260
  - lib/pulse_meter_core.rb
244
261
  - pulse_meter_core.gemspec
@@ -294,9 +311,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
294
311
  - - ! '>='
295
312
  - !ruby/object:Gem::Version
296
313
  version: '0'
297
- segments:
298
- - 0
299
- hash: 887814886917484037
300
314
  requirements: []
301
315
  rubyforge_project:
302
316
  rubygems_version: 1.8.23