functional-ruby 0.7.2 → 0.7.3
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.
- checksums.yaml +7 -0
- data/LICENSE +21 -21
- data/README.md +193 -193
- data/lib/functional.rb +4 -4
- data/lib/functional/behavior.rb +139 -132
- data/lib/functional/behaviour.rb +2 -2
- data/lib/functional/pattern_matching.rb +133 -133
- data/lib/functional/utilities.rb +192 -192
- data/lib/functional/version.rb +3 -3
- data/lib/functional_ruby.rb +1 -1
- data/md/behavior.md +188 -188
- data/md/pattern_matching.md +512 -512
- data/md/utilities.md +55 -55
- data/spec/functional/behavior_spec.rb +508 -464
- data/spec/functional/integration_spec.rb +205 -205
- data/spec/functional/pattern_matching_spec.rb +418 -418
- data/spec/functional/utilities_spec.rb +271 -271
- data/spec/spec_helper.rb +18 -18
- metadata +21 -20
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b8baa4b81ad36ad498a6c2b38625c4c96b9fa158
|
4
|
+
data.tar.gz: 3b7dd90ffa92d417a4ab801daf05d60ba4012717
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 089c767a1b6c8f40a6c1108f38f7e465c295acc2bb30746ebddac02f82333e96f5a9dfea7b50d3decab2ac0ac39f5563134468f32e7f0baa480b4c5761be1539
|
7
|
+
data.tar.gz: c54b9a671b8552ffa41a8e47e3689deef9c33f7f481369dcd6da31e29da3cc538dbe12330b9abcaca006e8df67e1bb2c0d0f1b03fb07866c9d97966fca567d1b
|
data/LICENSE
CHANGED
@@ -1,21 +1,21 @@
|
|
1
|
-
Copyright (c) Jerry D'Antonio -- released under the MIT license.
|
2
|
-
|
3
|
-
http://www.opensource.org/licenses/mit-license.php
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
7
|
-
in the Software without restriction, including without limitation the rights
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
10
|
-
furnished to do so, subject to the following conditions:
|
11
|
-
|
12
|
-
The above copyright notice and this permission notice shall be included in
|
13
|
-
all copies or substantial portions of the Software.
|
14
|
-
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
-
THE SOFTWARE.
|
1
|
+
Copyright (c) Jerry D'Antonio -- released under the MIT license.
|
2
|
+
|
3
|
+
http://www.opensource.org/licenses/mit-license.php
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,193 +1,193 @@
|
|
1
|
-
# Functional Ruby [](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [](https://gemnasium.com/jdantonio/functional-ruby)
|
2
|
-
|
3
|
-
A gem for adding Erlang, Clojure, and Go inspired functional programming tools to Ruby.
|
4
|
-
|
5
|
-
*NOTE: As of version 0.7.0 the concurrency tools from this gem have been moved to the
|
6
|
-
[concurrent-ruby](https://github.com/jdantonio/concurrent-ruby) gem.*
|
7
|
-
|
8
|
-
The project is hosted on the following sites:
|
9
|
-
|
10
|
-
* [RubyGems project page](https://rubygems.org/gems/functional-ruby)
|
11
|
-
* [Source code on GitHub](https://github.com/jdantonio/functional-ruby)
|
12
|
-
* [YARD documentation on RubyDoc.info](http://rubydoc.info/github/jdantonio/functional-ruby/frames)
|
13
|
-
* [Continuous integration on Travis-CI](https://travis-ci.org/jdantonio/functional-ruby)
|
14
|
-
* [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/functional-ruby)
|
15
|
-
* [Follow me on Twitter](https://twitter.com/jerrydantonio)
|
16
|
-
|
17
|
-
## Introduction
|
18
|
-
|
19
|
-
Three things I love are [Ruby](http://www.ruby-lang.org/en/),
|
20
|
-
[functional](https://en.wikipedia.org/wiki/Functional_programming)
|
21
|
-
[programming](http://c2.com/cgi/wiki?FunctionalProgramming) and
|
22
|
-
[concurrency](http://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Dstripbooks&field-keywords=concurrent%20programming).
|
23
|
-
Sadly, the first is generally not associated with the other two. First, I reject the
|
24
|
-
assertion that Ruby is an object-oriented language. It's certainly object-based, since
|
25
|
-
everything is an object, but entire large-scale programs can be built without ever
|
26
|
-
defining a single class. Ruby is a true multi-paradigm language and easily supports
|
27
|
-
many advanced functional techniques. As to concurrency, Ruby's bad reputation is
|
28
|
-
well earned, but recent versions of Ruby have made significan improvements in that
|
29
|
-
area. Ruby 2.0 is now a [relevant](https://blog.heroku.com/archives/2013/6/17/ruby-2-default-new-aps)
|
30
|
-
platform for concurrent applications.
|
31
|
-
|
32
|
-
This gem is my small and humble attempt to help Ruby reach its full potential as
|
33
|
-
a highly performant, functional programming language.
|
34
|
-
|
35
|
-
### Goals
|
36
|
-
|
37
|
-
My goal is to implement various functional programming patterns in Ruby. Specifically:
|
38
|
-
|
39
|
-
* Stay true to the spirit of the languages providing inspiration
|
40
|
-
* But implement in a way that makes sense for Ruby
|
41
|
-
* Keep the semantics as idiomatic Ruby as possible
|
42
|
-
* Support features that make sense in Ruby
|
43
|
-
* Exclude features that don't make sense in Ruby
|
44
|
-
* Keep everything small
|
45
|
-
* Be as fast as reasonably possible
|
46
|
-
|
47
|
-
## Features (and Documentation)
|
48
|
-
|
49
|
-
Several features from Erlang, Go, and Clojure have been implemented thus far:
|
50
|
-
|
51
|
-
* Function overloading with Erlang-style [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
52
|
-
* Interface specifications with Erlang-style [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
|
53
|
-
* Several useful functional [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
|
54
|
-
|
55
|
-
### Is it any good?
|
56
|
-
|
57
|
-
[Yes](http://news.ycombinator.com/item?id=3067434)
|
58
|
-
|
59
|
-
### Supported Ruby versions
|
60
|
-
|
61
|
-
MRI 1.9.2, 1.9.3, and 2.0. This library is pure Ruby and has no gem dependencies. It should be
|
62
|
-
fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
|
63
|
-
about JRuby, Rubinius, or the others to fully support them. I can promise good karma and
|
64
|
-
attribution on this page to anyone wishing to take responsibility for verifying compaitibility
|
65
|
-
with any Ruby other than MRI.
|
66
|
-
|
67
|
-
### Install
|
68
|
-
|
69
|
-
```shell
|
70
|
-
gem install functional-ruby
|
71
|
-
```
|
72
|
-
|
73
|
-
or add the following line to Gemfile:
|
74
|
-
|
75
|
-
```ruby
|
76
|
-
gem 'functional-ruby'
|
77
|
-
```
|
78
|
-
|
79
|
-
and run `bundle install` from your shell.
|
80
|
-
|
81
|
-
Once you've installed the gem you must `require` it in your project:
|
82
|
-
|
83
|
-
```ruby
|
84
|
-
require 'functional'
|
85
|
-
```
|
86
|
-
|
87
|
-
### Examples
|
88
|
-
|
89
|
-
For complete examples, see the specific documentation linked above. Below are a
|
90
|
-
few examples to whet your appetite.
|
91
|
-
|
92
|
-
#### Pattern Matching (Erlang)
|
93
|
-
|
94
|
-
Documentation: [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
95
|
-
|
96
|
-
```ruby
|
97
|
-
require 'functional/pattern_matching'
|
98
|
-
|
99
|
-
class Foo
|
100
|
-
include PatternMatching
|
101
|
-
|
102
|
-
defn(:greet, :male) {
|
103
|
-
puts "Hello, sir!"
|
104
|
-
}
|
105
|
-
|
106
|
-
defn(:greet, :female) {
|
107
|
-
puts "Hello, ma'am!"
|
108
|
-
}
|
109
|
-
end
|
110
|
-
|
111
|
-
foo = Foo.new
|
112
|
-
foo.greet(:male) #=> "Hello, sir!"
|
113
|
-
foo.greet(:female) #=> "Hello, ma'am!"
|
114
|
-
```
|
115
|
-
|
116
|
-
#### Behavior (Erlang)
|
117
|
-
|
118
|
-
Documentation: [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
|
119
|
-
|
120
|
-
```ruby
|
121
|
-
require 'functional/behavior'
|
122
|
-
|
123
|
-
behaviour_info(:gen_foo, foo: 0, self_bar: 1)
|
124
|
-
|
125
|
-
class Foo
|
126
|
-
behavior(:gen_foo)
|
127
|
-
|
128
|
-
def foo
|
129
|
-
return 'foo/0'
|
130
|
-
end
|
131
|
-
|
132
|
-
def self.bar(one, &block)
|
133
|
-
return 'bar/1'
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
foo = Foo.new
|
138
|
-
|
139
|
-
Foo.behaves_as? :gen_foo #=> true
|
140
|
-
foo.behaves_as?(:gen_foo) #=> true
|
141
|
-
foo.behaves_as?(:bogus) #=> false
|
142
|
-
'foo'.behaves_as? :gen_foo #=> false
|
143
|
-
```
|
144
|
-
|
145
|
-
#### Utilities
|
146
|
-
|
147
|
-
Documentation: [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
|
148
|
-
|
149
|
-
```ruby
|
150
|
-
Infinity #=> Infinity
|
151
|
-
NaN #=> NaN
|
152
|
-
|
153
|
-
repl? #=> true when called under irb, pry, bundle console, or rails console
|
154
|
-
|
155
|
-
safe(1, 2){|a, b| a + b} #=> 3
|
156
|
-
safe{ eval 'puts "Hello World!"' } #=> SecurityError: Insecure operation
|
157
|
-
|
158
|
-
pp_s [1,2,3,4] #=> "[1, 2, 3, 4]\n" props to Rha7
|
159
|
-
|
160
|
-
delta(-1, 1) #=> 2
|
161
|
-
delta({count: -1}, {count: 1}){|item| item[:count]} #=> 2
|
162
|
-
|
163
|
-
# And many more!
|
164
|
-
```
|
165
|
-
|
166
|
-
## Copyright
|
167
|
-
|
168
|
-
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
169
|
-
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
170
|
-
|
171
|
-
## License
|
172
|
-
|
173
|
-
Released under the MIT license.
|
174
|
-
|
175
|
-
http://www.opensource.org/licenses/mit-license.php
|
176
|
-
|
177
|
-
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
178
|
-
> of this software and associated documentation files (the "Software"), to deal
|
179
|
-
> in the Software without restriction, including without limitation the rights
|
180
|
-
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
181
|
-
> copies of the Software, and to permit persons to whom the Software is
|
182
|
-
> furnished to do so, subject to the following conditions:
|
183
|
-
>
|
184
|
-
> The above copyright notice and this permission notice shall be included in
|
185
|
-
> all copies or substantial portions of the Software.
|
186
|
-
>
|
187
|
-
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
188
|
-
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
189
|
-
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
190
|
-
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
191
|
-
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
192
|
-
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
193
|
-
> THE SOFTWARE.
|
1
|
+
# Functional Ruby [](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [](https://gemnasium.com/jdantonio/functional-ruby)
|
2
|
+
|
3
|
+
A gem for adding Erlang, Clojure, and Go inspired functional programming tools to Ruby.
|
4
|
+
|
5
|
+
*NOTE: As of version 0.7.0 the concurrency tools from this gem have been moved to the
|
6
|
+
[concurrent-ruby](https://github.com/jdantonio/concurrent-ruby) gem.*
|
7
|
+
|
8
|
+
The project is hosted on the following sites:
|
9
|
+
|
10
|
+
* [RubyGems project page](https://rubygems.org/gems/functional-ruby)
|
11
|
+
* [Source code on GitHub](https://github.com/jdantonio/functional-ruby)
|
12
|
+
* [YARD documentation on RubyDoc.info](http://rubydoc.info/github/jdantonio/functional-ruby/frames)
|
13
|
+
* [Continuous integration on Travis-CI](https://travis-ci.org/jdantonio/functional-ruby)
|
14
|
+
* [Dependency tracking on Gemnasium](https://gemnasium.com/jdantonio/functional-ruby)
|
15
|
+
* [Follow me on Twitter](https://twitter.com/jerrydantonio)
|
16
|
+
|
17
|
+
## Introduction
|
18
|
+
|
19
|
+
Three things I love are [Ruby](http://www.ruby-lang.org/en/),
|
20
|
+
[functional](https://en.wikipedia.org/wiki/Functional_programming)
|
21
|
+
[programming](http://c2.com/cgi/wiki?FunctionalProgramming) and
|
22
|
+
[concurrency](http://www.amazon.com/s/ref=nb_sb_noss_1?url=search-alias%3Dstripbooks&field-keywords=concurrent%20programming).
|
23
|
+
Sadly, the first is generally not associated with the other two. First, I reject the
|
24
|
+
assertion that Ruby is an object-oriented language. It's certainly object-based, since
|
25
|
+
everything is an object, but entire large-scale programs can be built without ever
|
26
|
+
defining a single class. Ruby is a true multi-paradigm language and easily supports
|
27
|
+
many advanced functional techniques. As to concurrency, Ruby's bad reputation is
|
28
|
+
well earned, but recent versions of Ruby have made significan improvements in that
|
29
|
+
area. Ruby 2.0 is now a [relevant](https://blog.heroku.com/archives/2013/6/17/ruby-2-default-new-aps)
|
30
|
+
platform for concurrent applications.
|
31
|
+
|
32
|
+
This gem is my small and humble attempt to help Ruby reach its full potential as
|
33
|
+
a highly performant, functional programming language.
|
34
|
+
|
35
|
+
### Goals
|
36
|
+
|
37
|
+
My goal is to implement various functional programming patterns in Ruby. Specifically:
|
38
|
+
|
39
|
+
* Stay true to the spirit of the languages providing inspiration
|
40
|
+
* But implement in a way that makes sense for Ruby
|
41
|
+
* Keep the semantics as idiomatic Ruby as possible
|
42
|
+
* Support features that make sense in Ruby
|
43
|
+
* Exclude features that don't make sense in Ruby
|
44
|
+
* Keep everything small
|
45
|
+
* Be as fast as reasonably possible
|
46
|
+
|
47
|
+
## Features (and Documentation)
|
48
|
+
|
49
|
+
Several features from Erlang, Go, and Clojure have been implemented thus far:
|
50
|
+
|
51
|
+
* Function overloading with Erlang-style [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
52
|
+
* Interface specifications with Erlang-style [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
|
53
|
+
* Several useful functional [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
|
54
|
+
|
55
|
+
### Is it any good?
|
56
|
+
|
57
|
+
[Yes](http://news.ycombinator.com/item?id=3067434)
|
58
|
+
|
59
|
+
### Supported Ruby versions
|
60
|
+
|
61
|
+
MRI 1.9.2, 1.9.3, and 2.0. This library is pure Ruby and has no gem dependencies. It should be
|
62
|
+
fully compatible with any Ruby interpreter that is 1.9.x compliant. I simply don't know enough
|
63
|
+
about JRuby, Rubinius, or the others to fully support them. I can promise good karma and
|
64
|
+
attribution on this page to anyone wishing to take responsibility for verifying compaitibility
|
65
|
+
with any Ruby other than MRI.
|
66
|
+
|
67
|
+
### Install
|
68
|
+
|
69
|
+
```shell
|
70
|
+
gem install functional-ruby
|
71
|
+
```
|
72
|
+
|
73
|
+
or add the following line to Gemfile:
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
gem 'functional-ruby'
|
77
|
+
```
|
78
|
+
|
79
|
+
and run `bundle install` from your shell.
|
80
|
+
|
81
|
+
Once you've installed the gem you must `require` it in your project:
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
require 'functional'
|
85
|
+
```
|
86
|
+
|
87
|
+
### Examples
|
88
|
+
|
89
|
+
For complete examples, see the specific documentation linked above. Below are a
|
90
|
+
few examples to whet your appetite.
|
91
|
+
|
92
|
+
#### Pattern Matching (Erlang)
|
93
|
+
|
94
|
+
Documentation: [Pattern Matching](https://github.com/jdantonio/functional-ruby/blob/master/md/pattern_matching.md)
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
require 'functional/pattern_matching'
|
98
|
+
|
99
|
+
class Foo
|
100
|
+
include PatternMatching
|
101
|
+
|
102
|
+
defn(:greet, :male) {
|
103
|
+
puts "Hello, sir!"
|
104
|
+
}
|
105
|
+
|
106
|
+
defn(:greet, :female) {
|
107
|
+
puts "Hello, ma'am!"
|
108
|
+
}
|
109
|
+
end
|
110
|
+
|
111
|
+
foo = Foo.new
|
112
|
+
foo.greet(:male) #=> "Hello, sir!"
|
113
|
+
foo.greet(:female) #=> "Hello, ma'am!"
|
114
|
+
```
|
115
|
+
|
116
|
+
#### Behavior (Erlang)
|
117
|
+
|
118
|
+
Documentation: [Behavior](https://github.com/jdantonio/functional-ruby/blob/master/md/behavior.md)
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
require 'functional/behavior'
|
122
|
+
|
123
|
+
behaviour_info(:gen_foo, foo: 0, self_bar: 1)
|
124
|
+
|
125
|
+
class Foo
|
126
|
+
behavior(:gen_foo)
|
127
|
+
|
128
|
+
def foo
|
129
|
+
return 'foo/0'
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.bar(one, &block)
|
133
|
+
return 'bar/1'
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
foo = Foo.new
|
138
|
+
|
139
|
+
Foo.behaves_as? :gen_foo #=> true
|
140
|
+
foo.behaves_as?(:gen_foo) #=> true
|
141
|
+
foo.behaves_as?(:bogus) #=> false
|
142
|
+
'foo'.behaves_as? :gen_foo #=> false
|
143
|
+
```
|
144
|
+
|
145
|
+
#### Utilities
|
146
|
+
|
147
|
+
Documentation: [Utilities](https://github.com/jdantonio/functional-ruby/blob/master/md/utilities.md)
|
148
|
+
|
149
|
+
```ruby
|
150
|
+
Infinity #=> Infinity
|
151
|
+
NaN #=> NaN
|
152
|
+
|
153
|
+
repl? #=> true when called under irb, pry, bundle console, or rails console
|
154
|
+
|
155
|
+
safe(1, 2){|a, b| a + b} #=> 3
|
156
|
+
safe{ eval 'puts "Hello World!"' } #=> SecurityError: Insecure operation
|
157
|
+
|
158
|
+
pp_s [1,2,3,4] #=> "[1, 2, 3, 4]\n" props to Rha7
|
159
|
+
|
160
|
+
delta(-1, 1) #=> 2
|
161
|
+
delta({count: -1}, {count: 1}){|item| item[:count]} #=> 2
|
162
|
+
|
163
|
+
# And many more!
|
164
|
+
```
|
165
|
+
|
166
|
+
## Copyright
|
167
|
+
|
168
|
+
*Functional Ruby* is Copyright © 2013 [Jerry D'Antonio](https://twitter.com/jerrydantonio).
|
169
|
+
It is free software and may be redistributed under the terms specified in the LICENSE file.
|
170
|
+
|
171
|
+
## License
|
172
|
+
|
173
|
+
Released under the MIT license.
|
174
|
+
|
175
|
+
http://www.opensource.org/licenses/mit-license.php
|
176
|
+
|
177
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
178
|
+
> of this software and associated documentation files (the "Software"), to deal
|
179
|
+
> in the Software without restriction, including without limitation the rights
|
180
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
181
|
+
> copies of the Software, and to permit persons to whom the Software is
|
182
|
+
> furnished to do so, subject to the following conditions:
|
183
|
+
>
|
184
|
+
> The above copyright notice and this permission notice shall be included in
|
185
|
+
> all copies or substantial portions of the Software.
|
186
|
+
>
|
187
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
188
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
189
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
190
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
191
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
192
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
193
|
+
> THE SOFTWARE.
|
data/lib/functional.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'functional/behavior'
|
2
|
-
require 'functional/pattern_matching'
|
3
|
-
require 'functional/utilities'
|
4
|
-
require 'functional/version'
|
1
|
+
require 'functional/behavior'
|
2
|
+
require 'functional/pattern_matching'
|
3
|
+
require 'functional/utilities'
|
4
|
+
require 'functional/version'
|
data/lib/functional/behavior.rb
CHANGED
@@ -1,132 +1,139 @@
|
|
1
|
-
module Kernel
|
2
|
-
|
3
|
-
BehaviorError = Class.new(StandardError)
|
4
|
-
|
5
|
-
# Define a behavioral specification (interface).
|
6
|
-
#
|
7
|
-
# @param name [Symbol] the name of the behavior
|
8
|
-
# @param functions [Hash] function names and their arity as key/value pairs
|
9
|
-
def behavior_info(name, functions = {})
|
10
|
-
$__behavior_info__ ||= {}
|
11
|
-
$__behavior_info__[name.to_sym] = functions.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
12
|
-
end
|
13
|
-
|
14
|
-
alias :behaviour_info :behavior_info
|
15
|
-
alias :interface :behavior_info
|
16
|
-
|
17
|
-
module_function :behavior_info
|
18
|
-
module_function :behaviour_info
|
19
|
-
module_function :interface
|
20
|
-
|
21
|
-
# Specify a #behavior_info to enforce on the enclosing class
|
22
|
-
#
|
23
|
-
# @param name [Symbol] name of the #behavior_info being implemented
|
24
|
-
def behavior(name)
|
25
|
-
|
26
|
-
name = name.to_sym
|
27
|
-
raise BehaviorError.new("undefined behavior '#{name}'") if $__behavior_info__[name].nil?
|
28
|
-
|
29
|
-
clazz = self.method(:behavior).receiver
|
30
|
-
|
31
|
-
unless clazz.instance_methods(false).include?(:behaviors)
|
32
|
-
class << clazz
|
33
|
-
def behaviors
|
34
|
-
@behaviors ||= []
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
clazz.behaviors << name
|
40
|
-
|
41
|
-
if self.class == Module
|
42
|
-
(class << self; self; end).class_eval do
|
43
|
-
define_method(:included) do |base|
|
44
|
-
base.class_eval do
|
45
|
-
behavior(name)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
if self.class == Class
|
52
|
-
unless self.respond_to?(:__new)
|
53
|
-
class << clazz
|
54
|
-
alias_method(:__new, :new)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class << clazz
|
60
|
-
def new(*args, &block)
|
61
|
-
obj = __new(*args, &block)
|
62
|
-
self.ancestors.each do |clazz|
|
63
|
-
if clazz.respond_to?(:behaviors)
|
64
|
-
clazz.behaviors.each do |behavior|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
alias :
|
78
|
-
|
79
|
-
|
80
|
-
module_function :
|
81
|
-
module_function :
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
#
|
88
|
-
#
|
89
|
-
#
|
90
|
-
# not necessarily have to include a corresponding
|
91
|
-
#
|
92
|
-
#
|
93
|
-
# @param name [Symbol] name of the #behavior_info to
|
94
|
-
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
# methods are implemented
|
98
|
-
def behaves_as?(name)
|
99
|
-
|
100
|
-
name = name.to_sym
|
101
|
-
bi = $__behavior_info__[name]
|
102
|
-
return false if bi.nil?
|
103
|
-
|
104
|
-
validator = proc do |obj, method, arity|
|
105
|
-
(obj.respond_to?(method) && arity == :any) || obj.method(method).arity == arity
|
106
|
-
end
|
107
|
-
|
108
|
-
if self.is_a?(Class) || self.is_a?(Module)
|
109
|
-
bi = bi.select{|method, arity| method.to_s =~ /^self_/ }
|
110
|
-
end
|
111
|
-
|
112
|
-
bi.each do |method, arity|
|
113
|
-
begin
|
114
|
-
|
115
|
-
obj = self
|
116
|
-
|
117
|
-
if (self.is_a?(Class) || self.is_a?(Module)) &&
|
118
|
-
|
119
|
-
elsif method =~ /^self_/
|
120
|
-
|
121
|
-
obj = self.class
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
return
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
1
|
+
module Kernel
|
2
|
+
|
3
|
+
BehaviorError = Class.new(StandardError)
|
4
|
+
|
5
|
+
# Define a behavioral specification (interface).
|
6
|
+
#
|
7
|
+
# @param name [Symbol] the name of the behavior
|
8
|
+
# @param functions [Hash] function names and their arity as key/value pairs
|
9
|
+
def behavior_info(name, functions = {})
|
10
|
+
$__behavior_info__ ||= {}
|
11
|
+
$__behavior_info__[name.to_sym] = functions.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
12
|
+
end
|
13
|
+
|
14
|
+
alias :behaviour_info :behavior_info
|
15
|
+
alias :interface :behavior_info
|
16
|
+
|
17
|
+
module_function :behavior_info
|
18
|
+
module_function :behaviour_info
|
19
|
+
module_function :interface
|
20
|
+
|
21
|
+
# Specify a #behavior_info to enforce on the enclosing class
|
22
|
+
#
|
23
|
+
# @param name [Symbol] name of the #behavior_info being implemented
|
24
|
+
def behavior(name)
|
25
|
+
|
26
|
+
name = name.to_sym
|
27
|
+
raise BehaviorError.new("undefined behavior '#{name}'") if $__behavior_info__[name].nil?
|
28
|
+
|
29
|
+
clazz = self.method(:behavior).receiver
|
30
|
+
|
31
|
+
unless clazz.instance_methods(false).include?(:behaviors)
|
32
|
+
class << clazz
|
33
|
+
def behaviors
|
34
|
+
@behaviors ||= []
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
clazz.behaviors << name
|
40
|
+
|
41
|
+
if self.class == Module
|
42
|
+
(class << self; self; end).class_eval do
|
43
|
+
define_method(:included) do |base|
|
44
|
+
base.class_eval do
|
45
|
+
behavior(name)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if self.class == Class
|
52
|
+
unless self.respond_to?(:__new)
|
53
|
+
class << clazz
|
54
|
+
alias_method(:__new, :new)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
class << clazz
|
60
|
+
def new(*args, &block)
|
61
|
+
obj = __new(*args, &block)
|
62
|
+
self.ancestors.each do |clazz|
|
63
|
+
if clazz.respond_to?(:behaviors)
|
64
|
+
clazz.behaviors.each do |behavior|
|
65
|
+
valid = obj.behaves_as?(behavior, true)
|
66
|
+
#unless obj.behaves_as?(behavior)
|
67
|
+
#raise BehaviorError.new("undefined callback functions in #{self} (behavior '#{behavior}')")
|
68
|
+
#end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
return obj
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
alias :behaviour :behavior
|
78
|
+
alias :behaves_as :behavior
|
79
|
+
|
80
|
+
module_function :behavior
|
81
|
+
module_function :behaviour
|
82
|
+
module_function :behaves_as
|
83
|
+
end
|
84
|
+
|
85
|
+
class Object
|
86
|
+
|
87
|
+
# Does the object implement the given #behavior_info?
|
88
|
+
#
|
89
|
+
# @note Will return true if the object implements the required methods. The
|
90
|
+
# object's class hierarchy does not necessarily have to include a corresponding
|
91
|
+
# #behavior call.
|
92
|
+
#
|
93
|
+
# @param name [Symbol] name of the #behavior_info to verify behavior against.
|
94
|
+
# @param abend [Boolean] raise an exception when true and the there are
|
95
|
+
# unimplemented methods
|
96
|
+
#
|
97
|
+
# @return [Boolean] whether or not the required public methods are implemented
|
98
|
+
def behaves_as?(name, abend = false)
|
99
|
+
|
100
|
+
name = name.to_sym
|
101
|
+
bi = $__behavior_info__[name]
|
102
|
+
return false if bi.nil?
|
103
|
+
|
104
|
+
validator = proc do |obj, method, arity|
|
105
|
+
(obj.respond_to?(method) && arity == :any) || obj.method(method).arity == arity
|
106
|
+
end
|
107
|
+
|
108
|
+
if self.is_a?(Class) || self.is_a?(Module)
|
109
|
+
bi = bi.select{|method, arity| method.to_s =~ /^self_/ }
|
110
|
+
end
|
111
|
+
|
112
|
+
bi.each do |method, arity|
|
113
|
+
begin
|
114
|
+
func = method.to_s
|
115
|
+
obj = self
|
116
|
+
|
117
|
+
if (self.is_a?(Class) || self.is_a?(Module)) && func =~ /^self_/
|
118
|
+
func = func.gsub(/^self_/, '')
|
119
|
+
elsif method =~ /^self_/
|
120
|
+
func = func.gsub(/^self_/, '')
|
121
|
+
obj = self.class
|
122
|
+
end
|
123
|
+
|
124
|
+
valid = validator.call(obj, func, arity)
|
125
|
+
raise NameError if abend && ! valid
|
126
|
+
return valid unless valid
|
127
|
+
rescue NameError
|
128
|
+
if abend
|
129
|
+
func = "#{method.to_s.gsub(/^self_/, 'self.')}/#{arity.to_s.gsub(/^any$/, ':any')}"
|
130
|
+
raise BehaviorError.new("undefined callback function ##{func} in #{self} (behavior '#{name}')")
|
131
|
+
else
|
132
|
+
return false
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
return true
|
138
|
+
end
|
139
|
+
end
|