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 +37 -34
- data/test/fib.rb +4 -4
- data/test/guards.rb +6 -6
- data/test/inheritance.rb +0 -4
- data/test/matchers.rb +1 -1
- data/test/with_self.rb +21 -0
- metadata +3 -2
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
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
31
|
-
|
33
|
+
|
34
|
+
def initialize( &block )
|
35
|
+
@rules = [] ; yield( self ) if block_given?
|
32
36
|
end
|
33
37
|
|
34
|
-
def
|
35
|
-
@rules.
|
36
|
-
@rules << [ pattern, action ]
|
38
|
+
def initialize_copy( from )
|
39
|
+
@rules = from.instance_eval { @rules.clone }
|
37
40
|
end
|
38
41
|
|
39
|
-
def
|
40
|
-
|
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
|
-
|
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
|
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
|
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( "
|
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(
|
5
|
-
given(
|
6
|
-
given(
|
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 ==
|
8
|
-
given( lambda { |x| x % 2 ==
|
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(
|
13
|
-
given(
|
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
|
+
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-
|
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:
|