functional-ruby 0.7.2 → 0.7.3
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](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 [![Build Status](https://secure.travis-ci.org/jdantonio/functional-ruby.png)](https://travis-ci.org/jdantonio/functional-ruby?branch=master) [![Dependency Status](https://gemnasium.com/jdantonio/functional-ruby.png)](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
|