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