instrumentable 0.0.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +68 -7
- data/lib/instrumentable.rb +81 -23
- data/lib/instrumentable/version.rb +1 -1
- data/test/instrumentable_test.rb +99 -37
- metadata +2 -2
data/README.md
CHANGED
@@ -1,12 +1,18 @@
|
|
1
|
-
# Instrumentable
|
1
|
+
# Instrumentable (1.0.0)
|
2
|
+
-note, this version is not backwards compatiable
|
2
3
|
|
3
|
-
|
4
|
+
Decorate all your favorite methods with ActiveSupport::Notifications.instrument
|
5
|
+
|
6
|
+
This gem allows you to wrap methods on your classes for use with AS::N without having
|
7
|
+
to put AS::N.instrument do blocks around everything. You can customize the
|
8
|
+
payload sent with the event, and in addition all of the method args(if any) are sent
|
9
|
+
as well
|
4
10
|
|
5
11
|
## Installation
|
6
12
|
|
7
13
|
Add this line to your application's Gemfile:
|
8
14
|
|
9
|
-
gem
|
15
|
+
gem "instrumentable", "~> 1.0.0"
|
10
16
|
|
11
17
|
And then execute:
|
12
18
|
|
@@ -17,20 +23,75 @@ Or install it yourself as:
|
|
17
23
|
$ gem install instrumentable
|
18
24
|
|
19
25
|
## Usage
|
26
|
+
include the gem in your class
|
27
|
+
```ruby
|
28
|
+
include Instrumentable
|
29
|
+
```
|
30
|
+
to instrument an instance method
|
31
|
+
```ruby
|
32
|
+
instrument_method :method_name, 'event.to.fire', :payload_key => :payload_value
|
33
|
+
```
|
34
|
+
|
35
|
+
``` :method_name ```
|
36
|
+
|
37
|
+
Is the name of the method you want to instrument
|
38
|
+
|
39
|
+
``` 'event.to.fire' ```
|
40
|
+
|
41
|
+
Is the name of the event you'll setup your subscriber for
|
42
|
+
This can be any string
|
43
|
+
|
44
|
+
``` :payload_key => :payload_value ```
|
45
|
+
|
46
|
+
The last part is the payload, which consists of a key and a value.
|
47
|
+
What is sent to the subscriber depends on what is passed in as the value
|
48
|
+
+ String
|
49
|
+
+ ex: ```'static_string'```
|
50
|
+
+ string is passed in as-is to the payload
|
51
|
+
+ Symbol
|
52
|
+
+ ex: ```:method_name```
|
53
|
+
+ calls symbol as a method on the class in the current context (class, intance)
|
54
|
+
+ Proc
|
55
|
+
+ ex: ```Proc.new { Time.now }```
|
56
|
+
+ calls the proc, returning the value
|
57
|
+
|
58
|
+
All payloads will recieve a list of the arugments called with the method under
|
59
|
+
```:_method_args```, this will be an empty array if the method was called with no args
|
60
|
+
|
61
|
+
If you want to instrument a class method, you must use a separate method
|
62
|
+
```ruby
|
63
|
+
class_instrument_method self, :method_name, 'event.to.fire', :payload_key => :payload_value
|
64
|
+
```
|
65
|
+
You must use ```class_instrument_method``` instead and pass the first argument in as ```self```
|
66
|
+
|
67
|
+
## Examples
|
20
68
|
```ruby
|
21
69
|
require "instrumentable"
|
22
70
|
|
23
71
|
class WidgetRenderer
|
24
72
|
include Instrumentable
|
25
73
|
|
26
|
-
attr_reader
|
27
|
-
attr_accessor :name
|
74
|
+
attr_reader :id, :name
|
28
75
|
|
29
76
|
def render
|
30
77
|
# do crazy render here
|
31
78
|
end
|
32
79
|
|
33
|
-
|
80
|
+
def load(options)
|
81
|
+
# make call here
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.add(location)
|
85
|
+
# ...
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
def valid?
|
90
|
+
# returns if call is valid
|
91
|
+
end
|
92
|
+
instrument_method :render, 'render.widget', :widget_id => :id, :widget_name => :name
|
93
|
+
instrument_method :load, 'load.widget', :status => 'loading', :valid => :valid?
|
94
|
+
class_instrument_method :add, 'add.widget'
|
34
95
|
end
|
35
96
|
```
|
36
97
|
|
@@ -39,7 +100,7 @@ end
|
|
39
100
|
|
40
101
|
## Running Tests
|
41
102
|
|
42
|
-
|
103
|
+
rake test
|
43
104
|
|
44
105
|
## Contributing
|
45
106
|
|
data/lib/instrumentable.rb
CHANGED
@@ -3,41 +3,99 @@ require "active_support/concern"
|
|
3
3
|
require "active_support/notifications"
|
4
4
|
require "instrumentable/version"
|
5
5
|
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# Instrumentable is a module you can include in your classes to help with
|
7
|
+
# setting up instrumention with ActiveSupport::Notifications.instrument
|
8
|
+
# Commoningly you would use ActiveSupport::Notifications.instrument and
|
9
|
+
# wrap it around the code you want you care about in order to fire off
|
10
|
+
# the approiate event. This allows you to do the same thing but without
|
11
|
+
# having to modify your existing method definitions.
|
12
|
+
#
|
13
|
+
# ==== Example
|
14
|
+
#
|
15
|
+
# class Person
|
16
|
+
# include Instrumentable
|
17
|
+
# attr_reader :phone
|
18
|
+
#
|
19
|
+
# def call
|
20
|
+
# puts "Calling at #{@phone} number"
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# def self.find(id)
|
24
|
+
# PersonStore.find(id)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# instrument_method :call, 'person.call', :phone => :phone
|
28
|
+
# class_instrument_method :find, 'person.find'
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# person = Person.find(5)
|
32
|
+
# person.call
|
33
|
+
#
|
34
|
+
# Result:
|
35
|
+
# An event with name 'person.find' would fire when `Person.find` is called
|
36
|
+
# with a payload of { :_method_args => 5 }
|
37
|
+
#
|
38
|
+
# An event with name 'person.call' would fire when person.call is called
|
39
|
+
# with a payload of { :phone => (555)555-555 }
|
9
40
|
module Instrumentable
|
10
41
|
extend ActiveSupport::Concern
|
11
42
|
|
12
43
|
module ClassMethods
|
13
|
-
#
|
14
|
-
#
|
44
|
+
# Wraps method_to_instrument in an AS:N:instrument with event_name as
|
45
|
+
# the name and passes the payload given to it.
|
15
46
|
#
|
16
|
-
#
|
47
|
+
# Payload is a hash of payload_name to payload_value
|
17
48
|
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
21
|
-
|
22
|
-
|
49
|
+
# payload_value supported types:
|
50
|
+
#
|
51
|
+
# String:: Passes the string straight to the payload_name
|
52
|
+
# and does nothing else with it
|
53
|
+
# Symbol:: A method to call on the class being instrumented
|
54
|
+
# Proc:: A proc to call with the object
|
55
|
+
#
|
56
|
+
# All of these have their value then calculated (if needed) and set
|
57
|
+
# as the value for the given key
|
58
|
+
def instrument_method(method_to_instrument, event_name, payload={})
|
59
|
+
Instrumentality.begin(self, method_to_instrument, event_name, payload)
|
60
|
+
end
|
23
61
|
|
24
|
-
|
25
|
-
|
62
|
+
# Class implementation of +instrument_method+
|
63
|
+
def class_instrument_method(klass, method_to_instrument, event_name, payload={})
|
64
|
+
class << klass; self; end.class_eval do
|
65
|
+
Instrumentality.begin(self, method_to_instrument, event_name, payload)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
26
69
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
70
|
+
private
|
71
|
+
class Instrumentality
|
72
|
+
|
73
|
+
def self.begin(klass, method, event, payload)
|
74
|
+
instrumentality = self
|
75
|
+
instrumented_method = :"_instrument_for_#{method}"
|
76
|
+
klass.send :alias_method, instrumented_method, method
|
77
|
+
|
78
|
+
klass.send(:define_method, method) do |*args, &block|
|
79
|
+
event_payload = payload.inject({}) do |result, (payload_key, payload_value)|
|
80
|
+
value = instrumentality.invoke_value(self, payload_value)
|
35
81
|
result.tap { |r| r[payload_key] = value }
|
36
82
|
end
|
37
|
-
|
38
|
-
|
83
|
+
event_payload.merge!({:_method_args => args})
|
84
|
+
ActiveSupport::Notifications.instrument event, event_payload do
|
85
|
+
__send__(instrumented_method, *args, &block)
|
39
86
|
end
|
40
87
|
end
|
41
88
|
end
|
89
|
+
|
90
|
+
def self.invoke_value(klass, obj)
|
91
|
+
case obj
|
92
|
+
when Symbol
|
93
|
+
klass.__send__ obj
|
94
|
+
when Proc
|
95
|
+
obj.call
|
96
|
+
when String
|
97
|
+
obj
|
98
|
+
end
|
99
|
+
end
|
42
100
|
end
|
43
101
|
end
|
data/test/instrumentable_test.rb
CHANGED
@@ -1,56 +1,118 @@
|
|
1
1
|
require "minitest_helper"
|
2
2
|
require_relative "../lib/instrumentable"
|
3
3
|
|
4
|
-
class
|
4
|
+
class FatherCat
|
5
5
|
include Instrumentable
|
6
|
-
def
|
7
|
-
def
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
6
|
+
def hello_everynyan; end
|
7
|
+
def fake_cat; end
|
8
|
+
def osaka; end
|
9
|
+
def teachers(home, pe, geo); end
|
10
|
+
def self.omg; end
|
11
|
+
def self.sorry; "I'mma so sorry"; end
|
12
|
+
def self.who?(person); end
|
13
|
+
|
14
|
+
# String
|
15
|
+
instrument_method :hello_everynyan, 'fathercat.greet', :payload => 'FatherCat'
|
16
|
+
# Symbol
|
17
|
+
instrument_method :fake_cat, 'fathercat.rant', :payload => :look_alike
|
18
|
+
# Proc
|
19
|
+
instrument_method :osaka, 'fathercat.ask', :payload => Proc.new { 'also cat tounged' }
|
20
|
+
# Args
|
21
|
+
instrument_method :teachers, 'fathercat.teachers', :payload => 'teachers'
|
22
|
+
|
23
|
+
# Class
|
24
|
+
class_instrument_method self, :omg, 'fathercat.anger', :payload => :sorry
|
25
|
+
# Args
|
26
|
+
class_instrument_method self, :who?, 'fathercat.who', :payload => 'who?'
|
12
27
|
end
|
13
28
|
|
14
29
|
describe Instrumentable do
|
15
30
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
31
|
+
describe ".instrument_method" do
|
32
|
+
it "must instrument fathercat.greet" do
|
33
|
+
cat = FatherCat.new
|
34
|
+
expected = ["fathercat.greet-FatherCat"]
|
35
|
+
events = []
|
21
36
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
37
|
+
callback = lambda { |*_| events << "#{_.first}-#{_.last[:payload]}" }
|
38
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.greet') do
|
39
|
+
cat.hello_everynyan
|
40
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
41
|
+
end
|
42
|
+
events.must_equal expected
|
43
|
+
end
|
44
|
+
|
45
|
+
it "must instrument fathercat.rant" do
|
46
|
+
cat = FatherCat.new
|
47
|
+
def cat.look_alike; 'Mori'; end
|
48
|
+
expected = ["fathercat.rant-#{cat.look_alike}"]
|
49
|
+
events = []
|
50
|
+
|
51
|
+
callback = lambda { |*_| events << "#{_.first}-#{_.last[:payload]}" }
|
52
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.rant') do
|
53
|
+
cat.fake_cat
|
54
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
55
|
+
end
|
56
|
+
events.must_equal expected
|
26
57
|
end
|
27
|
-
events.must_equal expected
|
28
|
-
end
|
29
58
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
events = []
|
59
|
+
it "must instrument fathercat.ask" do
|
60
|
+
cat = FatherCat.new
|
61
|
+
expected = ["fathercat.ask-also cat tounged"]
|
62
|
+
events = []
|
35
63
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
64
|
+
callback = lambda { |*_| events << "#{_.first}-#{_.last[:payload]}" }
|
65
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.ask') do
|
66
|
+
cat.osaka
|
67
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
68
|
+
end
|
69
|
+
events.must_equal expected
|
70
|
+
end
|
71
|
+
|
72
|
+
it "must pass method args to payload" do
|
73
|
+
cat = FatherCat.new
|
74
|
+
teachers = { :home => 'Yukari', :pe => 'Nyamo', :geo => 'Kimura' }
|
75
|
+
expected = ["fathercat.teachers-#{teachers[:home]}_#{teachers[:pe]}_#{teachers[:geo]}"]
|
76
|
+
events = []
|
77
|
+
|
78
|
+
callback = lambda do |*_|
|
79
|
+
event_name = _.first
|
80
|
+
payload = _.last[:_method_args].join('_')
|
81
|
+
events << "#{event_name}-#{payload}"
|
82
|
+
end
|
83
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.teachers') do
|
84
|
+
cat.teachers(teachers[:home], teachers[:pe], teachers[:geo])
|
85
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
86
|
+
end
|
87
|
+
events.must_equal expected
|
40
88
|
end
|
41
|
-
events.must_equal expected
|
42
89
|
end
|
43
90
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
91
|
+
describe ".class_instrument_method" do
|
92
|
+
it "must instrument fathercat.anger" do
|
93
|
+
expected = ["fathercat.anger-#{FatherCat.sorry}"]
|
94
|
+
events = []
|
95
|
+
|
96
|
+
callback = lambda { |*_| events << "#{_.first}-#{_.last[:payload]}" }
|
97
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.anger') do
|
98
|
+
FatherCat.omg
|
99
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
100
|
+
end
|
101
|
+
events.must_equal expected
|
102
|
+
end
|
103
|
+
|
104
|
+
#class_instrument_method self, :who?, 'fathercat.who', :payload => 'who?'
|
105
|
+
it "must pass method args to payload" do
|
106
|
+
person = 'chiyo'
|
107
|
+
expected = ["fathercat.who-#{person}"]
|
108
|
+
events = []
|
48
109
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
110
|
+
callback = lambda { |*_| events << "#{_.first}-#{_.last[:_method_args].first}" }
|
111
|
+
ActiveSupport::Notifications.subscribed(callback, 'fathercat.who') do
|
112
|
+
FatherCat.who?(person)
|
113
|
+
ActiveSupport::Notifications.instrument('some.other.event')
|
114
|
+
end
|
115
|
+
events.must_equal expected
|
53
116
|
end
|
54
|
-
events.must_equal expected
|
55
117
|
end
|
56
118
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: instrumentable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|