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
data/lib/functional/behaviour.rb
CHANGED
@@ -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
|
data/lib/functional/utilities.rb
CHANGED
@@ -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
|