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.
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