trick_bag 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. data/.gitignore +19 -0
  2. data/Gemfile +4 -0
  3. data/Guardfile +8 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +32 -0
  6. data/RELEASE_NOTES.md +3 -0
  7. data/Rakefile +1 -0
  8. data/lib/trick_bag.rb +7 -0
  9. data/lib/trick_bag/collections/linked_list.rb +97 -0
  10. data/lib/trick_bag/enumerables/buffered_enumerable.rb +90 -0
  11. data/lib/trick_bag/enumerables/compound_enumerable.rb +114 -0
  12. data/lib/trick_bag/enumerables/filtered_enumerable.rb +32 -0
  13. data/lib/trick_bag/io/temp_files.rb +22 -0
  14. data/lib/trick_bag/io/text_mode_status_updater.rb +59 -0
  15. data/lib/trick_bag/meta/classes.rb +87 -0
  16. data/lib/trick_bag/numeric/multi_counter.rb +44 -0
  17. data/lib/trick_bag/numeric/totals.rb +49 -0
  18. data/lib/trick_bag/operators/operators.rb +13 -0
  19. data/lib/trick_bag/timing/timing.rb +47 -0
  20. data/lib/trick_bag/validations/hash_validations.rb +21 -0
  21. data/lib/trick_bag/validations/object_validations.rb +26 -0
  22. data/lib/trick_bag/version.rb +3 -0
  23. data/spec/spec_helper.rb +11 -0
  24. data/spec/trick_bag/collections/linked_list_spec.rb +64 -0
  25. data/spec/trick_bag/enumerables/buffered_enumerable_spec.rb +117 -0
  26. data/spec/trick_bag/enumerables/compound_enumerable_spec.rb +141 -0
  27. data/spec/trick_bag/enumerables/filtered_enumerable_spec.rb +35 -0
  28. data/spec/trick_bag/io/temp_files_spec.rb +31 -0
  29. data/spec/trick_bag/io/text_mode_status_updater_spec.rb +24 -0
  30. data/spec/trick_bag/meta/classes_spec.rb +211 -0
  31. data/spec/trick_bag/numeric/multi_counter_spec.rb +28 -0
  32. data/spec/trick_bag/numeric/totals_spec.rb +38 -0
  33. data/spec/trick_bag/operators/operators_spec.rb +33 -0
  34. data/spec/trick_bag/timing/timing_spec.rb +17 -0
  35. data/spec/trick_bag/validations/hashes_validations_spec.rb +39 -0
  36. data/spec/trick_bag/validations/object_validations_spec.rb +23 -0
  37. data/trick_bag.gemspec +28 -0
  38. metadata +194 -0
@@ -0,0 +1,117 @@
1
+ require_relative '../../spec_helper'
2
+ require 'trick_bag/enumerables/buffered_enumerable'
3
+
4
+ module TrickBag
5
+ module Enumerables
6
+
7
+ describe BufferedEnumerable do
8
+
9
+ context 'when created with lambdas' do
10
+ # Returns an object that returns chunks of incrementing integers.
11
+ let(:fetcher) do
12
+ object = 0
13
+ ->(array, chunk_size) do
14
+ chunk_size.times do
15
+ object += 1
16
+ array << object
17
+ end
18
+ end
19
+ end
20
+
21
+ specify 'the values and number of fetches are correct' do
22
+ chunk_fetch_calls = 0
23
+ object_count = 0
24
+
25
+ fetch_notifier = ->(fetched_objects) do
26
+ chunk_fetch_calls += 1
27
+ object_count += fetched_objects.size
28
+ end
29
+
30
+ e = BufferedEnumerable.create_with_lambdas(4, fetcher, fetch_notifier).to_enum
31
+ (1..10).each do |n|
32
+ expect(e.next).to eq(n)
33
+ end
34
+ expect(chunk_fetch_calls).to eq(3)
35
+ expect(object_count).to eq(12)
36
+ end
37
+ end
38
+
39
+
40
+ context "when instantiating a subclass" do
41
+ specify 'the values and number of fetches are correct' do
42
+ create_test_class = ->() do
43
+ class BufferedEnumerableSubclass < BufferedEnumerable
44
+
45
+ attr_accessor :chunk_fetch_calls, :object_count
46
+
47
+ def initialize(chunk_size)
48
+ super
49
+ @chunk_fetch_calls = 0
50
+ @object_count = 0
51
+ end
52
+
53
+ def fetch_notify
54
+ @chunk_fetch_calls += 1
55
+ @object_count += data.size
56
+ end
57
+
58
+ def fetch
59
+ @object ||= 0
60
+ chunk_size.times do
61
+ @object += 1
62
+ self.data << @object
63
+ end
64
+ require 'pry'; binding.pry if self.data.is_a?(Fixnum)
65
+ end
66
+ end
67
+ end
68
+
69
+ create_test_class.()
70
+ enumerable = BufferedEnumerableSubclass.new(4)
71
+ enumerator = enumerable.each
72
+
73
+ (1..10).each do |n|
74
+ expect(enumerator.next).to eq(n)
75
+ end
76
+ expect(enumerable.chunk_fetch_calls).to eq(3)
77
+ expect(enumerable.object_count).to eq(12)
78
+ ::TrickBag::Meta::Classes.undef_class(:BufferedEnumerableSubclass, TrickBag)
79
+ end
80
+ end
81
+
82
+
83
+ context "an Array implementation" do
84
+
85
+ create_array_subclass = -> do
86
+ class ArrayBufferedEnumerable < BufferedEnumerable
87
+
88
+ def initialize(chunk_size, array)
89
+ super(chunk_size)
90
+ @array = array
91
+ end
92
+
93
+ def fetch
94
+ num_times = [chunk_size, @array.size].min
95
+ num_times.times { @data << @array.shift }
96
+ end
97
+ end
98
+ end
99
+
100
+ it 'should fulfill its basic functions' do
101
+ create_array_subclass.()
102
+ enumerable = ArrayBufferedEnumerable.new(4, (0..5).to_a)
103
+ enumerator = enumerable.each
104
+
105
+ (0..5).to_a.each do |n|
106
+ expect(enumerator.next).to eq(n)
107
+ end
108
+ expect(->{ enumerator.next }).to raise_error(StopIteration)
109
+ expect(enumerable.chunk_count).to eq(2)
110
+
111
+ ::TrickBag::Meta::Classes.undef_class(:ArrayBufferedEnumerable, TrickBag)
112
+
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,141 @@
1
+ require_relative '../../spec_helper'
2
+ require 'trick_bag/enumerables/compound_enumerable'
3
+
4
+ module TrickBag
5
+ module Enumerables
6
+
7
+ describe CompoundEnumerable do
8
+
9
+ context 'input validations' do
10
+
11
+ specify 'an initialization error will be raised if no enumerables are specified' do
12
+ expect(->{ CompoundEnumerable.array_enumerable() }).to raise_error
13
+ expect(->{ CompoundEnumerable.hash_enumerable([:key]) }).to raise_error
14
+ end
15
+
16
+ specify 'an initialization error will be raised if mode is not :yields_arrays or :yields_hashes' do
17
+ expect(->{ CompoundEnumerable.new(:bad_mode, [], [])}).to raise_error
18
+ end
19
+
20
+ specify 'an initialization error will be raised if key array size != enumerables size in :yields_hashes mode' do
21
+ expect(->{ CompoundEnumerable.new(:yields_hashes, [:key1, :key2], [])}).to raise_error
22
+ expect(->{ CompoundEnumerable.hash_enumerable([:key1, :key2], [])}).to raise_error
23
+ end
24
+ end
25
+
26
+
27
+
28
+ context "as array" do
29
+
30
+ context 'with 1 enumerable' do
31
+ specify 'Gets its values then raises a StopIteration error' do
32
+ array = [1, 2]
33
+ e = CompoundEnumerable.array_enumerable(array).each
34
+ expect(e.next).to eq(1)
35
+ expect(e.next).to eq(2)
36
+ expect(->{ e.next }).to raise_error(StopIteration)
37
+ end
38
+ end
39
+
40
+
41
+ context 'with 2 enumerables' do
42
+ specify 'offers correct values in the correct order' do
43
+ outer = %w(A B)
44
+ inner = [1, 2]
45
+ e = CompoundEnumerable.array_enumerable(outer, inner).each
46
+ expect(e.next).to eq(['A', 1])
47
+ expect(e.next).to eq(['A', 2])
48
+ expect(e.next).to eq(['B', 1])
49
+ expect(e.next).to eq(['B', 2])
50
+ expect(->{ e.next }).to raise_error(StopIteration)
51
+ end
52
+ end
53
+
54
+
55
+ context 'with 3 enumerables' do
56
+ specify 'offers correct values in the correct order' do
57
+ domains = %w(aaa.com zzz.com)
58
+ qtypes = %w(A NS)
59
+ qclasses = %w(IN CH)
60
+ e = CompoundEnumerable.array_enumerable(domains, qtypes, qclasses).each
61
+
62
+ expect(e.next).to eq(['aaa.com', 'A', 'IN'])
63
+ expect(e.next).to eq(['aaa.com', 'A', 'CH'])
64
+ expect(e.next).to eq(['aaa.com', 'NS', 'IN'])
65
+ expect(e.next).to eq(['aaa.com', 'NS', 'CH'])
66
+ expect(e.next).to eq(['zzz.com', 'A', 'IN'])
67
+ expect(e.next).to eq(['zzz.com', 'A', 'CH'])
68
+ expect(e.next).to eq(['zzz.com', 'NS', 'IN'])
69
+ expect(e.next).to eq(['zzz.com', 'NS', 'CH'])
70
+ expect(->{ e.next }).to raise_error(StopIteration)
71
+ end
72
+ end
73
+
74
+ context 'is an Enumerable' do
75
+ it "provides the 'take' method" do
76
+ expect(->{ CompoundEnumerable.array_enumerable([1, 2, 3]).take(2)}).not_to raise_error
77
+ end
78
+ end
79
+ end
80
+
81
+
82
+ context "as hash" do
83
+
84
+ context 'with 1 enumerable' do
85
+ specify 'Gets its values then raises a StopIteration error' do
86
+ array = [1, 2]
87
+ e = CompoundEnumerable.hash_enumerable([:number], array).each
88
+ expect(e.next).to eq({ number: 1 })
89
+ expect(e.next).to eq({ number: 2 })
90
+ expect(->{ e.next }).to raise_error(StopIteration)
91
+ end
92
+ end
93
+
94
+
95
+ context 'with 2 enumerables' do
96
+ specify 'offers correct values in the correct order' do
97
+ outer = %w(A B)
98
+ inner = [1, 2]
99
+ e = CompoundEnumerable.hash_enumerable([:outer, :inner], outer, inner).each
100
+ expect(e.next).to eq({ outer: 'A', inner: 1 })
101
+ expect(e.next).to eq({ outer: 'A', inner: 2 })
102
+ expect(e.next).to eq({ outer: 'B', inner: 1 })
103
+ expect(e.next).to eq({ outer: 'B', inner: 2 })
104
+ expect(->{ e.next }).to raise_error(StopIteration)
105
+ end
106
+ end
107
+
108
+
109
+ context 'with 3 enumerables' do
110
+ specify 'offers correct values in the correct order' do
111
+ domains = %w(aaa.com zzz.com)
112
+ qtypes = %w(A NS)
113
+ qclasses = %w(IN CH)
114
+ keys = [:domain, :qtype, :qclass]
115
+ e = CompoundEnumerable.hash_enumerable(keys, domains, qtypes, qclasses).each
116
+
117
+ expect(e.next).to eq({ domain: 'aaa.com', qtype: 'A', qclass: 'IN' })
118
+ expect(e.next).to eq({ domain: 'aaa.com', qtype: 'A', qclass: 'CH' })
119
+ expect(e.next).to eq({ domain: 'aaa.com', qtype: 'NS', qclass: 'IN' })
120
+ expect(e.next).to eq({ domain: 'aaa.com', qtype: 'NS', qclass: 'CH' })
121
+
122
+ expect(e.next).to eq({ domain: 'zzz.com', qtype: 'A', qclass: 'IN' })
123
+ expect(e.next).to eq({ domain: 'zzz.com', qtype: 'A', qclass: 'CH' })
124
+ expect(e.next).to eq({ domain: 'zzz.com', qtype: 'NS', qclass: 'IN' })
125
+ expect(e.next).to eq({ domain: 'zzz.com', qtype: 'NS', qclass: 'CH' })
126
+ expect(->{ e.next }).to raise_error(StopIteration)
127
+ end
128
+ end
129
+
130
+ context 'is an Enumerable' do
131
+ it "provides the 'take' method" do
132
+ expect(->{ CompoundEnumerable.hash_enumerable([:num], [1, 2, 3]).take(2)}).not_to raise_error
133
+
134
+ values = CompoundEnumerable.hash_enumerable([:num], [1, 2, 3]).take(2)
135
+ expect(values).to eq([{ num: 1 }, { num: 2 }])
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,35 @@
1
+ require_relative '../../spec_helper'
2
+ require 'trick_bag/enumerables/filtered_enumerable'
3
+
4
+ module TrickBag
5
+ module Enumerables
6
+
7
+ describe FilteredEnumerable do
8
+
9
+ let (:even_filter) { ->(n) { n.even? } }
10
+ specify 'with no filter it behaves as a regular enumberable' do
11
+ e = FilteredEnumerable.new([1,2,3])
12
+ expect(e.to_a).to eq([1, 2, 3])
13
+ end
14
+
15
+ specify 'an even number filter works correctly when filter passed to constructor' do
16
+ e = FilteredEnumerable.new((1..10).to_a, even_filter)
17
+ expect(e.to_a).to eq([2, 4, 6, 8, 10])
18
+ end
19
+
20
+ specify 'an even number filter works correctly when filter passed after creation' do
21
+ e = FilteredEnumerable.new((1..10).to_a)
22
+ e.filter = even_filter
23
+ expect(e.to_a).to eq([2, 4, 6, 8, 10])
24
+ end
25
+
26
+ specify 'works as an enumerator' do
27
+ e = FilteredEnumerable.new((1..5).to_a, even_filter)
28
+ enumerator = e.each
29
+ expect(enumerator.next).to eq(2)
30
+ expect(enumerator.next).to eq(4)
31
+ expect(-> { enumerator.next }).to raise_error(StopIteration)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,31 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ require 'trick_bag/io/temp_files'
4
+
5
+ module TrickBag::Io
6
+
7
+ describe TempFiles do
8
+
9
+ it 'creates a readable file with the correct content' do
10
+ TempFiles.file_containing('foo') do |filespec|
11
+ expect(File.read(filespec)).to eq('foo')
12
+ end
13
+ end
14
+
15
+ it 'deletes the file when done' do
16
+ filespec = nil
17
+ TempFiles.file_containing('foo') do |fspec|
18
+ filespec = fspec
19
+ end
20
+ expect(File.exist?(filespec)).to be_false
21
+ end
22
+
23
+ it 'uses the prefix in the filespec' do
24
+ prefix = 'jsiapewrqms'
25
+ TempFiles.file_containing('', prefix) do |filespec|
26
+ filename = File.split(filespec).last
27
+ expect(filename.start_with?(prefix)).to be_true
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,24 @@
1
+ require_relative '../../spec_helper'
2
+ require 'trick_bag/io/text_mode_status_updater'
3
+
4
+ module TrickBag
5
+ module Io
6
+ describe TextModeStatusUpdater do
7
+
8
+ it 'instantiates without error' do
9
+ expect(->{ TextModeStatusUpdater.new(->{ '' }) }).not_to raise_error
10
+ end
11
+
12
+ it 'creates a TextModeStatusUpdater' do
13
+ expect(TextModeStatusUpdater.new(->{ '' })).to be_a(TextModeStatusUpdater)
14
+ end
15
+
16
+ specify 'the output string contains the content provided by the passed lambda' do
17
+ string_io = StringIO.new
18
+ updater = TextModeStatusUpdater.new(->{ 'status' }, string_io, true)
19
+ updater.print
20
+ expect(string_io.string).to match(/status/)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,211 @@
1
+ require_relative '../../spec_helper'
2
+ require 'trick_bag/meta/classes'
3
+
4
+ module TrickBag
5
+ module Meta
6
+
7
+ describe Classes do
8
+
9
+ after(:each) { Classes.undef_class('ClassPatchTestClass') }
10
+
11
+
12
+ ####################################################### class?
13
+
14
+ context 'class?' do
15
+
16
+ it 'should recognize String as a class' do
17
+ expect(Classes.class?('String')).to be_true
18
+ end
19
+
20
+ it 'should recognize that RUBY_PLATFORM is defined but is not a class' do
21
+ expect(Classes.class?('RUBY_PLATFORM')).to be_false
22
+ end
23
+
24
+ it 'should recognize a nonexistent constant as not being a class' do
25
+ expect(Classes.class?('Afjkdiurqpweruwiqopurqpweriuqewprzvxcvzxcvzxvzvzvzvcxzvzv')).to be_false
26
+ end
27
+ end
28
+
29
+
30
+ ####################################################### undef_class
31
+
32
+ context 'undef_class' do
33
+ it 'should remove the class' do
34
+ fn = -> do
35
+ class ClassPatchTestClass; end
36
+ ClassPatchTestClass.new # to illustrate that it's available
37
+ Classes.undef_class('ClassPatchTestClass', TrickBag::Meta)
38
+ end
39
+ fn.()
40
+ expect(->{ ClassPatchTestClass.new }).to raise_error
41
+ end
42
+ end
43
+
44
+
45
+ ####################################################### private_attr_reader
46
+
47
+ context 'private_attr_reader' do
48
+
49
+ after(:each) { Classes.undef_class('ClassPatchTestClass') }
50
+
51
+ fn_create_class = ->do
52
+ class ClassPatchTestClass
53
+
54
+ extend Classes
55
+
56
+ private_attr_reader :foo
57
+
58
+ def initialize
59
+ @foo = 123
60
+ puts "foo = #{self.foo}"
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+ it "should be visible inside the class" do
67
+ expect(fn_create_class).not_to raise_error
68
+ end
69
+
70
+ it "should be invisible outside the class" do
71
+ fn_create_class.()
72
+ expect(->{ ClassPatchTestClass.new.foo }).to raise_error
73
+ end
74
+ end
75
+
76
+
77
+ ####################################################### private_attr_writer
78
+
79
+ context 'private_attr_writer' do
80
+
81
+ fn_create_class = ->do
82
+ class ClassPatchTestClass
83
+
84
+ extend Classes
85
+
86
+ private_attr_writer :foo
87
+
88
+ def initialize
89
+ self.foo = 123
90
+ end
91
+ end
92
+ end
93
+
94
+
95
+ it "should be visible inside the class" do
96
+ expect(fn_create_class).not_to raise_error
97
+ end
98
+
99
+ it "should be invisible outside the class" do
100
+ fn_create_class.()
101
+ expect(->{ ClassPatchTestClass.new.foo = 456}).to raise_error
102
+ end
103
+
104
+ end
105
+
106
+ ####################################################### private_attr_accessor
107
+
108
+ context 'private_attr_accessor' do
109
+
110
+ fn_create_class = ->do
111
+ class ClassPatchTestClass
112
+
113
+ extend Classes
114
+
115
+ private_attr_accessor :foo
116
+
117
+ def initialize
118
+ self.foo = 123
119
+ puts self.foo
120
+ end
121
+ end
122
+ end
123
+
124
+
125
+ it "should be visible inside the class" do
126
+ expect(fn_create_class).not_to raise_error
127
+ end
128
+
129
+ it "should be invisible outside the class" do
130
+ fn_create_class.()
131
+ expect(->{ ClassPatchTestClass.new.foo = 456}).to raise_error
132
+ expect(->{ ClassPatchTestClass.new.foo }).to raise_error
133
+ end
134
+
135
+ end
136
+
137
+
138
+ ####################################################### private_attr_writer_public_reader
139
+
140
+ context 'attr_access(:public, :private, :foo)' do
141
+
142
+ fn_create_class = ->do
143
+ class ClassPatchTestClass
144
+
145
+ extend Classes
146
+
147
+ attr_access :public, :private, :foo
148
+
149
+ def initialize
150
+ self.foo = 123
151
+ self.foo
152
+ end
153
+ end
154
+ end
155
+
156
+
157
+ it "should be readable and writable inside the class" do
158
+ expect(fn_create_class).not_to raise_error
159
+ end
160
+
161
+ it "should be readable outside the class" do
162
+ fn_create_class.()
163
+ expect(->{ ClassPatchTestClass.new.foo }).not_to raise_error
164
+ end
165
+
166
+ it "should not be writable outside the class" do
167
+ fn_create_class.()
168
+ expect(->{ ClassPatchTestClass.new.foo = 456}).to raise_error
169
+ end
170
+ end
171
+
172
+ context 'attr_access(:private, :none, :foo)' do
173
+
174
+ fn_create_class = ->do
175
+ class ClassPatchTestClass
176
+
177
+ extend Classes
178
+
179
+ attr_access :private, :none, :foo
180
+
181
+ def initialize
182
+ @foo = 1
183
+ self.foo
184
+ end
185
+ end
186
+ end
187
+
188
+
189
+ it "should be readable inside the class" do
190
+ expect(fn_create_class).not_to raise_error
191
+ end
192
+
193
+
194
+ it "should not be readable outside the class" do
195
+ fn_create_class.()
196
+ expect(->{ ClassPatchTestClass.new.foo }).to raise_error
197
+ end
198
+
199
+ it "should not be readable outside the class" do
200
+ fn_create_class.()
201
+ expect(->{ ClassPatchTestClass.new.foo }).to raise_error
202
+ end
203
+
204
+ it "should not be writable outside the class" do
205
+ fn_create_class.()
206
+ expect(->{ ClassPatchTestClass.new.foo = 456}).to raise_error
207
+ end
208
+ end
209
+ end
210
+ end
211
+ end