functional-ruby 0.7.7 → 1.0.0
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 +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
|