dyoder-functor 0.4.3 → 0.5.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/lib/functor.rb CHANGED
@@ -1,47 +1,50 @@
1
1
  require "#{File.dirname(__FILE__)}/object"
2
+ require 'ruby-debug'
2
3
 
3
4
  class Functor
4
5
 
5
6
  module Method
7
+ def self.copy_functors( functors )
8
+ r = {} ; functors.each do | name, functor |
9
+ r[ name ] = functor.clone
10
+ end
11
+ return r
12
+ end
6
13
  def self.included( k )
7
- def k.functors ; @__functors ||= {} ; end
14
+ def k.functors
15
+ @__functors ||= superclass.respond_to?( :functors ) ?
16
+ Functor::Method.copy_functors( superclass.functors ) : {}
17
+ end
8
18
  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 )
19
+ name = name.to_sym
20
+ ( f = ( functors[ name ] or
21
+ ( functors[ name ] = Functor.new ) ) ).given( *args, &block )
22
+ define_method( name ) { | *args | instance_exec( *args, &f.match( *args ) ) }
23
+ end
24
+ def k.functor_with_self( name, *args, &block )
25
+ name = name.to_sym
26
+ ( f = ( functors[ name ] or
27
+ ( functors[ name ] = Functor.new ) ) ).given( *args, &block )
28
+ define_method( name ) { | *args | instance_exec( *args, &f.match( self, *args ) ) }
26
29
  end
27
30
  end
28
31
  end
29
32
 
30
- def initialize( &block )
31
- @rules = [] ; instance_eval( &block ) if block_given?
33
+
34
+ def initialize( &block )
35
+ @rules = [] ; yield( self ) if block_given?
32
36
  end
33
37
 
34
- def given( *pattern, &action )
35
- @rules.delete_if { |p,a| p == pattern }
36
- @rules << [ pattern, action ]
38
+ def initialize_copy( from )
39
+ @rules = from.instance_eval { @rules.clone }
37
40
  end
38
41
 
39
- def apply( object, *args, &block )
40
- object.instance_exec( *args, &match( args, &block ) )
42
+ def given( *pattern, &action )
43
+ @rules << [ pattern, action ]
41
44
  end
42
45
 
43
46
  def call( *args, &block )
44
- match( args, &block ).call( *args )
47
+ match( *args, &block ).call( *args )
45
48
  end
46
49
 
47
50
  def []( *args, &block )
@@ -49,25 +52,25 @@ class Functor
49
52
  end
50
53
 
51
54
  def to_proc ; lambda { |*args| self.call( *args ) } ; end
52
-
53
- private
54
-
55
- def match( args, &block )
55
+
56
+ def match( *args, &block )
56
57
  args << block if block_given?
57
- pattern, action = @rules.find { | p, a | match?( args, p ) }
58
- action or raise argument_error( args )
58
+ pattern, action = @rules.reverse.find { | p, a | match?( args, p ) }
59
+ action or argument_error( args )
59
60
  end
60
61
 
62
+ private
63
+
61
64
  def match?( args, pattern )
62
65
  args.zip( pattern ).all? { | arg, rule | pair?( arg, rule ) } if args.length == pattern.length
63
66
  end
64
67
 
65
68
  def pair?( arg, rule )
66
- ( rule.respond_to? :call and rule.call( arg ) ) or rule === arg or rule == arg
69
+ ( rule.respond_to? :call and rule.call( arg ) ) or rule === arg
67
70
  end
68
71
 
69
72
  def argument_error( args )
70
- ArgumentError.new( "argument mismatch for argument(s): #{ args.map{ |arg| arg.inspect }.join(', ') }." )
73
+ raise ArgumentError.new( "Argument error: no functor matches the given arguments." )
71
74
  end
72
75
 
73
76
  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
@@ -3,14 +3,14 @@ require "#{File.dirname(__FILE__)}/helpers"
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: dyoder-functor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
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-09-05 00:00:00 -07:00
14
+ date: 2008-09-18 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: