matches 1.0.4 → 1.1.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/Rakefile CHANGED
@@ -52,10 +52,13 @@ Spec::Rake::SpecTask.new(:spec) do |spec|
52
52
  end
53
53
 
54
54
  Spec::Rake::SpecTask.new(:rcov) do |spec|
55
-
55
+ spec.spec_opts = ['--options', "\"spec/spec.opts\""]
56
+ spec.spec_files = FileList['spec/*_spec.rb']
56
57
  spec.libs << 'lib' << 'spec'
57
- spec.pattern = 'spec/*_spec.rb'
58
58
  spec.rcov = true
59
+ spec.rcov_opts = lambda do
60
+ IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
61
+ end
59
62
  end
60
63
 
61
64
  task :spec => :check_dependencies
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.4
1
+ 1.1.0
@@ -25,6 +25,7 @@ Scenario: Another meta-method
25
25
  end
26
26
 
27
27
  matches /^(\w+)\!$/ do |verb|
28
+ puts "Doing a #{verb}"
28
29
  @verbs << verb
29
30
  end
30
31
 
data/lib/match_method.rb CHANGED
@@ -38,19 +38,29 @@ class MatchMethod
38
38
  end
39
39
 
40
40
  # SWEET HACK ZOMG. Mind has been blown.
41
- # http://www.ruby-forum.com/topic/54096
42
- # Mauricio Fernandez is a Ruby beast.
43
41
  # This provides instance-exec-like functionality in Ruby 1.8.
42
+ # Originally saw this here:
43
+ # http://www.ruby-forum.com/topic/54096
44
+ # It appears to have been revised here, so this is a new version:
45
+ # http://eigenclass.org/hiki/bounded+space+instance_exec
44
46
 
45
47
  if RUBY_VERSION.split('.')[1].to_i < 9
46
48
  class Object
49
+ module InstanceExecHelper; end
50
+ include InstanceExecHelper
47
51
  def instance_exec(*args, &block)
48
- mname = "__instance_exec_#{Thread.current.object_id.abs}"
49
- class << self; self end.class_eval{ define_method(mname, &block) }
52
+ begin
53
+ old_critical, Thread.critical = Thread.critical, true
54
+ n = 0
55
+ n += 1 while respond_to?(mname="__instance_exec#{n}")
56
+ InstanceExecHelper.module_eval{ define_method(mname, &block) }
57
+ ensure
58
+ Thread.critical = old_critical
59
+ end
50
60
  begin
51
61
  ret = send(mname, *args)
52
62
  ensure
53
- class << self; self end.class_eval{ undef_method(mname) } rescue nil
63
+ InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil
54
64
  end
55
65
  ret
56
66
  end
data/lib/matches.rb CHANGED
@@ -2,14 +2,42 @@ require File.dirname(__FILE__) + "/match_method"
2
2
 
3
3
  # Defines all the necessary components to allow defining dynamic methods.
4
4
  module MatchDef
5
- # Defines a new class method that allows you to define dynamicinstance methods.
5
+ # Defines a new class method that allows you to define dynamic instance methods.
6
6
  # Takes a regular expression and a block, which are stored and called later.
7
7
  def matches(regexp, &block)
8
- @@match_methods ||= []
8
+ # This evil voodoo scares the crud out of me.
9
+ # It sneaks into the instance upon which we are called
10
+ # and sets a class variable, so we can keep track of what
11
+ # match methods have been defined.
12
+ unless self.send(:class_variable_defined?, :@@match_methods)
13
+ self.send(:class_variable_set, :@@match_methods, [])
14
+ self.send(:class_variable_set, :@@cached_match_methods, [])
15
+ end
16
+
17
+ append_to_methods = Proc.new do |method|
18
+ current = self.send(:class_variable_get, :@@match_methods)
19
+ self.send(:class_variable_set, :@@match_methods, current + [method])
20
+ end
21
+
22
+ @@available_methods = Proc.new do
23
+ self.send(:class_variable_get, :@@match_methods)
24
+ end
25
+
26
+ @@append_to_cached_methods = Proc.new do |method|
27
+ current = self.send(:class_variable_get, :@@cached_match_methods)
28
+ self.send(:class_variable_set, :@@cached_match_methods, current + [method])
29
+ end
30
+
31
+ # The above statement tweaks the scope, so when we refer to
32
+ # @@match_methods here, what we're really doing is referring to
33
+ # the above @@match_methods.
34
+ self.class_eval do
35
+ append_to_methods.call(MatchMethod.new( :matcher => regexp,
36
+ :proc => block ))
37
+ end
9
38
 
10
- @@match_methods << MatchMethod.new( :matcher => regexp,
11
- :proc => block )
12
39
  self.class_eval {
40
+
13
41
  unless method_defined? :method_missing
14
42
  def method_missing(meth, *args, &block); super; end
15
43
  end
@@ -18,13 +46,28 @@ module MatchDef
18
46
  # Defines a +method_missing+ that is aware of the
19
47
  # dynamically-defined methods and will call them if appropriate.
20
48
  def match_method_missing(message, *args, &block)
21
- # Attempt to evaluate this using a MetaMethod
22
- result = @@match_methods.find do |mm|
49
+ result = nil
50
+
51
+ # Attempt to evaluate this using a MatchMethod
52
+ matched = @@available_methods.call.find do |mm|
23
53
  if mm.matches?(message)
24
- return mm.match(self, message, args)
54
+
55
+ # Cache the method onto the class
56
+ self.class.send(:define_method, message, lambda { |*largs|
57
+ return mm.match(self, message, *largs)
58
+ })
59
+
60
+ # Store the name of the cached method in order to facilitate
61
+ # introspection.
62
+ @@append_to_cached_methods.call(message)
63
+
64
+ # Call the actual method
65
+ result = self.send(message, *args)
66
+
67
+ true
25
68
  end
26
69
  end
27
- return result || old_method_missing(message, args, &block)
70
+ return matched ? result : old_method_missing(message, args, &block)
28
71
  end
29
72
 
30
73
  alias_method :old_method_missing, :method_missing
@@ -33,10 +76,22 @@ module MatchDef
33
76
  }
34
77
  end
35
78
 
79
+ def reset_match_methods
80
+ # @@match_methods = []
81
+ end
82
+
36
83
  # Allows you to delete all defined dynamic methods on a class.
37
84
  # This permits testing.
38
85
  def reset_match_methods
39
- @@match_methods = []
86
+ self.send(:class_variable_set, :@@match_methods, [])
87
+
88
+ if self.send(:class_variable_defined?, :@@cached_match_methods)
89
+ self.send(:class_variable_get, :@@cached_match_methods).each do |method|
90
+ self.send(:undef_method, method)
91
+ end
92
+ end
93
+
94
+ self.send(:class_variable_set, :@@cached_match_methods, [])
40
95
  end
41
96
  end
42
97
 
data/matches.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{matches}
8
- s.version = "1.0.4"
8
+ s.version = "1.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Phil Calvin"]
data/spec/matches_spec.rb CHANGED
@@ -3,15 +3,9 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe MatchDef do
4
4
  before(:each) do
5
5
  Hippo.reset_match_methods
6
-
6
+
7
+ Object.send(:remove_const, :Hippo)
7
8
  class Hippo
8
- def method_missing(message, *args)
9
- super
10
- end
11
-
12
- if method_defined?(:match_method_missing)
13
- undef match_method_missing
14
- end
15
9
  end
16
10
  end
17
11
 
@@ -49,6 +43,18 @@ describe MatchDef do
49
43
  test.bar_fight()
50
44
  end
51
45
 
46
+ it "should support arguments, too" do
47
+ Hippo.class_eval do
48
+ matches /bar_(\w+)/ do |activity, style|
49
+ worked(activity, style)
50
+ end
51
+ end
52
+
53
+ test = Hippo.new
54
+ test.should_receive(:worked).once.with('fight', 'crazy')
55
+ test.bar_fight('crazy')
56
+ end
57
+
52
58
  it "should fall through normally if no match" do
53
59
  Hippo.class_eval do
54
60
  matches /bar_(\w+)/ do |activity|
@@ -85,9 +91,12 @@ describe MatchDef do
85
91
  throw "Should never be reached"
86
92
  end
87
93
  end
88
-
94
+
95
+ herman = Hippo.new
96
+ lambda { herman.second }.should raise_error(NameError)
97
+
89
98
  test = Rhino.new
90
- lambda { test.second() }.should raise_error
99
+ lambda { test.second }.should raise_error(NoMethodError)
91
100
  end
92
101
 
93
102
  end
@@ -109,22 +118,52 @@ describe MatchDef do
109
118
 
110
119
  it "should differentiate between class and instance methods" do
111
120
  class Hippo
112
- matches /foo/ do
113
- worked
121
+ matches /something/ do
122
+ throw "Called on instance"
114
123
  end
115
124
 
116
125
  class << self
117
- matches /foo/ do
118
- failed
126
+ matches /something/ do
127
+ throw "Called on class"
119
128
  end
120
129
  end
121
130
  end
122
131
 
123
132
  herman = Hippo.new
124
- Hippo.should_receive(:failed).never
125
- herman.should_receive(:worked).once
126
133
 
134
+ lambda { herman.something }.should raise_error(NameError)
135
+ lambda { Hippo.something }.should raise_error(NameError)
136
+ end
137
+ end
138
+
139
+ describe "caching" do
140
+ it "should cache methods when they are called" do
141
+ class Hippo
142
+ matches /foo/ do
143
+ worked
144
+ end
145
+ end
146
+
147
+ herman = Hippo.new
148
+ herman.should_receive(:worked).once
149
+
127
150
  herman.foo
151
+ herman.methods.should include('foo')
152
+ end
153
+
154
+ it "should support complex, multi-argument cached methods" do
155
+ class Hippo
156
+ matches /foo_(\w+)/ do |a, b|
157
+ worked(a, b)
158
+ end
159
+ end
160
+
161
+ herman = Hippo.new
162
+ herman.should_receive(:worked).twice.with('bar', 'baz')
163
+
164
+ herman.foo_bar('baz')
165
+ herman.methods.should include('foo_bar')
166
+ herman.foo_bar('baz')
128
167
  end
129
168
  end
130
169
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: matches
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phil Calvin