pulse-meter 0.4.1 → 0.4.2

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
  [![Build Status](https://secure.travis-ci.org/savonarola/pulse-meter.png)](http://travis-ci.org/savonarola/pulse-meter)
2
+ [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/savonarola/pulse-meter)
2
3
 
3
4
  # PulseMeter
4
5
 
@@ -68,6 +69,7 @@ The following timeline sensors are available:
68
69
  * Multifactor sensor
69
70
  * Median value
70
71
  * Percentile
72
+ * MultiPercentile
71
73
  * Unique counter
72
74
 
73
75
  There are several caveats with timeline sensors:
data/lib/cmd.rb CHANGED
@@ -9,6 +9,10 @@ module Cmd
9
9
  include PulseMeter::Mixins::Utils
10
10
  include PulseMeter::Mixins::Cmd
11
11
  no_tasks do
12
+ def create_redis
13
+ Redis.new :host => options[:host], :port => options[:port], :db => options[:db]
14
+ end
15
+
12
16
  def self.common_options
13
17
  method_option :host, :default => '127.0.0.1', :desc => "Redis host"
14
18
  method_option :port, :default => 6379, :desc => "Redis port"
@@ -53,7 +57,10 @@ module Cmd
53
57
  if "json" == options[:format]
54
58
  value = JSON.parse(value)
55
59
  end
56
- with_safe_restore_of(name) {|sensor| sensor.event(value)}
60
+ with_safe_restore_of(name) {|sensor|
61
+ sensor.event(value)
62
+ }
63
+ PulseMeter.command_aggregator.wait_for_pending_events
57
64
  end
58
65
 
59
66
  desc "timeline NAME SECONDS", "Get sensor's NAME timeline for last SECONDS"
@@ -142,13 +149,8 @@ module Cmd
142
149
  desc "drop NAME DATE_FROM(YYYYmmddHHMMSS) DATE_TO(YYYYmmddHHMMSS)", "Drop timeline data of a particular sensor"
143
150
  common_options
144
151
  def drop(name, from, to)
145
- time_from, time_to = [from, to].map do |str|
146
- str.match(/\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\z/) do |m|
147
- Time.gm(*m.captures.map(&:to_i))
148
- end
149
- end
150
- fail! "DATE_FROM is not a valid timestamp" unless time_from.is_a?(Time)
151
- fail! "DATE_TO is not a valid timestamp" unless time_to.is_a?(Time)
152
+ time_from = parse_time(from)
153
+ time_to = parse_time(to)
152
154
  with_safe_restore_of(name) do |sensor|
153
155
  fail! "Sensor #{name} has no drop_within method" unless sensor.respond_to?(:drop_within)
154
156
  sensor.drop_within(time_from, time_to)
@@ -5,7 +5,7 @@ module PulseMeter
5
5
  # Mixin with command-line utilities
6
6
  module Cmd
7
7
  def with_redis
8
- PulseMeter.redis = Redis.new :host => options[:host], :port => options[:port], :db => options[:db]
8
+ PulseMeter.redis = create_redis
9
9
  yield
10
10
  end
11
11
 
@@ -88,6 +88,20 @@ module PulseMeter
88
88
  (first_letter_upper ? first.capitalize : first.downcase) + terms.map(&:capitalize).join
89
89
  end
90
90
 
91
+ # Converts string of the form YYYYmmddHHMMSS (considered as UTC) to Time
92
+ # @param str [String] string to be converted
93
+ # @return [Time]
94
+ # @raise [ArgumentError] unless passed value responds to to_s
95
+ def parse_time(str)
96
+ raise ArgumentError unless str.respond_to?(:to_s)
97
+ m = str.to_s.match(/\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\z/)
98
+ if m
99
+ Time.gm(*m.captures.map(&:to_i))
100
+ else
101
+ raise ArgumentError, "`#{str}' is not a YYYYmmddHHMMSS time"
102
+ end
103
+ end
104
+
91
105
  # Symbolizes hash keys
92
106
  def symbolize_keys(h)
93
107
  h.each_with_object({}) do |(k, v), acc|
@@ -8,13 +8,9 @@ module PulseMeter
8
8
  # @param method [Symbol] instance method name
9
9
  def unobserve_method(klass, method)
10
10
  with_observer = method_with_observer(method)
11
- without_observer = method_without_observer(method)
12
-
13
- return unless klass.method_defined? with_observer
14
- klass.class_eval do
15
- alias_method method, without_observer
16
- remove_method with_observer
17
- remove_method without_observer
11
+ if klass.method_defined?(with_observer)
12
+ block = unchain_block(method)
13
+ klass.class_eval &block
18
14
  end
19
15
  end
20
16
 
@@ -23,13 +19,10 @@ module PulseMeter
23
19
  # @param method [Symbol] class method name
24
20
  def unobserve_class_method(klass, method)
25
21
  with_observer = method_with_observer(method)
26
- without_observer = method_without_observer(method)
27
-
28
- return unless klass.respond_to? with_observer
29
- metaclass(klass).instance_eval do
30
- alias_method method, without_observer
31
- remove_method with_observer
32
- remove_method without_observer
22
+ if klass.respond_to?(with_observer)
23
+ method_owner = metaclass(klass)
24
+ block = unchain_block(method)
25
+ method_owner.instance_eval &block
33
26
  end
34
27
  end
35
28
 
@@ -40,27 +33,9 @@ module PulseMeter
40
33
  # @param proc [Proc] proc to be called in context of receiver each time observed method called
41
34
  def observe_method(klass, method, sensor, &proc)
42
35
  with_observer = method_with_observer(method)
43
- without_observer = method_without_observer(method)
44
-
45
- return if klass.method_defined? with_observer
46
-
47
- klass.class_eval do
48
- alias_method without_observer, method
49
- define_method with_observer do |*args, &block|
50
- result = nil
51
- start_time = Time.now
52
- begin
53
- result = self.send without_observer, *args, &block
54
- ensure
55
- begin
56
- delta = ((Time.now - start_time) * 1000).to_i
57
- sensor.instance_exec delta, *args, &proc
58
- rescue Exception
59
- end
60
- end
61
- result
62
- end
63
- alias_method method, with_observer
36
+ unless klass.method_defined?(with_observer)
37
+ block = chain_block(method, sensor, &proc)
38
+ klass.class_eval &block
64
39
  end
65
40
  end
66
41
 
@@ -71,31 +46,55 @@ module PulseMeter
71
46
  # @param proc [Proc] proc to be called in context of receiver each time observed method called
72
47
  def observe_class_method(klass, method, sensor, &proc)
73
48
  with_observer = method_with_observer(method)
74
- without_observer = method_without_observer(method)
49
+ unless klass.respond_to?(with_observer)
50
+ method_owner = metaclass(klass)
51
+ block = chain_block(method, sensor, &proc)
52
+ method_owner.instance_eval &block
53
+ end
54
+ end
75
55
 
76
- return if klass.respond_to? with_observer
56
+ private
57
+
58
+ def unchain_block(method)
59
+ with_observer = method_with_observer(method)
60
+ without_observer = method_without_observer(method)
77
61
 
78
- metaclass(klass).instance_eval do
79
- alias_method without_observer, method
80
- define_method with_observer do |*args, &block|
81
- result = nil
82
- start_time = Time.now
62
+ Proc.new do
63
+ alias_method(method, without_observer)
64
+ remove_method(with_observer)
65
+ remove_method(without_observer)
66
+ end
67
+ end
68
+
69
+ def define_instrumented_method(method_owner, method, receiver, &handler)
70
+ with_observer = method_with_observer(method)
71
+ without_observer = method_without_observer(method)
72
+ method_owner.send(:define_method, with_observer) do |*args, &block|
73
+ start_time = Time.now
74
+ begin
75
+ self.send(without_observer, *args, &block)
76
+ ensure
83
77
  begin
84
- result = self.send without_observer, *args, &block
85
- ensure
86
- begin
87
- delta = ((Time.now - start_time) * 1000).to_i
88
- sensor.instance_exec delta, *args, &proc
89
- rescue Exception
90
- end
78
+ delta = ((Time.now - start_time) * 1000).to_i
79
+ receiver.instance_exec(delta, *args, &handler)
80
+ rescue StandardError
91
81
  end
92
- result
93
82
  end
94
- alias_method method, with_observer
95
83
  end
96
84
  end
97
85
 
98
- private
86
+ def chain_block(method, receiver, &handler)
87
+ with_observer = method_with_observer(method)
88
+ without_observer = method_without_observer(method)
89
+ me = self
90
+
91
+ Proc.new do
92
+ alias_method(without_observer, method)
93
+ method_owner = self
94
+ me.send(:define_instrumented_method, method_owner, method, receiver, &handler)
95
+ alias_method(method, with_observer)
96
+ end
97
+ end
99
98
 
100
99
  def metaclass(klass)
101
100
  klass.class_eval do
@@ -6,12 +6,14 @@ require 'pulse-meter/sensor/hashed_counter'
6
6
  require 'pulse-meter/sensor/hashed_indicator'
7
7
  require 'pulse-meter/sensor/multi'
8
8
  require 'pulse-meter/sensor/uniq_counter'
9
+ require 'pulse-meter/sensor/timeline_reduce'
9
10
  require 'pulse-meter/sensor/timeline'
10
11
  require 'pulse-meter/sensor/timelined/average'
11
12
  require 'pulse-meter/sensor/timelined/counter'
12
13
  require 'pulse-meter/sensor/timelined/indicator'
13
14
  require 'pulse-meter/sensor/timelined/hashed_counter'
14
15
  require 'pulse-meter/sensor/timelined/hashed_indicator'
16
+ require 'pulse-meter/sensor/timelined/zset_based'
15
17
  require 'pulse-meter/sensor/timelined/min'
16
18
  require 'pulse-meter/sensor/timelined/max'
17
19
  require 'pulse-meter/sensor/timelined/percentile'
@@ -6,9 +6,9 @@ module PulseMeter
6
6
  # one value for each consequent time interval.
7
7
  class Timeline < Base
8
8
  include PulseMeter::Mixins::Utils
9
+ include PulseMeter::Sensor::TimelineReduce
9
10
 
10
11
  MAX_TIMESPAN_POINTS = 1000
11
- MAX_INTERVALS = 100
12
12
 
13
13
  # @!attribute [r] interval
14
14
  # @return [Fixnum] Rotation interval
@@ -64,51 +64,6 @@ module PulseMeter
64
64
  false
65
65
  end
66
66
 
67
- # Reduces data in given interval.
68
- # @note Interval id is
69
- # just unixtime of its lower bound. Ruduction is a process
70
- # of 'compressing' all interval's raw data to a single value.
71
- # When reduction is done summarized data is saved to Redis
72
- # separately with expiration time taken from sensor configuration.
73
- # @param interval_id [Fixnum]
74
- def reduce(interval_id)
75
- interval_raw_data_key = raw_data_key(interval_id)
76
- return unless redis.exists(interval_raw_data_key)
77
- value = summarize(interval_raw_data_key)
78
- interval_data_key = data_key(interval_id)
79
- multi do
80
- redis.del(interval_raw_data_key)
81
- if redis.setnx(interval_data_key, value)
82
- redis.expire(interval_data_key, ttl)
83
- end
84
- end
85
- end
86
-
87
- # Reduces data in all raw interval
88
- def reduce_all_raw
89
- time = Time.now
90
- min_time = time - reduce_delay - interval
91
- max_depth = time - reduce_delay - interval * MAX_INTERVALS
92
- ids = []
93
- while (time > max_depth)
94
- time -= interval
95
- interval_id = get_interval_id(time)
96
- next if Time.at(interval_id) > min_time
97
-
98
- reduced_key = data_key(interval_id)
99
- raw_key = raw_data_key(interval_id)
100
- break if redis.exists(reduced_key)
101
- ids << interval_id
102
- end
103
- ids.reverse.each {|id| reduce(id)}
104
- end
105
-
106
- def self.reduce_all_raw
107
- list_objects.each do |sensor|
108
- sensor.reduce_all_raw if sensor.respond_to? :reduce_all_raw
109
- end
110
- end
111
-
112
67
  # Returts sensor data within some last seconds
113
68
  # @param time_ago [Fixnum] interval length in seconds
114
69
  # @return [Array<SensorData>]
@@ -128,31 +83,12 @@ module PulseMeter
128
83
  def timeline_within(from, till, skip_optimization = false)
129
84
  raise ArgumentError unless from.kind_of?(Time) && till.kind_of?(Time)
130
85
  start_time, end_time = from.to_i, till.to_i
131
- actual_interval = if skip_optimization
132
- interval
133
- else
134
- optimized_interval(start_time, end_time)
135
- end
136
- current_interval_id = get_interval_id(start_time) + actual_interval
137
- keys = []
138
- ids = []
139
- while current_interval_id < end_time
140
- ids << current_interval_id
141
- keys << data_key(current_interval_id)
142
- current_interval_id += actual_interval
143
- end
144
- values = keys.empty? ? [] : redis.mget(*keys)
145
- res = []
146
- ids.zip(values) do |(id, val)|
147
- res << if val.nil?
148
- get_raw_value(id)
149
- else
150
- sensor_data(id, val)
151
- end
152
- end
153
- res
86
+ actual_interval = optimized_interval(start_time, end_time, skip_optimization)
87
+ start_interval_id = get_interval_id(start_time) + actual_interval
88
+ ids, values = fetch_reduced_interval_data(start_interval_id, actual_interval, end_time)
89
+ zip_with_raw_data(ids, values)
154
90
  end
155
-
91
+
156
92
  # Returns sensor data for given interval making in-memory summarization
157
93
  # and returns calculated value
158
94
  # @param interval_id [Fixnum]
@@ -261,8 +197,9 @@ module PulseMeter
261
197
  # @param start_time [Fixnum] unix timestamp of timespan start
262
198
  # @param end_time [Fixnum] unix timestamp of timespan start
263
199
  # @return [Fixnum] optimized interval in seconds.
264
- def optimized_interval(start_time, end_time)
200
+ def optimized_interval(start_time, end_time, skip_optimization = false)
265
201
  res_interval = interval
202
+ return res_interval if skip_optimization
266
203
  timespan = end_time - start_time
267
204
  while timespan / res_interval > MAX_TIMESPAN_POINTS - 1
268
205
  res_interval *= 2
@@ -270,7 +207,30 @@ module PulseMeter
270
207
  res_interval
271
208
  end
272
209
 
210
+ def fetch_reduced_interval_data(start_interval_id, actual_interval, end_time)
211
+ keys = []
212
+ ids = []
213
+ current_interval_id = start_interval_id
214
+ while current_interval_id < end_time
215
+ ids << current_interval_id
216
+ keys << data_key(current_interval_id)
217
+ current_interval_id += actual_interval
218
+ end
219
+ values = keys.empty? ? [] : redis.mget(*keys)
220
+ [ids, values]
221
+ end
273
222
 
223
+ def zip_with_raw_data(ids, values)
224
+ res = []
225
+ ids.zip(values) do |(id, val)|
226
+ res << if val.nil?
227
+ get_raw_value(id)
228
+ else
229
+ sensor_data(id, val)
230
+ end
231
+ end
232
+ res
233
+ end
274
234
  end
275
235
  end
276
236
  end
@@ -0,0 +1,68 @@
1
+ module PulseMeter
2
+ module Sensor
3
+ # Methods for reducing raw data to single values
4
+ module TimelineReduce
5
+
6
+ def self.included(base)
7
+ base.extend(ClassMethods)
8
+ end
9
+
10
+ MAX_INTERVALS = 100
11
+
12
+ # @note Interval id is
13
+ # just unixtime of its lower bound. Ruduction is a process
14
+ # of 'compressing' all interval's raw data to a single value.
15
+ # When reduction is done summarized data is saved to Redis
16
+ # separately with expiration time taken from sensor configuration.
17
+ # @param interval_id [Fixnum]
18
+ def reduce(interval_id)
19
+ interval_raw_data_key = raw_data_key(interval_id)
20
+ return unless redis.exists(interval_raw_data_key)
21
+ value = summarize(interval_raw_data_key)
22
+ interval_data_key = data_key(interval_id)
23
+ multi do
24
+ redis.del(interval_raw_data_key)
25
+ if redis.setnx(interval_data_key, value)
26
+ redis.expire(interval_data_key, ttl)
27
+ end
28
+ end
29
+ end
30
+
31
+ # Reduces data in all raw intervals
32
+ def reduce_all_raw
33
+ time = Time.now
34
+ min_time = time - reduce_delay - interval
35
+ max_depth = time - reduce_delay - interval * MAX_INTERVALS
36
+ ids = collect_ids_to_reduce(time, max_depth, min_time)
37
+ ids.reverse.each {|id| reduce(id)}
38
+ end
39
+
40
+ def collect_ids_to_reduce(time, time_from, time_to)
41
+ ids = []
42
+ while (time > time_from) # go backwards
43
+ time -= interval
44
+ interval_id = get_interval_id(time)
45
+ next if Time.at(interval_id) > time_to
46
+
47
+ reduced_key = data_key(interval_id)
48
+ raw_key = raw_data_key(interval_id)
49
+ break if redis.exists(reduced_key)
50
+ ids << interval_id
51
+ end
52
+ ids
53
+ end
54
+
55
+ module ClassMethods
56
+
57
+ def reduce_all_raw
58
+ list_objects.each do |sensor|
59
+ sensor.reduce_all_raw if sensor.respond_to? :reduce_all_raw
60
+ end
61
+ end
62
+
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+
@@ -2,27 +2,15 @@ module PulseMeter
2
2
  module Sensor
3
3
  module Timelined
4
4
  # Calculates max value in interval
5
- class Max < Timeline
5
+ class Max < ZSetBased
6
6
 
7
- def aggregate_event(key, value)
8
- command_aggregator.zadd(key, value, "#{value}::#{uniqid}")
7
+ def update(key)
9
8
  command_aggregator.zremrangebyrank(key, 0, -2)
10
9
  end
11
10
 
12
- def summarize(key)
13
- count = redis.zcard(key)
14
- if count > 0
15
- max_el = redis.zrange(key, -1, -1)[0]
16
- redis.zscore(key, max_el)
17
- else
18
- nil
19
- end
20
- end
21
-
22
- private
23
-
24
- def deflate(value)
25
- value.to_f
11
+ def calculate(key, _)
12
+ max_el = redis.zrange(key, -1, -1)[0]
13
+ redis.zscore(key, max_el)
26
14
  end
27
15
 
28
16
  end
@@ -2,27 +2,15 @@ module PulseMeter
2
2
  module Sensor
3
3
  module Timelined
4
4
  # Calculates min value in interval
5
- class Min < Timeline
5
+ class Min < ZSetBased
6
6
 
7
- def aggregate_event(key, value)
8
- command_aggregator.zadd(key, value, "#{value}::#{uniqid}")
7
+ def update(key)
9
8
  command_aggregator.zremrangebyrank(key, 1, -1)
10
9
  end
11
10
 
12
- def summarize(key)
13
- count = redis.zcard(key)
14
- if count > 0
15
- min_el = redis.zrange(key, 0, 0)[0]
16
- redis.zscore(key, min_el)
17
- else
18
- nil
19
- end
20
- end
21
-
22
- private
23
-
24
- def deflate(value)
25
- value.to_f
11
+ def calculate(key, _)
12
+ min_el = redis.zrange(key, 0, 0)[0]
13
+ redis.zscore(key, min_el)
26
14
  end
27
15
 
28
16
  end
@@ -2,7 +2,7 @@ module PulseMeter
2
2
  module Sensor
3
3
  module Timelined
4
4
  # Calculates n'th percentile in interval
5
- class MultiPercentile < Timeline
5
+ class MultiPercentile < ZSetBased
6
6
  attr_reader :p_value
7
7
 
8
8
  def initialize(name, options)
@@ -11,22 +11,13 @@ module PulseMeter
11
11
  super(name, options)
12
12
  end
13
13
 
14
- def aggregate_event(key, value)
15
- command_aggregator.zadd(key, value, "#{value}::#{uniqid}")
16
- end
17
-
18
- def summarize(key)
19
- @p_value.each_with_object({}) do |p, acc|
20
- count = redis.zcard(key)
21
- percentile = if count > 0
22
- position = p > 0 ? (p * count).round - 1 : 0
23
- el = redis.zrange(key, position, position)[0]
24
- redis.zscore(key, el)
25
- else
26
- nil
27
- end
28
- acc[p] = percentile
29
- end.to_json
14
+ def calculate(key, count)
15
+ count =
16
+ @p_value.each_with_object({}) { |p, acc|
17
+ position = p > 0 ? (p * count).round - 1 : 0
18
+ el = redis.zrange(key, position, position)[0]
19
+ acc[p] = redis.zscore(key, el)
20
+ }.to_json
30
21
  end
31
22
 
32
23
  private
@@ -2,7 +2,7 @@ module PulseMeter
2
2
  module Sensor
3
3
  module Timelined
4
4
  # Calculates n'th percentile in interval
5
- class Percentile < Timeline
5
+ class Percentile < ZSetBased
6
6
  attr_reader :p_value
7
7
 
8
8
  def initialize(name, options)
@@ -10,25 +10,10 @@ module PulseMeter
10
10
  super(name, options)
11
11
  end
12
12
 
13
- def aggregate_event(key, value)
14
- command_aggregator.zadd(key, value, "#{value}::#{uniqid}")
15
- end
16
-
17
- def summarize(key)
18
- count = redis.zcard(key)
19
- if count > 0
20
- position = @p_value > 0 ? (@p_value * count).round - 1 : 0
21
- el = redis.zrange(key, position, position)[0]
22
- redis.zscore(key, el)
23
- else
24
- nil
25
- end
26
- end
27
-
28
- private
29
-
30
- def deflate(value)
31
- value.to_f
13
+ def calculate(key, count)
14
+ position = @p_value > 0 ? (@p_value * count).round - 1 : 0
15
+ el = redis.zrange(key, position, position)[0]
16
+ redis.zscore(key, el)
32
17
  end
33
18
 
34
19
  end
@@ -0,0 +1,38 @@
1
+ module PulseMeter
2
+ module Sensor
3
+ module Timelined
4
+ # Calculates min value in interval
5
+ class ZSetBased < Timeline
6
+
7
+ def update(_)
8
+ end
9
+
10
+ def calculate(key)
11
+ 0
12
+ end
13
+
14
+ def aggregate_event(key, value)
15
+ command_aggregator.zadd(key, value, "#{value}::#{uniqid}")
16
+ update(key)
17
+ end
18
+
19
+ def summarize(key)
20
+ count = redis.zcard(key)
21
+ if count > 0
22
+ calculate(key, count)
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def deflate(value)
31
+ value.to_f
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
38
+
@@ -1,3 +1,3 @@
1
1
  module PulseMeter
2
- VERSION = "0.4.1"
2
+ VERSION = "0.4.2"
3
3
  end
@@ -38,3 +38,14 @@
38
38
  #dynamic-plotarea {
39
39
  }
40
40
 
41
+ .ui-timepicker-div .ui-widget-header { margin-bottom: 8px; }
42
+ .ui-timepicker-div dl { text-align: left; }
43
+ .ui-timepicker-div dl dt { height: 25px; margin-bottom: -25px; }
44
+ .ui-timepicker-div dl dd { margin: 0 10px 10px 65px; }
45
+ .ui-timepicker-div td { font-size: 90%; }
46
+ .ui-tpicker-grid-label { background: none; border: none; margin: 0; padding: 0; }
47
+
48
+ .modal-body hr {
49
+ margin-top: 0px;
50
+ margin-bottom: 4px;
51
+ }
@@ -25,22 +25,6 @@ module PulseMeter
25
25
 
26
26
  protected
27
27
 
28
- def ensure_sensor_match!
29
- intervals = []
30
- sensors.each do |s|
31
- unless s.type < PulseMeter::Sensor::Timeline
32
- raise NotATimelinedSensorInWidget, "sensor `#{s.name}' is not timelined"
33
- end
34
- intervals << s.interval
35
- end
36
-
37
- unless intervals.all?{|i| i == intervals.first}
38
- interval_notice = sensors.map{|s| "#{s.name}: #{s.interval}"}.join(', ')
39
- raise DifferentSensorIntervalsInWidget, "Sensors with different intervals in a single widget: #{interval_notice}"
40
- end
41
- end
42
-
43
-
44
28
  def gauge_series_data
45
29
  ensure_gauge_indicators!
46
30
  sensors.map do |s|
@@ -43,7 +43,7 @@ module PulseMeter
43
43
  end
44
44
 
45
45
  def series_data(from, till)
46
- ensure_sensor_match!
46
+ ensure_equal_intervals!
47
47
  sensor_datas = sensors.map{ |s|
48
48
  s.timeline_data(from, till, show_last_point)
49
49
  }
@@ -73,7 +73,7 @@ module PulseMeter
73
73
  }
74
74
  end
75
75
 
76
- def ensure_sensor_match!
76
+ def ensure_equal_intervals!
77
77
  intervals = []
78
78
  sensors.each do |s|
79
79
  unless s.type < PulseMeter::Sensor::Timeline
@@ -1,16 +1,21 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe PulseMeter::Mixins::Cmd do
4
- class Dummy
5
- extend PulseMeter::Mixins::Cmd
4
+ class CmdDummy
5
+ include PulseMeter::Mixins::Cmd
6
6
 
7
- def self.options
8
- {host: :localhost, port: 6379, db: 0}
7
+ def initialize(redis)
8
+ @redis = redis
9
+ end
10
+
11
+ def create_redis
12
+ @redis
9
13
  end
10
14
  end
11
15
 
12
- let(:dummy){ Dummy }
13
- before {PulseMeter.redis = Redis.new}
16
+ let(:redis){ MockRedis.new }
17
+ let(:dummy){ CmdDummy.new(redis) }
18
+ before{ PulseMeter.redis = redis }
14
19
 
15
20
  describe "#fail!" do
16
21
  it "prints given message and exits" do
@@ -181,4 +181,24 @@ describe PulseMeter::Mixins::Utils do
181
181
  subsets.sort.should == [[], [1], [2], [1, 2]].sort
182
182
  end
183
183
  end
184
+
185
+ describe '#parse_time' do
186
+ context "when argument is a valid YYYYmmddHHMMSS string" do
187
+ it "should correct Time object" do
188
+ t = dummy.parse_time("19700101000000")
189
+ t.should be_kind_of(Time)
190
+ t.to_i.should == 0
191
+ end
192
+ end
193
+ context "when argument is an invalid YYYYmmddHHMMSS string" do
194
+ it "should raise ArgumentError" do
195
+ expect{ dummy.parse_time("19709901000000") }.to raise_exception(ArgumentError)
196
+ end
197
+ end
198
+ context "when argument is not a YYYYmmddHHMMSS string" do
199
+ it "should raise ArgumentError" do
200
+ expect{ dummy.parse_time("197099010000000") }.to raise_exception(ArgumentError)
201
+ end
202
+ end
203
+ end
184
204
  end
@@ -4,7 +4,7 @@ describe PulseMeter::Observer do
4
4
 
5
5
  context "instance methods observation" do
6
6
 
7
- class Dummy
7
+ class ObservedDummy
8
8
  attr_reader :count
9
9
 
10
10
  def initialize
@@ -23,20 +23,20 @@ describe PulseMeter::Observer do
23
23
  end
24
24
  end
25
25
 
26
- let!(:dummy) {Dummy.new}
26
+ let!(:dummy) {ObservedDummy.new}
27
27
  let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
28
28
  before do
29
- [:incr, :error].each {|m| described_class.unobserve_method(Dummy, m)}
29
+ [:incr, :error].each {|m| described_class.unobserve_method(ObservedDummy, m)}
30
30
  end
31
31
 
32
32
  def create_observer(method = :incr, increment = 1)
33
- described_class.observe_method(Dummy, method, sensor) do |*args|
33
+ described_class.observe_method(ObservedDummy, method, sensor) do |*args|
34
34
  event(increment)
35
35
  end
36
36
  end
37
37
 
38
38
  def remove_observer(method = :incr)
39
- described_class.unobserve_method(Dummy, method)
39
+ described_class.unobserve_method(ObservedDummy, method)
40
40
  end
41
41
 
42
42
  describe ".observe_method" do
@@ -53,7 +53,7 @@ describe PulseMeter::Observer do
53
53
  end
54
54
 
55
55
  it "passes methods' params to block" do
56
- described_class.observe_method(Dummy, :incr, sensor) do |time, cnt|
56
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |time, cnt|
57
57
  event(cnt)
58
58
  end
59
59
 
@@ -63,7 +63,7 @@ describe PulseMeter::Observer do
63
63
 
64
64
  it "passes execution time in milliseconds to block" do
65
65
  Timecop.freeze do
66
- described_class.observe_method(Dummy, :incr, sensor) do |time, cnt|
66
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |time, cnt|
67
67
  event(time)
68
68
  end
69
69
 
@@ -73,7 +73,7 @@ describe PulseMeter::Observer do
73
73
  end
74
74
 
75
75
  it "does not break observed method even is observer raises error" do
76
- described_class.observe_method(Dummy, :incr, sensor) do |*args|
76
+ described_class.observe_method(ObservedDummy, :incr, sensor) do |*args|
77
77
  raise RuntimeError
78
78
  end
79
79
 
@@ -125,7 +125,7 @@ describe PulseMeter::Observer do
125
125
 
126
126
  context "class methods observation" do
127
127
 
128
- class Dummy
128
+ class ObservedDummy
129
129
  @@count = 0
130
130
  class << self
131
131
  def count
@@ -149,21 +149,21 @@ describe PulseMeter::Observer do
149
149
  end
150
150
  end
151
151
 
152
- let!(:dummy) {Dummy}
152
+ let!(:dummy) {ObservedDummy}
153
153
  let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
154
154
  before do
155
155
  dummy.reset
156
- [:incr, :error].each {|m| described_class.unobserve_class_method(Dummy, m)}
156
+ [:incr, :error].each {|m| described_class.unobserve_class_method(ObservedDummy, m)}
157
157
  end
158
158
 
159
159
  def create_observer(method = :incr, increment = 1)
160
- described_class.observe_class_method(Dummy, method, sensor) do |*args|
160
+ described_class.observe_class_method(ObservedDummy, method, sensor) do |*args|
161
161
  event(increment)
162
162
  end
163
163
  end
164
164
 
165
165
  def remove_observer(method = :incr)
166
- described_class.unobserve_class_method(Dummy, method)
166
+ described_class.unobserve_class_method(ObservedDummy, method)
167
167
  end
168
168
 
169
169
  describe ".observe_class_method" do
@@ -180,7 +180,7 @@ describe PulseMeter::Observer do
180
180
  end
181
181
 
182
182
  it "passes methods' params to block" do
183
- described_class.observe_class_method(Dummy, :incr, sensor) do |time, cnt|
183
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |time, cnt|
184
184
  event(cnt)
185
185
  end
186
186
 
@@ -190,7 +190,7 @@ describe PulseMeter::Observer do
190
190
 
191
191
  it "passes execution time in milliseconds to block" do
192
192
  Timecop.freeze do
193
- described_class.observe_class_method(Dummy, :incr, sensor) do |time, cnt|
193
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |time, cnt|
194
194
  event(time)
195
195
  end
196
196
 
@@ -200,7 +200,7 @@ describe PulseMeter::Observer do
200
200
  end
201
201
 
202
202
  it "does not break observed method even is observer raises error" do
203
- described_class.observe_class_method(Dummy, :incr, sensor) do |*args|
203
+ described_class.observe_class_method(ObservedDummy, :incr, sensor) do |*args|
204
204
  raise RuntimeError
205
205
  end
206
206
 
data/spec/spec_helper.rb CHANGED
@@ -10,6 +10,8 @@ SimpleCov.start
10
10
 
11
11
  require 'pulse-meter'
12
12
  require 'pulse-meter/visualizer'
13
+ PulseMeter.redis = MockRedis.new
14
+
13
15
  require 'rack/test'
14
16
 
15
17
  Dir['spec/support/**/*.rb'].each{|f| require File.join(ROOT, f) }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pulse-meter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
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: 2012-11-11 00:00:00.000000000 Z
13
+ date: 2013-02-02 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: gon-sinatra
@@ -429,6 +429,7 @@ files:
429
429
  - lib/pulse-meter/sensor/indicator.rb
430
430
  - lib/pulse-meter/sensor/multi.rb
431
431
  - lib/pulse-meter/sensor/timeline.rb
432
+ - lib/pulse-meter/sensor/timeline_reduce.rb
432
433
  - lib/pulse-meter/sensor/timelined/average.rb
433
434
  - lib/pulse-meter/sensor/timelined/counter.rb
434
435
  - lib/pulse-meter/sensor/timelined/hashed_counter.rb
@@ -440,6 +441,7 @@ files:
440
441
  - lib/pulse-meter/sensor/timelined/multi_percentile.rb
441
442
  - lib/pulse-meter/sensor/timelined/percentile.rb
442
443
  - lib/pulse-meter/sensor/timelined/uniq_counter.rb
444
+ - lib/pulse-meter/sensor/timelined/zset_based.rb
443
445
  - lib/pulse-meter/sensor/uniq_counter.rb
444
446
  - lib/pulse-meter/server.rb
445
447
  - lib/pulse-meter/server/command_line_options.rb
@@ -605,6 +607,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
605
607
  - - ! '>='
606
608
  - !ruby/object:Gem::Version
607
609
  version: '0'
610
+ segments:
611
+ - 0
612
+ hash: -1795310197501122422
608
613
  requirements: []
609
614
  rubyforge_project:
610
615
  rubygems_version: 1.8.23