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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -152
  3. data/doc/memo.txt +192 -0
  4. data/doc/pattern_matching.txt +485 -0
  5. data/doc/protocol.txt +221 -0
  6. data/doc/record.txt +144 -0
  7. data/doc/thread_safety.txt +8 -0
  8. data/lib/functional.rb +48 -18
  9. data/lib/functional/abstract_struct.rb +161 -0
  10. data/lib/functional/delay.rb +117 -0
  11. data/lib/functional/either.rb +222 -0
  12. data/lib/functional/memo.rb +93 -0
  13. data/lib/functional/method_signature.rb +72 -0
  14. data/lib/functional/option.rb +209 -0
  15. data/lib/functional/pattern_matching.rb +117 -100
  16. data/lib/functional/protocol.rb +157 -0
  17. data/lib/functional/protocol_info.rb +193 -0
  18. data/lib/functional/record.rb +155 -0
  19. data/lib/functional/type_check.rb +112 -0
  20. data/lib/functional/union.rb +152 -0
  21. data/lib/functional/version.rb +3 -1
  22. data/spec/functional/abstract_struct_shared.rb +154 -0
  23. data/spec/functional/complex_pattern_matching_spec.rb +205 -0
  24. data/spec/functional/configuration_spec.rb +17 -0
  25. data/spec/functional/delay_spec.rb +147 -0
  26. data/spec/functional/either_spec.rb +237 -0
  27. data/spec/functional/memo_spec.rb +207 -0
  28. data/spec/functional/option_spec.rb +292 -0
  29. data/spec/functional/pattern_matching_spec.rb +279 -276
  30. data/spec/functional/protocol_info_spec.rb +444 -0
  31. data/spec/functional/protocol_spec.rb +274 -0
  32. data/spec/functional/record_spec.rb +175 -0
  33. data/spec/functional/type_check_spec.rb +103 -0
  34. data/spec/functional/union_spec.rb +110 -0
  35. data/spec/spec_helper.rb +6 -4
  36. metadata +55 -45
  37. data/lib/functional/behavior.rb +0 -138
  38. data/lib/functional/behaviour.rb +0 -2
  39. data/lib/functional/catalog.rb +0 -487
  40. data/lib/functional/collection.rb +0 -403
  41. data/lib/functional/inflect.rb +0 -127
  42. data/lib/functional/platform.rb +0 -120
  43. data/lib/functional/search.rb +0 -132
  44. data/lib/functional/sort.rb +0 -41
  45. data/lib/functional/utilities.rb +0 -189
  46. data/md/behavior.md +0 -188
  47. data/md/catalog.md +0 -32
  48. data/md/collection.md +0 -32
  49. data/md/inflect.md +0 -32
  50. data/md/pattern_matching.md +0 -512
  51. data/md/platform.md +0 -32
  52. data/md/search.md +0 -32
  53. data/md/sort.md +0 -32
  54. data/md/utilities.md +0 -55
  55. data/spec/functional/behavior_spec.rb +0 -528
  56. data/spec/functional/catalog_spec.rb +0 -1206
  57. data/spec/functional/collection_spec.rb +0 -752
  58. data/spec/functional/inflect_spec.rb +0 -85
  59. data/spec/functional/integration_spec.rb +0 -205
  60. data/spec/functional/platform_spec.rb +0 -501
  61. data/spec/functional/search_spec.rb +0 -187
  62. data/spec/functional/sort_spec.rb +0 -61
  63. data/spec/functional/utilities_spec.rb +0 -277
@@ -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
@@ -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
@@ -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
@@ -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