pulse-meter 0.2.6 → 0.2.7
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 +3 -0
- data/lib/pulse-meter/mixins/cmd.rb +5 -1
- data/lib/pulse-meter/mixins/utils.rb +25 -0
- data/lib/pulse-meter/sensor/configuration.rb +9 -1
- data/lib/pulse-meter/sensor/multi.rb +93 -0
- data/lib/pulse-meter/sensor/timelined/hashed_indicator.rb +30 -0
- data/lib/pulse-meter/sensor/timelined/indicator.rb +23 -0
- data/lib/pulse-meter/sensor.rb +4 -1
- data/lib/pulse-meter/version.rb +1 -1
- data/spec/pulse_meter/mixins/utils_spec.rb +43 -0
- data/spec/pulse_meter/sensor/configuration_spec.rb +28 -11
- data/spec/pulse_meter/sensor/multi_spec.rb +134 -0
- data/spec/pulse_meter/sensor/timelined/hashed_indicator_spec.rb +8 -0
- data/spec/pulse_meter/sensor/timelined/indicator_spec.rb +6 -0
- metadata +12 -6
- data/lib/pulse-meter/sensor/remote.rb +0 -60
- data/spec/pulse_meter/sensor/remote_spec.rb +0 -86
data/README.md
CHANGED
@@ -15,6 +15,19 @@ module PulseMeter
|
|
15
15
|
nil
|
16
16
|
end
|
17
17
|
|
18
|
+
# Ensures that hash value specified by key is Array
|
19
|
+
# @param options [Hash] hash to be looked at
|
20
|
+
# @param key [Object] hash key
|
21
|
+
# @param default [Object] default value to be returned
|
22
|
+
# @raise [ArgumentError] unless value is Array
|
23
|
+
# @return [Array]
|
24
|
+
def assert_array!(options, key, default = nil)
|
25
|
+
value = options[key] || default
|
26
|
+
raise ArgumentError, "#{key} should be defined" unless value
|
27
|
+
raise ArgumentError, "#{key} should be array" unless value.is_a?(Array)
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
18
31
|
# Ensures that hash value specified by key can be converted to positive integer.
|
19
32
|
# In case it can makes in-place conversion and returns the value.
|
20
33
|
# @param options [Hash] hash to be looked at
|
@@ -98,6 +111,18 @@ module PulseMeter
|
|
98
111
|
item
|
99
112
|
end
|
100
113
|
end
|
114
|
+
|
115
|
+
# Yields block for each subset of given array
|
116
|
+
# @param array [Array] given array
|
117
|
+
def each_subset(array)
|
118
|
+
subsets_of(array).each {|subset| yield(subset)}
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returs all array's subsets
|
122
|
+
# @param array [Array]
|
123
|
+
def subsets_of(array)
|
124
|
+
0.upto(array.length).flat_map { |n| array.combination(n).to_a }
|
125
|
+
end
|
101
126
|
end
|
102
127
|
end
|
103
128
|
end
|
@@ -2,6 +2,9 @@ module PulseMeter
|
|
2
2
|
module Sensor
|
3
3
|
class Configuration
|
4
4
|
include PulseMeter::Mixins::Utils
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :sensors
|
5
8
|
|
6
9
|
def initialize(opts = {})
|
7
10
|
@sensors = {}
|
@@ -23,6 +26,12 @@ module PulseMeter
|
|
23
26
|
@sensors[name.to_s]
|
24
27
|
end
|
25
28
|
|
29
|
+
def each
|
30
|
+
@sensors.each_value do |sensor|
|
31
|
+
yield(sensor)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
26
35
|
def method_missing(name, *args)
|
27
36
|
name = name.to_s
|
28
37
|
if @sensors.has_key?(name)
|
@@ -43,7 +52,6 @@ module PulseMeter
|
|
43
52
|
entries.unshift('PulseMeter::Sensor').join('::')
|
44
53
|
end
|
45
54
|
|
46
|
-
|
47
55
|
end
|
48
56
|
end
|
49
57
|
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Sensor
|
3
|
+
class Multi < Base
|
4
|
+
include PulseMeter::Mixins::Utils
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
attr_reader :name
|
8
|
+
attr_reader :factors
|
9
|
+
attr_reader :sensors
|
10
|
+
attr_reader :configuration_options
|
11
|
+
|
12
|
+
# TODO restore in initializer
|
13
|
+
|
14
|
+
def initialize(name, options)
|
15
|
+
@name = name
|
16
|
+
@factors = assert_array!(options, :factors)
|
17
|
+
@sensors = PulseMeter::Sensor::Configuration.new
|
18
|
+
@configuration_options = options[:configuration]
|
19
|
+
raise ArgumentError, "configuration option missing" unless @configuration_options
|
20
|
+
end
|
21
|
+
|
22
|
+
def sensor(name)
|
23
|
+
sensors.sensor(name)
|
24
|
+
end
|
25
|
+
|
26
|
+
def event(factors_hash, value)
|
27
|
+
ensure_valid_factors!(factors_hash)
|
28
|
+
|
29
|
+
each_factors_combination do |combination|
|
30
|
+
factor_values = factor_values_for_combination(combination, factors_hash)
|
31
|
+
sensor = get_or_create_sensor(combination, factor_values)
|
32
|
+
sensor.event(value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def each
|
37
|
+
sensors.each {|s| yield(s)}
|
38
|
+
end
|
39
|
+
|
40
|
+
def sensor_for_factors(factor_names, factor_values)
|
41
|
+
sensor(
|
42
|
+
get_sensor_name(factor_names, factor_values)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
def is_subsensor?(sensor)
|
49
|
+
sensor.name.start_with?(get_sensor_name([], []).to_s)
|
50
|
+
end
|
51
|
+
|
52
|
+
def get_or_create_sensor(factor_names, factor_values)
|
53
|
+
name = get_sensor_name(factor_names, factor_values)
|
54
|
+
unless sensor(name)
|
55
|
+
sensors.add_sensor(name, configuration_options)
|
56
|
+
dump!(false)
|
57
|
+
end
|
58
|
+
sensor(name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def ensure_valid_factors!(factors_hash)
|
62
|
+
factors.each do |factor_name|
|
63
|
+
unless factors_hash.has_key?(factor_name)
|
64
|
+
raise ArgumentError, "Value of factor #{factor_name} missing"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def each_factors_combination
|
70
|
+
each_subset(factors) do |combination|
|
71
|
+
yield(combination)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def factor_values_for_combination(combination, factors_hash)
|
76
|
+
combination.each_with_object([]) do |k, acc|
|
77
|
+
acc << factors_hash[k]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def get_sensor_name(factor_names, factor_values)
|
82
|
+
sensor_name = name.to_s
|
83
|
+
unless factor_names.empty?
|
84
|
+
factor_names.zip(factor_values).each do |n, v|
|
85
|
+
sensor_name << "_#{n}_#{v}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
sensor_name.to_sym
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PulseMeter
|
4
|
+
module Sensor
|
5
|
+
module Timelined
|
6
|
+
# Saves last registered values for multiple flags per interval.
|
7
|
+
# Good replacement for multiple indicators to be visualized together
|
8
|
+
class HashedIndicator < Timeline
|
9
|
+
def aggregate_event(key, data)
|
10
|
+
data.each_pair do |k, v|
|
11
|
+
redis.hset(key, k, v) if v.respond_to?(:to_f)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def summarize(key)
|
16
|
+
redis.
|
17
|
+
hgetall(key).
|
18
|
+
inject({}) {|h, (k, v)| h[k] = v.to_f; h}.
|
19
|
+
to_json
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def deflate(value)
|
25
|
+
JSON.parse(value)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Sensor
|
3
|
+
module Timelined
|
4
|
+
# Saves last registered flag float value for each interval
|
5
|
+
class Indicator < Timeline
|
6
|
+
def aggregate_event(key, value)
|
7
|
+
redis.set(key, value.to_f)
|
8
|
+
end
|
9
|
+
|
10
|
+
def summarize(key)
|
11
|
+
redis.get(key)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def deflate(value)
|
17
|
+
value.to_f
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/pulse-meter/sensor.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'pulse-meter/sensor/base'
|
2
|
+
require 'pulse-meter/sensor/configuration'
|
2
3
|
require 'pulse-meter/sensor/counter'
|
3
4
|
require 'pulse-meter/sensor/indicator'
|
4
5
|
require 'pulse-meter/sensor/hashed_counter'
|
5
6
|
require 'pulse-meter/sensor/hashed_indicator'
|
6
|
-
require 'pulse-meter/sensor/
|
7
|
+
require 'pulse-meter/sensor/multi'
|
7
8
|
require 'pulse-meter/sensor/uniq_counter'
|
8
9
|
require 'pulse-meter/sensor/timeline'
|
9
10
|
require 'pulse-meter/sensor/timelined/average'
|
10
11
|
require 'pulse-meter/sensor/timelined/counter'
|
12
|
+
require 'pulse-meter/sensor/timelined/indicator'
|
11
13
|
require 'pulse-meter/sensor/timelined/hashed_counter'
|
14
|
+
require 'pulse-meter/sensor/timelined/hashed_indicator'
|
12
15
|
require 'pulse-meter/sensor/timelined/min'
|
13
16
|
require 'pulse-meter/sensor/timelined/max'
|
14
17
|
require 'pulse-meter/sensor/timelined/percentile'
|
data/lib/pulse-meter/version.rb
CHANGED
@@ -72,6 +72,36 @@ describe PulseMeter::Mixins::Utils do
|
|
72
72
|
end
|
73
73
|
end
|
74
74
|
|
75
|
+
describe "#assert_array!" do
|
76
|
+
it "should extract value from hash by passed key" do
|
77
|
+
dummy.assert_array!({:val => [:foo]}, :val).should == [:foo]
|
78
|
+
end
|
79
|
+
|
80
|
+
context "when no default value given" do
|
81
|
+
it "should raise exception if th value is not an Array" do
|
82
|
+
expect{ dummy.assert_array!({:val => :bad}, :val) }.to raise_exception(ArgumentError)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should raise exception if the value is not defined" do
|
86
|
+
expect{ dummy.assert_array!({}, :val) }.to raise_exception(ArgumentError)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context "when default value given" do
|
91
|
+
it "should prefer value from options to default" do
|
92
|
+
dummy.assert_array!({:val => [:foo]}, :val, []).should == [:foo]
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should use default value when there is no one in options" do
|
96
|
+
dummy.assert_array!({}, :val, []).should == []
|
97
|
+
end
|
98
|
+
|
99
|
+
it "should check default value if it is to be used" do
|
100
|
+
expect{dummy.assert_array!({}, :val, :bad)}.to raise_exception(ArgumentError)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
75
105
|
describe "#assert_ranged_float!" do
|
76
106
|
|
77
107
|
it "should extract float value from hash by passed key" do
|
@@ -136,6 +166,19 @@ describe PulseMeter::Mixins::Utils do
|
|
136
166
|
it "should convert symbolizable keys to symbols" do
|
137
167
|
dummy.symbolize_keys({"a" => 5, 6 => 7}).should == {a: 5, 6 => 7}
|
138
168
|
end
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "#subsets_of" do
|
172
|
+
it "returns all subsets of given array" do
|
173
|
+
dummy.subsets_of([1, 2]).sort.should == [[], [1], [2], [1, 2]].sort
|
174
|
+
end
|
175
|
+
end
|
139
176
|
|
177
|
+
describe "#each_subset" do
|
178
|
+
it "iterates over each subset" do
|
179
|
+
subsets = []
|
180
|
+
dummy.each_subset([1, 2]) {|s| subsets << s}
|
181
|
+
subsets.sort.should == [[], [1], [2], [1, 2]].sort
|
182
|
+
end
|
140
183
|
end
|
141
184
|
end
|
@@ -1,6 +1,17 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
3
|
describe PulseMeter::Sensor::Configuration do
|
4
|
+
let(:counter_config) {
|
5
|
+
{
|
6
|
+
cnt: {
|
7
|
+
sensor_type: 'counter',
|
8
|
+
args: {
|
9
|
+
annotation: "MySensor"
|
10
|
+
}
|
11
|
+
},
|
12
|
+
}
|
13
|
+
}
|
14
|
+
|
4
15
|
describe "#add_sensor" do
|
5
16
|
let(:cfg) {described_class.new}
|
6
17
|
|
@@ -52,8 +63,6 @@ describe PulseMeter::Sensor::Configuration do
|
|
52
63
|
cfg.add_sensor(:foo, Dummy.new)
|
53
64
|
cfg.sensor(:foo).annotation.should == "My Foo Counter"
|
54
65
|
end
|
55
|
-
|
56
|
-
|
57
66
|
end
|
58
67
|
|
59
68
|
describe ".new" do
|
@@ -75,17 +84,25 @@ describe PulseMeter::Sensor::Configuration do
|
|
75
84
|
|
76
85
|
describe "#sensor" do
|
77
86
|
it "should give access to added sensors" do
|
78
|
-
|
79
|
-
cnt: {
|
80
|
-
sensor_type: 'counter',
|
81
|
-
args: {
|
82
|
-
annotation: "MySensor"
|
83
|
-
}
|
84
|
-
},
|
85
|
-
}
|
86
|
-
cfg = described_class.new(opts)
|
87
|
+
cfg = described_class.new(counter_config)
|
87
88
|
cfg.sensor(:cnt).annotation.should == "MySensor"
|
88
89
|
cfg.sensor("cnt").annotation.should == "MySensor"
|
89
90
|
end
|
90
91
|
end
|
92
|
+
|
93
|
+
describe "#sensors" do
|
94
|
+
it "returns hash of sensors" do
|
95
|
+
cfg = described_class.new(counter_config)
|
96
|
+
cfg.sensors.should == {"cnt" => cfg.sensor(:cnt)}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe "#each_sensor" do
|
101
|
+
it "yields block for each name/sensor pair" do
|
102
|
+
cfg = described_class.new(counter_config)
|
103
|
+
sensors = {}
|
104
|
+
cfg.each {|s| sensors[s.name.to_sym] = s}
|
105
|
+
sensors.should == {:cnt => cfg.sensor(:cnt)}
|
106
|
+
end
|
107
|
+
end
|
91
108
|
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Multi do
|
4
|
+
let(:name){ :foo }
|
5
|
+
let(:annotation) { "Multi sensor" }
|
6
|
+
let(:type) {'counter'}
|
7
|
+
let(:factors) {[:f1, :f2]}
|
8
|
+
let(:configuration) {
|
9
|
+
{
|
10
|
+
sensor_type: type,
|
11
|
+
args: {
|
12
|
+
annotation: annotation
|
13
|
+
}
|
14
|
+
}
|
15
|
+
}
|
16
|
+
let(:init_values) { { factors: factors, configuration: configuration } }
|
17
|
+
let!(:sensor) { described_class.new(name, init_values) }
|
18
|
+
let!(:redis){ PulseMeter.redis }
|
19
|
+
|
20
|
+
describe '#initialize' do
|
21
|
+
context "when factors are not corretly passed" do
|
22
|
+
it "raises ArgumentError" do
|
23
|
+
expect {described_class.new(name, {factors: :not_array, configuration: configuration})}.to raise_exception(ArgumentError)
|
24
|
+
expect {described_class.new(name, {configuration: configuration})}.to raise_exception(ArgumentError)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when configuration missing" do
|
29
|
+
it "raises ArgumentError" do
|
30
|
+
expect {described_class.new(name, {factors: factors})}.to raise_exception(ArgumentError)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#factors" do
|
36
|
+
it "returns factors passed to constructor" do
|
37
|
+
sensor.factors.should == factors
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#configuration_options" do
|
42
|
+
it "returns configuration option passed to constructor" do
|
43
|
+
sensor.configuration_options.should == configuration
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#sensors" do
|
48
|
+
it "returns PulseMeter::Sensor::Configuration instance" do
|
49
|
+
sensor.sensors.should be_instance_of(PulseMeter::Sensor::Configuration)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "#event" do
|
54
|
+
|
55
|
+
it "raises ArgumentError unless all factors' values given" do
|
56
|
+
expect {sensor.event({f1: :v1}, 1)}.to raise_exception(ArgumentError)
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
context "when sensors must be created" do
|
61
|
+
let(:factor_values) { {f1: :v1, f2: :v2} }
|
62
|
+
|
63
|
+
it "implicitly creates them" do
|
64
|
+
expect {sensor.event(factor_values, 1)}.to change{sensor.sensors.to_a.count}
|
65
|
+
end
|
66
|
+
|
67
|
+
it "assigns names based on factors' names and values" do
|
68
|
+
sensor.event(factor_values, 1)
|
69
|
+
names = sensor.sensors.to_a.map(&:name)
|
70
|
+
names.sort.should == [
|
71
|
+
"#{name}",
|
72
|
+
"#{name}_f1_v1",
|
73
|
+
"#{name}_f2_v2",
|
74
|
+
"#{name}_f1_v1_f2_v2"
|
75
|
+
].sort
|
76
|
+
end
|
77
|
+
|
78
|
+
it "creates sensors of given type with configuration options passed" do
|
79
|
+
sensor.event(factor_values, 1)
|
80
|
+
sensor.sensors.each do |s|
|
81
|
+
s.should be_instance_of(PulseMeter::Sensor::Counter)
|
82
|
+
s.annotation.should == annotation
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it "sends event to all combinations of factors and values" do
|
88
|
+
sensor.event({f1: :f1v1, f2: :f2v1}, 1)
|
89
|
+
sensor.event({f1: :f1v2, f2: :f2v1}, 2)
|
90
|
+
[
|
91
|
+
["#{name}", 3],
|
92
|
+
["#{name}_f1_f1v1", 1],
|
93
|
+
["#{name}_f1_f1v2", 2],
|
94
|
+
["#{name}_f2_f2v1", 3],
|
95
|
+
["#{name}_f1_f1v1_f2_f2v1", 1],
|
96
|
+
["#{name}_f1_f1v2_f2_f2v1", 2]
|
97
|
+
].each do |sensor_name, sum|
|
98
|
+
s = sensor.sensor(sensor_name)
|
99
|
+
s.value.should == sum
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#each" do
|
105
|
+
it "when used by Enumerable it lists all ever created subsensors of multisensor" do
|
106
|
+
sensor.event({f1: :f1v1, f2: :f2v1}, 1)
|
107
|
+
restored_sensor = PulseMeter::Sensor::Base.restore(name)
|
108
|
+
|
109
|
+
restored_sensor.to_a.map(&:name).sort.should == [
|
110
|
+
"#{name}",
|
111
|
+
"#{name}_f1_f1v1",
|
112
|
+
"#{name}_f2_f2v1",
|
113
|
+
"#{name}_f1_f1v1_f2_f2v1",
|
114
|
+
].sort
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
describe "#sensor_for_factors" do
|
119
|
+
context "when sensor has already been created" do
|
120
|
+
it "returns sensor for given combination of factors and their values" do
|
121
|
+
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"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "when such a sensor was not created" do
|
128
|
+
it "returns nil" do
|
129
|
+
sensor.sensor_for_factors([:foo], [:bar]).should be_nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Timelined::HashedIndicator do
|
4
|
+
it_should_behave_like "timeline sensor", {}, {:foo => 1}
|
5
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => 2}], {:foo => 2}.to_json
|
6
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => :bad_value}], {:foo => 1}.to_json
|
7
|
+
it_should_behave_like "timelined subclass", [{:foo => 1}, {:boo => 2}], {:foo => 1, :boo => 2}.to_json
|
8
|
+
end
|
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.2.
|
4
|
+
version: 0.2.7
|
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-07-
|
13
|
+
date: 2012-07-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: gon-sinatra
|
@@ -343,11 +343,13 @@ files:
|
|
343
343
|
- lib/pulse-meter/sensor/hashed_counter.rb
|
344
344
|
- lib/pulse-meter/sensor/hashed_indicator.rb
|
345
345
|
- lib/pulse-meter/sensor/indicator.rb
|
346
|
-
- lib/pulse-meter/sensor/
|
346
|
+
- lib/pulse-meter/sensor/multi.rb
|
347
347
|
- lib/pulse-meter/sensor/timeline.rb
|
348
348
|
- lib/pulse-meter/sensor/timelined/average.rb
|
349
349
|
- lib/pulse-meter/sensor/timelined/counter.rb
|
350
350
|
- lib/pulse-meter/sensor/timelined/hashed_counter.rb
|
351
|
+
- lib/pulse-meter/sensor/timelined/hashed_indicator.rb
|
352
|
+
- lib/pulse-meter/sensor/timelined/indicator.rb
|
351
353
|
- lib/pulse-meter/sensor/timelined/max.rb
|
352
354
|
- lib/pulse-meter/sensor/timelined/median.rb
|
353
355
|
- lib/pulse-meter/sensor/timelined/min.rb
|
@@ -411,11 +413,13 @@ files:
|
|
411
413
|
- spec/pulse_meter/sensor/hashed_counter_spec.rb
|
412
414
|
- spec/pulse_meter/sensor/hashed_indicator_spec.rb
|
413
415
|
- spec/pulse_meter/sensor/indicator_spec.rb
|
414
|
-
- spec/pulse_meter/sensor/
|
416
|
+
- spec/pulse_meter/sensor/multi_spec.rb
|
415
417
|
- spec/pulse_meter/sensor/timeline_spec.rb
|
416
418
|
- spec/pulse_meter/sensor/timelined/average_spec.rb
|
417
419
|
- spec/pulse_meter/sensor/timelined/counter_spec.rb
|
418
420
|
- spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb
|
421
|
+
- spec/pulse_meter/sensor/timelined/hashed_indicator_spec.rb
|
422
|
+
- spec/pulse_meter/sensor/timelined/indicator_spec.rb
|
419
423
|
- spec/pulse_meter/sensor/timelined/max_spec.rb
|
420
424
|
- spec/pulse_meter/sensor/timelined/median_spec.rb
|
421
425
|
- spec/pulse_meter/sensor/timelined/min_spec.rb
|
@@ -468,7 +472,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
468
472
|
version: '0'
|
469
473
|
segments:
|
470
474
|
- 0
|
471
|
-
hash: -
|
475
|
+
hash: -450227772948230282
|
472
476
|
requirements: []
|
473
477
|
rubyforge_project:
|
474
478
|
rubygems_version: 1.8.24
|
@@ -485,11 +489,13 @@ test_files:
|
|
485
489
|
- spec/pulse_meter/sensor/hashed_counter_spec.rb
|
486
490
|
- spec/pulse_meter/sensor/hashed_indicator_spec.rb
|
487
491
|
- spec/pulse_meter/sensor/indicator_spec.rb
|
488
|
-
- spec/pulse_meter/sensor/
|
492
|
+
- spec/pulse_meter/sensor/multi_spec.rb
|
489
493
|
- spec/pulse_meter/sensor/timeline_spec.rb
|
490
494
|
- spec/pulse_meter/sensor/timelined/average_spec.rb
|
491
495
|
- spec/pulse_meter/sensor/timelined/counter_spec.rb
|
492
496
|
- spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb
|
497
|
+
- spec/pulse_meter/sensor/timelined/hashed_indicator_spec.rb
|
498
|
+
- spec/pulse_meter/sensor/timelined/indicator_spec.rb
|
493
499
|
- spec/pulse_meter/sensor/timelined/max_spec.rb
|
494
500
|
- spec/pulse_meter/sensor/timelined/median_spec.rb
|
495
501
|
- spec/pulse_meter/sensor/timelined/min_spec.rb
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'json'
|
3
|
-
|
4
|
-
module PulseMeter
|
5
|
-
module Sensor
|
6
|
-
|
7
|
-
# Remote sensor, i.e. a simple UDP proxy for sending data without
|
8
|
-
# taking in account backend performance issues
|
9
|
-
class Remote < Base
|
10
|
-
|
11
|
-
DEFAULT_PORT = 27182
|
12
|
-
DEFAULT_HOST = 'localhost'
|
13
|
-
|
14
|
-
# @!attribute [r] name
|
15
|
-
# @return [String] sensor name
|
16
|
-
attr_reader :name
|
17
|
-
|
18
|
-
# Initializes sensor and creates UDP socket
|
19
|
-
# @param name [String] sensor name
|
20
|
-
# @option options [Symbol] :host host for remote pulse-meter daemon
|
21
|
-
# @option options [Symbol] :port port for remote pulse-meter daemon
|
22
|
-
# @raise [BadSensorName] if sensor name is malformed
|
23
|
-
# @raise [ConnectionError] if invalid host or port are provided
|
24
|
-
def initialize(name, options={})
|
25
|
-
@name = name.to_s
|
26
|
-
raise BadSensorName, @name unless @name =~ /\A\w+\z/
|
27
|
-
@host = options[:host].to_s || DEFAULT_HOST
|
28
|
-
@port = options[:port].to_i || DEFAULT_PORT
|
29
|
-
@socket = UDPSocket.new
|
30
|
-
end
|
31
|
-
|
32
|
-
# Send value to remote sensor
|
33
|
-
# @param value value for remote sensor
|
34
|
-
# @raise [ConnectionError] if remote daemon is not available
|
35
|
-
# @raise [MessageTooLarge] if event data is too large to be serialized into a UDP datagram
|
36
|
-
def event(value)
|
37
|
-
events(name => value)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Send values to multiple remote sensors
|
41
|
-
# @param event_data hash with remote sensor names as keys end event value for each value as sensor
|
42
|
-
def events(event_data)
|
43
|
-
raise ArgumentError unless event_data.is_a?(Hash)
|
44
|
-
socket_action do
|
45
|
-
@socket.send(event_data.to_json, 0, @host, @port)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def socket_action
|
52
|
-
yield
|
53
|
-
rescue SocketError, Errno::EADDRNOTAVAIL, Errno::EINVAL => exc
|
54
|
-
raise PulseMeter::Remote::ConnectionError, exc.to_s
|
55
|
-
rescue Errno::EMSGSIZE => exc
|
56
|
-
raise PulseMeter::Remote::MessageTooLarge, exc.to_s
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
@@ -1,86 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe PulseMeter::Sensor::Remote do
|
4
|
-
|
5
|
-
def data_sent_to(host, port)
|
6
|
-
socket = UDPSocket.new
|
7
|
-
socket.bind(host, port)
|
8
|
-
yield
|
9
|
-
data, _ = socket.recvfrom(65000)
|
10
|
-
socket.close
|
11
|
-
data
|
12
|
-
end
|
13
|
-
|
14
|
-
let(:host){'localhost'}
|
15
|
-
let(:port){56789}
|
16
|
-
let(:sensor){described_class.new(:some_remote_sensor, host: host, port: port)}
|
17
|
-
|
18
|
-
describe "#new" do
|
19
|
-
it "should raise exception if sensor name is bad" do
|
20
|
-
expect{described_class.new("aa bb")}.to raise_exception(PulseMeter::BadSensorName)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
describe "#event" do
|
25
|
-
it "should send event data to remote host and port" do
|
26
|
-
data_sent_to(host, port) {
|
27
|
-
sensor.event(123)
|
28
|
-
}.should_not be_empty
|
29
|
-
end
|
30
|
-
|
31
|
-
it "should use sensor name as a single key and sent data as its value" do
|
32
|
-
data = data_sent_to(host, port) do
|
33
|
-
sensor.event(123)
|
34
|
-
end
|
35
|
-
JSON.parse(data).should == {sensor.name => 123}
|
36
|
-
end
|
37
|
-
|
38
|
-
it "should raise MessageTooLarge if message is too long" do
|
39
|
-
expect{ sensor.event("123" * 100000) }.to raise_exception(PulseMeter::Remote::MessageTooLarge)
|
40
|
-
end
|
41
|
-
|
42
|
-
|
43
|
-
it "should raise PulseMeter::Remote::ConnectionError if remote host is invalid" do
|
44
|
-
expect{described_class.new("xxx", host: "bad host").event(123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
45
|
-
end
|
46
|
-
|
47
|
-
it "should raise PulseMeter::Remote::ConnectionError if remote port is invalid" do
|
48
|
-
expect{described_class.new("xxx", port: -123).event(123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
49
|
-
expect{described_class.new("xxx", port: 'bad port').event(123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
describe "#events" do
|
55
|
-
it "should send event data to remote host and port" do
|
56
|
-
data_sent_to(host, port) {
|
57
|
-
sensor.events(a: 1, b: 2)
|
58
|
-
}.should_not be_empty
|
59
|
-
end
|
60
|
-
|
61
|
-
it "should use sensor name as a single key and sent data as its value" do
|
62
|
-
data = data_sent_to(host, port) do
|
63
|
-
sensor.events(a: 1, b: 2)
|
64
|
-
end
|
65
|
-
JSON.parse(data).should == {"a" => 1, "b" => 2}
|
66
|
-
end
|
67
|
-
|
68
|
-
it "should raise ArgumentError if argument is not a hash" do
|
69
|
-
expect{ sensor.events(1213) }.to raise_exception(ArgumentError)
|
70
|
-
end
|
71
|
-
|
72
|
-
it "should raise MessageTooLarge if message is too long" do
|
73
|
-
expect{ sensor.events(a: "x" * 100000) }.to raise_exception(PulseMeter::Remote::MessageTooLarge)
|
74
|
-
end
|
75
|
-
|
76
|
-
it "should raise PulseMeter::Remote::ConnectionError if remote host is invalid" do
|
77
|
-
expect{described_class.new("xxx", host: "bad host").events(a: 123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
78
|
-
end
|
79
|
-
|
80
|
-
it "should raise PulseMeter::Remote::ConnectionError if remote port is invalid" do
|
81
|
-
expect{described_class.new("xxx", port: -123).events(a: 123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
82
|
-
expect{described_class.new("xxx", port: 'bad port').events(a: 123)}.to raise_exception(PulseMeter::Remote::ConnectionError)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
end
|