pulse-meter 0.3.2 → 0.4.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/lib/pulse-meter.rb +29 -1
- data/lib/pulse-meter/command_aggregator/async.rb +1 -1
- data/lib/pulse-meter/sensor/configuration.rb +66 -31
- data/lib/pulse-meter/sensor/multi.rb +12 -8
- data/lib/pulse-meter/version.rb +1 -1
- data/spec/pulse_meter/sensor/configuration_spec.rb +18 -23
- data/spec/pulse_meter/sensor/multi_spec.rb +10 -7
- data/spec/pulse_meter_spec.rb +19 -0
- data/spec/spec_helper.rb +1 -0
- metadata +2 -5
data/lib/pulse-meter.rb
CHANGED
@@ -14,18 +14,25 @@ require "pulse-meter/command_aggregator/sync"
|
|
14
14
|
module PulseMeter
|
15
15
|
@@redis = nil
|
16
16
|
|
17
|
+
# Returns global Redis client
|
17
18
|
def self.redis
|
18
19
|
@@redis
|
19
20
|
end
|
20
21
|
|
22
|
+
# Sets global Redis client
|
23
|
+
# @param redis [Redis] redis client
|
21
24
|
def self.redis=(redis)
|
22
25
|
@@redis = redis
|
23
26
|
end
|
24
27
|
|
28
|
+
# Returns global command aggegator (i.e. object that accumulates Redis commands emitted by events and sends them into client)
|
25
29
|
def self.command_aggregator
|
26
30
|
@@command_aggregator ||= PulseMeter::CommandAggregator::Async.instance
|
27
31
|
end
|
28
|
-
|
32
|
+
|
33
|
+
# Sets global command_aggregator
|
34
|
+
# @param type [Symbol] type of command aggegator (:async or :sync)
|
35
|
+
# @raise [ArgumentError] if type is none of :async, :sync
|
29
36
|
def self.command_aggregator=(command_aggregator)
|
30
37
|
@@command_aggregator = case command_aggregator
|
31
38
|
when :sync; PulseMeter::CommandAggregator::Sync.instance
|
@@ -33,4 +40,25 @@ module PulseMeter
|
|
33
40
|
else raise ArgumentError
|
34
41
|
end
|
35
42
|
end
|
43
|
+
|
44
|
+
# Sets global logger for all PulseMeter error messages
|
45
|
+
# @param logger [Logger] logger to be used
|
46
|
+
def self.logger=(new_logger)
|
47
|
+
@@logger = new_logger
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns global PulseMeter logger
|
51
|
+
def self.logger
|
52
|
+
@@logger ||= begin
|
53
|
+
logger = Logger.new($stderr)
|
54
|
+
logger.datetime_format = '%Y-%m-%d %H:%M:%S.%3N'
|
55
|
+
logger
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Sends error message to PulseMeter logger
|
60
|
+
# @param message [String] error message
|
61
|
+
def self.error(msg)
|
62
|
+
logger.error(msg)
|
63
|
+
end
|
36
64
|
end
|
@@ -5,59 +5,86 @@ module PulseMeter
|
|
5
5
|
include PulseMeter::Mixins::Utils
|
6
6
|
include Enumerable
|
7
7
|
|
8
|
-
# @!attribute [r] sensors
|
9
|
-
# @return [Hash] hash of sensors with names as keys and sensors as values
|
10
|
-
attr_reader :sensors
|
11
|
-
|
12
8
|
# Initializes sensors
|
13
9
|
# @param opts [Hash] sensors' configuration
|
14
10
|
def initialize(opts = {})
|
15
|
-
@
|
16
|
-
|
17
|
-
|
11
|
+
@opts = opts
|
12
|
+
end
|
13
|
+
|
14
|
+
# Returns previously initialized sensor by name
|
15
|
+
# @param name [Symbol] sensor name
|
16
|
+
# @yield [sensor] Gives sensor(if it is found) to the block
|
17
|
+
def sensor(name)
|
18
|
+
raise ArgumentError, "need a block" unless block_given?
|
19
|
+
with_resque do
|
20
|
+
s = sensors[name.to_s]
|
21
|
+
yield(s) if s
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
|
-
#
|
25
|
+
# Returns true value if sensor with specified name exists in configuration, false otherwise
|
22
26
|
# @param name [Symbol] sensor name
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
args = (opts.respond_to?(:args) ? opts.args : opts[:args]) || {}
|
30
|
-
@sensors[name.to_s] = klass.new(name, symbolize_keys(args.to_hash))
|
27
|
+
def has_sensor?(name)
|
28
|
+
has_sensor = false
|
29
|
+
with_resque do
|
30
|
+
has_sensor = sensors.has_key?(name)
|
31
|
+
end
|
32
|
+
has_sensor
|
31
33
|
end
|
32
34
|
|
33
|
-
#
|
35
|
+
# Adds sensor
|
34
36
|
# @param name [Symbol] sensor name
|
35
|
-
# @
|
36
|
-
def
|
37
|
-
|
37
|
+
# @param opts [Hash] sensor options
|
38
|
+
def add_sensor(name, opts)
|
39
|
+
with_resque do
|
40
|
+
sensors[name.to_s] = create_sensor(name, opts)
|
41
|
+
end
|
38
42
|
end
|
39
43
|
|
40
44
|
# Iterates over each sensor
|
41
45
|
def each
|
42
|
-
|
43
|
-
|
46
|
+
with_resque do
|
47
|
+
sensors.each_value do |s|
|
48
|
+
yield(s)
|
49
|
+
end
|
44
50
|
end
|
45
51
|
end
|
46
52
|
|
47
|
-
# Invokes event for any sensor
|
53
|
+
# Invokes event(_at) for any sensor
|
48
54
|
# @raise [ArgumentError] unless sensor exists
|
49
55
|
def method_missing(name, *args)
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
56
|
+
with_resque do
|
57
|
+
name = name.to_s
|
58
|
+
if sensors.has_key?(name)
|
59
|
+
sensors[name].event(*args)
|
60
|
+
elsif name =~ /\A(.*)_at\z/
|
61
|
+
sensor_name = $1
|
62
|
+
sensors[sensor_name].event_at(*args) if sensors.has_key?(sensor_name)
|
63
|
+
end
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
|
-
|
67
|
+
private
|
68
|
+
|
69
|
+
def with_resque
|
70
|
+
yield
|
71
|
+
rescue StandardError => e
|
72
|
+
PulseMeter.error "Configuration error: #{e}, #{e.backtrace.join("\n")}"
|
73
|
+
nil
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
# Tries to create a specific sensor
|
78
|
+
# @param name [Symbol] sensor name
|
79
|
+
# @param opts [Hash] sensor options
|
80
|
+
def create_sensor(name, opts)
|
81
|
+
sensor_type = opts.respond_to?(:sensor_type) ? opts.sensor_type : opts[:sensor_type]
|
82
|
+
klass_s = sensor_class(sensor_type)
|
83
|
+
klass = constantize(klass_s)
|
84
|
+
raise ArgumentError, "#{klass_s} is not a valid class for a sensor" unless klass
|
85
|
+
args = (opts.respond_to?(:args) ? opts.args : opts[:args]) || {}
|
86
|
+
klass.new(name, symbolize_keys(args.to_hash))
|
87
|
+
end
|
61
88
|
|
62
89
|
def sensor_class(sensor_type)
|
63
90
|
entries = sensor_type.to_s.split('/').map do |entry|
|
@@ -66,6 +93,14 @@ module PulseMeter
|
|
66
93
|
entries.unshift('PulseMeter::Sensor').join('::')
|
67
94
|
end
|
68
95
|
|
96
|
+
# Lazy collection of sensors, specified by opts
|
97
|
+
# @raise [ArgumentError] unless one of the sensors exists
|
98
|
+
def sensors
|
99
|
+
@sensors ||= @opts.each_with_object({}){ |(name, opts), sensor_acc|
|
100
|
+
sensor_acc[name.to_s] = create_sensor(name, opts)
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
69
104
|
end
|
70
105
|
end
|
71
106
|
end
|
@@ -20,7 +20,8 @@ module PulseMeter
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def sensor(name)
|
23
|
-
|
23
|
+
raise ArgumentError, 'need a block' unless block_given?
|
24
|
+
sensors.sensor(name){|s| yield(s)}
|
24
25
|
end
|
25
26
|
|
26
27
|
def event(factors_hash, value)
|
@@ -28,8 +29,9 @@ module PulseMeter
|
|
28
29
|
|
29
30
|
each_factors_combination do |combination|
|
30
31
|
factor_values = factor_values_for_combination(combination, factors_hash)
|
31
|
-
|
32
|
-
|
32
|
+
get_or_create_sensor(combination, factor_values) do |s|
|
33
|
+
s.event(value)
|
34
|
+
end
|
33
35
|
end
|
34
36
|
end
|
35
37
|
|
@@ -38,9 +40,8 @@ module PulseMeter
|
|
38
40
|
end
|
39
41
|
|
40
42
|
def sensor_for_factors(factor_names, factor_values)
|
41
|
-
|
42
|
-
|
43
|
-
)
|
43
|
+
raise ArgumentError, 'need a block' unless block_given?
|
44
|
+
sensor(get_sensor_name(factor_names, factor_values)){|s| yield(s)}
|
44
45
|
end
|
45
46
|
|
46
47
|
protected
|
@@ -50,12 +51,15 @@ module PulseMeter
|
|
50
51
|
end
|
51
52
|
|
52
53
|
def get_or_create_sensor(factor_names, factor_values)
|
54
|
+
raise ArgumentError, 'need a block' unless block_given?
|
53
55
|
name = get_sensor_name(factor_names, factor_values)
|
54
|
-
unless
|
56
|
+
unless sensors.has_sensor?(name)
|
55
57
|
sensors.add_sensor(name, configuration_options)
|
56
58
|
dump!(false)
|
57
59
|
end
|
58
|
-
sensor(name)
|
60
|
+
sensor(name) do |s|
|
61
|
+
yield(s)
|
62
|
+
end
|
59
63
|
end
|
60
64
|
|
61
65
|
def ensure_valid_factors!(factors_hash)
|
data/lib/pulse-meter/version.rb
CHANGED
@@ -16,38 +16,39 @@ describe PulseMeter::Sensor::Configuration do
|
|
16
16
|
let(:cfg) {described_class.new}
|
17
17
|
|
18
18
|
it "should create sensor available under passed name" do
|
19
|
-
cfg.
|
19
|
+
cfg.has_sensor?(:foo).should be_false
|
20
20
|
cfg.add_sensor(:foo, sensor_type: 'counter')
|
21
|
-
cfg.
|
21
|
+
cfg.has_sensor?(:foo).should_not be_true
|
22
22
|
end
|
23
23
|
|
24
24
|
it "should have event shortcut for the sensor" do
|
25
25
|
cfg.add_sensor(:foo, sensor_type: 'counter')
|
26
|
-
|
27
|
-
sensor.should_receive(:event).with(321)
|
26
|
+
puts cfg.to_yaml
|
27
|
+
cfg.sensor(:foo){|s| s.should_receive(:event).with(321)}
|
28
28
|
cfg.foo(321)
|
29
29
|
end
|
30
30
|
|
31
31
|
it "should have event_at shortcut for the sensor" do
|
32
32
|
cfg.add_sensor(:foo, sensor_type: 'counter')
|
33
|
-
sensor = cfg.sensor(:foo)
|
34
33
|
now = Time.now
|
35
|
-
sensor
|
34
|
+
cfg.sensor(:foo) do |sensor|
|
35
|
+
sensor.should_receive(:event_at).with(now, 321)
|
36
|
+
end
|
36
37
|
cfg.foo_at(now, 321)
|
37
38
|
end
|
38
39
|
|
39
40
|
it "should create sensor with correct type" do
|
40
41
|
cfg.add_sensor(:foo, sensor_type: 'counter')
|
41
|
-
cfg.sensor(:foo).should be_kind_of(PulseMeter::Sensor::Counter)
|
42
|
+
cfg.sensor(:foo){|s| s.should be_kind_of(PulseMeter::Sensor::Counter)}
|
42
43
|
end
|
43
44
|
|
44
|
-
it "should raise exception if sensor type is bad" do
|
45
|
-
expect{ cfg.add_sensor(:foo, sensor_type: 'baaaar') }.
|
45
|
+
it "should not raise exception if sensor type is bad" do
|
46
|
+
expect{ cfg.add_sensor(:foo, sensor_type: 'baaaar') }.not_to raise_exception
|
46
47
|
end
|
47
48
|
|
48
49
|
it "should pass args to created sensor" do
|
49
50
|
cfg.add_sensor(:foo, sensor_type: 'counter', args: {annotation: "My Foo Counter"} )
|
50
|
-
cfg.sensor(:foo).annotation.should == "My Foo Counter"
|
51
|
+
cfg.sensor(:foo){|s| s.annotation.should == "My Foo Counter" }
|
51
52
|
end
|
52
53
|
|
53
54
|
it "should accept hashie-objects" do
|
@@ -61,7 +62,7 @@ describe PulseMeter::Sensor::Configuration do
|
|
61
62
|
end
|
62
63
|
|
63
64
|
cfg.add_sensor(:foo, Dummy.new)
|
64
|
-
cfg.sensor(:foo).annotation.should == "My Foo Counter"
|
65
|
+
cfg.sensor(:foo){|s| s.annotation.should == "My Foo Counter"}
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
@@ -78,22 +79,15 @@ describe PulseMeter::Sensor::Configuration do
|
|
78
79
|
cfg1 = described_class.new(opts)
|
79
80
|
cfg2 = described_class.new
|
80
81
|
opts.each{|k,v| cfg2.add_sensor(k, v)}
|
81
|
-
cfg1.to_yaml.should == cfg2.to_yaml
|
82
|
+
cfg1.sensors.to_yaml.should == cfg2.sensors.to_yaml
|
82
83
|
end
|
83
84
|
end
|
84
85
|
|
85
86
|
describe "#sensor" do
|
86
|
-
it "should give access to added sensors" do
|
87
|
-
cfg = described_class.new(counter_config)
|
88
|
-
cfg.sensor(:cnt).annotation.should == "MySensor"
|
89
|
-
cfg.sensor("cnt").annotation.should == "MySensor"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
describe "#sensors" do
|
94
|
-
it "returns hash of sensors" do
|
87
|
+
it "should give access to added sensors via block" do
|
95
88
|
cfg = described_class.new(counter_config)
|
96
|
-
cfg.
|
89
|
+
cfg.sensor(:cnt){ |s| s.annotation.should == "MySensor" }
|
90
|
+
cfg.sensor("cnt"){ |s| s.annotation.should == "MySensor" }
|
97
91
|
end
|
98
92
|
end
|
99
93
|
|
@@ -102,7 +96,8 @@ describe PulseMeter::Sensor::Configuration do
|
|
102
96
|
cfg = described_class.new(counter_config)
|
103
97
|
sensors = {}
|
104
98
|
cfg.each {|s| sensors[s.name.to_sym] = s}
|
105
|
-
|
99
|
+
sensor = cfg.sensor(:cnt){|s| s}
|
100
|
+
sensors.should == {:cnt => sensor}
|
106
101
|
end
|
107
102
|
end
|
108
103
|
end
|
@@ -95,8 +95,9 @@ describe PulseMeter::Sensor::Multi do
|
|
95
95
|
["#{name}_f1_f1v1_f2_f2v1", 1],
|
96
96
|
["#{name}_f1_f1v2_f2_f2v1", 2]
|
97
97
|
].each do |sensor_name, sum|
|
98
|
-
|
99
|
-
|
98
|
+
sensor.sensor(sensor_name) { |s|
|
99
|
+
s.value.should == sum
|
100
|
+
}
|
100
101
|
end
|
101
102
|
end
|
102
103
|
end
|
@@ -117,16 +118,18 @@ describe PulseMeter::Sensor::Multi do
|
|
117
118
|
|
118
119
|
describe "#sensor_for_factors" do
|
119
120
|
context "when sensor has already been created" do
|
120
|
-
it "
|
121
|
+
it "yields block with sensor for given combination of factors and their values" do
|
121
122
|
sensor.event({f1: :f1v1, f2: :f2v1}, 1)
|
122
|
-
sensor.sensor_for_factors([:f1, :f2], [:f1v1, :f2v1]).name.should == "#{name}_f1_f1v1_f2_f2v1"
|
123
|
-
sensor.sensor_for_factors([:f1], [:f1v1]).name.should == "#{name}_f1_f1v1"
|
123
|
+
sensor.sensor_for_factors([:f1, :f2], [:f1v1, :f2v1]){|s| s.name.should == "#{name}_f1_f1v1_f2_f2v1"}
|
124
|
+
sensor.sensor_for_factors([:f1], [:f1v1]){|s| s.name.should == "#{name}_f1_f1v1"}
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
127
128
|
context "when such a sensor was not created" do
|
128
|
-
it "
|
129
|
-
|
129
|
+
it "does not yields block" do
|
130
|
+
yielded = false
|
131
|
+
sensor.sensor_for_factors([:foo], [:bar]){ yielded = true }
|
132
|
+
yielded.should be_false
|
130
133
|
end
|
131
134
|
end
|
132
135
|
end
|
data/spec/pulse_meter_spec.rb
CHANGED
@@ -50,4 +50,23 @@ describe PulseMeter do
|
|
50
50
|
ca1.should == ca2
|
51
51
|
end
|
52
52
|
end
|
53
|
+
|
54
|
+
describe "::logger" do
|
55
|
+
it "should return PulseMeter logger" do
|
56
|
+
PulseMeter.logger = 123
|
57
|
+
PulseMeter.logger.should == 123
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should return default logger" do
|
61
|
+
PulseMeter.logger = nil
|
62
|
+
PulseMeter.logger.should be_kind_of(Logger)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "::error" do
|
67
|
+
it "should delegate error message to logger" do
|
68
|
+
PulseMeter.logger.should_receive(:error)
|
69
|
+
PulseMeter.error("foo")
|
70
|
+
end
|
71
|
+
end
|
53
72
|
end
|
data/spec/spec_helper.rb
CHANGED
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
|
+
version: 0.4.0
|
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-
|
13
|
+
date: 2012-11-11 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: gon-sinatra
|
@@ -605,9 +605,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
605
605
|
- - ! '>='
|
606
606
|
- !ruby/object:Gem::Version
|
607
607
|
version: '0'
|
608
|
-
segments:
|
609
|
-
- 0
|
610
|
-
hash: 284340155524977771
|
611
608
|
requirements: []
|
612
609
|
rubyforge_project:
|
613
610
|
rubygems_version: 1.8.23
|