functional-ruby 0.7.7 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +92 -152
- data/doc/memo.txt +192 -0
- data/doc/pattern_matching.txt +485 -0
- data/doc/protocol.txt +221 -0
- data/doc/record.txt +144 -0
- data/doc/thread_safety.txt +8 -0
- data/lib/functional.rb +48 -18
- data/lib/functional/abstract_struct.rb +161 -0
- data/lib/functional/delay.rb +117 -0
- data/lib/functional/either.rb +222 -0
- data/lib/functional/memo.rb +93 -0
- data/lib/functional/method_signature.rb +72 -0
- data/lib/functional/option.rb +209 -0
- data/lib/functional/pattern_matching.rb +117 -100
- data/lib/functional/protocol.rb +157 -0
- data/lib/functional/protocol_info.rb +193 -0
- data/lib/functional/record.rb +155 -0
- data/lib/functional/type_check.rb +112 -0
- data/lib/functional/union.rb +152 -0
- data/lib/functional/version.rb +3 -1
- data/spec/functional/abstract_struct_shared.rb +154 -0
- data/spec/functional/complex_pattern_matching_spec.rb +205 -0
- data/spec/functional/configuration_spec.rb +17 -0
- data/spec/functional/delay_spec.rb +147 -0
- data/spec/functional/either_spec.rb +237 -0
- data/spec/functional/memo_spec.rb +207 -0
- data/spec/functional/option_spec.rb +292 -0
- data/spec/functional/pattern_matching_spec.rb +279 -276
- data/spec/functional/protocol_info_spec.rb +444 -0
- data/spec/functional/protocol_spec.rb +274 -0
- data/spec/functional/record_spec.rb +175 -0
- data/spec/functional/type_check_spec.rb +103 -0
- data/spec/functional/union_spec.rb +110 -0
- data/spec/spec_helper.rb +6 -4
- metadata +55 -45
- data/lib/functional/behavior.rb +0 -138
- data/lib/functional/behaviour.rb +0 -2
- data/lib/functional/catalog.rb +0 -487
- data/lib/functional/collection.rb +0 -403
- data/lib/functional/inflect.rb +0 -127
- data/lib/functional/platform.rb +0 -120
- data/lib/functional/search.rb +0 -132
- data/lib/functional/sort.rb +0 -41
- data/lib/functional/utilities.rb +0 -189
- data/md/behavior.md +0 -188
- data/md/catalog.md +0 -32
- data/md/collection.md +0 -32
- data/md/inflect.md +0 -32
- data/md/pattern_matching.md +0 -512
- data/md/platform.md +0 -32
- data/md/search.md +0 -32
- data/md/sort.md +0 -32
- data/md/utilities.md +0 -55
- data/spec/functional/behavior_spec.rb +0 -528
- data/spec/functional/catalog_spec.rb +0 -1206
- data/spec/functional/collection_spec.rb +0 -752
- data/spec/functional/inflect_spec.rb +0 -85
- data/spec/functional/integration_spec.rb +0 -205
- data/spec/functional/platform_spec.rb +0 -501
- data/spec/functional/search_spec.rb +0 -187
- data/spec/functional/sort_spec.rb +0 -61
- data/spec/functional/utilities_spec.rb +0 -277
data/lib/functional/platform.rb
DELETED
@@ -1,120 +0,0 @@
|
|
1
|
-
require 'rbconfig'
|
2
|
-
|
3
|
-
module Functional
|
4
|
-
|
5
|
-
class Platform
|
6
|
-
|
7
|
-
attr_reader :ruby_version
|
8
|
-
attr_reader :host_os
|
9
|
-
attr_reader :ruby_name
|
10
|
-
|
11
|
-
def rubies
|
12
|
-
@rubies ||= [
|
13
|
-
:ruby, # C Ruby (MRI) or Rubinius, but NOT Windows
|
14
|
-
:ruby_18, # ruby AND version 1.8
|
15
|
-
:ruby_19, # ruby AND version 1.9
|
16
|
-
:mri, # Same as ruby, but not Rubinius
|
17
|
-
:mri_18, # mri AND version 1.8
|
18
|
-
:mri_19, # mri AND version 1.9
|
19
|
-
:mri_20, # mri AND version 20
|
20
|
-
:rbx, # Same as ruby, but only Rubinius (not MRI)
|
21
|
-
:jruby, # JRuby
|
22
|
-
:mswin, # Windows
|
23
|
-
:mingw, # Windows 'mingw32' platform (aka RubyInstaller)
|
24
|
-
:mingw_18, # mingw AND version 1.8
|
25
|
-
:mingw_19, # mingw AND version 1.9
|
26
|
-
:mingw_20, # mingw AND version 2.0
|
27
|
-
].freeze
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(*args)
|
31
|
-
unless args.size == 0 || args.size == 3
|
32
|
-
raise ArgumentError.new("wrong number of arguments (#{args.size} for 0 or 3)")
|
33
|
-
end
|
34
|
-
|
35
|
-
@ruby_version = args[0] || RUBY_VERSION || RbConfig::CONFIG['ruby_version']
|
36
|
-
@host_os = args[1] || RbConfig::CONFIG['host_os']
|
37
|
-
@ruby_name = args[2] || RbConfig::CONFIG['ruby_install_name']
|
38
|
-
end
|
39
|
-
|
40
|
-
def windows?
|
41
|
-
truthy(@host_os =~ /win32/i) || truthy(@host_os =~ /mingw32/i)
|
42
|
-
end
|
43
|
-
|
44
|
-
def linux?
|
45
|
-
truthy(@host_os =~ /linux/i)
|
46
|
-
end
|
47
|
-
|
48
|
-
def osx?
|
49
|
-
truthy(@host_os =~ /darwin/i)
|
50
|
-
end
|
51
|
-
|
52
|
-
def ruby?
|
53
|
-
mri? || rbx?
|
54
|
-
end
|
55
|
-
|
56
|
-
def ruby_18?
|
57
|
-
ruby? && truthy(@ruby_version =~ /^1\.8/)
|
58
|
-
end
|
59
|
-
|
60
|
-
def ruby_19?
|
61
|
-
ruby? && truthy(@ruby_version =~ /^1\.9/)
|
62
|
-
end
|
63
|
-
|
64
|
-
def ruby_20?
|
65
|
-
ruby? && truthy(@ruby_version =~ /^2\.0/)
|
66
|
-
end
|
67
|
-
|
68
|
-
def mri?
|
69
|
-
truthy(@ruby_name =~ /^ruby$/i) && !windows?
|
70
|
-
end
|
71
|
-
|
72
|
-
def mri_18?
|
73
|
-
mri? && truthy(@ruby_version =~ /^1\.8/)
|
74
|
-
end
|
75
|
-
|
76
|
-
def mri_19?
|
77
|
-
mri? && truthy(@ruby_version =~ /^1\.9/)
|
78
|
-
end
|
79
|
-
|
80
|
-
def mri_20?
|
81
|
-
mri? && truthy(@ruby_version =~ /^2\.0/)
|
82
|
-
end
|
83
|
-
|
84
|
-
def rbx?
|
85
|
-
truthy(@ruby_name =~ /^rbx$/i)
|
86
|
-
end
|
87
|
-
|
88
|
-
def jruby?
|
89
|
-
truthy(@ruby_name =~ /^jruby$/i)
|
90
|
-
end
|
91
|
-
|
92
|
-
def mswin?
|
93
|
-
truthy(@host_os =~ /win32/i)
|
94
|
-
end
|
95
|
-
|
96
|
-
def mingw?
|
97
|
-
truthy(@host_os =~ /mingw32/i)
|
98
|
-
end
|
99
|
-
|
100
|
-
def mingw_18?
|
101
|
-
mingw? && truthy(@ruby_version =~ /^1\.8/)
|
102
|
-
end
|
103
|
-
|
104
|
-
def mingw_19?
|
105
|
-
mingw? && truthy(@ruby_version =~ /^1\.9/)
|
106
|
-
end
|
107
|
-
|
108
|
-
def mingw_20?
|
109
|
-
mingw? && truthy(@ruby_version =~ /^2\.0/)
|
110
|
-
end
|
111
|
-
|
112
|
-
private
|
113
|
-
|
114
|
-
def truthy(value)
|
115
|
-
return value == 0
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
PLATFORM = Functional::Platform.new
|
120
|
-
end
|
data/lib/functional/search.rb
DELETED
@@ -1,132 +0,0 @@
|
|
1
|
-
module Functional
|
2
|
-
|
3
|
-
module Search
|
4
|
-
extend self
|
5
|
-
|
6
|
-
# Conduct a linear search against an unsorted collection and
|
7
|
-
# return the index where the item was found. Returns nil if
|
8
|
-
# the item is not found.
|
9
|
-
#
|
10
|
-
# The default behavior is to search the entire collections. The
|
11
|
-
# options hash can be used to provide optional low and high indexes
|
12
|
-
# (:imin and :imax). If either :imin or :imax is out of range the
|
13
|
-
# natural collection boundary will be used.
|
14
|
-
#
|
15
|
-
# When a block is given the block will be applied to both arguments.
|
16
|
-
# Using a block in this way allows computation against a specific field
|
17
|
-
# in a data set of hashes or objects.
|
18
|
-
#
|
19
|
-
# @yield iterates over each element in the data set
|
20
|
-
# @yieldparam item each element in the data set
|
21
|
-
#
|
22
|
-
# @param [Enumerable] data the data set to search
|
23
|
-
# @param [Hash] opts search options
|
24
|
-
# @param [Block] block optional block for per-item processing
|
25
|
-
#
|
26
|
-
# @option opts [Integer] :imin minimum index to search
|
27
|
-
# @option opts [Integer] :imax maximum index to search
|
28
|
-
#
|
29
|
-
# @return [Array] the index where the item is found or nil when the
|
30
|
-
# collection is empty or nil
|
31
|
-
def linear_search(data, key, opts={}, &block)
|
32
|
-
|
33
|
-
imin, imax = check_search_options(data, key, opts, &block)
|
34
|
-
return nil if imin.nil? || imax.nil?
|
35
|
-
return imin if imin == imax
|
36
|
-
|
37
|
-
index = nil
|
38
|
-
(imin..imax).each do |i|
|
39
|
-
if (block_given? && yield(data[i]) == key) || data[i] == key
|
40
|
-
index = i
|
41
|
-
break
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
return index
|
46
|
-
end
|
47
|
-
|
48
|
-
# Conduct a binary search against the sorted collection and return
|
49
|
-
# a pair of indexes indicating the result of the search. The
|
50
|
-
# indexes will be returned as a two-element array.
|
51
|
-
#
|
52
|
-
# The default behavior is to search the entire collections. The
|
53
|
-
# options hash can be used to provide optional low and high indexes
|
54
|
-
# (:imin and :imax). If either :imin or :imax is out of range the
|
55
|
-
# natural collection boundary will be used.
|
56
|
-
#
|
57
|
-
# When a block is given the block will be applied to both arguments.
|
58
|
-
# Using a block in this way allows computation against a specific field
|
59
|
-
# in a data set of hashes or objects.
|
60
|
-
#
|
61
|
-
# When the key is found both returned indexes will be the index of
|
62
|
-
# the item. When the key is not found but the value is within the
|
63
|
-
# range of value in the data set the returned indexes will be
|
64
|
-
# immediately above and below where the key would reside. When
|
65
|
-
# the key is below the lowest value in the search range the result
|
66
|
-
# will be nil and the lowest index. When the key is higher than the
|
67
|
-
# highest value in the search range the result will be the highest
|
68
|
-
# index and nil.
|
69
|
-
#
|
70
|
-
# @yield iterates over each element in the data set
|
71
|
-
# @yieldparam item each element in the data set
|
72
|
-
#
|
73
|
-
# @param [Enumerable] data the data set to search
|
74
|
-
# @param [Hash] opts search options
|
75
|
-
# @param [Block] block optional block for per-item processing
|
76
|
-
#
|
77
|
-
# @option opts [Integer] :imin minimum index to search
|
78
|
-
# @option opts [Integer] :imax maximum index to search
|
79
|
-
#
|
80
|
-
# @return [Array] pair of indexes (see above) or nil when the collection
|
81
|
-
# is empty or nil
|
82
|
-
def binary_search(data, key, opts={}, &block)
|
83
|
-
|
84
|
-
imin, imax = check_search_options(data, key, opts, &block)
|
85
|
-
return nil if imin.nil? && imax.nil?
|
86
|
-
return [imin, imax] if imin == imax || imin.nil? || imax.nil?
|
87
|
-
|
88
|
-
while (imax >= imin)
|
89
|
-
imid = (imin + imax) / 2
|
90
|
-
current = data[imid]
|
91
|
-
current = yield(current) if block_given?
|
92
|
-
if current < key
|
93
|
-
imin = imid + 1
|
94
|
-
elsif current > key
|
95
|
-
imax = imid - 1
|
96
|
-
else
|
97
|
-
imin = imax = imid
|
98
|
-
break
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
return imax, imin
|
103
|
-
end
|
104
|
-
|
105
|
-
alias_method :bsearch, :binary_search
|
106
|
-
alias_method :half_interval_search, :binary_search
|
107
|
-
|
108
|
-
private
|
109
|
-
|
110
|
-
# :nodoc:
|
111
|
-
# @private
|
112
|
-
def check_search_options(data, key, opts={})
|
113
|
-
return [nil, nil] if data.nil? || data.empty?
|
114
|
-
|
115
|
-
imin = [opts[:imin].to_i, 0].max
|
116
|
-
imax = opts[:imax].nil? ? data.size-1 : [opts[:imax], data.size-1].min
|
117
|
-
return [nil, nil] if imin > imax
|
118
|
-
|
119
|
-
if block_given?
|
120
|
-
min, max = yield(data[imin]), yield(data[imax])
|
121
|
-
else
|
122
|
-
min, max = data[imin], data[imax]
|
123
|
-
end
|
124
|
-
return [nil, imin] if key < min
|
125
|
-
return [imin, imin] if key == min
|
126
|
-
return [imax, nil] if key > max
|
127
|
-
return [imax, imax] if key == max
|
128
|
-
|
129
|
-
return [imin, imax]
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
data/lib/functional/sort.rb
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
module Functional
|
2
|
-
|
3
|
-
module Sort
|
4
|
-
extend self
|
5
|
-
|
6
|
-
# Sorts the collection using the insertion sort algorithm.
|
7
|
-
#
|
8
|
-
# When a block is given the block will be applied to both arguments.
|
9
|
-
# Using a block in this way allows computation against a specific field
|
10
|
-
# in a data set of hashes or objects.
|
11
|
-
#
|
12
|
-
# @yield iterates over each element in the data set
|
13
|
-
# @yieldparam item each element in the data set
|
14
|
-
#
|
15
|
-
# @param [Enumerable] data the data set to search
|
16
|
-
# @param [Hash] opts search options
|
17
|
-
#
|
18
|
-
# @return [Array] the sorted collection
|
19
|
-
def insertion_sort!(data, opts={})
|
20
|
-
return data if data.nil? || data.size <= 1
|
21
|
-
|
22
|
-
(1..(data.size-1)).each do |j|
|
23
|
-
|
24
|
-
key = block_given? ? yield(data[j]) : data[j]
|
25
|
-
value = data[j]
|
26
|
-
i = j - 1
|
27
|
-
current = block_given? ? yield(data[i]) : data[i]
|
28
|
-
|
29
|
-
while i >= 0 && current > key
|
30
|
-
data[i+1] = data[i]
|
31
|
-
i = i - 1
|
32
|
-
current = block_given? ? yield(data[i]) : data[i]
|
33
|
-
end
|
34
|
-
|
35
|
-
data[i+1] = value
|
36
|
-
end
|
37
|
-
|
38
|
-
return data
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
data/lib/functional/utilities.rb
DELETED
@@ -1,189 +0,0 @@
|
|
1
|
-
require 'pp'
|
2
|
-
require 'stringio'
|
3
|
-
require 'erb'
|
4
|
-
require 'rbconfig'
|
5
|
-
|
6
|
-
module Kernel
|
7
|
-
|
8
|
-
# Compute the difference (delta) between two values.
|
9
|
-
#
|
10
|
-
# When a block is given the block will be applied to both arguments.
|
11
|
-
# Using a block in this way allows computation against a specific field
|
12
|
-
# in a data set of hashes or objects.
|
13
|
-
#
|
14
|
-
# @yield iterates over each element in the data set
|
15
|
-
# @yieldparam item each element in the data set
|
16
|
-
#
|
17
|
-
# @param [Object] v1 the first value
|
18
|
-
# @param [Object] v2 the second value
|
19
|
-
#
|
20
|
-
# @return [Float] positive value representing the difference
|
21
|
-
# between the two parameters
|
22
|
-
def delta(v1, v2)
|
23
|
-
if block_given?
|
24
|
-
v1 = yield(v1)
|
25
|
-
v2 = yield(v2)
|
26
|
-
end
|
27
|
-
return (v1 - v2).abs
|
28
|
-
end
|
29
|
-
module_function :delta
|
30
|
-
|
31
|
-
# Perform an operation numerous times, passing the value of the
|
32
|
-
# previous iteration, and collecting the results into an array.
|
33
|
-
#
|
34
|
-
# @yield iterates over each element in the data set
|
35
|
-
# @yieldparam previous the initial value (or nil) for the first
|
36
|
-
# iteration then the value of the previous iteration for all
|
37
|
-
# subsequent iterations
|
38
|
-
#
|
39
|
-
# @param [Integer] count the number of times to perform the operation
|
40
|
-
# @param [Object] initial the initial value to pass to the first iteration
|
41
|
-
#
|
42
|
-
# @return [Array] the results of the iterations collected into an array
|
43
|
-
def repeatedly(count, initial = nil)
|
44
|
-
return [] if (count = count.to_i) == 0
|
45
|
-
return count.times.collect{ nil } unless block_given?
|
46
|
-
return count.times.collect do
|
47
|
-
initial = yield(initial)
|
48
|
-
end
|
49
|
-
end
|
50
|
-
module_function :repeatedly
|
51
|
-
|
52
|
-
# Try an operation. If it fails (raises an exception), wait a second
|
53
|
-
# and try again. Try no more than the given number of times.
|
54
|
-
#
|
55
|
-
# @yield Tries the given block operation
|
56
|
-
#
|
57
|
-
# @param [Integer] tries The maximum number of times to attempt the operation.
|
58
|
-
# @param [Array] args Optional block arguments
|
59
|
-
#
|
60
|
-
# @return [Boolean] true if the operation succeeds on any attempt,
|
61
|
-
# false if none of the attempts are successful
|
62
|
-
def retro(tries, *args)
|
63
|
-
tries = tries.to_i
|
64
|
-
return false if tries == 0 || ! block_given?
|
65
|
-
yield(*args)
|
66
|
-
return true
|
67
|
-
rescue Exception
|
68
|
-
sleep(1)
|
69
|
-
if (tries = tries - 1) > 0
|
70
|
-
retry
|
71
|
-
else
|
72
|
-
return false
|
73
|
-
end
|
74
|
-
end
|
75
|
-
module_function :retro
|
76
|
-
|
77
|
-
# Sandbox the given operation at a high $SAFE level.
|
78
|
-
#
|
79
|
-
# @param args [Array] zero or more arguments to pass to the block
|
80
|
-
#
|
81
|
-
# @return [Object] the result of the block operation
|
82
|
-
def safe(*args)
|
83
|
-
raise ArgumentError.new('no block given') unless block_given?
|
84
|
-
if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
|
85
|
-
result = nil
|
86
|
-
t = Thread.new do
|
87
|
-
$SAFE = 3
|
88
|
-
result = yield(*args)
|
89
|
-
end
|
90
|
-
t.join
|
91
|
-
return result
|
92
|
-
else
|
93
|
-
return yield(*args)
|
94
|
-
end
|
95
|
-
end
|
96
|
-
module_function :safe
|
97
|
-
|
98
|
-
# Open a file, read it, close the file, and return its contents.
|
99
|
-
#
|
100
|
-
# @param file [String] path to and name of the file to open
|
101
|
-
#
|
102
|
-
# @return [String] file contents
|
103
|
-
#
|
104
|
-
# @see slurpee
|
105
|
-
def slurp(file)
|
106
|
-
File.open(file, 'rb') {|f| f.read }
|
107
|
-
end
|
108
|
-
module_function :slurp
|
109
|
-
|
110
|
-
# Open a file, read it, close the file, run the contents through the
|
111
|
-
# ERB parser, and return updated contents.
|
112
|
-
#
|
113
|
-
# @param file [String] path to and name of the file to open
|
114
|
-
# @param safe_level [Integer] when not nil, ERB will $SAFE set to this
|
115
|
-
#
|
116
|
-
# @return [String] file contents
|
117
|
-
#
|
118
|
-
# @see slurpee
|
119
|
-
def slurpee(file, safe_level = nil)
|
120
|
-
ERB.new(slurp(file), safe_level).result
|
121
|
-
end
|
122
|
-
module_function :slurpee
|
123
|
-
|
124
|
-
# Run the given block and time how long it takes in seconds. All arguments
|
125
|
-
# will be passed to the block. The function will return two values. The
|
126
|
-
# first value will be the duration of the timer in seconds. The second
|
127
|
-
# return value will be the result of the block.
|
128
|
-
#
|
129
|
-
# @param args [Array] zero or more arguments to pass to the block
|
130
|
-
#
|
131
|
-
# @return [Integer, Object] the duration of the operation in seconds and
|
132
|
-
# the result of the block operation
|
133
|
-
def timer(*args)
|
134
|
-
return 0,nil unless block_given?
|
135
|
-
t1 = Time.now
|
136
|
-
result = yield(*args)
|
137
|
-
t2 = Time.now
|
138
|
-
return (t2 - t1), result
|
139
|
-
end
|
140
|
-
module_function :timer
|
141
|
-
|
142
|
-
#############################################################################
|
143
|
-
|
144
|
-
# @private
|
145
|
-
# @see http://cirw.in/blog/find-references
|
146
|
-
def object_counts # :nodoc:
|
147
|
-
counts = Hash.new{ 0 }
|
148
|
-
ObjectSpace.each_object do |obj|
|
149
|
-
counts[obj.class] += 1
|
150
|
-
end
|
151
|
-
return counts
|
152
|
-
end
|
153
|
-
module_function :object_counts
|
154
|
-
|
155
|
-
# @private
|
156
|
-
# @see http://rhaseventh.blogspot.com/2008/07/ruby-and-rails-how-to-get-pp-pretty.html
|
157
|
-
def pp_s(*objs) # :nodoc:
|
158
|
-
s = StringIO.new
|
159
|
-
objs.each {|obj|
|
160
|
-
PP.pp(obj, s)
|
161
|
-
}
|
162
|
-
s.rewind
|
163
|
-
s.read
|
164
|
-
end
|
165
|
-
module_function :pp_s
|
166
|
-
|
167
|
-
# @private
|
168
|
-
def repl? # :nodoc:
|
169
|
-
return ($0 == 'irb' || $0 == 'pry' || $0 == 'script/rails' || !!($0 =~ /bin\/bundle$/))
|
170
|
-
end
|
171
|
-
module_function :repl?
|
172
|
-
|
173
|
-
# @private
|
174
|
-
def strftimer(seconds) # :nodoc:
|
175
|
-
Time.at(seconds).gmtime.strftime('%R:%S.%L')
|
176
|
-
end
|
177
|
-
module_function :strftimer
|
178
|
-
|
179
|
-
# @private
|
180
|
-
def timestamp # :nodoc:
|
181
|
-
return Time.now.getutc.to_i
|
182
|
-
end
|
183
|
-
|
184
|
-
def write_object_counts(name = 'ruby')
|
185
|
-
file = "#{name}_#{Time.now.to_i}.txt"
|
186
|
-
File.open(file, 'w') {|f| f.write(pp_s(object_counts)) }
|
187
|
-
end
|
188
|
-
module_function :write_object_counts
|
189
|
-
end
|