functional-ruby 0.7.1 → 0.7.2

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/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'
@@ -1,124 +1,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
- class << clazz
52
- def new(*args, &block)
53
- obj = super
54
- self.ancestors.each do |clazz|
55
- if clazz.respond_to?(:behaviors)
56
- clazz.behaviors.each do |behavior|
57
- unless obj.behaves_as?(behavior)
58
- raise BehaviorError.new("undefined callback functions in #{self} (behavior '#{behavior}')")
59
- end
60
- end
61
- end
62
- end
63
- return obj
64
- end
65
- end
66
- end
67
-
68
- alias :behaviour :behavior
69
- alias :behaves_as :behavior
70
-
71
- module_function :behavior
72
- module_function :behaviour
73
- module_function :behaves_as
74
- end
75
-
76
- class Object
77
-
78
- # Does the object implement the given #behavior_info?
79
- #
80
- # @note Will return true if the object implements the
81
- # required methods. The object's class hierarchy does
82
- # not necessarily have to include a corresponding
83
- # #behavior call.
84
- #
85
- # @param name [Symbol] name of the #behavior_info to
86
- # verify behavior against.
87
- #
88
- # @return [Boolean] whether or not the required public
89
- # methods are implemented
90
- def behaves_as?(name)
91
-
92
- name = name.to_sym
93
- bi = $__behavior_info__[name]
94
- return false if bi.nil?
95
-
96
- validator = proc do |obj, method, arity|
97
- (obj.respond_to?(method) && arity == :any) || obj.method(method).arity == arity
98
- end
99
-
100
- if self.is_a?(Class) || self.is_a?(Module)
101
- bi = bi.select{|method, arity| method.to_s =~ /^self_/ }
102
- end
103
-
104
- bi.each do |method, arity|
105
- begin
106
- method = method.to_s
107
- obj = self
108
-
109
- if (self.is_a?(Class) || self.is_a?(Module)) && method =~ /^self_/
110
- method = method.gsub(/^self_/, '')
111
- elsif method =~ /^self_/
112
- method = method.gsub(/^self_/, '')
113
- obj = self.class
114
- end
115
-
116
- return false unless validator.call(obj, method, arity)
117
- rescue NameError
118
- return false
119
- end
120
- end
121
-
122
- return true
123
- end
124
- end
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
+ unless obj.behaves_as?(behavior)
66
+ raise BehaviorError.new("undefined callback functions in #{self} (behavior '#{behavior}')")
67
+ end
68
+ end
69
+ end
70
+ end
71
+ return obj
72
+ end
73
+ end
74
+ end
75
+
76
+ alias :behaviour :behavior
77
+ alias :behaves_as :behavior
78
+
79
+ module_function :behavior
80
+ module_function :behaviour
81
+ module_function :behaves_as
82
+ end
83
+
84
+ class Object
85
+
86
+ # Does the object implement the given #behavior_info?
87
+ #
88
+ # @note Will return true if the object implements the
89
+ # required methods. The object's class hierarchy does
90
+ # not necessarily have to include a corresponding
91
+ # #behavior call.
92
+ #
93
+ # @param name [Symbol] name of the #behavior_info to
94
+ # verify behavior against.
95
+ #
96
+ # @return [Boolean] whether or not the required public
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
+ method = method.to_s
115
+ obj = self
116
+
117
+ if (self.is_a?(Class) || self.is_a?(Module)) && method =~ /^self_/
118
+ method = method.gsub(/^self_/, '')
119
+ elsif method =~ /^self_/
120
+ method = method.gsub(/^self_/, '')
121
+ obj = self.class
122
+ end
123
+
124
+ return false unless validator.call(obj, method, arity)
125
+ rescue NameError
126
+ return false
127
+ end
128
+ end
129
+
130
+ return true
131
+ end
132
+ end