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
@@ -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
|
-
|
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
|
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
|
data/pulse_meter_core.gemspec
CHANGED
@@ -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.
|
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(:
|
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
|
-
@
|
22
|
-
|
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(
|
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) {
|
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(
|
234
|
-
expect{ sensor.timeline_within(bad_value,
|
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 =
|
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 =
|
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(
|
336
|
-
expect{ sensor.drop_within(bad_value,
|
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 == @
|
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
|
+
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:
|
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:
|
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
|