pulse-meter 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
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