mushroom 0.0.1
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/.gitignore +18 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +30 -0
- data/LICENSE +22 -0
- data/README.md +75 -0
- data/Rakefile +14 -0
- data/lib/mushroom.rb +55 -0
- data/lib/mushroom/subscriber.rb +84 -0
- data/lib/mushroom/version.rb +3 -0
- data/mushroom.gemspec +21 -0
- data/spec/mushroom/subscriber_spec.rb +101 -0
- data/spec/mushroom_spec.rb +57 -0
- data/spec/spec_helper.rb +11 -0
- metadata +83 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
mushroom (0.0.1)
|
5
|
+
activesupport (>= 3.0.0)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
activesupport (3.2.3)
|
11
|
+
i18n (~> 0.6)
|
12
|
+
multi_json (~> 1.0)
|
13
|
+
diff-lcs (1.1.3)
|
14
|
+
i18n (0.6.0)
|
15
|
+
multi_json (1.3.2)
|
16
|
+
rspec (2.9.0)
|
17
|
+
rspec-core (~> 2.9.0)
|
18
|
+
rspec-expectations (~> 2.9.0)
|
19
|
+
rspec-mocks (~> 2.9.0)
|
20
|
+
rspec-core (2.9.0)
|
21
|
+
rspec-expectations (2.9.1)
|
22
|
+
diff-lcs (~> 1.1.3)
|
23
|
+
rspec-mocks (2.9.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
mushroom!
|
30
|
+
rspec (>= 2.8.0)
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Ivan Vanderbyl
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
# Mushroom
|
2
|
+
|
3
|
+
Mushroom is a super simple wrapper around ActiveSupport Instrumentation introduced in Rails 3.
|
4
|
+
|
5
|
+
Muchroom allows any component of your application to trigger events which later get subscribed to by any
|
6
|
+
other component in your application.
|
7
|
+
|
8
|
+
The returned event can be used to handle follow up behaviour, profile running code, and event dispatch requests to
|
9
|
+
other services.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Add this line to your application's Gemfile:
|
14
|
+
|
15
|
+
gem 'mushroom'
|
16
|
+
|
17
|
+
And then execute:
|
18
|
+
|
19
|
+
$ bundle
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
$ gem install mushroom
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
|
29
|
+
# app/models/server.rb
|
30
|
+
class Server
|
31
|
+
include Mushroom
|
32
|
+
|
33
|
+
def run
|
34
|
+
notify :start #=> Dispatches a 'server:start' event with the current server instance as the event target.
|
35
|
+
end
|
36
|
+
|
37
|
+
# You can instrument methods inline.
|
38
|
+
# This will trigger the server:stop event when you call #stop
|
39
|
+
# and notify all subscribers to the stop event.
|
40
|
+
#
|
41
|
+
# The server:stop event #duration will be 1 second.
|
42
|
+
#
|
43
|
+
def stop
|
44
|
+
sleep 1
|
45
|
+
end
|
46
|
+
instrument :stop
|
47
|
+
end
|
48
|
+
|
49
|
+
# lib/my\_app/event\_handler.rb
|
50
|
+
class EventHandler < Muchroom::Subscriber
|
51
|
+
events :start, :stop, :on => Server
|
52
|
+
events :cleanup, :on => ServerCache
|
53
|
+
|
54
|
+
def notify
|
55
|
+
# Handle event here
|
56
|
+
puts name #=> "server:start"
|
57
|
+
puts target #=> <Server id:1 ...>
|
58
|
+
puts duration #=> 0.001
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Server.new.run
|
63
|
+
```
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
72
|
+
|
73
|
+
# Author
|
74
|
+
|
75
|
+
- Ivan Vanderbyl
|
data/Rakefile
ADDED
data/lib/mushroom.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'mushroom/version'
|
2
|
+
require 'active_support'
|
3
|
+
require 'active_support/core_ext/array/extract_options'
|
4
|
+
require 'active_support/core_ext/class/attribute'
|
5
|
+
require 'active_support/inflector'
|
6
|
+
require 'active_support/core_ext/module/delegation'
|
7
|
+
require 'active_support/core_ext/module/aliasing'
|
8
|
+
|
9
|
+
module Mushroom
|
10
|
+
extend ActiveSupport::Concern
|
11
|
+
|
12
|
+
autoload :Subscriber, 'mushroom/subscriber'
|
13
|
+
|
14
|
+
included do
|
15
|
+
class_eval do
|
16
|
+
# Public: Instrument a method call
|
17
|
+
#
|
18
|
+
# Example:
|
19
|
+
# def start
|
20
|
+
# # work hard here
|
21
|
+
# end
|
22
|
+
# instrument :start
|
23
|
+
#
|
24
|
+
def self.instrument(method, options = {})
|
25
|
+
define_method(:"#{method}_with_instrument") do |*args, &block|
|
26
|
+
instrument(method) do
|
27
|
+
send(:"#{method}_without_instrument", *args, &block)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias_method_chain method, 'instrument'
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class << self
|
36
|
+
def event_name(event, sender)
|
37
|
+
if sender.is_a?(String)
|
38
|
+
klass_name = sender
|
39
|
+
else
|
40
|
+
klass_name = sender.respond_to?(:ancestors) && sender.ancestors.first == sender ? sender.name : sender.class.name
|
41
|
+
end
|
42
|
+
[klass_name.demodulize.underscore.gsub('/', ':'), event.to_s].join(':')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def notify(event, *args)
|
47
|
+
instrument(event, *args) {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def instrument(event, *args, &block)
|
51
|
+
event = Mushroom.event_name(event, self)
|
52
|
+
ActiveSupport::Notifications.instrument(event.to_s, :target => self, :args => args, &block)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'active_support/core_ext/module/delegation'
|
2
|
+
require 'active_support/core_ext/hash/slice'
|
3
|
+
|
4
|
+
module Mushroom
|
5
|
+
class Subscriber
|
6
|
+
|
7
|
+
attr_reader :event
|
8
|
+
delegate :payload, :name, :time, :transaction_id, :duration, :to => :event
|
9
|
+
|
10
|
+
def initialize(event)
|
11
|
+
@event = event
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Public: Setter and getter for events
|
17
|
+
#
|
18
|
+
# Can be called multiple times to set events on a Subscriber.
|
19
|
+
#
|
20
|
+
# Events cannot be inherited by subclassed objects.
|
21
|
+
#
|
22
|
+
# Example:
|
23
|
+
# class Handler < Mushroom::Subscriber
|
24
|
+
# events :start, :on => Server
|
25
|
+
# events :stop, :on => [Server, Time]
|
26
|
+
# events :register, :activate, :on => [User]
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Returns: Array of current event registrations.
|
30
|
+
def events(*events_and_options)
|
31
|
+
if events_and_options.empty?
|
32
|
+
self._events
|
33
|
+
else
|
34
|
+
create_event_subscription(*events_and_options)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Internal: Trigger the notification within this subscriber
|
39
|
+
def notify(event)
|
40
|
+
instance = new(event)
|
41
|
+
|
42
|
+
if instance.method(:notify).arity == 0
|
43
|
+
instance.notify
|
44
|
+
elsif instance.method(:notify).arity > 0
|
45
|
+
instance.notify(*event.payload[:args])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
protected
|
50
|
+
|
51
|
+
def _events
|
52
|
+
Thread.current[:"_events_#{object_id}"] ||= []
|
53
|
+
end
|
54
|
+
|
55
|
+
def _events=(hash)
|
56
|
+
Thread.current[:"_events_#{object_id}"] = []
|
57
|
+
end
|
58
|
+
|
59
|
+
def create_event_subscription(*events_and_options)
|
60
|
+
options = {
|
61
|
+
:on => nil
|
62
|
+
}.update(events_and_options.extract_options!)
|
63
|
+
|
64
|
+
targets = Array(options[:on]) || raise(ArgumentError, "Event subscription must include :on => Class")
|
65
|
+
|
66
|
+
targets.each do |target|
|
67
|
+
events_and_options.each do |event|
|
68
|
+
event = Mushroom.event_name(event, target)
|
69
|
+
|
70
|
+
# Actually subscribe the event
|
71
|
+
ActiveSupport::Notifications.subscribe(event) do |*args|
|
72
|
+
self.notify(ActiveSupport::Notifications::Event.new(*args))
|
73
|
+
end
|
74
|
+
|
75
|
+
(self._events ||=[]).push(event)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
return self._events
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
data/mushroom.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/mushroom/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ivan Vanderbyl"]
|
6
|
+
gem.email = ["ivanvanderbyl@me.com"]
|
7
|
+
gem.description = %q{Super simple events and instrumentation within your ruby app}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = "https://github.com/ivanvanderbyl/mushroom"
|
10
|
+
|
11
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
12
|
+
gem.files = `git ls-files`.split("\n")
|
13
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
14
|
+
gem.name = "mushroom"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Mushroom::VERSION
|
17
|
+
|
18
|
+
gem.add_dependency "activesupport", ">= 3.0.0"
|
19
|
+
|
20
|
+
gem.add_development_dependency "rspec", ">= 2.8.0"
|
21
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mushroom::Subscriber do
|
4
|
+
|
5
|
+
class Server
|
6
|
+
end
|
7
|
+
|
8
|
+
class Dummy
|
9
|
+
end
|
10
|
+
|
11
|
+
class EventHandler < Mushroom::Subscriber
|
12
|
+
def notify
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class ServerEventHandler < Mushroom::Subscriber
|
17
|
+
def notify
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
EventHandler.send(:_events=, {})
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.events' do
|
26
|
+
it 'allows adding event for a class' do
|
27
|
+
lambda { EventHandler.events :destroy, :on => Server }.should_not raise_error
|
28
|
+
EventHandler.events.should == ['server:destroy']
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'stores events which weve added' do
|
32
|
+
EventHandler.events :start, :stop, :on => Server
|
33
|
+
EventHandler.events.should == ['server:start', 'server:stop']
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'allows adding events for the same target multiple times' do
|
37
|
+
EventHandler.events :start, :on => Server
|
38
|
+
EventHandler.events :stop, :on => Server
|
39
|
+
EventHandler.events.should == ['server:start', 'server:stop']
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'allows subscribing the same events to multiple targets' do
|
43
|
+
EventHandler.events :start, :on => [Server, Dummy]
|
44
|
+
EventHandler.events.should == ['server:start', 'dummy:start']
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'does not inherit events across subscribers' do
|
48
|
+
EventHandler.send(:_events=, {})
|
49
|
+
EventHandler.events :stop, :on => Dummy
|
50
|
+
ServerEventHandler.events.should == []
|
51
|
+
ServerEventHandler.events :start, :on => Server
|
52
|
+
EventHandler.events.should == ['dummy:stop']
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'should be thread safe' do
|
56
|
+
t1 = Thread.new { EventHandler.events :start, :on => Server }
|
57
|
+
t1.join
|
58
|
+
EventHandler.events.should == []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.notify' do
|
63
|
+
it 'gets called when an event is triggered' do
|
64
|
+
class CustomHandler < Mushroom::Subscriber
|
65
|
+
events :start, :on => Server
|
66
|
+
end
|
67
|
+
CustomHandler.should_receive(:notify).once
|
68
|
+
Server.new.notify(:start)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'passes arguments to subscriber' do
|
72
|
+
class CustomHandler < Mushroom::Subscriber
|
73
|
+
events :start, :on => Server
|
74
|
+
|
75
|
+
def notify(payload)
|
76
|
+
payload.should == {:payload => {:id => 123}}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Server.new.notify(:start, {:payload => {:id => 123}})
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'passes multiple arguments to subscriber' do
|
84
|
+
Thread.current[:now] = Time.now
|
85
|
+
|
86
|
+
class CustomHandler < Mushroom::Subscriber
|
87
|
+
events :start, :on => Server
|
88
|
+
|
89
|
+
def notify(payload, time)
|
90
|
+
payload.should == {:payload => {:id => 123}}
|
91
|
+
time.should_not == nil
|
92
|
+
time.should == Thread.current[:now]
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
Server.new.notify(:start, {:payload => {:id => 123}}, Thread.current[:now])
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Mushroom do
|
4
|
+
class Server
|
5
|
+
include Mushroom
|
6
|
+
|
7
|
+
def start
|
8
|
+
notify :start
|
9
|
+
end
|
10
|
+
|
11
|
+
def stop
|
12
|
+
sleep 0.1
|
13
|
+
end
|
14
|
+
instrument :stop
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:server) { Server.new }
|
18
|
+
|
19
|
+
describe '.event_name' do
|
20
|
+
it 'constructs a valid event name from class and event triggered' do
|
21
|
+
server = Server.new
|
22
|
+
|
23
|
+
Mushroom.event_name(:start, server).should == 'server:start'
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'works with a string' do
|
27
|
+
Mushroom.event_name(:start, 'server').should == 'server:start'
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'works with a class' do
|
31
|
+
Mushroom.event_name(:start, Server).should == 'server:start'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'Server' do
|
36
|
+
describe '#start' do
|
37
|
+
it 'triggers a server:start event' do
|
38
|
+
ActiveSupport::Notifications.should_receive(:instrument).with("server:start", {:target=>server, :args=>[]}).once
|
39
|
+
server.start
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
describe '#stop' do
|
44
|
+
it 'triggers a server:stop event' do
|
45
|
+
ActiveSupport::Notifications.should_receive(:instrument).with("server:stop", {:target=>server, :args=>[]}).once
|
46
|
+
server.stop
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#instrument' do
|
52
|
+
it 'can be called from instance' do
|
53
|
+
ActiveSupport::Notifications.should_receive(:instrument).with("server:render", {:target=>server, :args=>[]}).once
|
54
|
+
server.instrument(:render) { sleep 0.01 }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mushroom
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Ivan Vanderbyl
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-04-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: &70190744237180 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.0.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70190744237180
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rspec
|
27
|
+
requirement: &70190744236020 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ! '>='
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.8.0
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *70190744236020
|
36
|
+
description: Super simple events and instrumentation within your ruby app
|
37
|
+
email:
|
38
|
+
- ivanvanderbyl@me.com
|
39
|
+
executables: []
|
40
|
+
extensions: []
|
41
|
+
extra_rdoc_files: []
|
42
|
+
files:
|
43
|
+
- .gitignore
|
44
|
+
- Gemfile
|
45
|
+
- Gemfile.lock
|
46
|
+
- LICENSE
|
47
|
+
- README.md
|
48
|
+
- Rakefile
|
49
|
+
- lib/mushroom.rb
|
50
|
+
- lib/mushroom/subscriber.rb
|
51
|
+
- lib/mushroom/version.rb
|
52
|
+
- mushroom.gemspec
|
53
|
+
- spec/mushroom/subscriber_spec.rb
|
54
|
+
- spec/mushroom_spec.rb
|
55
|
+
- spec/spec_helper.rb
|
56
|
+
homepage: https://github.com/ivanvanderbyl/mushroom
|
57
|
+
licenses: []
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ! '>='
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ! '>='
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '0'
|
74
|
+
requirements: []
|
75
|
+
rubyforge_project:
|
76
|
+
rubygems_version: 1.8.15
|
77
|
+
signing_key:
|
78
|
+
specification_version: 3
|
79
|
+
summary: Super simple events and instrumentation within your ruby app
|
80
|
+
test_files:
|
81
|
+
- spec/mushroom/subscriber_spec.rb
|
82
|
+
- spec/mushroom_spec.rb
|
83
|
+
- spec/spec_helper.rb
|