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 CHANGED
@@ -1,12 +1,18 @@
1
- # Instrumentable
1
+ # Instrumentable (1.0.0)
2
+ -note, this version is not backwards compatiable
2
3
 
3
- TODO: Write a gem description
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 'instrumentable'
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 :id
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
- instrument_for :render, 'load.widget', :widget_id => :id, :widget_name => :name
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
- ruby -Itest test/instrumentable_test.rb
103
+ rake test
43
104
 
44
105
  ## Contributing
45
106
 
@@ -3,41 +3,99 @@ require "active_support/concern"
3
3
  require "active_support/notifications"
4
4
  require "instrumentable/version"
5
5
 
6
- # Includes +instrument_for+ into the class. The class uses it by adding
7
- # the instrument_for method to the end of the class specifying
8
- # what method to apply it to.
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
- # Internal: Decorates :method_to_instrument with AS::N.instrument
14
- # firing with :event_name to the matching AS::N.subscribe
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
- # Example:
47
+ # Payload is a hash of payload_name to payload_value
17
48
  #
18
- # # Decorates render method with AS:N:instrument 'model.render' and passes
19
- # # a payload of :model_name and :id to the subscribe method
20
- # instrument_for :render, 'model.render', {:model_name => :model_name, :id => :id
21
- def instrument_for(method_to_instrument, event_name, payload={})
22
- instrument_method = :"instrument_for_#{method_to_instrument}"
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
- # Hide original method under new method
25
- alias_method instrument_method, method_to_instrument
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
- # Redefine method_to_instrument to call inside the Notification
28
- define_method(method_to_instrument) do |*args, &block|
29
- callable_payload = payload.inject({}) do |result, (payload_key, payload_value)|
30
- value = if respond_to?(payload_value)
31
- __send__ payload_value
32
- else
33
- payload_value
34
- end
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
- ActiveSupport::Notifications.instrument event_name, callable_payload do
38
- __send__(instrument_method, *args, &block)
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
@@ -1,3 +1,3 @@
1
1
  module Instrumentable
2
- VERSION = "0.0.4"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1,56 +1,118 @@
1
1
  require "minitest_helper"
2
2
  require_relative "../lib/instrumentable"
3
3
 
4
- class FakeModel
4
+ class FatherCat
5
5
  include Instrumentable
6
- def simple_event; end
7
- def false_event; end
8
- def payload_event; end
9
- instrument_for :simple_event, 'event.name', :my_payload => :id
10
- instrument_for :false_event, 'event.name', :my_payload => :valid
11
- instrument_for :payload_event, 'payload.name', :my_payload => 'megaman'
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
- it "must instrument simple_event" do
17
- fm = FakeModel.new
18
- def fm.id; 1; end
19
- expected = ["event.name-#{fm.id}"]
20
- events = []
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
- callback = lambda { |*_| events << "#{_.first}-#{_.last[:my_payload]}" }
23
- ActiveSupport::Notifications.subscribed(callback, 'event.name') do
24
- fm.simple_event
25
- ActiveSupport::Notifications.instrument('other.event')
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
- it "must work with callable payload that returns false" do
31
- fm = FakeModel.new
32
- def fm.valid; false; end
33
- expected = ["event.name-#{fm.valid}"]
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
- callback = lambda { |*_| events << "#{_.first}-#{_.last[:my_payload]}" }
37
- ActiveSupport::Notifications.subscribed(callback, 'event.name') do
38
- fm.false_event
39
- ActiveSupport::Notifications.instrument('other.event')
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
- it "must handle non-callable payloads" do
45
- fm = FakeModel.new
46
- expected = ['payload.name-megaman']
47
- events = []
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
- callback = lambda { |*_| events << "#{_.first}-#{_.last[:my_payload]}" }
50
- ActiveSupport::Notifications.subscribed(callback, 'payload.name') do
51
- fm.payload_event
52
- ActiveSupport::Notifications.instrument('other.event')
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
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-14 00:00:00.000000000 Z
12
+ date: 2012-12-17 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport