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,187 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Functional
4
-
5
- describe Search do
6
-
7
- context '#linear_search' do
8
-
9
- let(:sample) do
10
- [3, 5, 6, 7, 8, 11, 15, 21, 22, 28, 30, 32, 33, 34, 40].freeze
11
- end
12
-
13
- it 'returns nil for a nil sample' do
14
- Search.linear_search(nil, 1).should be_nil
15
- end
16
-
17
- it 'returns nil for an empty sample' do
18
- Search.linear_search([].freeze, 1).should be_nil
19
- end
20
-
21
- it 'returns the index of the item when found' do
22
- index = Search.linear_search(sample, 11)
23
- index.should eq 5
24
- end
25
-
26
- it 'returns the index of the item when using a block' do
27
- sample = [
28
- {:count => 11},
29
- {:count => 12},
30
- {:count => 13},
31
- {:count => 14},
32
- {:count => 16},
33
- {:count => 17},
34
- {:count => 18},
35
- {:count => 19},
36
- {:count => 20},
37
- {:count => 21}
38
- ].freeze
39
-
40
- index = Search.linear_search(sample, 14){|item| item[:count]}
41
- index.should eq 3
42
- end
43
-
44
- it 'returns nil when not found' do
45
- index = Search.linear_search(sample, 13)
46
- index.should be_nil
47
- end
48
-
49
- it 'supports an :imin option for an alternate low index' do
50
- index = Search.linear_search(sample, 11, :imin => 3)
51
- index.should eq 5
52
-
53
- index = Search.linear_search(sample, 11, :imin => 10)
54
- index.should be_nil
55
- end
56
-
57
- it 'supports an :imax option for an alternate high index' do
58
- index = Search.linear_search(sample, 11, :imax => 10)
59
- index.should eq 5
60
-
61
- index = Search.linear_search(sample, 11, :imax => 4)
62
- index.should be_nil
63
- end
64
-
65
- it 'behaves consistently when :imin equals :imax' do
66
- index = Search.linear_search(sample, 3, :imin => 5, :imax => 5)
67
- index.should be_nil
68
-
69
- index = Search.linear_search(sample, 11, :imin => 5, :imax => 5)
70
- index.should eq 5
71
-
72
- index = Search.linear_search(sample, 30, :imin => 5, :imax => 5)
73
- index.should be_nil
74
- end
75
-
76
- it 'sets :imin to zero (0) when given a negative number' do
77
- index = Search.linear_search(sample, 11, :imin => -1)
78
- index.should eq 5
79
- end
80
-
81
- it 'sets :imax to the uppermost index when :imax is out of range' do
82
- index = Search.linear_search(sample, 11, :imax => 100)
83
- index.should eq 5
84
- end
85
-
86
- it 'returns nil when :imin is greater than :imax' do
87
- index = Search.linear_search(sample, 1, :imin => 10, :imax => 5)
88
- index.should be_nil
89
- end
90
- end
91
-
92
- context '#binary_search' do
93
-
94
- let(:sample) do
95
- [3, 5, 6, 7, 8, 11, 15, 21, 22, 28, 30, 32, 33, 34, 40].freeze
96
- end
97
-
98
- it 'returns nil for a nil sample' do
99
- Search.binary_search(nil, 1).should be_nil
100
- end
101
-
102
- it 'returns nil for an empty sample' do
103
- Search.binary_search([].freeze, 1).should be_nil
104
- end
105
-
106
- it 'returns the index of the item when found as [index, index]' do
107
- index = Search.binary_search(sample, 11)
108
- index.should eq [5, 5]
109
- end
110
-
111
- it 'returns the index of the item when using a block' do
112
- sample = [
113
- {:count => 11},
114
- {:count => 12},
115
- {:count => 13},
116
- {:count => 14},
117
- {:count => 16},
118
- {:count => 17},
119
- {:count => 18},
120
- {:count => 19},
121
- {:count => 20},
122
- {:count => 21}
123
- ].freeze
124
-
125
- index = Search.binary_search(sample, 14){|item| item[:count]}
126
- index.should eq [3, 3]
127
- end
128
-
129
- it 'returns the indexes above and below when not found - below, above' do
130
- index = Search.binary_search(sample, 13)
131
- index.should eq [5, 6]
132
- end
133
-
134
- it 'returns nil and the low index when the item is out of range on the low end - [nil, low]' do
135
- index = Search.binary_search(sample, 1)
136
- index.should eq [nil, 0]
137
- end
138
-
139
- it 'returns the high index and nil when the item is out of range on the high end - [high, nil]' do
140
- index = Search.binary_search(sample, 41)
141
- index.should eq [14, nil]
142
- end
143
-
144
- it 'supports an :imin option for an alternate low index' do
145
- index = Search.binary_search(sample, 11, :imin => 3)
146
- index.should eq [5, 5]
147
-
148
- index = Search.binary_search(sample, 11, :imin => 10)
149
- index.should eq [nil, 10]
150
- end
151
-
152
- it 'supports an :imax option for an alternate high index' do
153
- index = Search.binary_search(sample, 11, :imax => 10)
154
- index.should eq [5, 5]
155
-
156
- index = Search.binary_search(sample, 11, :imax => 4)
157
- index.should eq [4, nil]
158
- end
159
-
160
- it 'behaves consistently when :imin equals :imax' do
161
- index = Search.binary_search(sample, 3, :imin => 5, :imax => 5)
162
- index.should eq [nil, 5]
163
-
164
- index = Search.binary_search(sample, 11, :imin => 5, :imax => 5)
165
- index.should eq [5, 5]
166
-
167
- index = Search.binary_search(sample, 30, :imin => 5, :imax => 5)
168
- index.should eq [5, nil]
169
- end
170
-
171
- it 'sets :imin to zero (0) when given a negative number' do
172
- index = Search.binary_search(sample, 1, :imin => -1)
173
- index.should eq [nil, 0]
174
- end
175
-
176
- it 'sets :imax to the uppermost index when :imax is out of range' do
177
- index = Search.binary_search(sample, 41, :imax => 100)
178
- index.should eq [14, nil]
179
- end
180
-
181
- it 'returns nil when :imin is greater than :imax' do
182
- index = Search.binary_search(sample, 1, :imin => 10, :imax => 5)
183
- index.should be_nil
184
- end
185
- end
186
- end
187
- end
@@ -1,61 +0,0 @@
1
- require 'spec_helper'
2
-
3
- module Functional
4
-
5
- describe Sort do
6
-
7
- context '#insertion_sort!' do
8
-
9
- it 'returns nil for a nil sample' do
10
- Sort.insertion_sort!(nil).should be_nil
11
- end
12
-
13
- it 'returns the sample when the sample is empty' do
14
- Sort.insertion_sort!([]).should be_empty
15
- end
16
-
17
- it 'returns the sample when the sample has one element' do
18
- Sort.insertion_sort!([100]).should eq [100]
19
- end
20
-
21
- it 'sorts an unsorted collection' do
22
- sample = [31, 37, 26, 30, 2, 30, 1, 33, 5, 14, 11, 13, 17, 35, 4]
23
- count = sample.count
24
- sorted = Sort.insertion_sort!(sample)
25
- Functional.ascending?(sorted).should be_true
26
- sorted.count.should eq count
27
- end
28
-
29
- it 'does not modify an unsorted collection' do
30
- sample = [1, 2, 4, 5, 11, 13, 14, 17, 26, 30, 30, 31, 33, 35, 37]
31
- control = sample.dup
32
- sorted = Sort.insertion_sort!(sample)
33
- sorted.should eq control
34
- end
35
-
36
- it 'it sorts a collection with a block' do
37
- sample = [
38
- {:count => 31},
39
- {:count => 37},
40
- {:count => 26},
41
- {:count => 30},
42
- {:count => 2},
43
- {:count => 30},
44
- {:count => 1}
45
- ]
46
-
47
- count = sample.count
48
- sorted = Sort.insertion_sort!(sample){|item| item[:count]}
49
- Functional.ascending?(sorted){|item| item[:count]}.should be_true
50
- sorted.count.should eq count
51
- end
52
-
53
- it 'performs the sort in place' do
54
- lambda {
55
- sample = [31, 37, 26, 30, 2, 30, 1, 33, 5, 14, 11, 13, 17, 35, 4].freeze
56
- Sort.insertion_sort!(sample)
57
- }.should raise_error
58
- end
59
- end
60
- end
61
- end
@@ -1,277 +0,0 @@
1
- require 'spec_helper'
2
- require 'fakefs/safe'
3
- require 'rbconfig'
4
-
5
- describe 'utilities' do
6
-
7
- context '#delta' do
8
-
9
- it 'computes the delta of two positive values' do
10
- delta(10.5, 5.0).should be_within(0.01).of(5.5)
11
- end
12
-
13
- it 'computes the delta of two negative values' do
14
- delta(-10.5, -5.0).should be_within(0.01).of(5.5)
15
- end
16
-
17
- it 'computes the delta of a positive and negative value' do
18
- delta(10.5, -5.0).should be_within(0.01).of(15.5)
19
- end
20
-
21
- it 'computes the delta of two positive values with a block' do
22
- v1 = {:count => 10.5}
23
- v2 = {:count => 5.0}
24
- delta(v1, v2){|x| x[:count]}.should be_within(0.01).of(5.5)
25
- end
26
-
27
- it 'computes the delta of two negative values with a block' do
28
- v1 = {:count => -10.5}
29
- v2 = {:count => -5.0}
30
- delta(v1, v2){|x| x[:count]}.should be_within(0.01).of(5.5)
31
- end
32
-
33
- it 'computes the delta of a positive and negative value with a block' do
34
- v1 = {:count => 10.5}
35
- v2 = {:count => -5.0}
36
- delta(v1, v2){|x| x[:count]}.should be_within(0.01).of(15.5)
37
- end
38
- end
39
-
40
- context '#repeatedly' do
41
-
42
- it 'returns an empty array when requested times is zero' do
43
- expected = repeatedly(0){ 1 }
44
- expected.should be_empty
45
- end
46
-
47
- it 'returns an array with all nil values when no block is given' do
48
- expected = repeatedly(10)
49
- expected.length.should eq 10
50
- expected.each do |elem|
51
- elem.should be_nil
52
- end
53
- end
54
-
55
- it 'iterates the requested number of times and puts the results into an array' do
56
- expected = repeatedly(10){ 5 }
57
- expected.length.should eq 10
58
- expected.each do |elem|
59
- elem.should eq 5
60
- end
61
- end
62
-
63
- it 'passes the initial value to the first iteration' do
64
- @expected = nil
65
- repeatedly(1,100){|previous| @expected = previous }
66
- @expected.should eq 100
67
- end
68
-
69
- it 'passes the result of each iteration to the next iteration' do
70
- expected = repeatedly(10, 1){|previous| previous * 2 }
71
- expected.should eq [2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
72
- end
73
- end
74
-
75
- context '#retro' do
76
-
77
- it 'does not run the block if requested times is zero' do
78
- @expected = true
79
- retro(0){ @expected = false }
80
- @expected.should be_true
81
- end
82
-
83
- it 'passes all arguments to the block' do
84
- @expected = nil
85
- retro(1, ?a, ?b, ?c){|*args| @expected = args }
86
- @expected.should eq [?a, ?b, ?c]
87
- end
88
-
89
- it 'calls the block once if the first pass is successful' do
90
- @expected = 0
91
- retro(5){ @expected += 1 }
92
- @expected.should eq 1
93
- end
94
-
95
- it 'calls the block more than once if the first pass fails' do
96
- @expected = 0
97
- retro(5) do
98
- @expected += 1
99
- raise StandardError if @expected < 3
100
- end
101
- @expected.should eq 3
102
- end
103
-
104
- it 'calls the block no more than the requested number of times' do
105
- @expected = 0
106
- retro(5) do
107
- @expected += 1
108
- raise StandardError
109
- end
110
- @expected.should eq 5
111
- end
112
-
113
- it 'returns true if any attempt succeeds' do
114
- expected = retro(1){ nil }
115
- expected.should be_true
116
- end
117
-
118
- it 'returns false if all attempts fail' do
119
- expected = retro(1){ raise StandardError }
120
- expected.should be_false
121
- end
122
-
123
- it 'returns false if no block is given' do
124
- expected = retro(10)
125
- expected.should eq false
126
- end
127
- end
128
-
129
- if RbConfig::CONFIG['ruby_install_name'] =~ /^ruby$/i
130
- context '#safe' do
131
-
132
- it 'allows safe operations' do
133
- lambda {
134
- safe{ 1 + 1 }
135
- }.should_not raise_error
136
- end
137
-
138
- it 'returns the value of the block when safe' do
139
- safe{ 1 + 1 }.should eq 2
140
- end
141
-
142
- it 'passes all arguments to the block' do
143
- safe(1, 2, 3){|x, y, z| x + y + z }.should eq 6
144
- end
145
-
146
- it 'rejects unsafe operations on tainted objects' do
147
- lambda {
148
- safe{ Signal.trap('INT'.taint) }
149
- }.should raise_error(SecurityError)
150
- end
151
-
152
- it 'rejects the use of #eval' do
153
- lambda {
154
- safe{ eval 'puts 1' }
155
- }.should raise_error(SecurityError)
156
- end
157
- end
158
- end
159
-
160
- context '#slurp' do
161
-
162
- unless Functional::PLATFORM.rbx?
163
-
164
- before(:all) { FakeFS.activate! }
165
- after(:all) { FakeFS.deactivate! }
166
-
167
- let!(:path){ 'slurp.txt' }
168
- let!(:text){ 'Hello, world!' }
169
-
170
- it 'returns the contents of the file' do
171
- File.open(path, 'w+') {|f| f.write(text) }
172
- slurp(path).should eq text
173
- end
174
-
175
- it 'raises an exception when the file does not exist' do
176
- lambda {
177
- slurp('path/does/not/exist')
178
- }.should raise_error(Errno::ENOENT)
179
- end
180
- end
181
- end
182
-
183
- context '#slurpee' do
184
-
185
- unless Functional::PLATFORM.rbx?
186
-
187
- before(:all) { FakeFS.activate! }
188
- after(:all) { FakeFS.deactivate! }
189
-
190
- let!(:path){ 'slurp.txt' }
191
- let!(:text){ 'You are number 6.' }
192
- let!(:erb) { 'You are number <%= 2 * 3 %>.' }
193
-
194
- it 'returns the processed contents of the file' do
195
- File.open(path, 'w+') {|f| f.write(erb) }
196
- slurpee(path).should eq text
197
- end
198
-
199
- it 'raises an exception when the file does not exist' do
200
- lambda {
201
- slurpee('path/does/not/exist')
202
- }.should raise_error(Errno::ENOENT)
203
- end
204
- end
205
- end
206
-
207
- context '#repl?' do
208
-
209
- before(:each) do
210
- @dollar_zero = $0
211
- end
212
-
213
- after(:each) do
214
- $0 = @dollar_zero
215
- end
216
-
217
- def set_dollar_zero(val)
218
- $0 = val
219
- end
220
-
221
- it 'recognizes IRB' do
222
- set_dollar_zero('irb')
223
- repl?.should be_true
224
- end
225
-
226
- it 'recognizes Pry' do
227
- set_dollar_zero('pry')
228
- repl?.should be_true
229
- end
230
-
231
- it 'recognizes Rails Console' do
232
- set_dollar_zero('script/rails')
233
- repl?.should be_true
234
- end
235
-
236
- it 'recognizes Bundle Console' do
237
- set_dollar_zero('bin/bundle')
238
- repl?.should be_true
239
- end
240
-
241
- it 'returns false when not in a REPL' do
242
- set_dollar_zero(__FILE__)
243
- repl?.should be_false
244
- end
245
- end
246
-
247
- context '#timer' do
248
-
249
- it 'returns [0, nil] if no block is given' do
250
- duration, result = timer()
251
- duration.should eq 0
252
- result.should be_nil
253
- end
254
-
255
- it 'yields to the block' do
256
- @expected = false
257
- duration, result = timer{ @expected = true }
258
- @expected.should be_true
259
- end
260
-
261
- it 'passes all arguments to the block' do
262
- @expected = nil
263
- duration, result = timer(1,2,3){|a,b,c| @expected = [a,b,c]}
264
- @expected.should eq [1,2,3]
265
- end
266
-
267
- it 'returns the duration as the first return value' do
268
- duration, result = timer{ sleep(0.1) }
269
- duration.should > 0
270
- end
271
-
272
- it 'returns the block result as the second return value' do
273
- duration, result = timer{ 42 }
274
- result.should eq 42
275
- end
276
- end
277
- end