instrumentable 0.0.4 → 1.0.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/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
|