lambda_driver 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/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in lambda_driver.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Tomohito Ozaki
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,170 @@
1
+ # LambdaDriver
2
+
3
+ 虚弦斥力場生成システム
4
+
5
+ LambdaDriver drives your code more functional.
6
+
7
+ ```ruby
8
+ # [:foo, :bar, :baz].map{|s| s.to_s }.map{|s| s.upcase }
9
+ # [:foo, :bar, :baz].map(&:to_s).map(&:upcase)
10
+
11
+ [:foo, :bar, :baz].map(&:to_s >> :upcase ) # => ["FOO", "BAR", "BAZ"]
12
+ ```
13
+
14
+ ```ruby
15
+ # [:foo, :hoge, :bar, :fuga].select{|s| s.to_s.length > 3} # => [:hoge, :fuga]
16
+
17
+ [:foo, :hoge, :bar, :fuga].select(&:to_s >> :length >> 3._(:<)) # => [:hoge, :fuga]
18
+ ```
19
+
20
+ ## Installation
21
+
22
+ Add this line to your application's Gemfile:
23
+
24
+ gem 'lambda_driver'
25
+
26
+ And then execute:
27
+
28
+ $ bundle
29
+
30
+ Or install it yourself as:
31
+
32
+ $ gem install lambda_driver
33
+
34
+ ## Usage
35
+
36
+ ### Proc/lambda/Symbol/Method extensions
37
+ - call
38
+ - compose
39
+ - with_args
40
+ - flip
41
+ - curry
42
+
43
+ #### alias :< to Proc#call
44
+
45
+ ```ruby
46
+ f = lamdba{|x| x.to_s }
47
+ f < :foo # => "foo"
48
+ ```
49
+
50
+ #### Proc#compose
51
+
52
+ Returns new lambda which composed self and given function.
53
+ A composed proc called with args, executes `self.(g(*args)).
54
+
55
+ ```ruby
56
+ f = lamdba{|x| x.to_s }
57
+ g = lambda{|y| y.length }
58
+ h = f compose g
59
+ h.(:hoge) # => 4
60
+ ```
61
+
62
+ This method is aliased as `<<`.
63
+
64
+ ```ruby
65
+ f << g # => f.compose(g)
66
+ ```
67
+
68
+ #### Proc#with_args
69
+
70
+ Returns partially applied function that has 2nd and more parameters
71
+ fixed by given *args.
72
+
73
+ ```ruby
74
+ f = lamdba{|x, y, z| [x, y, z]}
75
+ h = f.with_args(:a, :b)
76
+ h.(:c) # => [:c, :a, :b]
77
+ ```
78
+
79
+ This method is aliased as `*`.
80
+
81
+ ```ruby
82
+ f * :foo # => f.with_args(:foo)
83
+ ```
84
+
85
+
86
+ #### Proc#flip
87
+
88
+ Returns function whose parameter order spawed 1st for 2nd.
89
+ A result of filped fuction is curried by Proc#curry.
90
+
91
+ ```ruby
92
+ f = lamdba{|x, y, z| [x, y, z]}
93
+ h = f.flip
94
+ h.(:a).(:b).(:c) # => [:b, :a, :c]
95
+ ```
96
+
97
+ If arguments is var-args, pass explicitly arity to curring.
98
+
99
+ ```ruby
100
+ p = Proc.new{|*args| args.inspect }
101
+ p.flip(3).call(:a).(:b).(:c) # => "[:b, :a, :c]"
102
+ p.flip(4).call(:a).(:b).(:c).(:d) # => "[:b, :a, :c, :d]"
103
+ ```
104
+
105
+ If arity is 0 or 1, flip returns itself.
106
+
107
+ This method is aliased as `~@`.
108
+
109
+ ```ruby
110
+ ~f # => f.filp
111
+ ```
112
+
113
+ <!-- Symbol extensions -->
114
+ <!-- - to_method -->
115
+
116
+ ### Class extensions
117
+ - alias instance_method, :/
118
+
119
+ ```ruby
120
+ String / :index # => #<UnboundMethod: String#index>
121
+ ```
122
+
123
+ ### UnboundMethod extensions
124
+ - alias bind, :<
125
+
126
+ ```ruby
127
+ String / :index # => #<UnboundMethod: String#index>
128
+ String / :index < "foobarbaz" # => #<Method: String#index>
129
+ String / :index < "foobarbaz" < 3 # => 3 (== "foobarbaz".index(3) )
130
+ ```
131
+
132
+ ### Combinators
133
+ <!-- - && || -->
134
+ - ski combinator
135
+
136
+ ### Object extensions
137
+ - ap(|>) : obj.ap(f) => f.call(obj)
138
+ - obj._.method : returns obj.method(method) or lamdba{|*args| obj.send(method, *args) }
139
+ <!-- - obj.disjunction(f) : if f(self) is nil, return self else return f(self) -->
140
+
141
+ #### Object#ap
142
+
143
+ `Object#ap` is applies self to given proc/block.
144
+
145
+ ```ruby
146
+ f = lambda{|x| x * 2 }
147
+ "foo".ap(f) # => "fooffoo"
148
+ ```
149
+
150
+ #### Object#_
151
+
152
+ Object#op is shortcut to quickly extract Method object.
153
+
154
+ ```ruby
155
+ "foobarbaz"._.index #=> #<Method: String#index>
156
+ "foobarbaz"._.index < "bar" # => 3 (== "foobarbaz".index(3) )
157
+
158
+ 2._(:>=) # => #<Method: Fixnum#>=>
159
+ [1, 2, 3].select(&2._(:>=)) # => [1, 2]( = [1, 2].select{|n| 2 >= n})
160
+ ```
161
+
162
+
163
+
164
+ ## Contributing
165
+
166
+ 1. Fork it
167
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
168
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
169
+ 4. Push to the branch (`git push origin my-new-feature`)
170
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'lambda_driver/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "lambda_driver"
8
+ gem.version = LambdaDriver::VERSION
9
+ gem.authors = ["Tomohito Ozaki"]
10
+ gem.email = ["ozaki@yuroyoro.com"]
11
+ gem.description = %q{Drives your code more functioal!}
12
+ gem.summary = %q{Drives your code more functioal!}
13
+ gem.homepage = ""
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ # dependencies
21
+ gem.add_development_dependency 'rspec'
22
+ end
@@ -0,0 +1,13 @@
1
+ module LambdaDriver::Ap
2
+ def ap(f = nil, &block)
3
+ if f.nil? && (not block_given?)
4
+ return lambda{|g=nil,&inner_block| self.ap(g, &inner_block) }
5
+ end
6
+
7
+ if block_given?
8
+ yield self
9
+ else
10
+ f.call(self)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,94 @@
1
+ module LambdaDriver::Callable
2
+
3
+ def call(*args, &block)
4
+ if block_given?
5
+ self.to_proc.call(*args, &block)
6
+ else
7
+ self.to_proc.call(*args)
8
+ end
9
+ end
10
+
11
+ # Returns new lambda which composed self and given function.
12
+ # A composed proc called with args, executes `self.(g(*args)).
13
+ #
14
+ # f = lamdba{|x| x.to_s }
15
+ # g = lambda{|y| y.length }
16
+ # h = f compose g
17
+ # h.(:hoge) # => 4
18
+ #
19
+ # This method is aliased as `<<`.
20
+ #
21
+ # f << g # => f.compose(g)
22
+ #
23
+ def compose(g)
24
+ lambda{|*args| self.to_proc.call(g.to_proc.call(*args)) }
25
+ end
26
+
27
+ # g compose self
28
+ def >>(g)
29
+ g.to_proc << self
30
+ end
31
+
32
+ # Returns partially applied function that has 2nd and more parameters
33
+ # fixed by given *args.
34
+ #
35
+ # f = lamdba{|x, y, z| [x, y, z]}
36
+ # h = f.with_args(:a, :b)
37
+ # h.(:c) # => [:c, :a, :b]
38
+ #
39
+ # This method is aliased as `*`.
40
+ #
41
+ # f * :foo # => f.with_args(:foo)
42
+ #
43
+ def with_args(*args)
44
+ lambda{|v|
45
+ self.to_proc.call(*([v] + args))
46
+ }
47
+ end
48
+
49
+ # Returns function whose parameter order spawed 1st for 2nd.
50
+ # A result of filped fuction is curried by Proc#curry.
51
+ #
52
+ # f = lamdba{|x, y, z| [x, y, z]}
53
+ # h = f.flip
54
+ # h.(:a).(:b).(:c) # => [:b, :a, :c]
55
+ #
56
+ # If arguments is var-args, pass explicitly arity to curring.
57
+ #
58
+ # p = Proc.new{|*args| args.inspect }
59
+ # p.flip(3).call(:a).(:b).(:c) # => "[:b, :a, :c]"
60
+ # p.flip(4).call(:a).(:b).(:c).(:d) # => "[:b, :a, :c, :d]"
61
+ #
62
+ # If arity is 0 or 1, flip returns itself.
63
+ #
64
+ # This method is aliased as `~@`.
65
+ #
66
+ # ~f # => f.filp
67
+ #
68
+ def flip(arity = nil)
69
+ f = self.to_proc
70
+ return self if (0..1).include?(f.arity)
71
+ return self if f.arity == -1 && arity.nil?
72
+
73
+ curried = f.curry(arity)
74
+ lambda{|x|
75
+ lambda{|y|
76
+ g = curried[y]
77
+ (g.respond_to? :call) ? g[x] : g
78
+ }
79
+ }
80
+ end
81
+
82
+ def curry(arity = nil)
83
+ self.to_proc.curry(arity)
84
+ end
85
+
86
+ def self.included(klass)
87
+ klass.send(:alias_method, :+@, :to_proc)
88
+ klass.send(:alias_method, :<, :call)
89
+ klass.send(:alias_method, :<<, :compose)
90
+ klass.send(:alias_method, :*, :with_args)
91
+ klass.send(:alias_method, :~@, :flip)
92
+ klass.send(:alias_method, :%, :curry)
93
+ end
94
+ end
@@ -0,0 +1,3 @@
1
+ class Class
2
+ alias_method :/, :instance_method
3
+ end
@@ -0,0 +1,4 @@
1
+ class Method
2
+ include LambdaDriver::Callable
3
+ include LambdaDriver::Currying if RUBY_VERSION < '1.9.0'
4
+ end
@@ -0,0 +1,4 @@
1
+ class Object
2
+ include LambdaDriver::Op::Proxy
3
+ include LambdaDriver::Ap
4
+ end
@@ -0,0 +1,4 @@
1
+ class Proc
2
+ include LambdaDriver::Callable
3
+ include LambdaDriver::Currying if RUBY_VERSION < '1.9.0'
4
+ end
@@ -0,0 +1,4 @@
1
+ class Symbol
2
+ include LambdaDriver::Callable
3
+ include LambdaDriver::Currying if RUBY_VERSION < '1.9.0'
4
+ end
@@ -0,0 +1,3 @@
1
+ class UnboundMethod
2
+ alias_method :<, :bind
3
+ end
@@ -0,0 +1,3 @@
1
+ Dir["#{File.dirname(__FILE__)}/core_ext/*.rb"].sort.each do |path|
2
+ require "lambda_driver/core_ext/#{File.basename(path, '.rb')}"
3
+ end
@@ -0,0 +1,20 @@
1
+ module LambdaDriver::Currying
2
+ def curry
3
+ f = self.to_proc
4
+ arity = __arity(f)
5
+ return f if arity == 0
6
+
7
+ lambda{|arg| __curry(f, arity, arg, []) }
8
+ end
9
+
10
+ private
11
+ def __arity(f)
12
+ (f.arity >= 0) ? f.arity : -(f.arity + 1)
13
+ end
14
+
15
+ def __curry(f, arity, arg, passed)
16
+ args = passed + [arg]
17
+ return f.call(*args) if arity == 1
18
+ lambda{|arg| __curry(f, arity - 1, arg, args) }
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ module LambdaDriver::Op
2
+ module Proxy
3
+ def _(method = nil)
4
+ method ? ::LambdaDriver::Op.method_or_lambda(self, method) : OpProxy.new(self)
5
+ end
6
+ end
7
+
8
+ def method_or_lambda(obj, method)
9
+ (obj.respond_to?(method) && obj.method(method)) ||
10
+ lambda{|*args| obj.__send__(method, *args) }
11
+ end
12
+ module_function :method_or_lambda
13
+
14
+ class BlankSlate
15
+ instance_methods.each { |m| undef_method m unless m =~ /^(__|object_id)/ }
16
+ end
17
+
18
+ class OpProxy < (RUBY_VERSION < '1.9.0' ? BlankSlate : BasicObject)
19
+ def initialize(obj)
20
+ @obj = obj
21
+ end
22
+
23
+ def call(method)
24
+ ::LambdaDriver::Op.method_or_lambda(@obj, method)
25
+ end
26
+
27
+ def method_missing(method)
28
+ ::LambdaDriver::Op.method_or_lambda(@obj, method)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,3 @@
1
+ module LambdaDriver
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,26 @@
1
+ require "lambda_driver/version"
2
+
3
+ Dir["#{File.dirname(__FILE__)}/lambda_driver/*.rb"].sort.each do |path|
4
+ next if File.basename(path, '.rb') == 'core_ext'
5
+ require "lambda_driver/#{File.basename(path, '.rb')}"
6
+ end
7
+ require "lambda_driver/core_ext"
8
+
9
+ module LambdaDriver
10
+
11
+ # SKI combinators
12
+ I = lambda{|x| x }
13
+ K = lambda{|x| lambda{|y| y }}
14
+ S = lambda{|x| lambda{|y| lambda{|z| x.to_proc.call(z).call(y.to_proc.call(z)) } } }
15
+
16
+ # Boolean combinators
17
+ AND = lambda{|l| lambda{|r| l && r }}
18
+ OR = lambda{|l| lambda{|r| l || r }}
19
+
20
+ class << self
21
+ def i ; I end
22
+ def k ; K end
23
+ def s ; S end
24
+ end
25
+
26
+ end
data/spec/ap_spec.rb ADDED
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe LambdaDriver::Ap do
4
+ let(:object) { "foobarbaz" }
5
+ let(:f) { lambda{|x| x * 2 } }
6
+
7
+ describe '#ap'do
8
+ context 'given none' do
9
+ subject { object.ap }
10
+
11
+ it { should be_a_kind_of Proc }
12
+ it('obj.ap.call(f) should be f.call(obj)'){
13
+ subject.call(f).should == f.call(object)
14
+ }
15
+ it('obj.ap.call{block} should be result of execute block with obj'){
16
+ subject.call{|x| x * 3 }.should == (object * 3)
17
+ }
18
+ it('obj.ap.call(f){block} should be result of execute block with obj'){
19
+ subject.call(f){|x| x * 3 }.should == (object * 3)
20
+ }
21
+ end
22
+
23
+ context 'block given' do
24
+ it('obj.ap{block} should be result of execute block with obj'){
25
+ object.ap{|x| x * 3 }.should == (object * 3)
26
+ }
27
+ end
28
+
29
+ context 'proc object given' do
30
+ it('obj.ap(f) should be f.call(obj)'){
31
+ object.ap(f).should == f.call(object)
32
+ }
33
+ end
34
+
35
+ context 'block given and proc object given' do
36
+ it('obj.ap.call(f){block} should be result of execute block with obj'){
37
+ object.ap(f){|x| x * 3 }.should == (object * 3)
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'class' do
4
+ describe './' do
5
+ subject { String / :index }
6
+
7
+ it { should be_a_kind_of UnboundMethod }
8
+ it("(klass / method_name).bind(obj).call(x) should be obj.method_name(x)"){
9
+ subject.bind("foobarbaz").call("bar") == "foobarbaz".index("bar")
10
+ }
11
+ end
12
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'lambda' do
4
+ describe '#compose' do
5
+ subject { lambda{|x| x.to_s} }
6
+
7
+ it_should_behave_like 'composable'
8
+ end
9
+
10
+ describe '#with_args' do
11
+ subject { lambda{|x, y, *z| [x, y] + z.to_a } }
12
+ it_should_behave_like 'with_args'
13
+ end
14
+
15
+ describe '#flip' do
16
+ context 'arity = 1' do
17
+ subject { lambda{|x| [x] } }
18
+
19
+ it_should_behave_like 'flip(arity=1)'
20
+ end
21
+
22
+ context 'arity > 1' do
23
+ subject { lambda{|x, y| [x, y] } }
24
+
25
+ it_should_behave_like 'flip'
26
+ end
27
+
28
+ context 'varargs' do
29
+ subject { lambda{|*x| x } }
30
+
31
+ it_should_behave_like 'flip(varargs)'
32
+ end
33
+ end
34
+
35
+ describe '#curry' do
36
+ subject { lambda{|x, y| [x, y] } }
37
+
38
+ it_should_behave_like 'curry'
39
+
40
+ context 'varargs' do
41
+ subject { lambda{|*x| x } }
42
+
43
+ it_should_behave_like 'curry(varargs)'
44
+ end
45
+ end
46
+
47
+ describe '#call' do
48
+ subject { lambda{|x| x.to_s + "_f"} }
49
+
50
+ it_should_behave_like 'call'
51
+ end
52
+
53
+ describe 'ailases' do
54
+ subject { lambda{|x| x.to_s + "_f"} }
55
+
56
+ it_should_behave_like 'aliases'
57
+ it_should_behave_like 'aliases(varargs)' do
58
+ subject { lambda{|*x| x } }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+
3
+ describe Method do
4
+ describe '#compose' do
5
+ subject { "aaa".method(:+) }
6
+
7
+ it_should_behave_like 'composable' do
8
+ let(:x) { "foo" }
9
+ end
10
+ end
11
+
12
+ describe '#with_args' do
13
+ subject { "foobarbaz".method(:delete) }
14
+
15
+ it_should_behave_like 'with_args' do
16
+ let(:x) { 'r' }
17
+ let(:y) { 'o' }
18
+ let(:z) { 'a' }
19
+ end
20
+ end
21
+
22
+ describe '#flip' do
23
+ context 'arity = 1' do
24
+ subject { "aaa".method(:+) }
25
+
26
+ it_should_behave_like 'flip(arity=1)' do
27
+ let(:x) { "foo" }
28
+ let(:y) { "bar" }
29
+ end
30
+ end
31
+
32
+ context 'arity > 1' do
33
+ subject { "foobarbaz".method(:tr) }
34
+
35
+ it_should_behave_like 'flip' do
36
+ let(:x) { 'r' }
37
+ let(:y) { 'o' }
38
+ end
39
+ end
40
+
41
+ context 'varargs' do
42
+ subject { "foobarbaz".method(:delete) }
43
+
44
+ it_should_behave_like 'flip(varargs)' do
45
+ let(:x) { 'r' }
46
+ let(:y) { 'o' }
47
+ end
48
+ end
49
+ end
50
+
51
+ describe '#curry' do
52
+ context 'arity > 1' do
53
+ subject { "foobarbaz".method(:tr) }
54
+
55
+ it_should_behave_like 'curry' do
56
+ let(:x) { 'r' }
57
+ let(:y) { 'o' }
58
+ end
59
+ end
60
+
61
+ context 'varargs' do
62
+ subject { "foobarbaz".method(:delete) }
63
+
64
+ it_should_behave_like 'curry(varargs)' do
65
+ let(:x) { 'r' }
66
+ let(:y) { 'o' }
67
+ end
68
+ end
69
+ end
70
+
71
+ describe '#call' do
72
+ subject { "aaa".method(:+) }
73
+
74
+ it_should_behave_like 'call' do
75
+ let(:x) { "foo" }
76
+ end
77
+ end
78
+
79
+ describe 'ailases' do
80
+ it_should_behave_like 'aliases' do
81
+ subject { "aaa".method(:+) }
82
+ let(:x) { "foo" }
83
+ end
84
+
85
+ it_should_behave_like 'aliases(varargs)' do
86
+ subject { "foobarbaz".method(:delete) }
87
+ let(:x) { 'r' }
88
+ let(:y) { 'o' }
89
+ end
90
+ end
91
+ end
data/spec/op_spec.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'method_or_lambda' do |label|
4
+ let(:method_name) { :+ }
5
+ context 'given method name (is reciever object responds)' do
6
+ subject { method }
7
+
8
+ it { should be_a_kind_of Method }
9
+ it("#{label}.call(args) should be obj.send(method_name, args)"){
10
+ subject.call('bar').should == object.send(method_name, 'bar')
11
+ }
12
+ end
13
+
14
+ context 'given method name (is not reciever object responds)' do
15
+ subject { lambda_proc }
16
+ before {
17
+ def object.method_missing(method, *args)
18
+ method == :foo ? args : super(method, *args)
19
+ end
20
+ }
21
+
22
+ it { should be_a_kind_of Proc }
23
+ it("#{label}.call(args) should be obj.send(method_name, args)"){
24
+ lambda_proc.call('bar', 'baz').should == object.send(:foo, 'bar', 'baz')
25
+ }
26
+ end
27
+ end
28
+
29
+ describe LambdaDriver::Op do
30
+ describe '#_' do
31
+ let(:object) { "foobarbaz" }
32
+
33
+ context 'given nil' do
34
+ subject { object._ }
35
+
36
+ it { subject.class.should be_a_kind_of LambdaDriver::Op::Proxy }
37
+ end
38
+
39
+ it_should_behave_like 'method_or_lambda', 'obj._(method_name)' do
40
+ let(:method){ object._(:+) }
41
+ let(:lambda_proc){ object._(:foo) }
42
+ end
43
+ end
44
+ end
45
+
46
+ describe LambdaDriver::Op::Proxy do
47
+ let(:object) { "foobarbaz" }
48
+
49
+ describe '#call' do
50
+ it_should_behave_like 'method_or_lambda', 'obj._(method_name)' do
51
+ let(:method){ object._.call(:+) }
52
+ let(:lambda_proc){ object._.call(:foo) }
53
+ end
54
+ end
55
+
56
+ describe '#method_missing' do
57
+ it_should_behave_like 'method_or_lambda', 'obj._.method_name' do
58
+ let(:method){ object._.index }
59
+ let(:method_name) { :index }
60
+ let(:lambda_proc){ object._.foo }
61
+ end
62
+ end
63
+ end
data/spec/proc_spec.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+
3
+ describe Proc do
4
+ describe '#compose' do
5
+ subject { Proc.new{|x| x.to_s} }
6
+
7
+ it_should_behave_like 'composable'
8
+ end
9
+
10
+ describe '#with_args' do
11
+ subject { Proc.new{|x, y, *z| [x, y] + z.to_a } }
12
+ it_should_behave_like 'with_args'
13
+ end
14
+
15
+ describe '#flip' do
16
+ context 'arity = 1' do
17
+ subject { Proc.new{|x| [x] } }
18
+
19
+ it_should_behave_like 'flip(arity=1)'
20
+ end
21
+
22
+ context 'arity > 1' do
23
+ subject { Proc.new{|x, y| [x, y] } }
24
+
25
+ it_should_behave_like 'flip'
26
+ end
27
+
28
+ context 'varargs' do
29
+ subject { Proc.new{|*x| x } }
30
+
31
+ it_should_behave_like 'flip(varargs)'
32
+ end
33
+ end
34
+
35
+ describe '#curry' do
36
+ subject { Proc.new{|x, y| [x, y] } }
37
+
38
+ it_should_behave_like 'curry'
39
+
40
+ context 'varargs' do
41
+ subject { Proc.new{|*x| x } }
42
+
43
+ it_should_behave_like 'curry(varargs)'
44
+ end
45
+ end
46
+
47
+ describe '#call' do
48
+ subject { Proc.new{|x| x.to_s + "_f"} }
49
+
50
+ it_should_behave_like 'call'
51
+ end
52
+
53
+ describe 'ailases' do
54
+ subject { Proc.new{|x| x.to_s + "_f"} }
55
+
56
+ it_should_behave_like 'aliases'
57
+ it_should_behave_like 'aliases(varargs)' do
58
+ subject { Proc.new{|*x| x } }
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,193 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for 'composable' do
4
+ it { should respond_to :to_proc}
5
+ it { should respond_to :>> }
6
+ it { should respond_to :<< }
7
+ it { should respond_to :compose }
8
+
9
+ let(:x) { :foo }
10
+ let(:y) { 12 }
11
+ let(:g) { lambda{|x| (x.to_s * 2) + "_g" } }
12
+
13
+ it('" f >> g" returns g(f)'){
14
+ (subject >> g).should be_a_kind_of Proc
15
+ }
16
+ it('"(f >> g).call(x)" should be g(f(x))'){
17
+ (subject >> g).call(x).should == g.call(subject.to_proc.call(x))
18
+ }
19
+
20
+ it('" f << g" returns g(f)'){
21
+ (subject << g).should be_a_kind_of Proc
22
+ }
23
+ it('"(f << g).call(x)" should be f(g(x))'){
24
+ (subject << g).call(y).should == subject.to_proc.call(g.call(y)) }
25
+ end
26
+
27
+ shared_examples_for 'with_args' do
28
+ it { should respond_to :with_args }
29
+ it { should respond_to :*}
30
+
31
+ let(:x) { :foo }
32
+ let(:y) { :bar }
33
+ let(:z) { :baz }
34
+
35
+ it('" f.with_args(x) returns proc which is partaily appied arguments'){
36
+ subject.with_args(y).should be_a_kind_of Proc
37
+ }
38
+
39
+ it('" f.with_args(y).call(x)" should be f(x, y)'){
40
+ puts x.inspect
41
+ subject.with_args(y).call(x).should == subject.to_proc.call(x, y)
42
+ }
43
+
44
+ it('" f.with_args(*args).call(x)" should be f(x, *args)'){
45
+ subject.with_args(y, z).call(x).should == subject.to_proc.call(x, y, z)
46
+ }
47
+
48
+ it('" f * x returns proc which is partaily appied arguments'){
49
+ (subject * y).should be_a_kind_of Proc
50
+ }
51
+
52
+ it('" (f * y).call(x)" should be f(x, y)'){
53
+ (subject * y).call(x).should == subject.to_proc.call(x, y)
54
+ }
55
+ end
56
+
57
+ shared_examples_for 'flip' do
58
+ it { should respond_to :flip }
59
+ it { should respond_to :~}
60
+
61
+ let(:x) { :foo }
62
+ let(:y) { :bar }
63
+
64
+ it(' f.flip returns function whose parameter order swaped 1st for 2nd.'){
65
+ subject.flip.should be_a_kind_of Proc
66
+ }
67
+
68
+ it(' f.flip.call(x) returns proc ') {
69
+ subject.flip.call(y).should be_a_kind_of Proc
70
+ }
71
+ it(' f.flip.call(x).call(y) should be (y,x)'){
72
+ subject.flip.call(y).call(x).should == subject.to_proc.call(x, y)
73
+ }
74
+
75
+ it(' ~f returns function whose parameter order swaped 1st for 2nd.'){
76
+ (~subject).should be_a_kind_of Proc
77
+ }
78
+
79
+ it(' ~f.call(x) returns proc ') {
80
+ (~subject).call(y).should be_a_kind_of Proc
81
+ }
82
+ it(' ~f.call(x).call(y) should be (y,x)'){
83
+ (~subject).call(y).call(x).should == subject.to_proc.call(x, y)
84
+ }
85
+ end
86
+
87
+ shared_examples_for 'flip(arity=1)' do
88
+ let(:x) { :foo }
89
+ let(:y) { :bar }
90
+
91
+ it(' f.flip returns itself'){
92
+ subject.flip.should == subject
93
+ }
94
+
95
+ it(' f.flip.call(x) returns proc ') {
96
+ subject.flip.call(y) == subject.call(y)
97
+ }
98
+ end
99
+
100
+ shared_examples_for 'flip(varargs)' do
101
+ let(:x) { :foo }
102
+ let(:y) { :bar }
103
+
104
+ it('f.flip returns itself'){
105
+ subject.flip.should == subject
106
+ }
107
+
108
+ it('f.flip(2).call(x) returns proc ') {
109
+ subject.flip(2).call(y) == subject.call(y)
110
+ }
111
+
112
+ it('~f.flip(2).call(x).call(y) should be f.call(y,x)'){
113
+ subject.flip(2).call(y).call(x).should == subject.to_proc.call(x, y)
114
+ }
115
+ end
116
+
117
+ shared_examples_for 'curry' do
118
+ it { should respond_to :curry}
119
+ it { should respond_to :%}
120
+
121
+ let(:x) { :foo }
122
+ let(:y) { :bar }
123
+
124
+ it('f.curry returns curried function'){
125
+ subject.curry.should be_a_kind_of Proc
126
+ }
127
+
128
+ it('f.curry.call(x) returns proc ') {
129
+ subject.curry.call(y).should be_a_kind_of Proc
130
+ }
131
+ it('f.curry.call(x).call(y) should be f.call(x,y)'){
132
+ subject.curry.call(x).call(y).should == subject.to_proc.call(x, y)
133
+ }
134
+ end
135
+
136
+ shared_examples_for 'curry(varargs)' do
137
+ let(:x) { :foo }
138
+ let(:y) { :bar }
139
+
140
+ it('f % 2 returns curried function'){
141
+ (subject % 2).should be_a_kind_of Proc
142
+ }
143
+
144
+ it('(f % 2).call(x) returns proc ') {
145
+ (subject % 2).call(y).should be_a_kind_of Proc
146
+ }
147
+ it('(f % 2).call(x).call(y) should be f.call(x,y)'){
148
+ (subject % 2).call(x).call(y).should == subject.to_proc.call(x, y)
149
+ }
150
+ end
151
+
152
+ shared_examples_for 'call' do
153
+ it { should respond_to :call}
154
+ it { should respond_to :<}
155
+
156
+ let(:x) { :foo }
157
+
158
+ it('f.call(x) == f.call(x)'){
159
+ subject.call(x).should == subject.call(x)
160
+ }
161
+
162
+ it('(f < x) == f.call(x)'){
163
+ (subject < x).should == subject.call(x)
164
+ }
165
+ end
166
+
167
+ shared_examples_for 'aliases' do
168
+ let(:x) { :foo }
169
+ let(:y) { 12 }
170
+ let(:g) { lambda{|x| (x.to_s * 2).to_s + "_g" } }
171
+
172
+ it('(~f < x) should be f.flip.call(x)'){
173
+ (~subject < x).should == subject.flip.call(x)
174
+ }
175
+
176
+ it('(f << g < x) should f.compose(g).call(x)'){
177
+ (subject << g < x).should == subject.compose(g).call(x)
178
+ }
179
+
180
+ it('(f >> g < x) should g.compose(f).call(x)'){
181
+ (subject >> g < x).should == g.compose(subject).call(x)
182
+ }
183
+ end
184
+
185
+ shared_examples_for 'aliases(varargs)' do
186
+ let(:x) { :foo }
187
+ let(:y) { 12 }
188
+ let(:g) { lambda{|x| (x.to_s * 2).to_s + "_g" } }
189
+
190
+ it('(f % 2 * y << g < x) should be f.curry(2).with_args(y).compose(g).call(x)'){
191
+ (subject % 2 * y << g < x).should == subject.curry(2).with_args(y).compose(g).call(x)
192
+ }
193
+ end
@@ -0,0 +1,17 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper.rb"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ #
8
+ require 'rspec'
9
+ require 'lambda_driver'
10
+
11
+ Dir["#{File.dirname(__FILE__)}/shared/**/*.rb"].each {|f| require f}
12
+
13
+ RSpec.configure do |config|
14
+ config.treat_symbols_as_metadata_keys_with_true_values = true
15
+ config.run_all_when_everything_filtered = true
16
+ config.filter_run :focus
17
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Symbol do
4
+ describe '#compose' do
5
+ subject { :to_s }
6
+
7
+ it_should_behave_like 'composable'
8
+ end
9
+
10
+ describe '#with_args' do
11
+ subject {:delete }
12
+
13
+ it_should_behave_like 'with_args' do
14
+ let(:x) { 'foobar' }
15
+ let(:y) { 'o' }
16
+ let(:z) { 'a'}
17
+ end
18
+ end
19
+
20
+ describe '#flip' do
21
+ context 'arity = 1' do
22
+ subject { :to_s }
23
+
24
+ it_should_behave_like 'flip(arity=1)'
25
+ end
26
+
27
+ context 'varargs' do
28
+ subject {:product }
29
+
30
+ it_should_behave_like 'flip(varargs)' do
31
+ let(:x) { [:bar] }
32
+ let(:y) { [:foo] }
33
+ end
34
+ end
35
+ end
36
+
37
+ describe '#curry' do
38
+ subject { :product }
39
+
40
+ it_should_behave_like 'curry(varargs)' do
41
+ let(:x) { [:bar] }
42
+ let(:y) { [:foo] }
43
+ end
44
+ end
45
+
46
+ describe '#call' do
47
+ subject { :to_s }
48
+
49
+ it_should_behave_like 'call'
50
+ end
51
+
52
+ describe 'ailases' do
53
+ subject { :to_s }
54
+
55
+ it_should_behave_like 'aliases'
56
+
57
+ it_should_behave_like 'aliases(varargs)' do
58
+ subject { :product }
59
+ let(:x) { [:bar] }
60
+ let(:y) { [:foo] }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe UnboundMethod do
4
+ describe '#<' do
5
+ let(:unbound_method){ String.instance_method(:index) }
6
+ subject { unbound_method < "foobarbaz" }
7
+
8
+ it { should be_a_kind_of Method }
9
+ it('(unbound_method < obj).call(x) should be unbound_method.bind(obj).call(x)'){
10
+ subject.call('bar').should == unbound_method.bind("foobarbaz").call("bar")
11
+ }
12
+ end
13
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: lambda_driver
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tomohito Ozaki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-03-26 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70364479275960 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70364479275960
25
+ description: Drives your code more functioal!
26
+ email:
27
+ - ozaki@yuroyoro.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - .rspec
34
+ - Gemfile
35
+ - LICENSE.txt
36
+ - README.md
37
+ - Rakefile
38
+ - lambda_driver.gemspec
39
+ - lib/lambda_driver.rb
40
+ - lib/lambda_driver/ap.rb
41
+ - lib/lambda_driver/callable.rb
42
+ - lib/lambda_driver/core_ext.rb
43
+ - lib/lambda_driver/core_ext/class.rb
44
+ - lib/lambda_driver/core_ext/method.rb
45
+ - lib/lambda_driver/core_ext/object.rb
46
+ - lib/lambda_driver/core_ext/proc.rb
47
+ - lib/lambda_driver/core_ext/symbol.rb
48
+ - lib/lambda_driver/core_ext/unbound_method.rb
49
+ - lib/lambda_driver/currying.rb
50
+ - lib/lambda_driver/op.rb
51
+ - lib/lambda_driver/version.rb
52
+ - spec/ap_spec.rb
53
+ - spec/class_spec.rb
54
+ - spec/lambda_spec.rb
55
+ - spec/method_spec.rb
56
+ - spec/op_spec.rb
57
+ - spec/proc_spec.rb
58
+ - spec/shared/composable_spec.rb
59
+ - spec/spec_helper.rb
60
+ - spec/symbol_spec.rb
61
+ - spec/unbound_method_spec.rb
62
+ homepage: ''
63
+ licenses: []
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project:
82
+ rubygems_version: 1.8.10
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Drives your code more functioal!
86
+ test_files:
87
+ - spec/ap_spec.rb
88
+ - spec/class_spec.rb
89
+ - spec/lambda_spec.rb
90
+ - spec/method_spec.rb
91
+ - spec/op_spec.rb
92
+ - spec/proc_spec.rb
93
+ - spec/shared/composable_spec.rb
94
+ - spec/spec_helper.rb
95
+ - spec/symbol_spec.rb
96
+ - spec/unbound_method_spec.rb