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.
@@ -1,2 +1,2 @@
1
- # Support the Scandinavian spelling
2
- require_relative 'behavior'
1
+ # Support the Scandinavian spelling
2
+ require_relative 'behavior'
@@ -1,133 +1,133 @@
1
- module PatternMatching
2
-
3
- UNBOUND = Class.new
4
- ALL = Class.new
5
-
6
- private
7
-
8
- GUARD_CLAUSE = Class.new do # :nodoc:
9
- def initialize(func, clazz, matcher) # :nodoc:
10
- @func = func
11
- @clazz = clazz
12
- @matcher = matcher
13
- end
14
- def when(&block) # :nodoc:
15
- unless block_given?
16
- raise ArgumentError.new("block missing for `when` guard on function `#{@func}` of class #{@clazz}")
17
- end
18
- @matcher[@matcher.length-1] = block
19
- return nil
20
- end
21
- end
22
-
23
- def self.__match_pattern__(args, pattern) # :nodoc:
24
- return unless (pattern.last == ALL && args.length >= pattern.length) \
25
- || (args.length == pattern.length)
26
- pattern.each_with_index do |p, i|
27
- break if p == ALL && i+1 == pattern.length
28
- arg = args[i]
29
- next if p.is_a?(Class) && arg.is_a?(p)
30
- if p.is_a?(Hash) && arg.is_a?(Hash) && ! p.empty?
31
- p.each do |key, value|
32
- return false unless arg.has_key?(key)
33
- next if value == UNBOUND
34
- return false unless arg[key] == value
35
- end
36
- next
37
- end
38
- return false unless p == UNBOUND || p == arg
39
- end
40
- return true
41
- end
42
-
43
- def self.__unbound_args__(match, args) # :nodoc:
44
- argv = []
45
- match.first.each_with_index do |p, i|
46
- if p == ALL && i == match.first.length-1
47
- argv << args[(i..args.length)].reduce([]){|memo, arg| memo << arg }
48
- elsif p.is_a?(Hash) && p.values.include?(UNBOUND)
49
- p.each do |key, value|
50
- argv << args[i][key] if value == UNBOUND
51
- end
52
- elsif p.is_a?(Hash) || p == UNBOUND || p.is_a?(Class)
53
- argv << args[i]
54
- end
55
- end
56
- return argv
57
- end
58
-
59
- def self.__pattern_match__(clazz, func, *args, &block) # :nodoc:
60
- args = args.first
61
-
62
- matchers = clazz.__function_pattern_matches__[func]
63
- return [:nodef, nil] if matchers.nil?
64
-
65
- match = matchers.detect do |matcher|
66
- if PatternMatching.__match_pattern__(args, matcher.first)
67
- if matcher.last.nil?
68
- true # no guard clause
69
- else
70
- self.instance_exec(*PatternMatching.__unbound_args__(matcher, args), &matcher.last)
71
- end
72
- end
73
- end
74
-
75
- return (match ? [:ok, match] : [:nomatch, nil])
76
- end
77
-
78
- protected
79
-
80
- def self.included(base)
81
-
82
- class << base
83
-
84
- public
85
-
86
- def _() # :nodoc:
87
- return UNBOUND
88
- end
89
-
90
- def defn(func, *args, &block)
91
- unless block_given?
92
- raise ArgumentError.new("block missing for definition of function `#{func}` on class #{self}")
93
- end
94
-
95
- pattern = __add_pattern_for__(func, *args, &block)
96
-
97
- unless self.instance_methods(false).include?(func)
98
-
99
- define_method(func) do |*args, &block|
100
- result, match = PatternMatching.__pattern_match__(self.method(func).owner, func, args, block)
101
- if result == :ok
102
- # if a match is found call the block
103
- argv = PatternMatching.__unbound_args__(match, args)
104
- return self.instance_exec(*argv, &match[1])
105
- else # if result == :nodef || result == :nomatch
106
- begin
107
- super(*args, &block)
108
- rescue NoMethodError, ArgumentError
109
- raise NoMethodError.new("no method `#{func}` matching #{args} found for class #{self.class}")
110
- end
111
- end
112
- end
113
- end
114
-
115
- return GUARD_CLAUSE.new(func, self, pattern)
116
- end
117
-
118
- public
119
-
120
- def __function_pattern_matches__ # :nodoc:
121
- @__function_pattern_matches__ ||= Hash.new
122
- end
123
-
124
- def __add_pattern_for__(func, *args, &block) # :nodoc:
125
- block = Proc.new{} unless block_given?
126
- matchers = self.__function_pattern_matches__
127
- matchers[func] = [] unless matchers.has_key?(func)
128
- matchers[func] << [args, block, nil]
129
- return matchers[func].last
130
- end
131
- end
132
- end
133
- end
1
+ module PatternMatching
2
+
3
+ UNBOUND = Class.new
4
+ ALL = Class.new
5
+
6
+ private
7
+
8
+ GUARD_CLAUSE = Class.new do # :nodoc:
9
+ def initialize(func, clazz, matcher) # :nodoc:
10
+ @func = func
11
+ @clazz = clazz
12
+ @matcher = matcher
13
+ end
14
+ def when(&block) # :nodoc:
15
+ unless block_given?
16
+ raise ArgumentError.new("block missing for `when` guard on function `#{@func}` of class #{@clazz}")
17
+ end
18
+ @matcher[@matcher.length-1] = block
19
+ return nil
20
+ end
21
+ end
22
+
23
+ def self.__match_pattern__(args, pattern) # :nodoc:
24
+ return unless (pattern.last == ALL && args.length >= pattern.length) \
25
+ || (args.length == pattern.length)
26
+ pattern.each_with_index do |p, i|
27
+ break if p == ALL && i+1 == pattern.length
28
+ arg = args[i]
29
+ next if p.is_a?(Class) && arg.is_a?(p)
30
+ if p.is_a?(Hash) && arg.is_a?(Hash) && ! p.empty?
31
+ p.each do |key, value|
32
+ return false unless arg.has_key?(key)
33
+ next if value == UNBOUND
34
+ return false unless arg[key] == value
35
+ end
36
+ next
37
+ end
38
+ return false unless p == UNBOUND || p == arg
39
+ end
40
+ return true
41
+ end
42
+
43
+ def self.__unbound_args__(match, args) # :nodoc:
44
+ argv = []
45
+ match.first.each_with_index do |p, i|
46
+ if p == ALL && i == match.first.length-1
47
+ argv << args[(i..args.length)].reduce([]){|memo, arg| memo << arg }
48
+ elsif p.is_a?(Hash) && p.values.include?(UNBOUND)
49
+ p.each do |key, value|
50
+ argv << args[i][key] if value == UNBOUND
51
+ end
52
+ elsif p.is_a?(Hash) || p == UNBOUND || p.is_a?(Class)
53
+ argv << args[i]
54
+ end
55
+ end
56
+ return argv
57
+ end
58
+
59
+ def self.__pattern_match__(clazz, func, *args, &block) # :nodoc:
60
+ args = args.first
61
+
62
+ matchers = clazz.__function_pattern_matches__[func]
63
+ return [:nodef, nil] if matchers.nil?
64
+
65
+ match = matchers.detect do |matcher|
66
+ if PatternMatching.__match_pattern__(args, matcher.first)
67
+ if matcher.last.nil?
68
+ true # no guard clause
69
+ else
70
+ self.instance_exec(*PatternMatching.__unbound_args__(matcher, args), &matcher.last)
71
+ end
72
+ end
73
+ end
74
+
75
+ return (match ? [:ok, match] : [:nomatch, nil])
76
+ end
77
+
78
+ protected
79
+
80
+ def self.included(base)
81
+
82
+ class << base
83
+
84
+ public
85
+
86
+ def _() # :nodoc:
87
+ return UNBOUND
88
+ end
89
+
90
+ def defn(func, *args, &block)
91
+ unless block_given?
92
+ raise ArgumentError.new("block missing for definition of function `#{func}` on class #{self}")
93
+ end
94
+
95
+ pattern = __add_pattern_for__(func, *args, &block)
96
+
97
+ unless self.instance_methods(false).include?(func)
98
+
99
+ define_method(func) do |*args, &block|
100
+ result, match = PatternMatching.__pattern_match__(self.method(func).owner, func, args, block)
101
+ if result == :ok
102
+ # if a match is found call the block
103
+ argv = PatternMatching.__unbound_args__(match, args)
104
+ return self.instance_exec(*argv, &match[1])
105
+ else # if result == :nodef || result == :nomatch
106
+ begin
107
+ super(*args, &block)
108
+ rescue NoMethodError, ArgumentError
109
+ raise NoMethodError.new("no method `#{func}` matching #{args} found for class #{self.class}")
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ return GUARD_CLAUSE.new(func, self, pattern)
116
+ end
117
+
118
+ public
119
+
120
+ def __function_pattern_matches__ # :nodoc:
121
+ @__function_pattern_matches__ ||= Hash.new
122
+ end
123
+
124
+ def __add_pattern_for__(func, *args, &block) # :nodoc:
125
+ block = Proc.new{} unless block_given?
126
+ matchers = self.__function_pattern_matches__
127
+ matchers[func] = [] unless matchers.has_key?(func)
128
+ matchers[func] << [args, block, nil]
129
+ return matchers[func].last
130
+ end
131
+ end
132
+ end
133
+ end
@@ -1,192 +1,192 @@
1
- require 'pp'
2
- require 'stringio'
3
- require 'erb'
4
- require 'rbconfig'
5
-
6
- Infinity = 1/0.0 unless defined?(Infinity)
7
- NaN = 0/0.0 unless defined?(NaN)
8
-
9
- module Kernel
10
-
11
- # Compute the difference (delta) between two values.
12
- #
13
- # When a block is given the block will be applied to both arguments.
14
- # Using a block in this way allows computation against a specific field
15
- # in a data set of hashes or objects.
16
- #
17
- # @yield iterates over each element in the data set
18
- # @yieldparam item each element in the data set
19
- #
20
- # @param [Object] v1 the first value
21
- # @param [Object] v2 the second value
22
- #
23
- # @return [Float] positive value representing the difference
24
- # between the two parameters
25
- def delta(v1, v2)
26
- if block_given?
27
- v1 = yield(v1)
28
- v2 = yield(v2)
29
- end
30
- return (v1 - v2).abs
31
- end
32
- module_function :delta
33
-
34
- # Perform an operation numerous times, passing the value of the
35
- # previous iteration, and collecting the results into an array.
36
- #
37
- # @yield iterates over each element in the data set
38
- # @yieldparam previous the initial value (or nil) for the first
39
- # iteration then the value of the previous iteration for all
40
- # subsequent iterations
41
- #
42
- # @param [Integer] count the number of times to perform the operation
43
- # @param [Object] initial the initial value to pass to the first iteration
44
- #
45
- # @return [Array] the results of the iterations collected into an array
46
- def repeatedly(count, initial = nil)
47
- return [] if (count = count.to_i) == 0
48
- return count.times.collect{ nil } unless block_given?
49
- return count.times.collect do
50
- initial = yield(initial)
51
- end
52
- end
53
- module_function :repeatedly
54
-
55
- # Try an operation. If it fails (raises an exception), wait a second
56
- # and try again. Try no more than the given number of times.
57
- #
58
- # @yield Tries the given block operation
59
- #
60
- # @param [Integer] tries The maximum number of times to attempt the operation.
61
- # @param [Array] args Optional block arguments
62
- #
63
- # @return [Boolean] true if the operation succeeds on any attempt,
64
- # false if none of the attempts are successful
65
- def retro(tries, *args)
66
- tries = tries.to_i
67
- return false if tries == 0 || ! block_given?
68
- yield(*args)
69
- return true
70
- rescue Exception
71
- sleep(1)
72
- if (tries = tries - 1) > 0
73
- retry
74
- else
75
- return false
76
- end
77
- end
78
- module_function :retro
79
-
80
- # Sandbox the given operation at a high $SAFE level.
81
- #
82
- # @param args [Array] zero or more arguments to pass to the block
83
- #
84
- # @return [Object] the result of the block operation
85
- def safe(*args)
86
- raise ArgumentError.new('no block given') unless block_given?
87
- if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
88
- result = nil
89
- t = Thread.new do
90
- $SAFE = 3
91
- result = yield(*args)
92
- end
93
- t.join
94
- return result
95
- else
96
- return yield(*args)
97
- end
98
- end
99
- module_function :safe
100
-
101
- # Open a file, read it, close the file, and return its contents.
102
- #
103
- # @param file [String] path to and name of the file to open
104
- #
105
- # @return [String] file contents
106
- #
107
- # @see slurpee
108
- def slurp(file)
109
- File.open(file, 'rb') {|f| f.read }
110
- end
111
- module_function :slurp
112
-
113
- # Open a file, read it, close the file, run the contents through the
114
- # ERB parser, and return updated contents.
115
- #
116
- # @param file [String] path to and name of the file to open
117
- # @param safe_level [Integer] when not nil, ERB will $SAFE set to this
118
- #
119
- # @return [String] file contents
120
- #
121
- # @see slurpee
122
- def slurpee(file, safe_level = nil)
123
- ERB.new(slurp(file), safe_level).result
124
- end
125
- module_function :slurpee
126
-
127
- # Run the given block and time how long it takes in seconds. All arguments
128
- # will be passed to the block. The function will return two values. The
129
- # first value will be the duration of the timer in seconds. The second
130
- # return value will be the result of the block.
131
- #
132
- # @param args [Array] zero or more arguments to pass to the block
133
- #
134
- # @return [Integer, Object] the duration of the operation in seconds and
135
- # the result of the block operation
136
- def timer(*args)
137
- return 0,nil unless block_given?
138
- t1 = Time.now
139
- result = yield(*args)
140
- t2 = Time.now
141
- return (t2 - t1), result
142
- end
143
- module_function :timer
144
-
145
- #############################################################################
146
-
147
- # @private
148
- # @see http://cirw.in/blog/find-references
149
- def object_counts # :nodoc:
150
- counts = Hash.new{ 0 }
151
- ObjectSpace.each_object do |obj|
152
- counts[obj.class] += 1
153
- end
154
- return counts
155
- end
156
- module_function :object_counts
157
-
158
- # @private
159
- # @see http://rhaseventh.blogspot.com/2008/07/ruby-and-rails-how-to-get-pp-pretty.html
160
- def pp_s(*objs) # :nodoc:
161
- s = StringIO.new
162
- objs.each {|obj|
163
- PP.pp(obj, s)
164
- }
165
- s.rewind
166
- s.read
167
- end
168
- module_function :pp_s
169
-
170
- # @private
171
- def repl? # :nodoc:
172
- return ($0 == 'irb' || $0 == 'pry' || $0 == 'script/rails' || !!($0 =~ /bin\/bundle$/))
173
- end
174
- module_function :repl?
175
-
176
- # @private
177
- def strftimer(seconds) # :nodoc:
178
- Time.at(seconds).gmtime.strftime('%R:%S.%L')
179
- end
180
- module_function :strftimer
181
-
182
- # @private
183
- def timestamp # :nodoc:
184
- return Time.now.getutc.to_i
185
- end
186
-
187
- def write_object_counts(name = 'ruby')
188
- file = "#{name}_#{Time.now.to_i}.txt"
189
- File.open(file, 'w') {|f| f.write(pp_s(object_counts)) }
190
- end
191
- module_function :write_object_counts
192
- end
1
+ require 'pp'
2
+ require 'stringio'
3
+ require 'erb'
4
+ require 'rbconfig'
5
+
6
+ Infinity = 1/0.0 unless defined?(Infinity)
7
+ NaN = 0/0.0 unless defined?(NaN)
8
+
9
+ module Kernel
10
+
11
+ # Compute the difference (delta) between two values.
12
+ #
13
+ # When a block is given the block will be applied to both arguments.
14
+ # Using a block in this way allows computation against a specific field
15
+ # in a data set of hashes or objects.
16
+ #
17
+ # @yield iterates over each element in the data set
18
+ # @yieldparam item each element in the data set
19
+ #
20
+ # @param [Object] v1 the first value
21
+ # @param [Object] v2 the second value
22
+ #
23
+ # @return [Float] positive value representing the difference
24
+ # between the two parameters
25
+ def delta(v1, v2)
26
+ if block_given?
27
+ v1 = yield(v1)
28
+ v2 = yield(v2)
29
+ end
30
+ return (v1 - v2).abs
31
+ end
32
+ module_function :delta
33
+
34
+ # Perform an operation numerous times, passing the value of the
35
+ # previous iteration, and collecting the results into an array.
36
+ #
37
+ # @yield iterates over each element in the data set
38
+ # @yieldparam previous the initial value (or nil) for the first
39
+ # iteration then the value of the previous iteration for all
40
+ # subsequent iterations
41
+ #
42
+ # @param [Integer] count the number of times to perform the operation
43
+ # @param [Object] initial the initial value to pass to the first iteration
44
+ #
45
+ # @return [Array] the results of the iterations collected into an array
46
+ def repeatedly(count, initial = nil)
47
+ return [] if (count = count.to_i) == 0
48
+ return count.times.collect{ nil } unless block_given?
49
+ return count.times.collect do
50
+ initial = yield(initial)
51
+ end
52
+ end
53
+ module_function :repeatedly
54
+
55
+ # Try an operation. If it fails (raises an exception), wait a second
56
+ # and try again. Try no more than the given number of times.
57
+ #
58
+ # @yield Tries the given block operation
59
+ #
60
+ # @param [Integer] tries The maximum number of times to attempt the operation.
61
+ # @param [Array] args Optional block arguments
62
+ #
63
+ # @return [Boolean] true if the operation succeeds on any attempt,
64
+ # false if none of the attempts are successful
65
+ def retro(tries, *args)
66
+ tries = tries.to_i
67
+ return false if tries == 0 || ! block_given?
68
+ yield(*args)
69
+ return true
70
+ rescue Exception
71
+ sleep(1)
72
+ if (tries = tries - 1) > 0
73
+ retry
74
+ else
75
+ return false
76
+ end
77
+ end
78
+ module_function :retro
79
+
80
+ # Sandbox the given operation at a high $SAFE level.
81
+ #
82
+ # @param args [Array] zero or more arguments to pass to the block
83
+ #
84
+ # @return [Object] the result of the block operation
85
+ def safe(*args)
86
+ raise ArgumentError.new('no block given') unless block_given?
87
+ if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
88
+ result = nil
89
+ t = Thread.new do
90
+ $SAFE = 3
91
+ result = yield(*args)
92
+ end
93
+ t.join
94
+ return result
95
+ else
96
+ return yield(*args)
97
+ end
98
+ end
99
+ module_function :safe
100
+
101
+ # Open a file, read it, close the file, and return its contents.
102
+ #
103
+ # @param file [String] path to and name of the file to open
104
+ #
105
+ # @return [String] file contents
106
+ #
107
+ # @see slurpee
108
+ def slurp(file)
109
+ File.open(file, 'rb') {|f| f.read }
110
+ end
111
+ module_function :slurp
112
+
113
+ # Open a file, read it, close the file, run the contents through the
114
+ # ERB parser, and return updated contents.
115
+ #
116
+ # @param file [String] path to and name of the file to open
117
+ # @param safe_level [Integer] when not nil, ERB will $SAFE set to this
118
+ #
119
+ # @return [String] file contents
120
+ #
121
+ # @see slurpee
122
+ def slurpee(file, safe_level = nil)
123
+ ERB.new(slurp(file), safe_level).result
124
+ end
125
+ module_function :slurpee
126
+
127
+ # Run the given block and time how long it takes in seconds. All arguments
128
+ # will be passed to the block. The function will return two values. The
129
+ # first value will be the duration of the timer in seconds. The second
130
+ # return value will be the result of the block.
131
+ #
132
+ # @param args [Array] zero or more arguments to pass to the block
133
+ #
134
+ # @return [Integer, Object] the duration of the operation in seconds and
135
+ # the result of the block operation
136
+ def timer(*args)
137
+ return 0,nil unless block_given?
138
+ t1 = Time.now
139
+ result = yield(*args)
140
+ t2 = Time.now
141
+ return (t2 - t1), result
142
+ end
143
+ module_function :timer
144
+
145
+ #############################################################################
146
+
147
+ # @private
148
+ # @see http://cirw.in/blog/find-references
149
+ def object_counts # :nodoc:
150
+ counts = Hash.new{ 0 }
151
+ ObjectSpace.each_object do |obj|
152
+ counts[obj.class] += 1
153
+ end
154
+ return counts
155
+ end
156
+ module_function :object_counts
157
+
158
+ # @private
159
+ # @see http://rhaseventh.blogspot.com/2008/07/ruby-and-rails-how-to-get-pp-pretty.html
160
+ def pp_s(*objs) # :nodoc:
161
+ s = StringIO.new
162
+ objs.each {|obj|
163
+ PP.pp(obj, s)
164
+ }
165
+ s.rewind
166
+ s.read
167
+ end
168
+ module_function :pp_s
169
+
170
+ # @private
171
+ def repl? # :nodoc:
172
+ return ($0 == 'irb' || $0 == 'pry' || $0 == 'script/rails' || !!($0 =~ /bin\/bundle$/))
173
+ end
174
+ module_function :repl?
175
+
176
+ # @private
177
+ def strftimer(seconds) # :nodoc:
178
+ Time.at(seconds).gmtime.strftime('%R:%S.%L')
179
+ end
180
+ module_function :strftimer
181
+
182
+ # @private
183
+ def timestamp # :nodoc:
184
+ return Time.now.getutc.to_i
185
+ end
186
+
187
+ def write_object_counts(name = 'ruby')
188
+ file = "#{name}_#{Time.now.to_i}.txt"
189
+ File.open(file, 'w') {|f| f.write(pp_s(object_counts)) }
190
+ end
191
+ module_function :write_object_counts
192
+ end