pulse-meter 0.4.9 → 0.4.11
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/Rakefile +1 -48
- data/lib/pulse-meter.rb +1 -68
- data/lib/pulse-meter/visualizer.rb +1 -40
- data/pulse-meter.gemspec +4 -26
- metadata +10 -558
- data/Procfile +0 -3
- data/bin/pulse +0 -6
- data/examples/basic.ru +0 -145
- data/examples/basic_sensor_data.rb +0 -96
- data/examples/udp_benchmark.rb +0 -29
- data/lib/cmd.rb +0 -171
- data/lib/pulse-meter/command_aggregator/async.rb +0 -83
- data/lib/pulse-meter/command_aggregator/sync.rb +0 -18
- data/lib/pulse-meter/command_aggregator/udp.rb +0 -48
- data/lib/pulse-meter/extensions/enumerable.rb +0 -24
- data/lib/pulse-meter/mixins/cmd.rb +0 -46
- data/lib/pulse-meter/mixins/dumper.rb +0 -87
- data/lib/pulse-meter/mixins/utils.rb +0 -155
- data/lib/pulse-meter/observer.rb +0 -120
- data/lib/pulse-meter/observer/extended.rb +0 -34
- data/lib/pulse-meter/sensor.rb +0 -61
- data/lib/pulse-meter/sensor/base.rb +0 -88
- data/lib/pulse-meter/sensor/configuration.rb +0 -106
- data/lib/pulse-meter/sensor/counter.rb +0 -39
- data/lib/pulse-meter/sensor/hashed_counter.rb +0 -36
- data/lib/pulse-meter/sensor/hashed_indicator.rb +0 -24
- data/lib/pulse-meter/sensor/indicator.rb +0 -35
- data/lib/pulse-meter/sensor/multi.rb +0 -97
- data/lib/pulse-meter/sensor/timeline.rb +0 -236
- data/lib/pulse-meter/sensor/timeline_reduce.rb +0 -68
- data/lib/pulse-meter/sensor/timelined/average.rb +0 -32
- data/lib/pulse-meter/sensor/timelined/counter.rb +0 -23
- data/lib/pulse-meter/sensor/timelined/hashed_counter.rb +0 -31
- data/lib/pulse-meter/sensor/timelined/hashed_indicator.rb +0 -30
- data/lib/pulse-meter/sensor/timelined/indicator.rb +0 -23
- data/lib/pulse-meter/sensor/timelined/max.rb +0 -19
- data/lib/pulse-meter/sensor/timelined/median.rb +0 -14
- data/lib/pulse-meter/sensor/timelined/min.rb +0 -19
- data/lib/pulse-meter/sensor/timelined/multi_percentile.rb +0 -34
- data/lib/pulse-meter/sensor/timelined/percentile.rb +0 -22
- data/lib/pulse-meter/sensor/timelined/uniq_counter.rb +0 -22
- data/lib/pulse-meter/sensor/timelined/zset_based.rb +0 -38
- data/lib/pulse-meter/sensor/uniq_counter.rb +0 -24
- data/lib/pulse-meter/server.rb +0 -0
- data/lib/pulse-meter/server/command_line_options.rb +0 -0
- data/lib/pulse-meter/server/config_options.rb +0 -0
- data/lib/pulse-meter/server/sensors.rb +0 -0
- data/lib/pulse-meter/udp_server.rb +0 -45
- data/lib/pulse-meter/version.rb +0 -3
- data/lib/pulse-meter/visualize/app.rb +0 -78
- data/lib/pulse-meter/visualize/base.rb +0 -15
- data/lib/pulse-meter/visualize/coffee/application.coffee +0 -40
- data/lib/pulse-meter/visualize/coffee/collections/page_info_list.coffee +0 -17
- data/lib/pulse-meter/visualize/coffee/collections/sensor_info_list.coffee +0 -4
- data/lib/pulse-meter/visualize/coffee/collections/widget_list.coffee +0 -14
- data/lib/pulse-meter/visualize/coffee/extensions.coffee +0 -26
- data/lib/pulse-meter/visualize/coffee/models/dinamic_widget.coffee +0 -34
- data/lib/pulse-meter/visualize/coffee/models/page_info.coffee +0 -2
- data/lib/pulse-meter/visualize/coffee/models/sensor_info.coffee +0 -2
- data/lib/pulse-meter/visualize/coffee/models/widget.coffee +0 -54
- data/lib/pulse-meter/visualize/coffee/presenters/area.coffee +0 -2
- data/lib/pulse-meter/visualize/coffee/presenters/gauge.coffee +0 -11
- data/lib/pulse-meter/visualize/coffee/presenters/line.coffee +0 -2
- data/lib/pulse-meter/visualize/coffee/presenters/pie.coffee +0 -20
- data/lib/pulse-meter/visualize/coffee/presenters/series.coffee +0 -44
- data/lib/pulse-meter/visualize/coffee/presenters/table.coffee +0 -10
- data/lib/pulse-meter/visualize/coffee/presenters/timeline.coffee +0 -13
- data/lib/pulse-meter/visualize/coffee/presenters/widget.coffee +0 -65
- data/lib/pulse-meter/visualize/coffee/router.coffee +0 -21
- data/lib/pulse-meter/visualize/coffee/views/dynamic_chart.coffee +0 -91
- data/lib/pulse-meter/visualize/coffee/views/dynamic_widget.coffee +0 -58
- data/lib/pulse-meter/visualize/coffee/views/page_title.coffee +0 -17
- data/lib/pulse-meter/visualize/coffee/views/page_titles.coffee +0 -15
- data/lib/pulse-meter/visualize/coffee/views/sensor_info_list.coffee +0 -19
- data/lib/pulse-meter/visualize/coffee/views/widget.coffee +0 -99
- data/lib/pulse-meter/visualize/coffee/views/widget_chart.coffee +0 -13
- data/lib/pulse-meter/visualize/coffee/views/widget_list.coffee +0 -15
- data/lib/pulse-meter/visualize/dsl/base.rb +0 -131
- data/lib/pulse-meter/visualize/dsl/errors.rb +0 -40
- data/lib/pulse-meter/visualize/dsl/layout.rb +0 -27
- data/lib/pulse-meter/visualize/dsl/page.rb +0 -33
- data/lib/pulse-meter/visualize/dsl/sensor.rb +0 -20
- data/lib/pulse-meter/visualize/dsl/widget.rb +0 -37
- data/lib/pulse-meter/visualize/dsl/widgets/area.rb +0 -20
- data/lib/pulse-meter/visualize/dsl/widgets/gauge.rb +0 -12
- data/lib/pulse-meter/visualize/dsl/widgets/line.rb +0 -21
- data/lib/pulse-meter/visualize/dsl/widgets/pie.rb +0 -16
- data/lib/pulse-meter/visualize/dsl/widgets/table.rb +0 -19
- data/lib/pulse-meter/visualize/layout.rb +0 -79
- data/lib/pulse-meter/visualize/page.rb +0 -25
- data/lib/pulse-meter/visualize/public/css/application.css +0 -56
- data/lib/pulse-meter/visualize/public/css/bootstrap.css +0 -4883
- data/lib/pulse-meter/visualize/public/css/bootstrap.min.css +0 -729
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_222222_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_2e83ff_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_454545_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_888888_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_cd0a0a_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/images/ui-icons_f6cf3b_256x240.png +0 -0
- data/lib/pulse-meter/visualize/public/css/jquery-ui-1.8.16.bootstrap.css +0 -1320
- data/lib/pulse-meter/visualize/public/favicon.ico +0 -208
- data/lib/pulse-meter/visualize/public/img/glyphicons-halflings-white.png +0 -0
- data/lib/pulse-meter/visualize/public/img/glyphicons-halflings.png +0 -0
- data/lib/pulse-meter/visualize/public/js/application.js +0 -973
- data/lib/pulse-meter/visualize/public/js/backbone-min.js +0 -38
- data/lib/pulse-meter/visualize/public/js/bootstrap.js +0 -1835
- data/lib/pulse-meter/visualize/public/js/jquery-1.7.2.min.js +0 -4
- data/lib/pulse-meter/visualize/public/js/jquery-ui-1.8.16.bootstrap.min.js +0 -791
- data/lib/pulse-meter/visualize/public/js/jquery-ui-1.8.23.custom.min.js +0 -21
- data/lib/pulse-meter/visualize/public/js/jquery-ui-timepicker-addon.js +0 -1687
- data/lib/pulse-meter/visualize/public/js/json2.js +0 -487
- data/lib/pulse-meter/visualize/public/js/underscore-min.js +0 -32
- data/lib/pulse-meter/visualize/sensor.rb +0 -63
- data/lib/pulse-meter/visualize/series_extractor.rb +0 -107
- data/lib/pulse-meter/visualize/views/main.haml +0 -30
- data/lib/pulse-meter/visualize/views/sensors.haml +0 -76
- data/lib/pulse-meter/visualize/views/widgets/area.haml +0 -53
- data/lib/pulse-meter/visualize/views/widgets/extend_options.haml +0 -11
- data/lib/pulse-meter/visualize/views/widgets/gauge.haml +0 -13
- data/lib/pulse-meter/visualize/views/widgets/line.haml +0 -54
- data/lib/pulse-meter/visualize/views/widgets/pie.haml +0 -13
- data/lib/pulse-meter/visualize/views/widgets/table.haml +0 -45
- data/lib/pulse-meter/visualize/widget.rb +0 -38
- data/lib/pulse-meter/visualize/widgets/gauge.rb +0 -47
- data/lib/pulse-meter/visualize/widgets/pie.rb +0 -36
- data/lib/pulse-meter/visualize/widgets/timeline.rb +0 -114
- data/spec/pulse_meter/command_aggregator/async_spec.rb +0 -53
- data/spec/pulse_meter/command_aggregator/sync_spec.rb +0 -25
- data/spec/pulse_meter/command_aggregator/udp_spec.rb +0 -45
- data/spec/pulse_meter/extensions/enumerable_spec.rb +0 -58
- data/spec/pulse_meter/mixins/cmd_spec.rb +0 -117
- data/spec/pulse_meter/mixins/dumper_spec.rb +0 -162
- data/spec/pulse_meter/mixins/utils_spec.rb +0 -212
- data/spec/pulse_meter/observer/extended_spec.rb +0 -92
- data/spec/pulse_meter/observer_spec.rb +0 -207
- data/spec/pulse_meter/sensor/base_spec.rb +0 -106
- data/spec/pulse_meter/sensor/configuration_spec.rb +0 -103
- data/spec/pulse_meter/sensor/counter_spec.rb +0 -54
- data/spec/pulse_meter/sensor/hashed_counter_spec.rb +0 -43
- data/spec/pulse_meter/sensor/hashed_indicator_spec.rb +0 -39
- data/spec/pulse_meter/sensor/indicator_spec.rb +0 -43
- data/spec/pulse_meter/sensor/multi_spec.rb +0 -137
- data/spec/pulse_meter/sensor/timeline_spec.rb +0 -88
- data/spec/pulse_meter/sensor/timelined/average_spec.rb +0 -6
- data/spec/pulse_meter/sensor/timelined/counter_spec.rb +0 -6
- data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +0 -8
- data/spec/pulse_meter/sensor/timelined/hashed_indicator_spec.rb +0 -8
- data/spec/pulse_meter/sensor/timelined/indicator_spec.rb +0 -6
- data/spec/pulse_meter/sensor/timelined/max_spec.rb +0 -7
- data/spec/pulse_meter/sensor/timelined/median_spec.rb +0 -7
- data/spec/pulse_meter/sensor/timelined/min_spec.rb +0 -7
- data/spec/pulse_meter/sensor/timelined/multi_percentile_spec.rb +0 -21
- data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +0 -17
- data/spec/pulse_meter/sensor/timelined/uniq_counter_spec.rb +0 -9
- data/spec/pulse_meter/sensor/uniq_counter_spec.rb +0 -28
- data/spec/pulse_meter/udp_server_spec.rb +0 -36
- data/spec/pulse_meter/visualize/app_spec.rb +0 -27
- data/spec/pulse_meter/visualize/dsl/layout_spec.rb +0 -64
- data/spec/pulse_meter/visualize/dsl/page_spec.rb +0 -62
- data/spec/pulse_meter/visualize/dsl/sensor_spec.rb +0 -30
- data/spec/pulse_meter/visualize/dsl/widget_spec.rb +0 -6
- data/spec/pulse_meter/visualize/dsl/widgets/area_spec.rb +0 -44
- data/spec/pulse_meter/visualize/dsl/widgets/gauge_spec.rb +0 -22
- data/spec/pulse_meter/visualize/dsl/widgets/line_spec.rb +0 -44
- data/spec/pulse_meter/visualize/dsl/widgets/pie_spec.rb +0 -35
- data/spec/pulse_meter/visualize/dsl/widgets/table_spec.rb +0 -36
- data/spec/pulse_meter/visualize/layout_spec.rb +0 -54
- data/spec/pulse_meter/visualize/page_spec.rb +0 -153
- data/spec/pulse_meter/visualize/sensor_spec.rb +0 -120
- data/spec/pulse_meter/visualize/series_extractor_spec.rb +0 -80
- data/spec/pulse_meter/visualize/widgets/area_spec.rb +0 -6
- data/spec/pulse_meter/visualize/widgets/gauge_spec.rb +0 -63
- data/spec/pulse_meter/visualize/widgets/line_spec.rb +0 -6
- data/spec/pulse_meter/visualize/widgets/pie_spec.rb +0 -73
- data/spec/pulse_meter/visualize/widgets/table_spec.rb +0 -6
- data/spec/pulse_meter/visualizer_spec.rb +0 -42
- data/spec/pulse_meter_spec.rb +0 -73
- data/spec/shared_examples/dsl_widget.rb +0 -106
- data/spec/shared_examples/timeline_sensor.rb +0 -439
- data/spec/shared_examples/timelined_subclass.rb +0 -23
- data/spec/shared_examples/widget.rb +0 -97
- data/spec/spec_helper.rb +0 -40
- data/spec/support/matchers.rb +0 -34
- data/spec/support/observered.rb +0 -40
@@ -1,83 +0,0 @@
|
|
1
|
-
require 'singleton'
|
2
|
-
require 'thread'
|
3
|
-
|
4
|
-
module PulseMeter
|
5
|
-
module CommandAggregator
|
6
|
-
class Async
|
7
|
-
include Singleton
|
8
|
-
|
9
|
-
MAX_QUEUE_LENGTH = 10_000
|
10
|
-
|
11
|
-
attr_reader :max_queue_length
|
12
|
-
|
13
|
-
def initialize
|
14
|
-
@max_queue_length = MAX_QUEUE_LENGTH
|
15
|
-
@queue = Queue.new
|
16
|
-
@buffer = []
|
17
|
-
@in_multi = false
|
18
|
-
@consumer_thread = run_consumer
|
19
|
-
at_exit{ wait_for_pending_events }
|
20
|
-
end
|
21
|
-
|
22
|
-
def multi
|
23
|
-
@in_multi = true
|
24
|
-
yield
|
25
|
-
ensure
|
26
|
-
@in_multi = false
|
27
|
-
send_buffer_to_queue
|
28
|
-
end
|
29
|
-
|
30
|
-
def method_missing(*args)
|
31
|
-
@buffer << args
|
32
|
-
send_buffer_to_queue unless @in_multi
|
33
|
-
end
|
34
|
-
|
35
|
-
def wait_for_pending_events(max_seconds = 1)
|
36
|
-
left_to_wait = max_seconds.to_f
|
37
|
-
sleep_step = 0.01
|
38
|
-
while has_pending_events? && left_to_wait > 0
|
39
|
-
left_to_wait -= sleep_step
|
40
|
-
sleep(sleep_step)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
private
|
45
|
-
|
46
|
-
def has_pending_events?
|
47
|
-
!@queue.empty?
|
48
|
-
end
|
49
|
-
|
50
|
-
def send_buffer_to_queue
|
51
|
-
if @queue.size < @max_queue_length
|
52
|
-
@queue << @buffer
|
53
|
-
end
|
54
|
-
@buffer = []
|
55
|
-
end
|
56
|
-
|
57
|
-
def redis
|
58
|
-
PulseMeter.redis
|
59
|
-
end
|
60
|
-
|
61
|
-
def consume_commands
|
62
|
-
# redis and @queue are threadsafe
|
63
|
-
while commands = @queue.pop
|
64
|
-
begin
|
65
|
-
redis.multi do
|
66
|
-
commands.each do |command|
|
67
|
-
redis.send(*command)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
rescue StandardError => e
|
71
|
-
PulseMeter.error "error in consumer thread: #{e}, #{e.backtrace.join("\n")}"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
def run_consumer
|
77
|
-
Thread.new do
|
78
|
-
consume_commands
|
79
|
-
end
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require 'socket'
|
2
|
-
require 'fcntl'
|
3
|
-
|
4
|
-
module PulseMeter
|
5
|
-
module CommandAggregator
|
6
|
-
class UDP
|
7
|
-
|
8
|
-
def initialize(host, port = nil)
|
9
|
-
@servers = if host.is_a?(Array)
|
10
|
-
host
|
11
|
-
else
|
12
|
-
[[host, port]]
|
13
|
-
end
|
14
|
-
raise ArgumentError, "No servers specified" if @servers.empty?
|
15
|
-
@buffer = []
|
16
|
-
@in_multi = false
|
17
|
-
@sock = UDPSocket.new
|
18
|
-
@sock.fcntl(Fcntl::F_SETFL, @sock.fcntl(Fcntl::F_GETFL) | Fcntl::O_NONBLOCK)
|
19
|
-
end
|
20
|
-
|
21
|
-
def multi
|
22
|
-
@in_multi = true
|
23
|
-
yield
|
24
|
-
ensure
|
25
|
-
@in_multi = false
|
26
|
-
send_buffer
|
27
|
-
end
|
28
|
-
|
29
|
-
def method_missing(*args)
|
30
|
-
@buffer << args
|
31
|
-
send_buffer unless @in_multi
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
def send_buffer
|
37
|
-
data = @buffer.to_json
|
38
|
-
@sock.send(data, 0, *@servers.sample)
|
39
|
-
rescue StandardError => e
|
40
|
-
PulseMeter.error "error sending data: #{e}, #{e.backtrace.join("\n")}"
|
41
|
-
ensure
|
42
|
-
@buffer = []
|
43
|
-
end
|
44
|
-
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
@@ -1,24 +0,0 @@
|
|
1
|
-
module Enumerable
|
2
|
-
require 'csv'
|
3
|
-
require 'terminal-table'
|
4
|
-
|
5
|
-
def convert_time
|
6
|
-
map {|el| el.is_a?(Time) ? el.to_i : el}
|
7
|
-
end
|
8
|
-
|
9
|
-
def to_table(format = nil)
|
10
|
-
if "csv" == format.to_s
|
11
|
-
CSV.generate(:col_sep => ';') do |csv|
|
12
|
-
self.each {|row| csv << row.convert_time}
|
13
|
-
end
|
14
|
-
else
|
15
|
-
self.each_with_object(Terminal::Table.new) do |row, table|
|
16
|
-
table << if row.respond_to?(:map)
|
17
|
-
row.map(&:to_s)
|
18
|
-
else
|
19
|
-
row
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'pulse-meter/extensions/enumerable'
|
2
|
-
|
3
|
-
module PulseMeter
|
4
|
-
module Mixins
|
5
|
-
# Mixin with command-line utilities
|
6
|
-
module Cmd
|
7
|
-
def with_redis
|
8
|
-
PulseMeter.redis = create_redis
|
9
|
-
yield
|
10
|
-
end
|
11
|
-
|
12
|
-
def with_safe_restore_of(name, &block)
|
13
|
-
with_redis do
|
14
|
-
sensor = PulseMeter::Sensor::Base.restore(name)
|
15
|
-
block.call(sensor)
|
16
|
-
end
|
17
|
-
rescue PulseMeter::RestoreError
|
18
|
-
fail! "Sensor #{name} is unknown or cannot be restored"
|
19
|
-
end
|
20
|
-
|
21
|
-
def all_sensors
|
22
|
-
PulseMeter::Sensor::Timeline.list_objects
|
23
|
-
end
|
24
|
-
|
25
|
-
def all_sensors_table(format = nil)
|
26
|
-
data = [
|
27
|
-
["Name", "Class", "ttl", "raw data ttl", "interval", "reduce delay"],
|
28
|
-
]
|
29
|
-
data << :separator unless 'csv' == format.to_s
|
30
|
-
all_sensors.each do |s|
|
31
|
-
if s.kind_of? PulseMeter::Sensor::Timeline
|
32
|
-
data << [s.name, s.class, s.ttl, s.raw_data_ttl, s.interval, s.reduce_delay]
|
33
|
-
else
|
34
|
-
data << [s.name, s.class] + [''] * 4
|
35
|
-
end
|
36
|
-
end
|
37
|
-
data.to_table(format)
|
38
|
-
end
|
39
|
-
|
40
|
-
def fail!(description = nil)
|
41
|
-
puts description if description
|
42
|
-
exit 1
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,87 +0,0 @@
|
|
1
|
-
require 'yaml'
|
2
|
-
|
3
|
-
module PulseMeter
|
4
|
-
module Mixins
|
5
|
-
# Mixin with dumping utilities
|
6
|
-
module Dumper
|
7
|
-
# Prefix for Redis keys with dumped sensors' metadata
|
8
|
-
DUMP_REDIS_KEY = "pulse_meter:dump"
|
9
|
-
|
10
|
-
module InstanceMethods
|
11
|
-
# Serializes object and saves it to Redis
|
12
|
-
# @param safe [Boolean] forbids dump if sensor has already been dumped
|
13
|
-
# @raise [DumpConflictError] if object class conflicts with stored object class
|
14
|
-
# @raise [DumpError] if dumping fails for any reason
|
15
|
-
def dump!(safe = true)
|
16
|
-
ensure_storability!
|
17
|
-
serialized_obj = to_yaml
|
18
|
-
if safe
|
19
|
-
unless redis.hsetnx(DUMP_REDIS_KEY, name, serialized_obj)
|
20
|
-
stored = self.class.restore(name)
|
21
|
-
unless stored.class == self.class
|
22
|
-
raise DumpConflictError, "Attempt to create sensor #{name} of class #{self.class} but it already has class #{stored.class}"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
else
|
26
|
-
redis.hset(DUMP_REDIS_KEY, name, serialized_obj)
|
27
|
-
end
|
28
|
-
rescue DumpError, RestoreError => exc
|
29
|
-
raise exc
|
30
|
-
rescue StandardError => exc
|
31
|
-
raise DumpError, "object cannot be dumped: #{exc}"
|
32
|
-
end
|
33
|
-
|
34
|
-
# Ensures that object is dumpable
|
35
|
-
# @raise [DumpError] if object cannot be dumped
|
36
|
-
def ensure_storability!
|
37
|
-
raise DumpError, "#name attribute must be readable" unless self.respond_to?(:name)
|
38
|
-
raise DumpError, "#redis attribute must be available" unless self.respond_to?(:redis) && self.redis
|
39
|
-
end
|
40
|
-
|
41
|
-
# Cleans up object dump in Redis
|
42
|
-
def cleanup_dump
|
43
|
-
redis.hdel(DUMP_REDIS_KEY, self.name)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
module ClassMethods
|
48
|
-
# Restores object from Redis
|
49
|
-
# @param name [String] object name
|
50
|
-
# @return [Object]
|
51
|
-
# @raise [RestoreError] if object cannot be restored for any reason
|
52
|
-
def restore(name)
|
53
|
-
serialized_obj = PulseMeter.redis.hget(DUMP_REDIS_KEY, name)
|
54
|
-
YAML::load(serialized_obj)
|
55
|
-
rescue
|
56
|
-
raise RestoreError, "cannot restore #{name}"
|
57
|
-
end
|
58
|
-
|
59
|
-
# Lists all dumped objects' names
|
60
|
-
# @return [Array<String>]
|
61
|
-
# @raise [RestoreError] if list cannot be retrieved for any reason
|
62
|
-
def list_names
|
63
|
-
PulseMeter.redis.hkeys(DUMP_REDIS_KEY)
|
64
|
-
rescue
|
65
|
-
raise RestoreError, "cannot get data from redis"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Safely restores all dumped objects
|
69
|
-
# @return [Array<Object>]
|
70
|
-
def list_objects
|
71
|
-
list_names.each_with_object([]) do |name, objects|
|
72
|
-
begin
|
73
|
-
objects << restore(name)
|
74
|
-
rescue
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
78
|
-
end
|
79
|
-
|
80
|
-
def self.included(base)
|
81
|
-
base.send :include, InstanceMethods
|
82
|
-
base.send :extend, ClassMethods
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
@@ -1,155 +0,0 @@
|
|
1
|
-
require 'securerandom'
|
2
|
-
|
3
|
-
module PulseMeter
|
4
|
-
module Mixins
|
5
|
-
# Mixin with various useful functions
|
6
|
-
module Utils
|
7
|
-
# Tries to find a class with the name specified in the argument string
|
8
|
-
# @param const_name [String] class name
|
9
|
-
# @return [Class] if given class definde
|
10
|
-
# @return [NilClass] if given class is not defined
|
11
|
-
def constantize(const_name)
|
12
|
-
return unless const_name.respond_to?(:to_s)
|
13
|
-
const_name.to_s.split('::').reduce(Module, :const_get)
|
14
|
-
rescue NameError
|
15
|
-
nil
|
16
|
-
end
|
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
|
-
|
31
|
-
# Ensures that hash value specified by key can be converted to positive integer.
|
32
|
-
# In case it can makes in-place conversion and returns the value.
|
33
|
-
# @param options [Hash] hash to be looked at
|
34
|
-
# @param key [Object] hash key
|
35
|
-
# @param default [Object] default value to be returned
|
36
|
-
# @raise [ArgumentError] unless value is positive integer
|
37
|
-
# @return [Fixnum]
|
38
|
-
def assert_positive_integer!(options, key, default = nil)
|
39
|
-
value = options[key] || default
|
40
|
-
raise ArgumentError, "#{key} should be defined" unless value
|
41
|
-
raise ArgumentError, "#{key} should be integer" unless value.respond_to?(:to_i)
|
42
|
-
raise ArgumentError, "#{key} should be positive" unless value.to_i > 0
|
43
|
-
options[key] = value.to_i
|
44
|
-
end
|
45
|
-
|
46
|
-
# Ensures that hash value specified by key is can be converted to float
|
47
|
-
# and it is within given range.
|
48
|
-
# In case it can makes in-place conversion and returns the value.
|
49
|
-
# @param options [Hash] hash to be looked at
|
50
|
-
# @param key [Object] hash key
|
51
|
-
# @param from [Float] lower bound
|
52
|
-
# @param to [Float] upper bound
|
53
|
-
# @raise [ArgumentError] unless value is float within given range
|
54
|
-
# @return [Float]
|
55
|
-
def assert_ranged_float!(options, key, from, to)
|
56
|
-
f = options[key]
|
57
|
-
raise ArgumentError, "#{key} should be defined" unless f
|
58
|
-
raise ArgumentError, "#{key} should be float" unless f.respond_to?(:to_f)
|
59
|
-
f = f.to_f
|
60
|
-
raise ArgumentError, "#{key} should be between #{from} and #{to}" unless f >= from && f <= to
|
61
|
-
options[key] = f
|
62
|
-
end
|
63
|
-
|
64
|
-
# Generates uniq random string
|
65
|
-
# @return [String]
|
66
|
-
def uniqid
|
67
|
-
SecureRandom.hex(32)
|
68
|
-
end
|
69
|
-
|
70
|
-
# Capitalizes the first letter of each word in string
|
71
|
-
# @param str [String]
|
72
|
-
# @return [String]
|
73
|
-
# @raise [ArgumentError] unless passed value responds to to_s
|
74
|
-
def titleize(str)
|
75
|
-
raise ArgumentError unless str.respond_to?(:to_s)
|
76
|
-
str.to_s.split(/[\s_]+/).map(&:capitalize).join(' ')
|
77
|
-
end
|
78
|
-
|
79
|
-
# Converts string from snake_case to CamelCase
|
80
|
-
# @param str [String] string to be camelized
|
81
|
-
# @param first_letter_upper [TrueClass, FalseClass] says if the first letter must be uppercased
|
82
|
-
# @return [String]
|
83
|
-
# @raise [ArgumentError] unless passed value responds to to_s
|
84
|
-
def camelize(str, first_letter_upper = false)
|
85
|
-
raise ArgumentError unless str.respond_to?(:to_s)
|
86
|
-
terms = str.to_s.split(/_/)
|
87
|
-
first = terms.shift
|
88
|
-
(first_letter_upper ? first.capitalize : first.downcase) + terms.map(&:capitalize).join
|
89
|
-
end
|
90
|
-
|
91
|
-
# Converts string from CamelCase to snake_case
|
92
|
-
# @param str [String] string to be underscore
|
93
|
-
# @return [String]
|
94
|
-
# @raise [ArgumentError] unless passed value responds to to_s
|
95
|
-
def underscore(str)
|
96
|
-
raise ArgumentError unless str.respond_to?(:to_s)
|
97
|
-
str.to_s.gsub(/::/, '/').
|
98
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
99
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
100
|
-
tr("-", "_").
|
101
|
-
downcase
|
102
|
-
end
|
103
|
-
|
104
|
-
# Converts string of the form YYYYmmddHHMMSS (considered as UTC) to Time
|
105
|
-
# @param str [String] string to be converted
|
106
|
-
# @return [Time]
|
107
|
-
# @raise [ArgumentError] unless passed value responds to to_s
|
108
|
-
def parse_time(str)
|
109
|
-
raise ArgumentError unless str.respond_to?(:to_s)
|
110
|
-
m = str.to_s.match(/\A(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})\z/)
|
111
|
-
if m
|
112
|
-
Time.gm(*m.captures.map(&:to_i))
|
113
|
-
else
|
114
|
-
raise ArgumentError, "`#{str}' is not a YYYYmmddHHMMSS time"
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
# Symbolizes hash keys
|
119
|
-
def symbolize_keys(h)
|
120
|
-
h.each_with_object({}) do |(k, v), acc|
|
121
|
-
new_k = if k.respond_to?(:to_sym)
|
122
|
-
k.to_sym
|
123
|
-
else
|
124
|
-
k
|
125
|
-
end
|
126
|
-
acc[new_k] = v
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Deeply capitalizes Array values or Hash keys
|
131
|
-
def camelize_keys(item)
|
132
|
-
case item
|
133
|
-
when Array
|
134
|
-
item.map{|i| camelize_keys(i)}
|
135
|
-
when Hash
|
136
|
-
item.each_with_object({}) { |(k, v), h| h[camelize(k)] = camelize_keys(v)}
|
137
|
-
else
|
138
|
-
item
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
# Yields block for each subset of given array
|
143
|
-
# @param array [Array] given array
|
144
|
-
def each_subset(array)
|
145
|
-
subsets_of(array).each {|subset| yield(subset)}
|
146
|
-
end
|
147
|
-
|
148
|
-
# Returs all array's subsets
|
149
|
-
# @param array [Array]
|
150
|
-
def subsets_of(array)
|
151
|
-
0.upto(array.length).flat_map { |n| array.combination(n).to_a }
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|