functor 0.4.2 → 0.5.1

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/functor.rb CHANGED
@@ -3,67 +3,70 @@ require "#{File.dirname(__FILE__)}/object"
3
3
  class Functor
4
4
 
5
5
  module Method
6
+ def self.copy_functors( functors )
7
+ r = {} ; functors.each do | name, functor |
8
+ r[ name ] = functor.clone
9
+ end
10
+ return r
11
+ end
6
12
  def self.included( k )
7
- def k.functors ; @__functors ||= {} ; end
13
+ def k.functors
14
+ @__functors ||= superclass.respond_to?( :functors ) ?
15
+ Functor::Method.copy_functors( superclass.functors ) : {}
16
+ end
8
17
  def k.functor( name, *args, &block )
9
- unless functors[ name ]
10
- functors[ name ] = Functor.new
11
- klass = self.name ; module_eval <<-CODE
12
- def #{name}( *args, &block )
13
- begin
14
- #{klass}.functors[ :#{name} ].apply( self, *args, &block )
15
- rescue ArgumentError => e
16
- begin
17
- super
18
- rescue NoMethodError => f
19
- raise e
20
- end
21
- end
22
- end
23
- CODE
24
- end
25
- functors[ name ].given( *args, &block )
18
+ name = name.to_sym
19
+ ( f = ( functors[ name ] or
20
+ ( functors[ name ] = Functor.new ) ) ).given( *args, &block )
21
+ define_method( name ) { | *args | instance_exec( *args, &f.match( *args ) ) }
22
+ end
23
+ def k.functor_with_self( name, *args, &block )
24
+ name = name.to_sym
25
+ ( f = ( functors[ name ] or
26
+ ( functors[ name ] = Functor.new ) ) ).given( *args, &block )
27
+ define_method( name ) { | *args | instance_exec( *args, &f.match( self, *args ) ) }
26
28
  end
27
29
  end
28
30
  end
29
31
 
30
- def initialize( &block )
31
- @rules = [] ; instance_eval( &block ) if block_given?
32
+
33
+ def initialize( &block )
34
+ @rules = [] ; yield( self ) if block_given?
35
+ end
36
+
37
+ def initialize_copy( from )
38
+ @rules = from.instance_eval { @rules.clone }
32
39
  end
33
40
 
34
41
  def given( *pattern, &action )
35
- @rules.delete_if { |p,a| p == pattern }
36
42
  @rules << [ pattern, action ]
37
43
  end
38
44
 
39
- def apply( object, *args, &block )
40
- object.instance_exec( *args, &match( args, &block ) )
45
+ def call( *args, &block )
46
+ match( *args, &block ).call( *args )
41
47
  end
42
48
 
43
- def call( *args, &block )
44
- match( args, &block ).call( *args )
49
+ def []( *args, &block )
50
+ call( *args, &block )
45
51
  end
46
52
 
47
53
  def to_proc ; lambda { |*args| self.call( *args ) } ; end
48
-
49
- private
50
-
51
- def match( args, &block )
54
+
55
+ def match( *args, &block )
52
56
  args << block if block_given?
53
- pattern, action = @rules.find { | p, a | match?( args, p ) }
54
- action or raise argument_error( args )
57
+ pattern, action = @rules.reverse.find { | p, a | match?( args, p ) }
58
+ action or
59
+ raise ArgumentError.new( "Argument error: no functor matches the given arguments." )
55
60
  end
56
61
 
62
+ private
63
+
57
64
  def match?( args, pattern )
58
65
  args.zip( pattern ).all? { | arg, rule | pair?( arg, rule ) } if args.length == pattern.length
59
66
  end
60
67
 
61
68
  def pair?( arg, rule )
62
- ( rule.is_a?( Proc ) and rule.call( arg ) ) or rule === arg or rule == arg
63
- end
64
-
65
- def argument_error( args )
66
- ArgumentError.new( "argument mismatch for argument(s): #{ args.map{ |arg| arg.inspect }.join(', ') }." )
69
+ ( rule.respond_to? :call and rule.call( arg ) ) or rule === arg
67
70
  end
68
-
71
+
69
72
  end
data/test/fib.rb CHANGED
@@ -1,9 +1,9 @@
1
1
  require "#{File.dirname(__FILE__)}/helpers"
2
2
 
3
- fib ||= Functor.new do
4
- given( 0 ) { 0 }
5
- given( 1 ) { 1 }
6
- given( Integer ) { | n | self.call( n - 1 ) + self.call( n - 2 ) }
3
+ fib ||= Functor.new do |f|
4
+ f.given( Integer ) { | n | f.call( n - 1 ) + f.call( n - 2 ) }
5
+ f.given( 0 ) { 0 }
6
+ f.given( 1 ) { 1 }
7
7
  end
8
8
 
9
9
  describe "Dispatch on a functor object should" do
data/test/guards.rb CHANGED
@@ -1,16 +1,16 @@
1
- require 'test/helpers'
1
+ require "#{File.dirname(__FILE__)}/helpers"
2
2
 
3
3
  describe "Dispatch should support guards" do
4
4
 
5
5
  before do
6
- @stripe = Functor.new do
7
- given( lambda { |x| x % 2 == 0 } ) { 'white' }
8
- given( lambda { |x| x % 2 == 1 } ) { 'silver' }
6
+ @stripe = Functor.new do |f|
7
+ f.given( lambda { |x| x % 2 == 1 } ) { 'silver' }
8
+ f.given( lambda { |x| x % 2 == 0 } ) { 'white' }
9
9
  end
10
10
 
11
- @safe_divide = Functor.new do
12
- given( lambda { |x| x == 0 }, Integer ) { |x,y| false }
13
- given( Integer, Integer ) { |x,y| ( y / ( x * 1.0 )) }
11
+ @safe_divide = Functor.new do |f|
12
+ f.given( Integer, Integer ) { |x,y| ( y / ( x * 1.0 )) }
13
+ f.given( lambda { |x| x == 0 }, Integer ) { |x,y| false }
14
14
  end
15
15
  end
16
16
 
data/test/inheritance.rb CHANGED
@@ -9,7 +9,6 @@ end
9
9
 
10
10
  class B < A
11
11
  functor( :foo, String ) { |s| [ B, String ] }
12
- functor( :foo, Float ) { |f| [ B, *A.functors[:foo].apply( self, f ) ] }
13
12
  end
14
13
 
15
14
  describe "Functor methods should support inheritance" do
@@ -22,7 +21,4 @@ describe "Functor methods should support inheritance" do
22
21
  B.new.foo( "bar" ).should == [ B, String ]
23
22
  end
24
23
 
25
- specify "by allowing you to call base class functors using #functors" do
26
- B.new.foo( 1.0 ).should == [ B, A, Float ]
27
- end
28
24
  end
data/test/matchers.rb CHANGED
@@ -2,8 +2,8 @@ require "#{File.dirname(__FILE__)}/helpers"
2
2
 
3
3
  class C
4
4
  include Functor::Method
5
- functor( :foo, 1 ) { |a| "==" }
6
5
  functor( :foo, Integer ) { |a| "===" }
6
+ functor( :foo, 1 ) { |a| "==" }
7
7
  functor( :foo, lambda { |a| a == "boo" } ) { |v| "Lambda: #{v}" }
8
8
  end
9
9
 
data/test/with_self.rb ADDED
@@ -0,0 +1,21 @@
1
+ require "#{File.dirname(__FILE__)}/helpers"
2
+
3
+ class A
4
+ attr_accessor :bar
5
+ include Functor::Method
6
+ def initialize( x ) ; @bar = x ; end
7
+ functor_with_self( :foo, self, Integer ) { |x| x }
8
+ functor_with_self( :foo, lambda{ |x| x.bar == true }, Integer ) { |s| 'bar' }
9
+ end
10
+
11
+ describe "Functor methods should support allow matching on self" do
12
+
13
+ specify "by allowing functor_with_self to provide a guard on self" do
14
+ A.new( true ).foo( 5 ).should == 'bar'
15
+ end
16
+
17
+ specify "or by simply providing self as an argument" do
18
+ A.new( false ).foo( 5 ).should == 5
19
+ end
20
+
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: functor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dan Yoder
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2008-07-11 00:00:00 -07:00
14
+ date: 2008-10-08 00:00:00 -07:00
15
15
  default_executable:
16
16
  dependencies: []
17
17
 
@@ -64,6 +64,7 @@ files:
64
64
  - test/inheritance.rb
65
65
  - test/matchers.rb
66
66
  - test/reopening.rb
67
+ - test/with_self.rb
67
68
  has_rdoc: true
68
69
  homepage: http://dev.zeraweb.com/
69
70
  post_install_message:
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  requirements: []
87
88
 
88
89
  rubyforge_project: functor
89
- rubygems_version: 1.0.1
90
+ rubygems_version: 1.2.0
90
91
  signing_key:
91
92
  specification_version: 2
92
93
  summary: Pattern-based dispatch for Ruby, inspired by Topher Cyll's multi.