nakajima-booty-call 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: