nakajima-booty-call 0.0.1 → 0.0.2

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/lib/booty-call.rb ADDED
@@ -0,0 +1 @@
1
+ require File.dirname(__FILE__) + '/booty_call.rb'
@@ -19,7 +19,6 @@ module BootyCall
19
19
 
20
20
  def extend_klass
21
21
  klass.class_eval do
22
- meta_eval { attr_reader :pristine_cache, :callback_cache }
23
22
  @pristine_cache = Hash.new
24
23
  @callback_cache = { :after => Hash.new([]), :before => Hash.new([]) }
25
24
  extend ClassMethods
@@ -42,6 +41,9 @@ module BootyCall
42
41
  end
43
42
 
44
43
  def redefine_method(method_id)
44
+ safe_method_id = method_id.to_s
45
+ safe_method_id.gsub!(/=/, '__EQUALS__')
46
+ safe_method_id.gsub!(/\?/, '__PREDICATE__')
45
47
  klass.class_eval(<<-EOS, "(__DELEGATION__)", 1)
46
48
  def #{method_id}(*args, &block)
47
49
  catch(#{method_id.to_sym.inspect}) do
@@ -52,13 +54,21 @@ module BootyCall
52
54
  end
53
55
  end
54
56
 
55
- def #{method_id}_without_callbacks(*args, &block)
57
+ def #{safe_method_id}_without_callbacks(*args, &block)
56
58
  __PRISTINE__(#{method_id.inspect}, *args, &block)
57
59
  end
58
60
  EOS
59
61
  end
60
62
 
61
63
  module ClassMethods
64
+ def pristine_cache
65
+ @pristine_cache || superclass.pristine_cache
66
+ end
67
+
68
+ def callback_cache
69
+ @callback_cache || superclass.callback_cache
70
+ end
71
+
62
72
  def run_callbacks_for(target, position, method_id, *results)
63
73
  callbacks = callback_cache[position][method_id.to_sym]
64
74
 
@@ -17,8 +17,8 @@ module BootyCall
17
17
  callback(:after, method_id, *args, &block)
18
18
  end
19
19
 
20
- def observe(method_id, &block)
21
- @introspector.observe(method_id, &block)
20
+ def observe(method_id, options={}, &block)
21
+ @introspector.observe(method_id, options, &block)
22
22
  end
23
23
 
24
24
  def callback(position, method_id, *args, &block)
@@ -1,18 +1,19 @@
1
1
  module BootyCall
2
2
  class Introspector
3
- attr_reader :klass
3
+ attr_reader :klass, :options
4
4
 
5
- def initialize(klass)
6
- @klass = klass
5
+ def initialize(klass, options={})
6
+ @klass, @options = klass, options
7
7
  @observed_methods = { }
8
8
  end
9
9
 
10
10
  def observe_klass!
11
11
  @observed ||= begin
12
12
  this = self
13
- klass.meta_def(:method_added) do |m|
13
+ hook = options[:metaclass] ? :singleton_method_added : :method_added
14
+ klass.meta_def(hook) do |m|
14
15
  this.check_method(m)
15
- end and true
16
+ end; true
16
17
  end
17
18
  end
18
19
 
@@ -20,22 +21,29 @@ module BootyCall
20
21
  not not @observed_methods[method_id]
21
22
  end
22
23
 
23
- def observe(method_id, &block)
24
+ def observe(method_id, options={}, &block)
24
25
  observe_klass!
25
- @observed_methods[method_id] ||= []
26
- @observed_methods[method_id].tap do |set|
27
- set.push(block) if block_given?
28
- set.tap.compact!.uniq!
29
- end
30
- end
31
-
32
- def check_method(method_id)
33
- handlers = @observed_methods.delete(method_id)
34
- handlers.each(&:call) rescue nil
26
+ @observed_methods[method_id] ||= ObservedMethod.new(method_id, options)
27
+ @observed_methods[method_id].push(block) if block_given?
35
28
  end
36
29
 
37
30
  def defined_methods
38
31
  (klass.instance_methods - Object.instance_methods).map(&:to_sym)
39
32
  end
33
+
34
+ def check_method(sym)
35
+ @observed_methods.each do |method_id, observer|
36
+ stop_observing(method_id) do
37
+ observer.match(sym)
38
+ observer.valid?
39
+ end
40
+ end
41
+ end
42
+
43
+ def stop_observing(method_id, &block)
44
+ @observed_methods.delete(method_id).tap do |observer|
45
+ @observed_methods[method_id] = observer if block.try(:call, observer)
46
+ end
47
+ end
40
48
  end
41
49
  end
@@ -0,0 +1,31 @@
1
+ module BootyCall
2
+ class ObservedMethod
3
+ attr_reader :method_id
4
+
5
+ def initialize(method_id, options={})
6
+ @method_id = method_id
7
+ @callbacks = []
8
+ @count = 0
9
+ @times = options[:times] || 1
10
+ end
11
+
12
+ def match(sym)
13
+ return unless case method_id
14
+ when Symbol then valid?
15
+ when Regexp then valid? and method_id.try(:match, sym.to_s)
16
+ else ; nil
17
+ end
18
+ @callbacks.each { |fn| fn.call(sym) }
19
+ @count += 1
20
+ end
21
+
22
+ def valid?
23
+ @times.eql?(:infinite) or @count < @times
24
+ end
25
+
26
+ def push(*args)
27
+ @callbacks += args
28
+ @callbacks.tap.compact!.uniq!
29
+ end
30
+ end
31
+ end
data/lib/booty_call.rb CHANGED
@@ -1,11 +1,12 @@
1
1
  $LOAD_PATH << File.dirname(__FILE__) + '/booty_call'
2
2
 
3
3
  module BootyCall
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
6
6
 
7
7
  require 'rubygems'
8
8
  require 'nakajima'
9
9
  require 'callbacker'
10
+ require 'observed_method'
10
11
  require 'introspector'
11
12
  require 'hook'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: nakajima-booty-call
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Nakajima
@@ -31,10 +31,12 @@ extra_rdoc_files: []
31
31
 
32
32
  files:
33
33
  - lib/booty_call
34
- - lib/booty_call/callbacker.rb
34
+ - lib/booty-call.rb
35
+ - lib/booty_call.rb
35
36
  - lib/booty_call/hook.rb
37
+ - lib/booty_call/callbacker.rb
36
38
  - lib/booty_call/introspector.rb
37
- - lib/booty_call.rb
39
+ - lib/booty_call/observed_method.rb
38
40
  has_rdoc: false
39
41
  homepage: http://github.com/nakajima/booty-call
40
42
  post_install_message: