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 +5 -2
- data/VERSION +1 -1
- data/features/define_matches.feature +1 -0
- data/lib/match_method.rb +15 -5
- data/lib/matches.rb +64 -9
- data/matches.gemspec +1 -1
- data/spec/matches_spec.rb +55 -16
- metadata +1 -1
    
        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 | 
| 1 | 
            +
            1.1.0
         | 
    
        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 | 
            -
                   | 
| 49 | 
            -
             | 
| 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 | 
            -
                     | 
| 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  | 
| 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 | 
            -
                 | 
| 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 | 
            -
                       | 
| 22 | 
            -
                       | 
| 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 | 
            -
                           | 
| 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  | 
| 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 | 
            -
                 | 
| 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
    
    
    
        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 | 
| 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 / | 
| 113 | 
            -
                       | 
| 121 | 
            +
                    matches /something/ do
         | 
| 122 | 
            +
                      throw "Called on instance"
         | 
| 114 123 | 
             
                    end
         | 
| 115 124 |  | 
| 116 125 | 
             
                    class << self
         | 
| 117 | 
            -
                      matches / | 
| 118 | 
            -
                         | 
| 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
         |