call_me_maybe 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.pryrc ADDED
@@ -0,0 +1,34 @@
1
+ require 'call_me_maybe'
2
+
3
+ begin
4
+ require 'awesome_print'
5
+ Pry.config.print = proc { |output, value| output.puts value.ai }
6
+ rescue LoadError => err
7
+ puts "no awesome_print :("
8
+ end
9
+
10
+ class New
11
+ def apple
12
+ puts "Apples are tasty ^_^"
13
+ end
14
+
15
+ def self.orange
16
+ puts "Oranges are too sweet for me ^.^'"
17
+ end
18
+
19
+ call_me_maybe klass: "#{self}", method: :apple, event: :before do
20
+ puts "Before Callback: Instance Level"
21
+ end
22
+
23
+ call_me_maybe klass: "#{self}", method: :orange, type: :class, event: :before do
24
+ puts "Before Callback: Class Method"
25
+ end
26
+
27
+ call_me_maybe method: :apple, event: :after do
28
+ puts "After Callback: Instance Level"
29
+ end
30
+
31
+ call_me_maybe klass: "New.orange", type: :class, event: :after do
32
+ puts "After Callback: Class Method"
33
+ end
34
+ end
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activesupport', '>= 3.2.12', require: [
4
+ 'active_support/core_ext/module/attribute_accessors',
5
+ 'active_support/inflector'
6
+ ]
7
+
8
+ # Specify your gem's dependencies in call_me_maybe.gemspec
9
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kelly Becker
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.
@@ -0,0 +1,84 @@
1
+ # Call Me Maybe
2
+
3
+ Call Me Maybe is a simple event engine for Ruby. This event engine was modeled a little after the event engine from EvolutionSDK 3.0 and then taking it a step further. The idea here is that you can capture the call of any method as well as bind any method to listen for an event trigger and fire events. It's simply awesome!
4
+
5
+ ## Dependencies
6
+
7
+ * activesupport
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'call_me_maybe'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install call_me_maybe
22
+
23
+ ## Usage - Catching Method Calls
24
+
25
+ So this is really awesome. When it comes to use. So the real logic for awesome callbacks is this `call_me_maybe` method. This method is actually attached to the Module object so it's extremely versitile and is not instance or class specific.
26
+
27
+ The arguments for `call_me_maybe` are:
28
+
29
+ - klass: Klass is the object that owns the method you are planning to track. If the method is on the same object as where you are executing `call_me_maybe`, then you do not need to pass this argument. Klass also accepts methods on the klass name if klass is provided as a string (i.e. "New.other"; where New is the class and other is the method).
30
+ - method: Method is the method that you would like to call. Just the name of the method it does not matter whether or not the method is a singelton method or a instance method.
31
+ - type: Type tells `call_me_maybe` which method to watch; the instance method or the singleton method. The acceptable options are either :class or :instance, :instance is set by default.
32
+ - event: The method you would like to capture. The only supported events that the `call_me_maybe` method supports are :before and :after.
33
+ - me: Me is hash arguments that refer to what should be run when the method you are tracking is called and the event is fired.
34
+ * when: When is the event you want your task to run on. It is synonymous with the event: option
35
+ * event: When is the event you want your task to run on. It is synonymous with the when: option
36
+ * block: Block a Proc object that will be called when the event is fired. You can also pass a block to `call_me_maybe` and it will be used here instead. If you pass a block then the klass-method pairing for the callbacks will not be fired.
37
+ * klass: So this klass is the reference to the object that you want to run the method on when the event fires. This accepts a klass object, klass string name or a klass string name with a method attached (see example above).
38
+ * method: Method is the method you wish to run on the klass provided to to be used when the event fires.
39
+
40
+ ```ruby
41
+ class New
42
+ def apple
43
+ puts "Apples are tasty ^_^"
44
+ end
45
+
46
+ def self.orange
47
+ puts "Oranges are too sweet for me ^.^'"
48
+ end
49
+
50
+ call_me_maybe klass: "#{self}", method: :apple, event: :before do
51
+ puts "Before Callback: Instance Level"
52
+ end
53
+
54
+ call_me_maybe klass: "#{self}", method: :orange, type: :class, event: :before do
55
+ puts "Before Callback: Class Method"
56
+ end
57
+
58
+ call_me_maybe method: :apple, event: :after do
59
+ puts "After Callback: Instance Level"
60
+ end
61
+
62
+ call_me_maybe klass: "New.orange", type: :class, event: :after do
63
+ puts "After Callback: Class Method"
64
+ end
65
+ end
66
+ ```
67
+
68
+ ## Usage - Registering Events and Triggers
69
+
70
+ Coming Soon
71
+
72
+ ```ruby
73
+ CallMeMaybe::Exchange.add_listener
74
+
75
+ CallMeMaybe::Exchange.trigger
76
+ ```
77
+
78
+ ## Contributing
79
+
80
+ 1. Fork it
81
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
82
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
83
+ 4. Push to the branch (`git push origin my-new-feature`)
84
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'call_me_maybe/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "call_me_maybe"
8
+ spec.version = CallMeMaybe::VERSION
9
+ spec.authors = ["Kelly Becker"]
10
+ spec.email = ["kellylsbkr@gmail.com"]
11
+ spec.description = %q{Event handler and listener for Ruby}
12
+ spec.summary = %q{Listens and captures event comings from objects}
13
+ spec.homepage = "http://kellybecker.me"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "awesome_print"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "pry"
26
+
27
+ spec.add_development_dependency "activesupport", ">= 3.2.12"
28
+ end
@@ -0,0 +1,21 @@
1
+ # Load our specificly requested AS components
2
+ require "active_support/core_ext/module/attribute_accessors"
3
+ require "active_support/inflector"
4
+ require 'awesome_print'
5
+
6
+ # Load in the Call Me Maybe files
7
+ require "call_me_maybe/version"
8
+ require "call_me_maybe/exchange"
9
+ require "call_me_maybe/module"
10
+
11
+ # Add in the `call_me_maybe` command
12
+ module CallMeMaybe
13
+ class << self
14
+ def setup
15
+ ::Module.__send__(:include, CallMeMaybe::Module)
16
+ end
17
+ end
18
+ end
19
+
20
+ # Startup the module
21
+ CallMeMaybe.setup
@@ -0,0 +1,121 @@
1
+ module CallMeMaybe
2
+ module Exchange
3
+ class Careful < StandardError; end
4
+ class MethodDoesNotExist < StandardError; end
5
+ class CantCaptureMethod < StandardError; end
6
+
7
+ mattr_accessor :methods, instance_accessor: false
8
+ mattr_accessor :listeners, instance_accessor: false
9
+
10
+ class << self
11
+ def add_method(args = {})
12
+ id, name = get_id(args)
13
+
14
+ # Create the cache
15
+ CallMeMaybe::Exchange.methods ||= {}
16
+
17
+ # Create the method if needed
18
+ if CallMeMaybe::Exchange.methods[id] && ! CallMeMaybe::Exchange.methods[id].has_key?(:method_obj)
19
+ CallMeMaybe::Exchange.methods[id].merge!(args)
20
+ true
21
+ elsif ! methods[id]
22
+ CallMeMaybe::Exchange.methods[id] = args.merge(name: name)
23
+ true
24
+ else
25
+ false
26
+ end
27
+ end
28
+
29
+ def get_method(args = {})
30
+ # Ensure the cache's presence
31
+ CallMeMaybe::Exchange.methods ||= {}
32
+ raise MethodDoesNotExist unless method_exists?(args)
33
+ CallMeMaybe::Exchange.methods[get_id(args).first][:method_obj]
34
+ end
35
+
36
+ def method_exists?(args = {})
37
+ # Ensure the cache's presence
38
+ CallMeMaybe::Exchange.methods ||= {}
39
+ CallMeMaybe::Exchange.methods.has_key?(get_id(args).first)
40
+ end
41
+
42
+ def del_method(args = {})
43
+ raise Careful, "Please use `del_method!` if you really want to do this."
44
+ end
45
+
46
+ def del_method!(args = {})
47
+ # Ensure the cache's presence
48
+ CallMeMaybe::Exchange.methods ||= {}
49
+ CallMeMaybe::Exchange.methods.delete(get_id(args).first)
50
+ true
51
+ end
52
+
53
+ def trigger(event, obj, args = {}, &block)
54
+ id = get_id(args).first
55
+
56
+ # Prepare the cache
57
+ CallMeMaybe::Exchange.listeners ||= {}
58
+ CallMeMaybe::Exchange.listeners[event] ||= {}
59
+ CallMeMaybe::Exchange.listeners[event][id] ||= []
60
+
61
+ # Run the listening events
62
+ CallMeMaybe::Exchange.listeners[event][id].map do |callback|
63
+ if callback[:block] && callback[:block].is_a?(Proc)
64
+ callback[:block].call(obj, args, &block)
65
+ else
66
+ callback[:klass].constantize.__send__(callback[:method], obj, args[:meta], &block)
67
+ end
68
+ end
69
+
70
+ CallMeMaybe::Exchange.listeners[event][id].count
71
+ end
72
+
73
+ def add_listener(args = {}, &block)
74
+
75
+ # Prepare the listeners data
76
+ args[:me] ||= {}
77
+ args[:me][:when] ||= args.delete(:when) if args.has_key?(:when)
78
+ args[:me][:when] ||= args.delete(:event) if args.has_key?(:event)
79
+ args[:me][:block] ||= args.delete(:block) if args.has_key?(:block)
80
+ args[:me][:block] ||= block if block_given?
81
+
82
+ # Ensure that our event name we are capturing is in the right format
83
+ args[:me][:when] = "#{args[:me][:when]}".underscore.to_sym if args[:me].has_key?(:when)
84
+
85
+ # Get id
86
+ id = get_id(args).first
87
+
88
+ # Prepare the cache
89
+ CallMeMaybe::Exchange.listeners ||= {}
90
+ CallMeMaybe::Exchange.listeners[args[:me][:when]] ||= {}
91
+ CallMeMaybe::Exchange.listeners[args[:me][:when]][id] ||= []
92
+
93
+ # Add the details to the cache
94
+ if args[:block].is_a?(Proc)
95
+ CallMeMaybe::Exchange.listeners[args[:me][:when]][id] << args[:me][:block]
96
+ else
97
+ CallMeMaybe::Exchange.listeners[args[:me][:when]][id] << args[:me]
98
+ end
99
+
100
+ # Unique on the callbacks
101
+ a = CallMeMaybe::Exchange.listeners[args[:me][:when]][id].uniq
102
+ CallMeMaybe::Exchange.listeners[args[:me][:when]][id] = a
103
+ true
104
+ end
105
+
106
+ private
107
+
108
+ # Return the cache id.
109
+ def get_id(args = {})
110
+ if args.has_key?(:type) && args.has_key?(:klass) && args.has_key?(:method)
111
+ name = "#{args[:type]}: #{args[:klass]}.#{args[:method]}".to_sym
112
+ [name.object_id, name]
113
+ elsif args.has_key?(:name)
114
+ ["#{args[:name]}".downcase.to_sym]
115
+ else
116
+ raise CantCaptureMethod, "We need name; or type, klass and method passed in order to generate an id properly."
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,117 @@
1
+ module CallMeMaybe
2
+ class MissingArgs < StandardError; end
3
+
4
+ module Module
5
+ def call_me_maybe(args = {}, &block)
6
+
7
+ ###################
8
+ #= Listener Data =#
9
+ ###################
10
+
11
+ # Prepare the listeners data
12
+ args[:me] ||= {}
13
+ args[:me][:when] ||= args.delete(:when) if args.has_key?(:when)
14
+ args[:me][:when] ||= args.delete(:event) if args.has_key?(:event)
15
+ args[:me][:block] ||= args.delete(:block) if args.has_key?(:block)
16
+ args[:me][:block] ||= block if block_given?
17
+
18
+ # Ensure that our event name we are capturing is in the right format
19
+ args[:me][:when] = "#{args[:me][:when]}".underscore.to_sym if args[:me].has_key?(:when)
20
+
21
+ #####################
22
+ #= Capture Wrapper =#
23
+ #####################
24
+
25
+ # Default to catching instance methods
26
+ # and ensure klass is present and a string
27
+ args[:type] ||= :instance
28
+ args[:klass] ||= self
29
+ args[:klass] = "#{args[:klass]}"
30
+
31
+ # If the method was attached to the klass name
32
+ # then go ahead and parse it out
33
+ if args[:klass].include?('.')
34
+ klass, method = args[:klass].split('.')
35
+
36
+ args[:klass] = klass
37
+ args[:method] = method
38
+ end
39
+
40
+ # Ensure we have the klass and method we are capturing
41
+ raise MissingArgs, "We need the klass we are capturing" unless args[:klass]
42
+ raise MissingArgs, "We need the method we are capturing" unless args[:method]
43
+
44
+ # Prepare the format for the klass, method, type and event firing time
45
+ args[:klass] = "#{args[:klass]}".camelize
46
+ args[:method] = "#{args[:method]}".underscore.to_sym
47
+ args[:type] = "#{args[:type]}".downcase.to_sym
48
+
49
+ # Don't bother re-adding the method if it is already added
50
+ unless CallMeMaybe::Exchange.method_exists?(args)
51
+
52
+ # Constantize the klass
53
+ klass = args[:klass].constantize
54
+
55
+ # Add method args
56
+ margs = args.dup
57
+ margs.delete(:me)
58
+
59
+ # Proc for setting up the capturing of the method in question
60
+ capture_proc = Proc.new do
61
+ gmethod = args[:type] == :instance ? :instance_method : :method
62
+ cmethod = args[:type] == :instance ? :define_method : :define_singleton_method
63
+ CallMeMaybe::Exchange.add_method(margs.merge(method_obj: klass.__send__(gmethod, args[:method])))
64
+
65
+ klass.__send__(cmethod, args[:method]) do |*a, &b|
66
+
67
+ # Trigger before method is called callback
68
+ CallMeMaybe::Exchange.trigger(:before, self, args.merge(meta: {args: a, block: b}))
69
+
70
+ # Run the actual method using the CallMeMaybe cache
71
+ result = CallMeMaybe::Exchange.get_method(args)
72
+ result = (result.is_a?(UnboundMethod) ? result.bind(self) : result).call(*a, &b)
73
+
74
+ # Trigger after method is called callback
75
+ CallMeMaybe::Exchange.trigger(:after, self, args.merge(meta: {args: a, block: b, result: result}))
76
+
77
+ # Return the result
78
+ result
79
+ end
80
+ end
81
+
82
+ # Apply capture_proc to the klasses that we need to use
83
+ if args[:type] == :class && klass.singleton_methods.include?(args[:method])
84
+ klass.class.__send__(:class_exec, &capture_proc)
85
+ elsif args[:type] == :instance && klass.instance_methods.include?(args[:method])
86
+ klass.__send__(:class_exec, &capture_proc)
87
+ end
88
+ end
89
+
90
+ ####################
91
+ #= Listener Stuff =#
92
+ ####################
93
+
94
+ # If we need to add a listener as well go ahead and start that implementation
95
+ if args.has_key?(:me) && ! args[:me].empty?
96
+
97
+ # We need an event to listen to.
98
+ raise MissingArgs, "We need the event that we are capturing." unless args[:me][:when] || args[:me][:event]
99
+
100
+ # We need information on who to call when the event occurs
101
+ unless args[:me][:block] || (args[:me][:klass] && args[:me][:method])
102
+ raise MissingArgs, "We need the a proc, block or information on what to run when the event occurs."
103
+ end
104
+
105
+ # Faking some arguments
106
+ args[:me][:klass] ||= args[:klass]
107
+ if args[:me][:block] && ( ! args[:me][:klass] || ! args[:me][:method])
108
+ args[:me][:klass] = 'Proc'
109
+ args[:me][:method] = 'call'
110
+ end
111
+
112
+ # Add a listener for the method declared here
113
+ CallMeMaybe::Exchange.add_listener(args)
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,3 @@
1
+ module CallMeMaybe
2
+ VERSION = "0.0.1"
3
+ end
metadata ADDED
@@ -0,0 +1,154 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: call_me_maybe
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kelly Becker
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-11-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: awesome_print
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rake
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: activesupport
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 3.2.12
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 3.2.12
110
+ description: Event handler and listener for Ruby
111
+ email:
112
+ - kellylsbkr@gmail.com
113
+ executables: []
114
+ extensions: []
115
+ extra_rdoc_files: []
116
+ files:
117
+ - .gitignore
118
+ - .pryrc
119
+ - Gemfile
120
+ - LICENSE.txt
121
+ - README.md
122
+ - Rakefile
123
+ - call_me_maybe.gemspec
124
+ - lib/call_me_maybe.rb
125
+ - lib/call_me_maybe/exchange.rb
126
+ - lib/call_me_maybe/module.rb
127
+ - lib/call_me_maybe/version.rb
128
+ homepage: http://kellybecker.me
129
+ licenses:
130
+ - MIT
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ none: false
137
+ requirements:
138
+ - - ! '>='
139
+ - !ruby/object:Gem::Version
140
+ version: '0'
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ none: false
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 1.8.23
150
+ signing_key:
151
+ specification_version: 3
152
+ summary: Listens and captures event comings from objects
153
+ test_files: []
154
+ has_rdoc: