matches 1.0.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/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Phil Calvin
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,30 @@
1
+ Matches – easygoing methods
2
+ ===========================
3
+
4
+ Matches is an easy DSL for defining regular-expression-based methods in Ruby.
5
+
6
+ Start playing with matches:
7
+
8
+ require 'matches'
9
+
10
+ class Hippo
11
+ def initialize
12
+ @verbs = []
13
+ end
14
+
15
+ matches /(\w+)\!/ do |verb|
16
+ @verbs << verb
17
+ end
18
+
19
+ matches /(\w+)ed\?/ do |verb|
20
+ @verbs.include?(verb)
21
+ end
22
+ end
23
+
24
+ herman = Hippo.new
25
+ herman.fatten!
26
+ herman.touch!
27
+
28
+ herman.touched?
29
+ ==> true
30
+
data/README.rdoc ADDED
@@ -0,0 +1,18 @@
1
+ = matches - easygoing methods
2
+
3
+ Matches is an easy DSL for defining regular-expression-based methods in Ruby.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but
13
+ bump version in a commit by itself I can ignore when I pull)
14
+ * Send me a pull request. Bonus points for topic branches.
15
+
16
+ == Copyright
17
+
18
+ Copyright (c) 2009 Phil Calvin. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,63 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "matches"
8
+ gem.summary = %Q{A DSL for defining regular-expression-based methods in Ruby.}
9
+ gem.description = %Q{Matches allows you to define methods that have regular
10
+ expressions rather than names, and automatically
11
+ configires method_missing to handle them.}
12
+ gem.email = "pncalvin@gmail.com"
13
+ gem.homepage = "http://github.com/pnc/matches"
14
+ gem.authors = ["Phil Calvin"]
15
+ gem.add_development_dependency "rspec", ">= 1.2.9"
16
+ gem.add_development_dependency "cucumber", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
24
+ begin
25
+ require 'rcov/rcovtask'
26
+ Rcov::RcovTask.new do |test|
27
+ test.libs << 'test'
28
+ test.pattern = 'test/**/test_*.rb'
29
+ test.verbose = true
30
+ end
31
+ rescue LoadError
32
+ task :rcov do
33
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
34
+ end
35
+ end
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "matches #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/*.rb')
45
+ end
46
+
47
+ require 'spec/rake/spectask'
48
+ Spec::Rake::SpecTask.new(:spec) do |spec|
49
+ spec.spec_opts = ['--options', "\"spec/spec.opts\""]
50
+ spec.libs << 'lib' << 'spec'
51
+ spec.spec_files = FileList['spec/*_spec.rb']
52
+ end
53
+
54
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
55
+
56
+ spec.libs << 'lib' << 'spec'
57
+ spec.pattern = 'spec/*_spec.rb'
58
+ spec.rcov = true
59
+ end
60
+
61
+ task :spec => :check_dependencies
62
+
63
+ task :default => :spec
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
@@ -0,0 +1,44 @@
1
+ As a fancy-pants programmer
2
+ I want to define match methods using regular expressions
3
+ So that I don't have to screw around with method_missing
4
+
5
+ Scenario: Define a matcher method
6
+ Given I have the following Ruby code:
7
+ """
8
+ class Hippo
9
+ matches /(\w+)\!/ do |verb|
10
+ puts "I've been #{verb}ed!"
11
+ end
12
+ end
13
+
14
+ Hippo.new.touch!
15
+ """
16
+ When I execute the code
17
+ Then I should see "I've been touched!" in the output
18
+
19
+ Scenario: Another meta-method
20
+ Given I reset the class Hippo
21
+ Given I have the following Ruby code:
22
+ """
23
+ class Hippo
24
+ def initialize
25
+ @verbs = []
26
+ end
27
+
28
+ matches /(\w+)\!/ do |verb|
29
+ @verbs << verb
30
+ end
31
+
32
+ matches /(\w+)ed\?/ do |verb|
33
+ @verbs.include?(verb)
34
+ end
35
+ end
36
+
37
+ herman = Hippo.new
38
+ herman.fatten!
39
+ herman.touch!
40
+
41
+ puts herman.touched?
42
+ """
43
+ When I execute the code
44
+ Then I should see "true" in the output
@@ -0,0 +1,33 @@
1
+ Given /^I reset the class (.+)$/ do |klass|
2
+ eval("#{klass}.reset_meta_methods")
3
+ end
4
+
5
+ Given /^I have the following Ruby code:$/ do |code|
6
+ @code = code
7
+ end
8
+
9
+ When /^I execute the code$/ do
10
+ module Kernel
11
+ def puts(str)
12
+ OutputStorage.write("#{str}\n")
13
+ end
14
+ end
15
+
16
+ eval(@code)
17
+ end
18
+
19
+ Then /^I should see \"(.+)\" in the output$/ do |text|
20
+ OutputStorage.output.should =~ /#{text}/
21
+ end
22
+
23
+ class OutputStorage
24
+ @@output = ""
25
+
26
+ def self.write(str)
27
+ @@output += str
28
+ end
29
+
30
+ def self.output
31
+ @@output
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + "/../../lib/matches"
2
+
3
+ require 'spec/stubs/cucumber'
@@ -0,0 +1,48 @@
1
+ class MatchMethod
2
+ attr_accessor :matcher, :proc
3
+
4
+ # Allows properties to be specified in the constructor.
5
+ # E.g.,
6
+ # MetaMethod.new(:matcher => /foo/)
7
+ def initialize(args)
8
+ args.each do |key, value|
9
+ send("#{key}=", value)
10
+ end
11
+ end
12
+
13
+ # Returns whether this MetaMethod is capable of matching the given
14
+ # message.
15
+ def matches?(message)
16
+ !!(message.to_s =~ matcher)
17
+ end
18
+
19
+ # Calls the method's proc if the message matches.
20
+ def match(instance, message, *args)
21
+ groups = message.to_s.match(matcher)[1..-1]
22
+
23
+ # Should curry here: instance.instance_eval( &proc.curry(groups + args) )
24
+ # Or we could use instance_exec.
25
+ # But these require 1.9. Darn. One day!
26
+
27
+ full = (groups + args).flatten
28
+ instance.instance_exec(*full, &proc)
29
+ end
30
+ end
31
+
32
+ # SWEET HACK ZOMG. Mind has been blown.
33
+ # http://www.ruby-forum.com/topic/54096
34
+ # Mauricio Fernandez is a Ruby beast.
35
+ # This provides instance-exec-like functionality in Ruby 1.8.
36
+
37
+ class Object
38
+ def instance_exec(*args, &block)
39
+ mname = "__instance_exec_#{Thread.current.object_id.abs}"
40
+ class << self; self end.class_eval{ define_method(mname, &block) }
41
+ begin
42
+ ret = send(mname, *args)
43
+ ensure
44
+ class << self; self end.class_eval{ undef_method(mname) } rescue nil
45
+ end
46
+ ret
47
+ end
48
+ end
data/lib/matches.rb ADDED
@@ -0,0 +1,33 @@
1
+ require File.dirname(__FILE__) + "/match_method"
2
+
3
+ module MatchDef
4
+ def matches(regexp, &block)
5
+ @@match_methods ||= []
6
+
7
+ @@match_methods << MatchMethod.new( :matcher => regexp,
8
+ :proc => block )
9
+ self.class_eval {
10
+ unless respond_to?(:match_method_missing)
11
+ def match_method_missing(message, *args)
12
+ # Attempt to evaluate this using a MetaMethod
13
+ result = @@match_methods.find do |mm|
14
+ if mm.matches?(message)
15
+ return mm.match(self, message, args)
16
+ end
17
+ end
18
+ return result if result
19
+ return old_method_missing(message, args)
20
+ end
21
+
22
+ alias_method :old_method_missing, :method_missing
23
+ alias_method :method_missing, :match_method_missing
24
+ end
25
+ }
26
+ end
27
+
28
+ def reset_meta_methods
29
+ @@match_methods = []
30
+ end
31
+ end
32
+
33
+ Class.class_eval { include MatchDef }
@@ -0,0 +1,75 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MatchMethod do
4
+ it "should store its matcher" do
5
+ mm = MatchMethod.new(:matcher => /foo/)
6
+ mm.matcher.should == /foo/
7
+ end
8
+
9
+ it "should know if a message matches" do
10
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/)
11
+ mm.matches?('find_by_something').should be_true
12
+ end
13
+
14
+ it "should not match if the message doesn't match" do
15
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/)
16
+ mm.matches?('not').should be_false
17
+ end
18
+
19
+ it "should store a Proc" do
20
+ real_proc = Proc.new {}
21
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/,
22
+ :proc => real_proc)
23
+ mm.proc.should == real_proc
24
+ end
25
+
26
+ it "should call its Proc when a message matches" do
27
+ mock_proc = Proc.new {}
28
+ object = Object.new
29
+ object.should_receive(:instance_exec).once.with('something')
30
+
31
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/,
32
+ :proc => mock_proc)
33
+ mm.match(object, 'find_by_something')
34
+ end
35
+
36
+ it "should work with no groups" do
37
+ mock_proc = Proc.new {}
38
+ object = Object.new
39
+ object.should_receive(:instance_exec).once.with(no_args())
40
+
41
+ mm = MatchMethod.new(:matcher => /find_by_something/,
42
+ :proc => mock_proc)
43
+ mm.match(object, :find_by_something)
44
+ end
45
+
46
+ it "should pass each group in as an argument" do
47
+ mock_proc = Proc.new {}
48
+ object = Object.new
49
+ object.should_receive(:instance_exec).once.with('something')
50
+
51
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/,
52
+ :proc => mock_proc)
53
+ mm.match(object, :find_by_something)
54
+ end
55
+
56
+ it "should work with arguments, too" do
57
+ mock_proc = Proc.new {}
58
+ object = Object.new
59
+ object.should_receive(:instance_exec).once.with('argument')
60
+
61
+ mm = MatchMethod.new(:matcher => /find_by_something/,
62
+ :proc => mock_proc)
63
+ mm.match(object, :find_by_something, 'argument')
64
+ end
65
+
66
+ it "should work with both match groups and arguments" do
67
+ mock_proc = Proc.new {}
68
+ object = Object.new
69
+ object.should_receive(:instance_exec).once.with('something', 'argument')
70
+
71
+ mm = MatchMethod.new(:matcher => /find_by_(\w+)/,
72
+ :proc => mock_proc)
73
+ mm.match(object, :find_by_something, 'argument')
74
+ end
75
+ end
@@ -0,0 +1,74 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe MatchDef do
4
+ before(:each) do
5
+ Hippo.reset_meta_methods
6
+ end
7
+
8
+ it "should provide a matches method on classes" do
9
+ lambda {
10
+ Hippo.class_eval do
11
+ matches /foo/
12
+ end
13
+ }.should_not raise_error
14
+ end
15
+
16
+ it "should call the method if it matches" do
17
+ Hippo.class_eval do
18
+ matches /bar/ do
19
+ worked
20
+ end
21
+ end
22
+
23
+ test = Hippo.new
24
+ test.should_receive(:worked).once
25
+ test.bar
26
+ end
27
+
28
+ it "should pass in the match groups" do
29
+ Hippo.class_eval do
30
+ matches /bar_(\w+)/ do |activity|
31
+ worked(activity)
32
+ end
33
+ end
34
+
35
+ test = Hippo.new
36
+ test.should_receive(:worked).once.with('fight')
37
+ test.bar_fight()
38
+ end
39
+
40
+ it "should respect an existing method_missing" do
41
+ Hippo.class_eval do
42
+ def method_missing(message, *args)
43
+ affirm(message)
44
+ end
45
+
46
+ matches /second/ do
47
+ affirm(:second)
48
+ end
49
+ end
50
+
51
+ test = Hippo.new
52
+ test.should_receive(:affirm).once.with(:first)
53
+ test.should_receive(:affirm).once.with(:second)
54
+ test.first()
55
+ test.second()
56
+ end
57
+
58
+ it "should not pollute other classes" do
59
+ Hippo.class_eval do
60
+ matches /second/ do
61
+ throw "Should never be reached"
62
+ end
63
+ end
64
+
65
+ test = Rhino.new
66
+ lambda { test.second() }.should raise_error
67
+ end
68
+ end
69
+
70
+ class Hippo
71
+ end
72
+
73
+ class Rhino
74
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'matches'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: matches
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Phil Calvin
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-23 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rspec
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.9
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: cucumber
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: |-
36
+ Matches allows you to define methods that have regular
37
+ expressions rather than names, and automatically
38
+ configires method_missing to handle them.
39
+ email: pncalvin@gmail.com
40
+ executables: []
41
+
42
+ extensions: []
43
+
44
+ extra_rdoc_files:
45
+ - LICENSE
46
+ - README.md
47
+ - README.rdoc
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - Rakefile
52
+ - VERSION
53
+ - features/define_matches.feature
54
+ - features/step_definitions/method_steps.rb
55
+ - features/support/env.rb
56
+ - lib/match_method.rb
57
+ - lib/matches.rb
58
+ - spec/match_method_spec.rb
59
+ - spec/matches_spec.rb
60
+ - spec/spec_helper.rb
61
+ - README.rdoc
62
+ has_rdoc: true
63
+ homepage: http://github.com/pnc/matches
64
+ licenses: []
65
+
66
+ post_install_message:
67
+ rdoc_options:
68
+ - --charset=UTF-8
69
+ require_paths:
70
+ - lib
71
+ required_ruby_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ required_rubygems_version: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: "0"
82
+ version:
83
+ requirements: []
84
+
85
+ rubyforge_project:
86
+ rubygems_version: 1.3.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: A DSL for defining regular-expression-based methods in Ruby.
90
+ test_files:
91
+ - spec/match_method_spec.rb
92
+ - spec/matches_spec.rb
93
+ - spec/spec_helper.rb