pulse-meter 0.2.11 → 0.3.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.
Files changed (79) hide show
  1. data/.rbenv-version +1 -1
  2. data/README.md +32 -16
  3. data/Rakefile +29 -10
  4. data/examples/basic.ru +1 -1
  5. data/examples/full/server.ru +1 -1
  6. data/examples/minimal/server.ru +1 -1
  7. data/lib/pulse-meter.rb +1 -0
  8. data/lib/pulse-meter/observer.rb +117 -0
  9. data/lib/pulse-meter/sensor.rb +1 -0
  10. data/lib/pulse-meter/sensor/timeline.rb +3 -2
  11. data/lib/pulse-meter/sensor/timelined/multi_percentile.rb +43 -0
  12. data/lib/pulse-meter/version.rb +1 -1
  13. data/lib/pulse-meter/visualize/app.rb +28 -12
  14. data/lib/pulse-meter/visualize/coffee/application.coffee +40 -0
  15. data/lib/pulse-meter/visualize/coffee/collections/page_info_list.coffee +17 -0
  16. data/lib/pulse-meter/visualize/coffee/collections/sensor_info_list.coffee +4 -0
  17. data/lib/pulse-meter/visualize/coffee/collections/widget_list.coffee +14 -0
  18. data/lib/pulse-meter/visualize/coffee/extensions.coffee +26 -0
  19. data/lib/pulse-meter/visualize/coffee/models/dinamic_widget.coffee +34 -0
  20. data/lib/pulse-meter/visualize/coffee/models/page_info.coffee +2 -0
  21. data/lib/pulse-meter/visualize/coffee/models/sensor_info.coffee +2 -0
  22. data/lib/pulse-meter/visualize/coffee/models/widget.coffee +54 -0
  23. data/lib/pulse-meter/visualize/coffee/presenters/area.coffee +2 -0
  24. data/lib/pulse-meter/visualize/coffee/presenters/gauge.coffee +11 -0
  25. data/lib/pulse-meter/visualize/coffee/presenters/line.coffee +2 -0
  26. data/lib/pulse-meter/visualize/coffee/presenters/pie.coffee +20 -0
  27. data/lib/pulse-meter/visualize/coffee/presenters/series.coffee +44 -0
  28. data/lib/pulse-meter/visualize/coffee/presenters/table.coffee +10 -0
  29. data/lib/pulse-meter/visualize/coffee/presenters/timeline.coffee +13 -0
  30. data/lib/pulse-meter/visualize/coffee/presenters/widget.coffee +65 -0
  31. data/lib/pulse-meter/visualize/coffee/router.coffee +21 -0
  32. data/lib/pulse-meter/visualize/coffee/views/dynamic_chart.coffee +91 -0
  33. data/lib/pulse-meter/visualize/coffee/views/dynamic_widget.coffee +58 -0
  34. data/lib/pulse-meter/visualize/coffee/views/page_title.coffee +17 -0
  35. data/lib/pulse-meter/visualize/coffee/views/page_titles.coffee +15 -0
  36. data/lib/pulse-meter/visualize/coffee/views/sensor_info_list.coffee +19 -0
  37. data/lib/pulse-meter/visualize/coffee/views/widget.coffee +99 -0
  38. data/lib/pulse-meter/visualize/coffee/views/widget_chart.coffee +13 -0
  39. data/lib/pulse-meter/visualize/coffee/views/widget_list.coffee +15 -0
  40. data/lib/pulse-meter/visualize/layout.rb +4 -4
  41. data/lib/pulse-meter/visualize/public/css/application.css +13 -4
  42. data/lib/pulse-meter/visualize/public/css/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  43. data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  44. data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  45. data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  46. data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  47. data/lib/pulse-meter/visualize/public/css/images/ui-bg_glass_75_ffffff_1x400.png +0 -0
  48. data/lib/pulse-meter/visualize/public/css/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  49. data/lib/pulse-meter/visualize/public/css/images/ui-bg_inset-soft_95_fef1ec_1x100.png +0 -0
  50. data/lib/pulse-meter/visualize/public/css/images/ui-icons_222222_256x240.png +0 -0
  51. data/lib/pulse-meter/visualize/public/css/images/ui-icons_2e83ff_256x240.png +0 -0
  52. data/lib/pulse-meter/visualize/public/css/images/ui-icons_454545_256x240.png +0 -0
  53. data/lib/pulse-meter/visualize/public/css/images/ui-icons_888888_256x240.png +0 -0
  54. data/lib/pulse-meter/visualize/public/css/images/ui-icons_cd0a0a_256x240.png +0 -0
  55. data/lib/pulse-meter/visualize/public/css/images/ui-icons_f6cf3b_256x240.png +0 -0
  56. data/lib/pulse-meter/visualize/public/css/jquery-ui-1.8.16.bootstrap.css +1320 -0
  57. data/lib/pulse-meter/visualize/public/js/application.js +900 -691
  58. data/lib/pulse-meter/visualize/public/js/jquery-ui-1.8.16.bootstrap.min.js +791 -0
  59. data/lib/pulse-meter/visualize/public/js/jquery-ui-1.8.23.custom.min.js +21 -0
  60. data/lib/pulse-meter/visualize/public/js/jquery-ui-timepicker-addon.js +1687 -0
  61. data/lib/pulse-meter/visualize/sensor.rb +2 -2
  62. data/lib/pulse-meter/visualize/views/main.haml +2 -3
  63. data/lib/pulse-meter/visualize/views/sensors.haml +14 -1
  64. data/lib/pulse-meter/visualize/views/widgets/area.haml +46 -24
  65. data/lib/pulse-meter/visualize/views/widgets/line.haml +46 -23
  66. data/lib/pulse-meter/visualize/views/widgets/table.haml +37 -15
  67. data/lib/pulse-meter/visualize/widgets/timeline.rb +20 -5
  68. data/pulse-meter.gemspec +4 -0
  69. data/spec/pulse_meter/observer_spec.rb +252 -0
  70. data/spec/pulse_meter/sensor/timelined/multi_percentile_spec.rb +21 -0
  71. data/spec/pulse_meter/visualize/sensor_spec.rb +5 -5
  72. data/spec/pulse_meter/visualize/widgets/area_spec.rb +1 -74
  73. data/spec/pulse_meter/visualize/widgets/line_spec.rb +1 -73
  74. data/spec/pulse_meter/visualize/widgets/table_spec.rb +1 -73
  75. data/spec/shared_examples/timeline_sensor.rb +10 -0
  76. data/spec/shared_examples/widget.rb +97 -0
  77. data/spec/spec_helper.rb +1 -0
  78. metadata +120 -5
  79. data/lib/pulse-meter/visualize/public/js/application.coffee +0 -616
@@ -23,9 +23,9 @@ module PulseMeter
23
23
  extractor.point_data(last_value(now, need_incomplete))
24
24
  end
25
25
 
26
- def timeline_data(now, time_span, need_incomplete = false)
26
+ def timeline_data(from, till, need_incomplete = false)
27
27
  sensor = real_sensor
28
- timeline_data = sensor.timeline_within(now - time_span, now)
28
+ timeline_data = sensor.timeline_within(from, till)
29
29
  timeline_data.pop unless need_incomplete
30
30
  extractor.series_data(timeline_data)
31
31
  end
@@ -4,9 +4,9 @@
4
4
  :javascript
5
5
  var ROOT = "#{url('/')}";
6
6
  = include_gon
7
- - %w{jquery-1.7.2.min.js json2.js underscore-min.js backbone-min.js application.js bootstrap.js}.each do |jsfile|
7
+ - %w{jquery-1.7.2.min.js jquery-ui-1.8.16.bootstrap.min.js jquery-ui-timepicker-addon.js json2.js underscore-min.js backbone-min.js application.js bootstrap.js}.each do |jsfile|
8
8
  %script{type: 'text/javascript', src: url("/js/#{jsfile}")}
9
- - %w{bootstrap.min.css application.css}.each do |cssfile|
9
+ - %w{bootstrap.min.css application.css jquery-ui-1.8.16.bootstrap.css}.each do |cssfile|
10
10
  %link{rel: 'stylesheet', href: url("/css/#{cssfile}"), type: 'text/css', media: 'screen'}
11
11
  %script{type: 'text/javascript', src: "https://www.google.com/jsapi"}
12
12
  :javascript
@@ -16,7 +16,6 @@
16
16
  document.startApp();
17
17
  }
18
18
  %body
19
-
20
19
  - %w(area line table pie gauge).each do |wtype|
21
20
  = partial "widgets/#{wtype}"
22
21
  = partial "sensors"
@@ -19,7 +19,7 @@
19
19
 
20
20
  %script#dynamic-widget-error{type: 'text/template'}
21
21
  .alert.alert-error
22
- .button.close{'data-dismiss' => 'alert'} ×
22
+ .button.close{'data-dismiss' => 'alert'} ×
23
23
  <%- error %>
24
24
 
25
25
  %script#dynamic-widget-plotarea{type: 'text/template'}
@@ -31,6 +31,7 @@
31
31
  %button#refresh-chart.btn.btn-mini
32
32
  %i.icon-refresh
33
33
  Refresh chart
34
+ .form-inline
34
35
  Timespan:
35
36
  %select#extend-timespan-val.btn-mini.span1
36
37
  = partial "widgets/extend_options"
@@ -40,6 +41,18 @@
40
41
  %button#reset-timespan.btn.btn-mini
41
42
  %i.icon-arrow-right
42
43
  Reset
44
+ .form-inline
45
+ %label
46
+ From:
47
+ %span#start-time
48
+ %input.datepicker
49
+ %label
50
+ Till:
51
+ %span#end-time
52
+ %input.datepicker
53
+ %button#set-interval.btn.btn-mini
54
+ %i.icon-time
55
+ Set interval
43
56
 
44
57
 
45
58
  %script#dynamic-widget{type: 'text/template'}
@@ -5,27 +5,49 @@
5
5
  %button#refresh.btn.btn-mini
6
6
  %i.icon-refresh
7
7
  Refresh
8
- %span.space
9
- %label
10
- Cutoff min:
11
- %input.btn-mini#cutoff-min
12
- %span.space
13
- %label
14
- Cutoff max:
15
- %input.btn-mini#cutoff-max
16
- %span.space
17
- %label
18
- Refresh:
19
- %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
20
- %label
21
- Timespan:
22
- %select#extend-timespan-val.btn-mini.span1
23
- = partial "widgets/extend_options"
24
- %button#extend-timespan.btn.btn-mini
25
- %i.icon-arrow-left
26
- Extend
27
- %button#reset-timespan.btn.btn-mini
28
- %i.icon-arrow-right
29
- Reset
30
- %hr
31
-
8
+ %a#configure-button.btn.btn-mini{href: "#configure", role: 'button', 'data-toggle' => 'modal'}
9
+ %i.icon-wrench
10
+ Configure...
11
+ .modal.hide.fade#configure
12
+ .modal-header
13
+ %button.close{type: 'button', 'data-dismiss' => 'modal', 'aria-hidden' => 'true'} &times;
14
+ %h3 Configure
15
+ .modal-body
16
+ %p
17
+ %label
18
+ Cutoff min:
19
+ %input.btn-mini#cutoff-min
20
+ %span.space
21
+ %label
22
+ Cutoff max:
23
+ %input.btn-mini#cutoff-max
24
+ %hr
25
+ %p
26
+ %label
27
+ Refresh:
28
+ %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
29
+ %hr
30
+ %p
31
+ %label
32
+ Timespan:
33
+ %select#extend-timespan-val.btn-mini.span1
34
+ = partial "widgets/extend_options"
35
+ %button#extend-timespan.btn.btn-mini
36
+ %i.icon-arrow-left
37
+ Extend
38
+ %button#reset-timespan.btn.btn-mini
39
+ %i.icon-arrow-right
40
+ Reset
41
+ %hr
42
+ %p
43
+ %label
44
+ From:
45
+ %span#start-time
46
+ %input.datepicker
47
+ %label
48
+ Till:
49
+ %span#end-time
50
+ %input.datepicker
51
+ %button#set-interval.btn.btn-mini
52
+ %i.icon-time
53
+ Set interval
@@ -5,27 +5,50 @@
5
5
  %button#refresh.btn.btn-mini
6
6
  %i.icon-refresh
7
7
  Refresh
8
- %span.space
9
- %label
10
- Cutoff min:
11
- %input.btn-mini#cutoff-min
12
- %span.space
13
- %label
14
- Cutoff max:
15
- %input.btn-mini#cutoff-max
16
- %span.space
17
- %label
18
- Refresh:
19
- %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
20
- %label
21
- Timespan:
22
- %select#extend-timespan-val.btn-mini.span1
23
- = partial "widgets/extend_options"
24
- %button#extend-timespan.btn.btn-mini
25
- %i.icon-arrow-left
26
- Extend
27
- %button#reset-timespan.btn.btn-mini
28
- %i.icon-arrow-right
29
- Reset
30
- %hr
8
+ %a#configure-button.btn.btn-mini{href: "#configure", role: 'button', 'data-toggle' => 'modal'}
9
+ %i.icon-wrench
10
+ Configure...
11
+ .modal.hide.fade#configure
12
+ .modal-header
13
+ %button.close{type: 'button', 'data-dismiss' => 'modal', 'aria-hidden' => 'true'} &times;
14
+ %h3 Configure
15
+ .modal-body
16
+ %p
17
+ %label
18
+ Cutoff min:
19
+ %input.btn-mini#cutoff-min
20
+ %span.space
21
+ %label
22
+ Cutoff max:
23
+ %input.btn-mini#cutoff-max
24
+ %hr
25
+ %p
26
+ %label
27
+ Refresh:
28
+ %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
29
+ %hr
30
+ %p
31
+ %label
32
+ Timespan:
33
+ %select#extend-timespan-val.btn-mini.span1
34
+ = partial "widgets/extend_options"
35
+ %button#extend-timespan.btn.btn-mini
36
+ %i.icon-arrow-left
37
+ Extend
38
+ %button#reset-timespan.btn.btn-mini
39
+ %i.icon-arrow-right
40
+ Reset
41
+ %hr
42
+ %p
43
+ %label
44
+ From:
45
+ %span#start-time
46
+ %input.datepicker
47
+ %label
48
+ Till:
49
+ %span#end-time
50
+ %input.datepicker
51
+ %button#set-interval.btn.btn-mini
52
+ %i.icon-time
53
+ Set interval
31
54
 
@@ -5,19 +5,41 @@
5
5
  %button#refresh.btn.btn-mini
6
6
  %i.icon-refresh
7
7
  Refresh
8
- %span.space
9
- %label
10
- Refresh:
11
- %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
12
- %label
13
- Timespan:
14
- %select#extend-timespan-val.btn-mini.span1
15
- = partial "widgets/extend_options"
16
- %button#extend-timespan.btn.btn-mini
17
- %i.icon-arrow-down
18
- Extend
19
- %button#reset-timespan.btn.btn-mini
20
- %i.icon-arrow-up
21
- Reset
22
- %hr
8
+ %a#configure-button.btn.btn-mini{href: "#configure", role: 'button', 'data-toggle' => 'modal'}
9
+ %i.icon-wrench
10
+ Configure...
11
+ .modal.hide.fade#configure
12
+ .modal-header
13
+ %button.close{type: 'button', 'data-dismiss' => 'modal', 'aria-hidden' => 'true'} &times;
14
+ %h3 Configure
15
+ .modal-body
16
+ %p
17
+ %label
18
+ Refresh:
19
+ %input.btn-mini#need-refresh{type: :checkbox, checked: :true}
20
+ %hr
21
+ %p
22
+ %label
23
+ Timespan:
24
+ %select#extend-timespan-val.btn-mini.span1
25
+ = partial "widgets/extend_options"
26
+ %button#extend-timespan.btn.btn-mini
27
+ %i.icon-arrow-left
28
+ Extend
29
+ %button#reset-timespan.btn.btn-mini
30
+ %i.icon-arrow-right
31
+ Reset
32
+ %hr
33
+ %p
34
+ %label
35
+ From:
36
+ %span#start-time
37
+ %input.datepicker
38
+ %label
39
+ Till:
40
+ %span#end-time
41
+ %input.datepicker
42
+ %button#set-interval.btn.btn-mini
43
+ %i.icon-time
44
+ Set interval
23
45
 
@@ -15,10 +15,10 @@ module PulseMeter
15
15
  end
16
16
 
17
17
  def data(options = {})
18
- real_timespan = options[:timespan] || timespan
18
+ from, till = get_interval_borders(options)
19
19
  super().merge({
20
20
  values_title: values_label,
21
- series: series_data(real_timespan),
21
+ series: series_data(from, till),
22
22
  timespan: timespan,
23
23
  interval: interval
24
24
  })
@@ -26,11 +26,26 @@ module PulseMeter
26
26
 
27
27
  protected
28
28
 
29
- def series_data(tspan)
29
+ def get_interval_borders(options)
30
+ from = if options[:start_time] && (options[:start_time] > 0)
31
+ Time.at options[:start_time]
32
+ else
33
+ tspan = options[:timespan] || timespan
34
+ Time.now - tspan
35
+ end
36
+
37
+ till = if options[:end_time] && (options[:end_time] > 0)
38
+ Time.at options[:end_time]
39
+ else
40
+ Time.now
41
+ end
42
+ [from, till]
43
+ end
44
+
45
+ def series_data(from, till)
30
46
  ensure_sensor_match!
31
- now = Time.now
32
47
  sensor_datas = sensors.map{ |s|
33
- s.timeline_data(now, tspan, show_last_point)
48
+ s.timeline_data(from, till, show_last_point)
34
49
  }
35
50
  rows = []
36
51
  titles = []
data/pulse-meter.gemspec CHANGED
@@ -28,14 +28,18 @@ Gem::Specification.new do |gem|
28
28
  gem.add_runtime_dependency('terminal-table')
29
29
  gem.add_runtime_dependency('thor')
30
30
 
31
+ gem.add_development_dependency('coffee-script')
31
32
  gem.add_development_dependency('foreman')
32
33
  gem.add_development_dependency('hashie')
34
+ gem.add_development_dependency('listen')
33
35
  gem.add_development_dependency('mock_redis')
34
36
  gem.add_development_dependency('rack-test')
35
37
  gem.add_development_dependency('rake')
38
+ gem.add_development_dependency('rb-fsevent')
36
39
  gem.add_development_dependency('redcarpet')
37
40
  gem.add_development_dependency('rspec')
38
41
  gem.add_development_dependency('simplecov')
42
+ gem.add_development_dependency('sprockets')
39
43
  gem.add_development_dependency('timecop')
40
44
  gem.add_development_dependency('yard')
41
45
 
@@ -0,0 +1,252 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Observer do
4
+
5
+ context "instance methods observation" do
6
+
7
+ class Dummy
8
+ attr_reader :count
9
+
10
+ def initialize
11
+ @count = 0
12
+ end
13
+
14
+ def incr(value = 1, &proc)
15
+ Timecop.travel(Time.now + 1)
16
+ @count += value
17
+ @count += proc.call if proc
18
+ @count
19
+ end
20
+
21
+ def error
22
+ raise RuntimeError
23
+ end
24
+ end
25
+
26
+ let!(:dummy) {Dummy.new}
27
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
28
+ before do
29
+ [:incr, :error].each {|m| described_class.unobserve_method(Dummy, m)}
30
+ end
31
+
32
+ def create_observer(method = :incr, increment = 1)
33
+ described_class.observe_method(Dummy, method, sensor) do |*args|
34
+ event(increment)
35
+ end
36
+ end
37
+
38
+ def remove_observer(method = :incr)
39
+ described_class.unobserve_method(Dummy, method)
40
+ end
41
+
42
+ describe ".observe_method" do
43
+ it "executes block in context of sensor each time specified method of given class called" do
44
+ create_observer
45
+ 5.times {dummy.incr}
46
+ sensor.value.should == 5
47
+ end
48
+
49
+ it "passes arguments to observed method" do
50
+ create_observer
51
+ 5.times {dummy.incr(10)}
52
+ dummy.count.should == 50
53
+ end
54
+
55
+ it "passes methods' params to block" do
56
+ described_class.observe_method(Dummy, :incr, sensor) do |time, cnt|
57
+ event(cnt)
58
+ end
59
+
60
+ 5.times {dummy.incr(10)}
61
+ sensor.value.should == 50
62
+ end
63
+
64
+ it "passes execution time in milliseconds to block" do
65
+ Timecop.freeze do
66
+ described_class.observe_method(Dummy, :incr, sensor) do |time, cnt|
67
+ event(time)
68
+ end
69
+
70
+ dummy.incr
71
+ sensor.value.should >= 1000
72
+ end
73
+ end
74
+
75
+ it "does not break observed method even is observer raises error" do
76
+ described_class.observe_method(Dummy, :incr, sensor) do |*args|
77
+ raise RuntimeError
78
+ end
79
+
80
+ lambda {dummy.incr}.should_not raise_error
81
+ dummy.count.should == 1
82
+ end
83
+
84
+ it "uses first observer in case of double observation" do
85
+ create_observer(:incr, 1)
86
+ create_observer(:incr, 2)
87
+ 5.times {dummy.incr}
88
+ sensor.value.should == 5
89
+ end
90
+
91
+ it "keeps observed methods' errors" do
92
+ create_observer(:error)
93
+ lambda {dummy.error}.should raise_error(RuntimeError)
94
+ sensor.value.should == 1
95
+ end
96
+
97
+ it "makes observed method return its value" do
98
+ create_observer
99
+ dummy.incr.should == 1
100
+ end
101
+
102
+ it "allows to pass blocks to observed method" do
103
+ create_observer
104
+ dummy.incr do
105
+ 2
106
+ end
107
+ dummy.count.should == 3
108
+ end
109
+ end
110
+
111
+ describe ".unobserve_method" do
112
+ it "does nothing unless method is observed" do
113
+ lambda {remove_observer}.should_not raise_error
114
+ end
115
+
116
+ it "removes observation from observed method" do
117
+ create_observer
118
+ dummy.incr
119
+ remove_observer
120
+ dummy.incr
121
+ sensor.value.should == 1
122
+ end
123
+ end
124
+ end
125
+
126
+ context "class methods observation" do
127
+
128
+ class Dummy
129
+ @@count = 0
130
+ class << self
131
+ def count
132
+ @@count
133
+ end
134
+
135
+ def reset
136
+ @@count = 0
137
+ end
138
+
139
+ def incr(value = 1, &proc)
140
+ Timecop.travel(Time.now + 1)
141
+ @@count += value
142
+ @@count += proc.call if proc
143
+ @@count
144
+ end
145
+
146
+ def error
147
+ raise RuntimeError
148
+ end
149
+ end
150
+ end
151
+
152
+ let!(:dummy) {Dummy}
153
+ let!(:sensor) {PulseMeter::Sensor::Counter.new(:foo)}
154
+ before do
155
+ dummy.reset
156
+ [:incr, :error].each {|m| described_class.unobserve_class_method(Dummy, m)}
157
+ end
158
+
159
+ def create_observer(method = :incr, increment = 1)
160
+ described_class.observe_class_method(Dummy, method, sensor) do |*args|
161
+ event(increment)
162
+ end
163
+ end
164
+
165
+ def remove_observer(method = :incr)
166
+ described_class.unobserve_class_method(Dummy, method)
167
+ end
168
+
169
+ describe ".observe_class_method" do
170
+ it "executes block in context of sensor each time specified method of given class called" do
171
+ create_observer
172
+ 5.times {dummy.incr}
173
+ sensor.value.should == 5
174
+ end
175
+
176
+ it "passes arguments to observed method" do
177
+ create_observer
178
+ 5.times {dummy.incr(10)}
179
+ dummy.count.should == 50
180
+ end
181
+
182
+ it "passes methods' params to block" do
183
+ described_class.observe_class_method(Dummy, :incr, sensor) do |time, cnt|
184
+ event(cnt)
185
+ end
186
+
187
+ 5.times {dummy.incr(10)}
188
+ sensor.value.should == 50
189
+ end
190
+
191
+ it "passes execution time in milliseconds to block" do
192
+ Timecop.freeze do
193
+ described_class.observe_class_method(Dummy, :incr, sensor) do |time, cnt|
194
+ event(time)
195
+ end
196
+
197
+ dummy.incr
198
+ sensor.value.should == 1000
199
+ end
200
+ end
201
+
202
+ it "does not break observed method even is observer raises error" do
203
+ described_class.observe_class_method(Dummy, :incr, sensor) do |*args|
204
+ raise RuntimeError
205
+ end
206
+
207
+ lambda {dummy.incr}.should_not raise_error
208
+ dummy.count.should == 1
209
+ end
210
+
211
+ it "uses first observer in case of double observation" do
212
+ create_observer(:incr, 1)
213
+ create_observer(:incr, 2)
214
+ 5.times {dummy.incr}
215
+ sensor.value.should == 5
216
+ end
217
+
218
+ it "keeps observed methods' errors" do
219
+ create_observer(:error)
220
+ lambda {dummy.error}.should raise_error(RuntimeError)
221
+ sensor.value.should == 1
222
+ end
223
+
224
+ it "makes observed method return its value" do
225
+ create_observer
226
+ dummy.incr.should == 1
227
+ end
228
+
229
+ it "allows to pass blocks to observed method" do
230
+ create_observer
231
+ dummy.incr do
232
+ 2
233
+ end
234
+ dummy.count.should == 3
235
+ end
236
+ end
237
+
238
+ describe ".unobserve_class_method" do
239
+ it "does nothing unless method is observed" do
240
+ lambda {remove_observer}.should_not raise_error
241
+ end
242
+
243
+ it "removes observation from observed method" do
244
+ create_observer
245
+ dummy.incr
246
+ remove_observer
247
+ dummy.incr
248
+ sensor.value.should == 1
249
+ end
250
+ end
251
+ end
252
+ end