guacamole 0.0.1

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +3 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +18 -0
  6. data/CONTRIBUTING.md +26 -0
  7. data/Gemfile +16 -0
  8. data/Gemfile.devtools +55 -0
  9. data/Guardfile +13 -0
  10. data/LICENSE +202 -0
  11. data/README.md +130 -0
  12. data/Rakefile +7 -0
  13. data/config/devtools.yml +4 -0
  14. data/config/flay.yml +3 -0
  15. data/config/flog.yml +2 -0
  16. data/config/mutant.yml +3 -0
  17. data/config/reek.yml +104 -0
  18. data/config/rubocop.yml +68 -0
  19. data/config/yardstick.yml +2 -0
  20. data/guacamole.gemspec +28 -0
  21. data/lib/guacamole.rb +19 -0
  22. data/lib/guacamole/collection.rb +246 -0
  23. data/lib/guacamole/configuration.rb +123 -0
  24. data/lib/guacamole/document_model_mapper.rb +95 -0
  25. data/lib/guacamole/model.rb +210 -0
  26. data/lib/guacamole/query.rb +72 -0
  27. data/lib/guacamole/railtie.rb +27 -0
  28. data/lib/guacamole/railtie/database.rake +5 -0
  29. data/lib/guacamole/version.rb +5 -0
  30. data/log/.gitkeep +0 -0
  31. data/spec/acceptance/.gitkeep +0 -0
  32. data/spec/acceptance/basic_spec.rb +112 -0
  33. data/spec/acceptance/config/guacamole.yml +11 -0
  34. data/spec/acceptance/spec_helper.rb +61 -0
  35. data/spec/fabricators/article_fabricator.rb +10 -0
  36. data/spec/fabricators/comment_fabricator.rb +5 -0
  37. data/spec/setup/arangodb.sh +65 -0
  38. data/spec/spec_helper.rb +19 -0
  39. data/spec/unit/.gitkeep +0 -0
  40. data/spec/unit/collection_spec.rb +380 -0
  41. data/spec/unit/configuration_spec.rb +119 -0
  42. data/spec/unit/document_model_mapper_spec.rb +120 -0
  43. data/spec/unit/example_spec.rb +8 -0
  44. data/spec/unit/model_spec.rb +116 -0
  45. data/spec/unit/query_spec.rb +140 -0
  46. data/tasks/adjustments.rake +35 -0
  47. metadata +191 -0
@@ -0,0 +1,27 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'guacamole'
4
+ require 'guacamole/configuration'
5
+
6
+ require 'rails'
7
+
8
+ module Guacamole
9
+ # Class to hook into Rails configuration and initializer
10
+ # @api private
11
+ class Railtie < Rails::Railtie
12
+
13
+ rake_tasks do
14
+ load 'guacamole/railtie/database.rake'
15
+ end
16
+
17
+ config.guacamole = ::Guacamole::Configuration
18
+
19
+ initializer 'guacamole.load-config' do
20
+ config_file = Rails.root.join('config', 'guacamole.yml')
21
+ if config_file.file?
22
+ Guacamole::Configuration.load config_file
23
+ end
24
+ end
25
+
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ namespace :db do
2
+ # TODO: Add all the Rails-tasks.
3
+ # If we cannot do something meaningful provide a noop.
4
+ # Foremost the creation of databases should be provided.
5
+ end
@@ -0,0 +1,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Guacamole
3
+ # Current version of the gem
4
+ VERSION = '0.0.1'
5
+ end
data/log/.gitkeep ADDED
File without changes
File without changes
@@ -0,0 +1,112 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'guacamole'
3
+ require 'acceptance/spec_helper'
4
+
5
+ class Comment
6
+ include Guacamole::Model
7
+
8
+ attribute :text, String
9
+ end
10
+
11
+ class Article
12
+ include Guacamole::Model
13
+
14
+ attribute :title, String
15
+ attribute :comments, Array[Comment]
16
+
17
+ validates :title, presence: true
18
+ end
19
+
20
+ class ArticlesCollection
21
+ include Guacamole::Collection
22
+
23
+ map do
24
+ embeds :comments
25
+ end
26
+ end
27
+
28
+ describe 'ModelBasics' do
29
+
30
+ describe Article do
31
+ it 'should allow setting its title' do
32
+ subject.title = 'This is my fancy article'
33
+
34
+ expect(subject.title).to eq('This is my fancy article')
35
+ end
36
+
37
+ it 'should have key and rev attributes' do
38
+ expect(subject.key).to be_nil
39
+ expect(subject.rev).to be_nil
40
+ end
41
+
42
+ it 'should have timestamp attributes which are empty' do
43
+ expect(subject.created_at).to be_nil
44
+ expect(subject.updated_at).to be_nil
45
+ end
46
+
47
+ it 'should validate its attributes' do
48
+ expect(subject.valid?).to be_false
49
+ subject.title = 'The Legend of Zelda'
50
+ expect(subject.valid?).to be_true
51
+ end
52
+
53
+ it 'should know its model name' do
54
+ # This test passes when you only require ActiveModel::Validations
55
+ expect(subject.class.model_name).to eq 'Article'
56
+ end
57
+
58
+ it 'should convert itself to params' do
59
+ subject.key = 'random_number'
60
+ expect(subject.to_param).to eq 'random_number'
61
+ end
62
+ end
63
+
64
+ end
65
+
66
+ describe 'CollectionBasics' do
67
+
68
+ describe ArticlesCollection do
69
+ subject { ArticlesCollection }
70
+
71
+ let(:some_article) { Fabricate(:article) }
72
+
73
+ it 'should provide a method to find documents by key and return the appropriate model' do
74
+ found_model = subject.by_key some_article.key
75
+ expect(found_model).to eq some_article
76
+ end
77
+
78
+ it 'should save models to the database' do
79
+ new_article = Fabricate.build(:article)
80
+ subject.save new_article
81
+
82
+ expect(subject.by_key(new_article.key)).to eq new_article
83
+ end
84
+
85
+ it 'should update models in the database' do
86
+ some_article.title = 'Has been updated'
87
+ subject.replace some_article
88
+
89
+ updated_article = subject.by_key(some_article.key)
90
+
91
+ expect(updated_article.title).to eq 'Has been updated'
92
+ end
93
+
94
+ it 'should receive all documents by title' do
95
+ subject.save Fabricate.build(:article, title: 'Disturbed')
96
+ subject.save Fabricate.build(:article, title: 'Not so Disturbed')
97
+
98
+ result = subject.by_example(title: 'Disturbed').first
99
+
100
+ expect(result.title).to eq 'Disturbed'
101
+ end
102
+
103
+ it 'should allow to nest models' do
104
+ article_with_comments = Fabricate(:article_with_two_comments)
105
+ found_article = subject.by_key(article_with_comments.key)
106
+
107
+ expect(found_article.comments.first).to be_a Comment
108
+ expect(found_article.comments).to eq article_with_comments.comments
109
+ end
110
+ end
111
+
112
+ end
@@ -0,0 +1,11 @@
1
+ # FIXME: This is copied from Ashikawa::Core for now but is not recommended. This
2
+ # setup uses the default database instead of a custom DB. Due to this we're deleting
3
+ # all collections in the default database each time we run the specs.
4
+ # => This is not good!
5
+ test:
6
+ protocol: 'http'
7
+ host: 'localhost'
8
+ port: 8529
9
+ password: ''
10
+ username: ''
11
+ database: '_system'
@@ -0,0 +1,61 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+
6
+ require 'fabrication'
7
+ require 'logging'
8
+ require 'ashikawa-core'
9
+
10
+ begin
11
+ require 'debugger'
12
+ rescue LoadError
13
+ puts "Debugger is not available. Maybe you're Travis."
14
+ end
15
+
16
+ class Fabrication::Generator::Guacamole < Fabrication::Generator::Base
17
+
18
+ def self.supports?(klass)
19
+ defined?(Guacamole) && klass.ancestors.include?(Guacamole::Model)
20
+ end
21
+
22
+ def persist
23
+ collection = [_instance.class.name.pluralize, 'Collection'].join.constantize
24
+ collection.save _instance
25
+ end
26
+
27
+ def validate_instance
28
+ _instance.valid?
29
+ end
30
+
31
+ end
32
+
33
+ Fabrication::Schematic::Definition::GENERATORS.unshift Fabrication::Generator::Guacamole
34
+
35
+ ENV['GUACAMOLE_ENV'] = 'test'
36
+
37
+ Guacamole.configure do |config|
38
+ logger = Logging.logger['guacamole_logger']
39
+ logger.add_appenders(
40
+ Logging.appenders.file('log/acceptance.log')
41
+ )
42
+ logger.level = :info
43
+
44
+ config.logger = logger
45
+
46
+ config.load File.join(File.dirname(__FILE__), 'config', 'guacamole.yml')
47
+ end
48
+
49
+ RSpec.configure do |config|
50
+ config.expect_with :rspec do |c|
51
+ c.syntax = :expect
52
+ end
53
+
54
+ config.mock_with :rspec do |c|
55
+ c.syntax = :expect
56
+ end
57
+
58
+ config.before(:each) do
59
+ Guacamole.configuration.database.collections.each { |collection| collection.truncate! }
60
+ end
61
+ end
@@ -0,0 +1,10 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Fabricator(:article) do
4
+ title 'And then there was silence'
5
+ end
6
+
7
+ Fabricator(:article_with_two_comments, from: Article) do
8
+ title 'I have two comments'
9
+ comments(count: 2) { |attrs, i| Fabricate.build(:comment, text: "I'm comment number #{i}") }
10
+ end
@@ -0,0 +1,5 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Fabricator(:comment) do
4
+ text 'This article is super awesome'
5
+ end
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+
3
+ DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
4
+ cd $DIR
5
+
6
+ # VERSION=1.4.devel
7
+ NAME=ArangoDB-$VERSION
8
+
9
+ if [ ! -d "$DIR/$NAME" ]; then
10
+ # download ArangoDB
11
+ echo "wget http://www.arangodb.org/travisCI/$NAME.tar.gz"
12
+ wget http://www.arangodb.org/travisCI/$NAME.tar.gz
13
+ echo "tar zxf $NAME.tar.gz"
14
+ tar zvxf $NAME.tar.gz
15
+ fi
16
+
17
+ ARCH=$(arch)
18
+ PID=$(echo $PPID)
19
+ TMP_DIR="/tmp/arangodb.$PID"
20
+ PID_FILE="/tmp/arangodb.$PID.pid"
21
+ ARANGODB_DIR="$DIR/$NAME"
22
+
23
+ ARANGOD="${ARANGODB_DIR}/bin/arangod"
24
+ if [ "$ARCH" == "x86_64" ]; then
25
+ ARANGOD="${ARANGOD}_x86_64"
26
+ fi
27
+
28
+ # create database directory
29
+ mkdir ${TMP_DIR}
30
+
31
+ echo "Starting arangodb '${ARANGOD}'"
32
+
33
+ ${ARANGOD} \
34
+ --database.directory ${TMP_DIR} \
35
+ --configuration none \
36
+ --server.endpoint tcp://127.0.0.1:8529 \
37
+ --javascript.startup-directory ${ARANGODB_DIR}/js \
38
+ --server.admin-directory ${ARANGODB_DIR}/html/admin \
39
+ --javascript.modules-path ${ARANGODB_DIR}/js/server/modules:${ARANGODB_DIR}/js/common/modules:${ARANGODB_DIR}/js/node \
40
+ --javascript.package-path ${ARANGODB_DIR}/js/npm:${ARANGODB_DIR}/js/common/test-data/modules \
41
+ --javascript.action-directory ${ARANGODB_DIR}/js/actions \
42
+ --javascript.app-path ${ARANGODB_DIR}/js/apps \
43
+ --database.maximal-journal-size 1048576 \
44
+ --server.disable-admin-interface ${ARANGODB_DISABLE_AUTHENTIFICATION} \
45
+ --server.disable-authentication true \
46
+ --javascript.gc-interval 1 &
47
+
48
+ sleep 2
49
+
50
+ echo "Check for arangod process"
51
+ process=$(ps auxww | grep "bin/arangod" | grep -v grep)
52
+
53
+ if [ "x$process" == "x" ]; then
54
+ echo "no 'arangod' process found"
55
+ echo "ARCH = $ARCH"
56
+ exit 1
57
+ fi
58
+
59
+ echo "Waiting until ArangoDB is ready on port 8529"
60
+ while [[ -z `curl -s 'http://127.0.0.1:8529/_api/version' ` ]] ; do
61
+ echo -n "."
62
+ sleep 2s
63
+ done
64
+
65
+ echo "ArangoDB is up"
@@ -0,0 +1,19 @@
1
+ # -*- encoding : utf-8 -*-
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ begin
6
+ require 'debugger'
7
+ rescue LoadError
8
+ puts "Debugger is not available. Maybe you're Travis."
9
+ end
10
+
11
+ RSpec.configure do |config|
12
+ config.expect_with :rspec do |c|
13
+ c.syntax = :expect
14
+ end
15
+
16
+ config.mock_with :rspec do |c|
17
+ c.syntax = :expect
18
+ end
19
+ end
File without changes
@@ -0,0 +1,380 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'guacamole/collection'
5
+
6
+ class Test
7
+ end
8
+
9
+ class TestCollection
10
+ include Guacamole::Collection
11
+ end
12
+
13
+ describe Guacamole::Collection do
14
+ subject { TestCollection }
15
+
16
+ describe 'Configuration' do
17
+ it 'should set the connection to the ArangoDB collection' do
18
+ mock_collection_connection = double('ConnectionToCollection')
19
+ subject.connection = mock_collection_connection
20
+
21
+ expect(subject.connection).to eq mock_collection_connection
22
+ end
23
+
24
+ it 'should set the Mapper instance to map documents to models and vice versa' do
25
+ mock_mapper = double('Mapper')
26
+ subject.mapper = mock_mapper
27
+
28
+ expect(subject.mapper).to eq mock_mapper
29
+ end
30
+
31
+ it 'should set the connection to ArangoDB' do
32
+ mock_db = double('Ashikawa::Core::Database')
33
+ subject.database = mock_db
34
+
35
+ expect(subject.database).to eq mock_db
36
+ end
37
+
38
+ it 'should know the name of the collection in ArangoDB' do
39
+ expect(subject.collection_name).to eq 'test'
40
+ end
41
+
42
+ it 'should know the class of the model to manage' do
43
+ expect(subject.model_class).to eq Test
44
+ end
45
+ end
46
+
47
+ describe 'database' do
48
+ before do
49
+ subject.database = nil
50
+ end
51
+
52
+ it 'should default to Guacamole.configuration.database' do
53
+ default_database = double('Database')
54
+ configuration = double('Configuration', database: default_database)
55
+ allow(Guacamole).to receive(:configuration).and_return(configuration)
56
+
57
+ expect(subject.database).to eq default_database
58
+ end
59
+ end
60
+
61
+ describe 'connection' do
62
+ before do
63
+ subject.connection = nil
64
+ end
65
+
66
+ it 'should default to the collection "collection_name" in the database' do
67
+ database = double('Database')
68
+ allow(subject).to receive(:database).and_return(database)
69
+
70
+ expect(database).to receive(:[]).with(subject.collection_name)
71
+
72
+ subject.connection
73
+ end
74
+ end
75
+
76
+ describe 'mapper' do
77
+ before do
78
+ subject.mapper = nil
79
+ end
80
+
81
+ it 'should default to Guacamole.configuration.default_mapper' do
82
+ default_mapper = double('Mapper')
83
+ mapper_instance = double('MapperInstance')
84
+ configuration = double('Configuration', default_mapper: default_mapper)
85
+ allow(Guacamole).to receive(:configuration).and_return(configuration)
86
+ allow(default_mapper).to receive(:new).with(subject.model_class).and_return(mapper_instance)
87
+
88
+ expect(subject.mapper).to eq mapper_instance
89
+ end
90
+ end
91
+
92
+ let(:connection) { double('Connection') }
93
+ let(:mapper) { double('Mapper') }
94
+
95
+ before do
96
+ subject.connection = connection
97
+ subject.mapper = mapper
98
+ end
99
+
100
+ describe 'by_key' do
101
+ it 'should get mapped documents by key from the database' do
102
+ document = { data: 'foo' }
103
+ model = double('Model')
104
+
105
+ expect(connection).to receive(:fetch).with('some_key').and_return(document)
106
+ expect(mapper).to receive(:document_to_model).with(document).and_return(model)
107
+
108
+ expect(subject.by_key('some_key')).to eq model
109
+ end
110
+
111
+ it 'should raise a Ashikawa::Core::DocumentNotFoundException exception for nil' do
112
+ expect { subject.by_key(nil) }.to raise_error(Ashikawa::Core::DocumentNotFoundException)
113
+ end
114
+ end
115
+
116
+ describe 'save' do
117
+
118
+ before do
119
+ allow(connection).to receive(:create_document).with(document).and_return(document)
120
+ allow(mapper).to receive(:model_to_document).with(model).and_return(document)
121
+ end
122
+
123
+ let(:key) { double('Key') }
124
+ let(:rev) { double('Rev') }
125
+ let(:document) { double('Document', key: key, revision: rev).as_null_object }
126
+ let(:model) { double('Model').as_null_object }
127
+
128
+ context 'a valid model' do
129
+ before do
130
+ allow(model).to receive(:valid?).and_return(true)
131
+ end
132
+
133
+ it 'should create a document' do
134
+ expect(connection).to receive(:create_document).with(document).and_return(document)
135
+ expect(mapper).to receive(:model_to_document).with(model).and_return(document)
136
+
137
+ subject.save model
138
+ end
139
+
140
+ it 'should return the model after calling save' do
141
+ expect(subject.save(model)).to eq model
142
+ end
143
+
144
+ it 'should set timestamps before creating the document' do
145
+ now = double('Time.now')
146
+
147
+ allow(Time).to receive(:now).once.and_return(now)
148
+
149
+ expect(model).to receive(:created_at=).with(now).ordered
150
+ expect(model).to receive(:updated_at=).with(now).ordered
151
+
152
+ allow(connection).to receive(:create_document).with(document).and_return(document).ordered
153
+
154
+ subject.save model
155
+ end
156
+
157
+ it 'should add key to model' do
158
+ expect(model).to receive(:key=).with(key)
159
+
160
+ subject.save model
161
+ end
162
+
163
+ it 'should add rev to model' do
164
+ expect(model).to receive(:rev=).with(rev)
165
+
166
+ subject.save model
167
+ end
168
+ end
169
+
170
+ context 'an invalid model' do
171
+
172
+ before do
173
+ expect(model).to receive(:valid?).and_return(false)
174
+ end
175
+
176
+ it 'should not be used to create the document' do
177
+ expect(connection).to receive(:create_document).never
178
+
179
+ subject.save model
180
+ end
181
+
182
+ it 'should not be changed' do
183
+ expect(model).to receive(:created_at=).never
184
+ expect(model).to receive(:updated_at=).never
185
+ expect(model).to receive(:key=).never
186
+ expect(model).to receive(:rev=).never
187
+
188
+ subject.save model
189
+ end
190
+
191
+ it 'should return false' do
192
+ expect(subject.save(model)).to be false
193
+ end
194
+ end
195
+ end
196
+
197
+ describe 'delete' do
198
+ let(:document) { double('Document') }
199
+ let(:key) { double('Key') }
200
+ let(:model) { double('Model', key: key) }
201
+
202
+ before do
203
+ allow(connection).to receive(:fetch).with(key).and_return(document)
204
+ allow(document).to receive(:delete)
205
+ end
206
+
207
+ context 'a key was provided' do
208
+ it 'should delete the according document' do
209
+ expect(document).to receive(:delete)
210
+
211
+ subject.delete key
212
+ end
213
+
214
+ it 'should return the according key' do
215
+ expect(subject.delete(key)).to eq key
216
+ end
217
+ end
218
+
219
+ context 'a model was provided' do
220
+ it 'should delete the according document' do
221
+ expect(document).to receive(:delete)
222
+
223
+ subject.delete model
224
+ end
225
+
226
+ it 'should return the according key' do
227
+ expect(subject.delete(model)).to eq key
228
+ end
229
+ end
230
+ end
231
+
232
+ describe 'replace' do
233
+ let(:key) { double('Key') }
234
+ let(:rev) { double('Rev') }
235
+ let(:model) { double('Model', key: key).as_null_object }
236
+ let(:document) { double('Document').as_null_object }
237
+ let(:response) { double('Hash') }
238
+
239
+ before do
240
+ allow(mapper).to receive(:model_to_document).with(model).and_return(document)
241
+ allow(connection).to receive(:replace).and_return(response)
242
+ allow(response).to receive(:[]).with('_rev').and_return(rev)
243
+ end
244
+
245
+ context 'a valid model' do
246
+ before do
247
+ allow(model).to receive(:valid?).and_return(true)
248
+ end
249
+
250
+ it 'should set the updated_at timestamp before replacing the document' do
251
+ now = double('Time.now')
252
+
253
+ allow(Time).to receive(:now).once.and_return(now)
254
+ expect(model).to receive(:updated_at=).with(now)
255
+
256
+ subject.replace model
257
+ end
258
+
259
+ it 'should replace the document by key via the connection' do
260
+ expect(connection).to receive(:replace).with(key, document)
261
+
262
+ subject.replace model
263
+ end
264
+
265
+ it 'should update the revision after replacing the document' do
266
+ allow(connection).to receive(:replace).and_return(response).ordered
267
+ expect(model).to receive(:rev=).with(rev).ordered
268
+
269
+ subject.replace model
270
+ end
271
+
272
+ it 'should return the model' do
273
+ expect(subject.replace(model)).to eq model
274
+ end
275
+
276
+ it 'should not update created_at' do
277
+ expect(model).to receive(:created_at=).never
278
+
279
+ subject.replace model
280
+ end
281
+ end
282
+
283
+ context 'an invalid model' do
284
+ before do
285
+ allow(model).to receive(:valid?).and_return(false)
286
+ end
287
+
288
+ it 'should not be used to replace the document' do
289
+ expect(connection).to receive(:replace).never
290
+
291
+ subject.replace model
292
+ end
293
+
294
+ it 'should not be changed' do
295
+ expect(model).to receive(:rev=).never
296
+ expect(model).to receive(:updated_at=).never
297
+
298
+ subject.replace model
299
+ end
300
+
301
+ it 'should return false' do
302
+ expect(subject.replace(model)).to be false
303
+ end
304
+ end
305
+ end
306
+
307
+ describe 'by_example' do
308
+ let(:example) { double }
309
+ let(:query_connection) { double }
310
+ let(:query) { double }
311
+
312
+ before do
313
+ allow(connection).to receive(:query)
314
+ .and_return(query_connection)
315
+
316
+ allow(Guacamole::Query).to receive(:new)
317
+ .and_return(query)
318
+
319
+ allow(query).to receive(:example=)
320
+ end
321
+
322
+ it 'should create a new query with the query connection and mapper' do
323
+ expect(Guacamole::Query).to receive(:new)
324
+ .with(query_connection, mapper)
325
+
326
+ subject.by_example(example)
327
+ end
328
+
329
+ it 'should set the example for the query' do
330
+ expect(query).to receive(:example=)
331
+ .with(example)
332
+
333
+ subject.by_example(example)
334
+ end
335
+
336
+ it 'should return the query' do
337
+ expect(subject.by_example(example)).to be query
338
+ end
339
+ end
340
+
341
+ describe 'all' do
342
+ let(:query_connection) { double }
343
+ let(:query) { double }
344
+
345
+ before do
346
+ allow(connection).to receive(:query)
347
+ .and_return(query_connection)
348
+
349
+ allow(Guacamole::Query).to receive(:new)
350
+ .and_return(query)
351
+ end
352
+
353
+ it 'should create a new query with the query connection and mapper' do
354
+ expect(Guacamole::Query).to receive(:new)
355
+ .with(query_connection, mapper)
356
+
357
+ subject.all
358
+ end
359
+
360
+ it 'should return the query' do
361
+ expect(subject.all).to be query
362
+ end
363
+ end
364
+
365
+ describe 'map' do
366
+ let(:mapper) { double('Mapper') }
367
+
368
+ before do
369
+ subject.mapper = mapper
370
+ end
371
+
372
+ it 'should evaluate the block on the mapper instance' do
373
+ expect(mapper).to receive(:method_to_call_on_mapper)
374
+
375
+ subject.map do
376
+ method_to_call_on_mapper
377
+ end
378
+ end
379
+ end
380
+ end