veritas-do-adapter 0.0.5

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 (65) hide show
  1. data/.document +5 -0
  2. data/.gemtest +0 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +11 -0
  5. data/Gemfile +46 -0
  6. data/Guardfile +18 -0
  7. data/LICENSE +20 -0
  8. data/README.md +21 -0
  9. data/Rakefile +27 -0
  10. data/TODO +0 -0
  11. data/config/flay.yml +3 -0
  12. data/config/flog.yml +2 -0
  13. data/config/roodi.yml +16 -0
  14. data/config/site.reek +96 -0
  15. data/config/yardstick.yml +2 -0
  16. data/lib/veritas/adapter/data_objects/statement.rb +107 -0
  17. data/lib/veritas/adapter/data_objects/version.rb +9 -0
  18. data/lib/veritas/adapter/data_objects.rb +55 -0
  19. data/lib/veritas/relation/gateway.rb +363 -0
  20. data/lib/veritas-do-adapter.rb +4 -0
  21. data/spec/rcov.opts +6 -0
  22. data/spec/shared/binary_relation_method_behaviour.rb +51 -0
  23. data/spec/shared/command_method_behavior.rb +7 -0
  24. data/spec/shared/each_method_behaviour.rb +15 -0
  25. data/spec/shared/idempotent_method_behaviour.rb +7 -0
  26. data/spec/shared/unary_relation_method_behaviour.rb +21 -0
  27. data/spec/spec.opts +3 -0
  28. data/spec/spec_helper.rb +28 -0
  29. data/spec/unit/veritas/adapter/data_objects/class_methods/new_spec.rb +15 -0
  30. data/spec/unit/veritas/adapter/data_objects/read_spec.rb +70 -0
  31. data/spec/unit/veritas/adapter/data_objects/statement/class_methods/new_spec.rb +28 -0
  32. data/spec/unit/veritas/adapter/data_objects/statement/each_spec.rb +63 -0
  33. data/spec/unit/veritas/adapter/data_objects/statement/to_s_spec.rb +53 -0
  34. data/spec/unit/veritas/relation/gateway/class_methods/new_spec.rb +16 -0
  35. data/spec/unit/veritas/relation/gateway/difference_spec.rb +17 -0
  36. data/spec/unit/veritas/relation/gateway/drop_spec.rb +21 -0
  37. data/spec/unit/veritas/relation/gateway/each_spec.rb +84 -0
  38. data/spec/unit/veritas/relation/gateway/extend_spec.rb +27 -0
  39. data/spec/unit/veritas/relation/gateway/intersect_spec.rb +17 -0
  40. data/spec/unit/veritas/relation/gateway/join_spec.rb +44 -0
  41. data/spec/unit/veritas/relation/gateway/materialize_spec.rb +27 -0
  42. data/spec/unit/veritas/relation/gateway/optimize_spec.rb +23 -0
  43. data/spec/unit/veritas/relation/gateway/product_spec.rb +17 -0
  44. data/spec/unit/veritas/relation/gateway/project_spec.rb +21 -0
  45. data/spec/unit/veritas/relation/gateway/remove_spec.rb +21 -0
  46. data/spec/unit/veritas/relation/gateway/rename_spec.rb +21 -0
  47. data/spec/unit/veritas/relation/gateway/respond_to_spec.rb +29 -0
  48. data/spec/unit/veritas/relation/gateway/restrict_spec.rb +27 -0
  49. data/spec/unit/veritas/relation/gateway/reverse_spec.rb +21 -0
  50. data/spec/unit/veritas/relation/gateway/sort_by_spec.rb +27 -0
  51. data/spec/unit/veritas/relation/gateway/summarize_spec.rb +148 -0
  52. data/spec/unit/veritas/relation/gateway/take_spec.rb +21 -0
  53. data/spec/unit/veritas/relation/gateway/union_spec.rb +17 -0
  54. data/tasks/metrics/ci.rake +7 -0
  55. data/tasks/metrics/flay.rake +41 -0
  56. data/tasks/metrics/flog.rake +43 -0
  57. data/tasks/metrics/heckle.rake +209 -0
  58. data/tasks/metrics/metric_fu.rake +29 -0
  59. data/tasks/metrics/reek.rake +9 -0
  60. data/tasks/metrics/roodi.rake +15 -0
  61. data/tasks/metrics/yardstick.rake +23 -0
  62. data/tasks/spec.rake +39 -0
  63. data/tasks/yard.rake +9 -0
  64. data/veritas-do-adapter.gemspec +124 -0
  65. metadata +257 -0
@@ -0,0 +1,363 @@
1
+ # encoding: utf-8
2
+
3
+ module Veritas
4
+ class Relation
5
+
6
+ # A relation backed by an adapter
7
+ class Gateway < Relation
8
+
9
+ DECORATED_CLASS = superclass
10
+
11
+ # remove methods so they can be proxied
12
+ undef_method *DECORATED_CLASS.public_instance_methods(false).map(&:to_s) - %w[ materialize ]
13
+ undef_method :project, :remove, :extend, :rename, :restrict, :sort_by, :reverse, :drop, :take
14
+
15
+ # The adapter the gateway will use to fetch results
16
+ #
17
+ # @return [Adapter::DataObjects]
18
+ #
19
+ # @api private
20
+ attr_reader :adapter
21
+ protected :adapter
22
+
23
+ # The relation the gateway will use to generate SQL
24
+ #
25
+ # @return [Relation]
26
+ #
27
+ # @api private
28
+ attr_reader :relation
29
+ protected :relation
30
+
31
+ # Initialize a Gateway
32
+ #
33
+ # @param [Adapter::DataObjects] adapter
34
+ #
35
+ # @param [Relation] relation
36
+ #
37
+ # @return [undefined]
38
+ #
39
+ # @api private
40
+ def initialize(adapter, relation)
41
+ @adapter = adapter
42
+ @relation = relation
43
+ end
44
+
45
+ # Iterate over each row in the results
46
+ #
47
+ # @example
48
+ # gateway = Gateway.new(adapter, relation)
49
+ # gateway.each { |tuple| ... }
50
+ #
51
+ # @yield [tuple]
52
+ #
53
+ # @yieldparam [Tuple] tuple
54
+ # each tuple in the results
55
+ #
56
+ # @return [self]
57
+ #
58
+ # @api public
59
+ def each
60
+ return to_enum unless block_given?
61
+ tuples.each { |tuple| yield tuple }
62
+ self
63
+ end
64
+
65
+ # Return a relation that is the join of two relations
66
+ #
67
+ # @example natural join
68
+ # join = relation.join(other)
69
+ #
70
+ # @example theta-join using a block
71
+ # join = relation.join(other) { |r| r.a.gte(r.b) }
72
+ #
73
+ # @param [Relation] other
74
+ # the other relation to join
75
+ #
76
+ # @yield [relation]
77
+ # optional block to restrict the tuples with
78
+ #
79
+ # @yieldparam [Relation] relation
80
+ # the context to evaluate the restriction with
81
+ #
82
+ # @yieldreturn [Function, #call]
83
+ # predicate to restrict the tuples with
84
+ #
85
+ # @return [Gateway]
86
+ # return a gateway if the adapters are equal
87
+ # @return [Algebra::Join]
88
+ # return a normal join when the adapters are not equal
89
+ # @return [Algebra::Restriction]
90
+ # return a normal restriction when the adapters are not equal
91
+ # for a theta-join
92
+ #
93
+ # @api public
94
+ def join(other)
95
+ if block_given?
96
+ super
97
+ else
98
+ binary_operation(__method__, other, Algebra::Join)
99
+ end
100
+ end
101
+
102
+ # Return a relation that is the cartesian product of two relations
103
+ #
104
+ # @example
105
+ # product = gateway.product(other)
106
+ #
107
+ # @param [Relation] other
108
+ # the other relation to find the product with
109
+ #
110
+ # @return [Gateway]
111
+ # return a gateway if the adapters are equal
112
+ # @return [Algebra::Product]
113
+ # return a normal product when the adapters are not equal
114
+ #
115
+ # @api public
116
+ def product(other)
117
+ binary_operation(__method__, other, Algebra::Product)
118
+ end
119
+
120
+ # Return the union between relations
121
+ #
122
+ # @example
123
+ # union = gateway.union(other)
124
+ #
125
+ # @param [Relation] other
126
+ # the other relation to find the union with
127
+ #
128
+ # @return [Gateway]
129
+ # return a gateway if the adapters are equal
130
+ # @return [Algebra::Union]
131
+ # return a normal union when the adapters are not equal
132
+ #
133
+ # @api public
134
+ def union(other)
135
+ binary_operation(__method__, other, Algebra::Union)
136
+ end
137
+
138
+ # Return the intersection between relations
139
+ #
140
+ # @example
141
+ # intersect = gateway.intersect(other)
142
+ #
143
+ # @param [Relation] other
144
+ # the other relation to find the intersect with
145
+ #
146
+ # @return [Gateway]
147
+ # return a gateway if the adapters are equal
148
+ # @return [Algebra::Intersection]
149
+ # return a normal intersection when the adapters are not equal
150
+ #
151
+ # @api public
152
+ def intersect(other)
153
+ binary_operation(__method__, other, Algebra::Intersection)
154
+ end
155
+
156
+ # Return the diferrence between relations
157
+ #
158
+ # @example
159
+ # difference = gateway.difference(other)
160
+ #
161
+ # @param [Relation] other
162
+ # the other relation to find the difference with
163
+ #
164
+ # @return [Gateway]
165
+ # return a gateway if the adapters are equal
166
+ # @return [Algebra::Difference]
167
+ # return a normal dfference when the adapters are not equal
168
+ #
169
+ # @api public
170
+ def difference(other)
171
+ binary_operation(__method__, other, Algebra::Difference)
172
+ end
173
+
174
+ # Return a summarized relation
175
+ #
176
+ # @example with no arguments
177
+ # summarization = gateway.summarize do |context|
178
+ # context.add(:count, context[:id].count)
179
+ # end
180
+ #
181
+ # @example with a relation
182
+ # summarization = gateway.summarize(relation) do |context|
183
+ # context.add(:count, context[:id].count)
184
+ # end
185
+ #
186
+ # @example with a header
187
+ # summarization = gateway.summarize([ :name ]) do |context|
188
+ # context.add(:count, context[:id].count)
189
+ # end
190
+ #
191
+ # @example with another gateway
192
+ # summarization = gateway.summarize(other_gateway) do |context|
193
+ # context.add(:count, context[:id].count)
194
+ # end
195
+ #
196
+ # @param [Gateway, Relation, Header, #to_ary] summarize_with
197
+ #
198
+ # @yield [function]
199
+ # Evaluate a summarization function
200
+ #
201
+ # @yieldparam [Evaluator::Context] context
202
+ # the context to evaluate the function within
203
+ #
204
+ # @return [Gateway]
205
+ # return a gateway if the adapters are equal, or there is no adapter
206
+ # @return [Algebra::Summarization]
207
+ # return a normal summarization when the adapters are not equal
208
+ #
209
+ # @api public
210
+ def summarize(summarize_with = TABLE_DEE, &block)
211
+ if summarize_merge?(summarize_with)
212
+ summarize_merge(summarize_with, &block)
213
+ else
214
+ summarize_split(summarize_with, &block)
215
+ end
216
+ end
217
+
218
+ # Test if the method is supported on this object
219
+ #
220
+ # @param [Symbol] method
221
+ #
222
+ # @return [Boolean]
223
+ #
224
+ # @api private
225
+ def respond_to?(method, *)
226
+ super || forwardable?(method)
227
+ end
228
+
229
+ private
230
+
231
+ # Proxy the message to the relation
232
+ #
233
+ # @param [Symbol] method
234
+ #
235
+ # @param [Array] *args
236
+ #
237
+ # @return [self]
238
+ # return self for all command methods
239
+ # @return [Object]
240
+ # return response from all query methods
241
+ #
242
+ # @api private
243
+ def method_missing(method, *args, &block)
244
+ forwardable?(method) ? forward(method, *args, &block) : super
245
+ end
246
+
247
+ # Test if the method can be forwarded to the relation
248
+ #
249
+ # @param [Symbol] method
250
+ #
251
+ # @return [Boolean]
252
+ #
253
+ # @api private
254
+ def forwardable?(method)
255
+ relation.respond_to?(method)
256
+ end
257
+
258
+ # Forward the message to the relation
259
+ #
260
+ # @param [Array] *args
261
+ #
262
+ # @return [self]
263
+ # return self for all command methods
264
+ # @return [Object]
265
+ # return response from all query methods
266
+ #
267
+ # @api private
268
+ def forward(*args, &block)
269
+ relation = self.relation
270
+ response = relation.public_send(*args, &block)
271
+ if response.equal?(relation)
272
+ self
273
+ elsif response.kind_of?(DECORATED_CLASS)
274
+ self.class.new(adapter, response)
275
+ else
276
+ response
277
+ end
278
+ end
279
+
280
+ # Return a list of tuples to iterate over
281
+ #
282
+ # @return [#each]
283
+ #
284
+ # @api private
285
+ def tuples
286
+ relation = self.relation
287
+ if materialized?
288
+ relation
289
+ else
290
+ DECORATED_CLASS.new(header, adapter.read(relation))
291
+ end
292
+ end
293
+
294
+ # Return a binary relation
295
+ #
296
+ # @param [Relation] other
297
+ #
298
+ # @return [Gateway]
299
+ # return a gateway if the adapters are equal
300
+ # @return [Relation]
301
+ # return a binary relation when the adapters are not equal
302
+ #
303
+ # @api private
304
+ def binary_operation(method, other, factory)
305
+ if same_adapter?(other)
306
+ forward(method, other.relation)
307
+ else
308
+ factory.new(self, other)
309
+ end
310
+ end
311
+
312
+ # Test if the other object uses the same adapter
313
+ #
314
+ # @param [Gateway, Relation] other
315
+ #
316
+ # @return [Boolean]
317
+ #
318
+ # @api private
319
+ def same_adapter?(other)
320
+ other.respond_to?(:adapter) && adapter.eql?(other.adapter)
321
+ end
322
+
323
+ # Test if the summarize_with object can be merged into the summarization
324
+ #
325
+ # @param [Gateway, Relation, Header] summarize_with
326
+ #
327
+ # @return [Boolean]
328
+ #
329
+ # @api private
330
+ def summarize_merge?(summarize_with)
331
+ !summarize_with.respond_to?(:header) ||
332
+ summarize_with.equal?(TABLE_DEE) ||
333
+ same_adapter?(summarize_with)
334
+ end
335
+
336
+ # Merge the summarize_with into the summarization
337
+ #
338
+ # @param [Gateway, Relation, Header] summarize_with
339
+ #
340
+ # @return [Gateway]
341
+ #
342
+ # @api private
343
+ def summarize_merge(summarize_with, &block)
344
+ summarize_with = summarize_with.relation if summarize_with.respond_to?(:relation)
345
+ forward(:summarize, summarize_with, &block)
346
+ end
347
+
348
+ # Split the summarize_with into a separate relation, wrapped in a summarization
349
+ #
350
+ # @param [Gateway, Relation, Header] summarize_with
351
+ #
352
+ # @return [Algebra::Summarization]
353
+ #
354
+ # @api private
355
+ def summarize_split(summarize_with, &block)
356
+ # evaluate the gateway, then summarize with the provided relation
357
+ context = Evaluator::Context.new(header - summarize_with.header, &block)
358
+ Algebra::Summarization.new(self, summarize_with, context.functions)
359
+ end
360
+
361
+ end # class Gateway
362
+ end # class Relation
363
+ end # module Veritas
@@ -0,0 +1,4 @@
1
+ # encoding: utf-8
2
+
3
+ require 'veritas/adapter/data_objects'
4
+ require 'veritas/relation/gateway'
data/spec/rcov.opts ADDED
@@ -0,0 +1,6 @@
1
+ --exclude-only "spec/,^/"
2
+ --sort coverage
3
+ --callsites
4
+ --xrefs
5
+ --profile
6
+ --text-summary
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a binary relation method' do
4
+ describe 'when other has the same adapter' do
5
+ let(:other_relation) { mock('Other Relation') }
6
+ let(:other) { described_class.new(adapter, other_relation) }
7
+ let(:gateway) { mock('Other Gateway') }
8
+
9
+ before do
10
+ relation.stub!(operation).and_return(gateway)
11
+ end
12
+
13
+ it { should equal(gateway) }
14
+
15
+ it 'passes the other relation to the binary operation' do
16
+ relation.should_receive(operation).with(other_relation)
17
+ subject
18
+ end
19
+ end
20
+
21
+ describe 'when other has a different adapter' do
22
+ let(:other_adapter) { mock('Other Adapter') }
23
+ let(:other) { described_class.new(other_adapter, stub) }
24
+
25
+ before do
26
+ factory.stub!(:new).and_return(binary_relation)
27
+ end
28
+
29
+ it { should equal(binary_relation) }
30
+
31
+ it 'initializes the binary operation with the gateways' do
32
+ factory.should_receive(:new).with(object, other)
33
+ subject
34
+ end
35
+ end
36
+
37
+ describe 'when other has no adapter' do
38
+ let(:other) { mock('Other Relation') }
39
+
40
+ before do
41
+ factory.stub!(:new).and_return(binary_relation)
42
+ end
43
+
44
+ it { should equal(binary_relation) }
45
+
46
+ it 'initializes the binary operation with the gateway and other relation' do
47
+ factory.should_receive(:new).with(object, other)
48
+ subject
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a command method' do
4
+ it 'returns self' do
5
+ should equal(object)
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an #each method' do
4
+ it_should_behave_like 'a command method'
5
+
6
+ context 'with no block' do
7
+ subject { object.each }
8
+
9
+ it { should be_instance_of(to_enum.class) }
10
+
11
+ it 'yields the expected values' do
12
+ subject.to_a.should eql(object.to_a)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'an idempotent method' do
4
+ it 'is idempotent' do
5
+ should equal(instance_eval(&self.class.subject))
6
+ end
7
+ end
@@ -0,0 +1,21 @@
1
+ # encoding: utf-8
2
+
3
+ shared_examples_for 'a unary relation method' do
4
+ let(:gateway) { mock('New Gateway') }
5
+
6
+ before do
7
+ described_class.stub!(:new).and_return(gateway)
8
+ end
9
+
10
+ it { should equal(gateway) }
11
+
12
+ it 'tests the response is a relation' do
13
+ response.should_receive(:kind_of?).with(Relation)
14
+ subject
15
+ end
16
+
17
+ it 'initializes the new gateway with the adapter and response' do
18
+ described_class.should_receive(:new).with(adapter, response)
19
+ subject
20
+ end
21
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1,3 @@
1
+ --color
2
+ --loadby random
3
+ --format profile
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'backports'
5
+ require 'backports/basic_object'
6
+ require 'spec'
7
+ require 'spec/autorun'
8
+ require 'veritas'
9
+ require 'veritas-sql-generator'
10
+
11
+ include Veritas
12
+
13
+ # require spec support files and shared behavior
14
+ Dir[File.expand_path('../{support,shared}/**/*.rb', __FILE__)].each { |f| require f }
15
+
16
+ Spec::Runner.configure do |config|
17
+
18
+ # Record the original Attribute descendants
19
+ config.before do
20
+ @original_descendants = Attribute.descendants.dup
21
+ end
22
+
23
+ # Reset the Attribute descendants
24
+ config.after do
25
+ Attribute.descendants.replace(@original_descendants)
26
+ end
27
+
28
+ end
@@ -0,0 +1,15 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'veritas/adapter/data_objects'
5
+
6
+ describe Adapter::DataObjects, '.new' do
7
+ subject { object.new(uri) }
8
+
9
+ let(:uri) { stub }
10
+ let(:object) { described_class }
11
+
12
+ it { should be_instance_of(described_class) }
13
+
14
+ it { should be_frozen }
15
+ end
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'veritas/adapter/data_objects'
5
+
6
+ shared_examples_for 'it uses the data_objects driver' do
7
+ let(:connection) { mock('Connection', :close => nil) }
8
+
9
+ before do
10
+ DataObjects::Connection.stub!(:new).and_return(connection)
11
+ end
12
+
13
+ it 'opens a connection' do
14
+ DataObjects::Connection.should_receive(:new).with(uri).and_return(connection)
15
+ subject
16
+ end
17
+
18
+ it 'closes a connection' do
19
+ connection.should_receive(:close).with(no_args)
20
+ subject
21
+ end
22
+
23
+ it 'does not close a connection if the constructor throws an exception' do
24
+ mock_exception = Class.new(Exception)
25
+ DataObjects::Connection.should_receive(:new).and_raise(mock_exception)
26
+ connection.should_not_receive(:close)
27
+ expect { subject }.to raise_error(mock_exception)
28
+ end
29
+ end
30
+
31
+ describe Adapter::DataObjects, '#read' do
32
+ let(:uri) { stub }
33
+ let(:object) { described_class.new(uri) }
34
+ let(:relation) { mock('Relation') }
35
+ let(:statement) { mock('Statement') }
36
+ let(:rows) { [ [ 1 ], [ 2 ], [ 3 ] ] }
37
+ let(:yields) { [] }
38
+
39
+ before do
40
+ expectation = statement.stub(:each)
41
+ rows.each { |row| expectation.and_yield(row) }
42
+
43
+ described_class::Statement.stub!(:new).and_return(statement)
44
+ end
45
+
46
+ context 'with a block' do
47
+ subject { object.read(relation) { |row| yields << row } }
48
+
49
+
50
+ it_should_behave_like 'it uses the data_objects driver'
51
+ it_should_behave_like 'a command method'
52
+
53
+ it 'yields each row' do
54
+ expect { subject }.to change { yields.dup }.
55
+ from([]).
56
+ to(rows)
57
+ end
58
+
59
+ it 'initializes a statement' do
60
+ described_class::Statement.should_receive(:new).with(connection, relation).and_return(statement)
61
+ subject
62
+ end
63
+ end
64
+
65
+ context 'without a block' do
66
+ subject { object.read(relation) }
67
+
68
+ it { should be_instance_of(to_enum.class) }
69
+ end
70
+ end
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'veritas/adapter/data_objects/statement'
5
+
6
+ describe Adapter::DataObjects::Statement, '.new' do
7
+ let(:connection) { stub }
8
+ let(:relation) { stub }
9
+ let(:object) { described_class }
10
+
11
+ context 'without a visitor' do
12
+ subject { object.new(connection, relation) }
13
+
14
+ it { should be_instance_of(described_class) }
15
+
16
+ it { should be_frozen }
17
+ end
18
+
19
+ context 'with a visitor' do
20
+ subject { object.new(connection, relation, visitor) }
21
+
22
+ let(:visitor) { stub }
23
+
24
+ it { should be_instance_of(described_class) }
25
+
26
+ it { should be_frozen }
27
+ end
28
+ end
@@ -0,0 +1,63 @@
1
+ # encoding: utf-8
2
+
3
+ require 'spec_helper'
4
+ require 'veritas/adapter/data_objects/statement'
5
+
6
+ describe Adapter::DataObjects::Statement, '#each' do
7
+ let(:reader) { mock('Reader', :next! => false).as_null_object }
8
+ let(:command) { mock('Command', :execute_reader => reader).as_null_object }
9
+ let(:connection) { mock('Connection', :create_command => command) }
10
+ let(:attribute) { stub }
11
+ let(:primitive) { stub }
12
+ let(:relation) { mock('Relation', :header => [ attribute ]) }
13
+ let(:generator) { mock('Generator').as_null_object }
14
+ let(:rows) { [ stub, stub ] }
15
+ let(:object) { described_class.new(connection, relation, generator) }
16
+ let(:yields) { [] }
17
+
18
+ before do
19
+ command.stub!(:dup => command, :freeze => command)
20
+ attribute.stub_chain(:class, :primitive).and_return(primitive)
21
+ end
22
+
23
+ context 'with no block' do
24
+ subject { object.each }
25
+
26
+ it { should be_instance_of(to_enum.class) }
27
+
28
+ it 'yields the expected attributes' do
29
+ subject.to_a.should eql(object.to_a)
30
+ end
31
+ end
32
+
33
+ context 'with a block' do
34
+ subject { object.each { |row| yields << row } }
35
+
36
+ before do
37
+ connection.should_receive(:create_command).with(object.to_s)
38
+
39
+ command.should_receive(:set_types).with([ primitive ]).ordered
40
+ command.should_receive(:execute_reader).with(no_args).ordered
41
+
42
+ rows.each do |values|
43
+ reader.should_receive(:next!).with(no_args).ordered.and_return(true)
44
+ reader.should_receive(:values).with(no_args).ordered.and_return(values)
45
+ end
46
+
47
+ reader.should_receive(:next!).with(no_args).ordered.and_return(false)
48
+ reader.should_receive(:close).with(no_args).ordered
49
+ end
50
+
51
+ before do
52
+ relation.should_receive(:header)
53
+ end
54
+
55
+ it_should_behave_like 'a command method'
56
+
57
+ it 'yields each row' do
58
+ expect { subject }.to change { yields.dup }.
59
+ from([]).
60
+ to(rows)
61
+ end
62
+ end
63
+ end