guacamole 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +1 -1
  3. data/.travis.yml +16 -15
  4. data/CHANGELOG.md +16 -0
  5. data/GOALS.md +8 -0
  6. data/Guardfile +4 -0
  7. data/README.md +21 -81
  8. data/guacamole.gemspec +3 -3
  9. data/lib/guacamole.rb +1 -0
  10. data/lib/guacamole/aql_query.rb +6 -1
  11. data/lib/guacamole/collection.rb +34 -66
  12. data/lib/guacamole/configuration.rb +53 -25
  13. data/lib/guacamole/document_model_mapper.rb +149 -38
  14. data/lib/guacamole/edge.rb +74 -0
  15. data/lib/guacamole/edge_collection.rb +91 -0
  16. data/lib/guacamole/exceptions.rb +0 -5
  17. data/lib/guacamole/graph_query.rb +31 -0
  18. data/lib/guacamole/model.rb +4 -0
  19. data/lib/guacamole/proxies/proxy.rb +7 -3
  20. data/lib/guacamole/proxies/relation.rb +22 -0
  21. data/lib/guacamole/railtie.rb +1 -1
  22. data/lib/guacamole/transaction.rb +177 -0
  23. data/lib/guacamole/version.rb +1 -1
  24. data/shared/transaction.js +66 -0
  25. data/spec/acceptance/aql_spec.rb +32 -40
  26. data/spec/acceptance/relations_spec.rb +239 -0
  27. data/spec/acceptance/spec_helper.rb +2 -2
  28. data/spec/fabricators/author_fabricator.rb +2 -0
  29. data/spec/setup/arangodb.sh +2 -2
  30. data/spec/unit/collection_spec.rb +20 -97
  31. data/spec/unit/configuration_spec.rb +73 -50
  32. data/spec/unit/document_model_mapper_spec.rb +84 -77
  33. data/spec/unit/edge_collection_spec.rb +174 -0
  34. data/spec/unit/edge_spec.rb +57 -0
  35. data/spec/unit/proxies/relation_spec.rb +35 -0
  36. metadata +22 -14
  37. data/lib/guacamole/proxies/referenced_by.rb +0 -15
  38. data/lib/guacamole/proxies/references.rb +0 -15
  39. data/spec/acceptance/association_spec.rb +0 -40
  40. data/spec/unit/example_spec.rb +0 -8
@@ -81,6 +81,57 @@ describe Guacamole::Configuration do
81
81
  end
82
82
  end
83
83
 
84
+ describe 'graph' do
85
+ let(:database) { instance_double('Ashikawa::Core::Database') }
86
+ let(:graph) { instance_double('Ashikawa::Core::Graph') }
87
+ let(:graph_name) { 'my-amazing-graph' }
88
+
89
+ before do
90
+ subject.graph_name = nil
91
+ allow(subject).to receive(:database).and_return(database)
92
+ allow(database).to receive(:graph).with(graph_name).and_return(graph)
93
+ end
94
+
95
+ it 'should allow access to the associated graph based on the graph_name attribute' do
96
+ allow(subject).to receive(:graph_name).and_return(graph_name)
97
+
98
+ expect(subject.graph).to eq graph
99
+ end
100
+
101
+ context 'configure the graph name' do
102
+ context 'within a Rails application' do
103
+ let(:rails_module) { double('Rails') }
104
+ let(:application_class) { double('ApplicationClass', name: 'MyAwesomeApp::Application') }
105
+ let(:rails_application) { double('MyAwesomeApp::Application', class: application_class) }
106
+
107
+ before do
108
+ allow(rails_module).to receive(:application).and_return(rails_application)
109
+ stub_const('Rails', rails_module)
110
+ end
111
+
112
+ its(:graph_name) { should eq 'my_awesome_app_graph' }
113
+ end
114
+
115
+ it 'should be generated based on the current database name' do
116
+ allow(database).to receive(:name).and_return('my_database')
117
+
118
+ expect(subject.graph_name).to eq 'my_database_graph'
119
+ end
120
+
121
+ it 'should use a custom set graph name' do
122
+ subject.graph_name = 'fabulous_graph'
123
+
124
+ expect(subject.graph_name).to eq 'fabulous_graph'
125
+ end
126
+
127
+ it 'should take the graph name from the ENV' do
128
+ allow(ENV).to receive(:[]).with('GUACAMOLE_GRAPH').and_return('graph_from_env')
129
+
130
+ expect(subject.graph_name).to eq 'graph_from_env'
131
+ end
132
+ end
133
+ end
134
+
84
135
  describe 'build_config' do
85
136
  context 'from a hash' do
86
137
  let(:config_hash) do
@@ -90,47 +141,29 @@ describe Guacamole::Configuration do
90
141
  'port' => 8529,
91
142
  'username' => 'username',
92
143
  'password' => 'password',
93
- 'database' => 'awesome_db'
144
+ 'database' => 'awesome_db',
145
+ 'graph' => 'custom_graph'
94
146
  }
95
147
  end
96
- let(:config_struct) { subject.build_config(config_hash) }
97
-
98
- it 'should create a struct with a database URL' do
99
- expect(config_struct.url).to eq 'http://localhost:8529'
100
- end
101
-
102
- it 'should create a struct with a username' do
103
- expect(config_struct.username).to eq 'username'
104
- end
105
148
 
106
- it 'should create a struct with password' do
107
- expect(config_struct.password).to eq 'password'
108
- end
149
+ subject { Guacamole::Configuration.build_config(config_hash) }
109
150
 
110
- it 'should create a struct with database' do
111
- expect(config_struct.database).to eq 'awesome_db'
112
- end
151
+ its(:url) { should eq 'http://localhost:8529' }
152
+ its(:username) { should eq 'username' }
153
+ its(:password) { should eq 'password' }
154
+ its(:database) { should eq 'awesome_db' }
155
+ its(:graph) { should eq 'custom_graph' }
113
156
  end
114
157
 
115
158
  context 'from a URL' do
116
159
  let(:database_url) { 'http://username:password@localhost:8529/_db/awesome_db' }
117
- let(:config_struct) { subject.build_config(database_url) }
118
-
119
- it 'should create a struct with a database URL' do
120
- expect(config_struct.url).to eq 'http://localhost:8529'
121
- end
122
-
123
- it 'should create a struct with a username' do
124
- expect(config_struct.username).to eq 'username'
125
- end
126
160
 
127
- it 'should create a struct with password' do
128
- expect(config_struct.password).to eq 'password'
129
- end
161
+ subject { Guacamole::Configuration.build_config(database_url) }
130
162
 
131
- it 'should create a struct with database' do
132
- expect(config_struct.database).to eq 'awesome_db'
133
- end
163
+ its(:url) { should eq 'http://localhost:8529' }
164
+ its(:username) { should eq 'username' }
165
+ its(:password) { should eq 'password' }
166
+ its(:database) { should eq 'awesome_db' }
134
167
  end
135
168
  end
136
169
 
@@ -178,8 +211,9 @@ describe Guacamole::Configuration do
178
211
  allow(subject).to receive(:create_database_connection)
179
212
  allow(subject).to receive(:process_file_with_erb).with('config_file.yml')
180
213
  allow(subject).to receive(:build_config).and_return(config_struct)
181
- allow(config).to receive(:[]).with('development').and_return(env_config)
182
- allow(YAML).to receive(:load).and_return(config)
214
+ allow(config).to receive(:[]).with('development').and_return(env_config)
215
+ allow(config_struct).to receive(:graph).and_return('custom_graph_name')
216
+ allow(YAML).to receive(:load).and_return(config)
183
217
  end
184
218
 
185
219
  it 'should parse a YAML configuration' do
@@ -206,6 +240,12 @@ describe Guacamole::Configuration do
206
240
  subject.load 'config_file.yml'
207
241
  end
208
242
 
243
+ it 'should set the graph name as read from the YAML file' do
244
+ expect(subject).to receive(:graph_name=).with('custom_graph_name')
245
+
246
+ subject.load 'config_file.yml'
247
+ end
248
+
209
249
  it 'should warn if the database was not found' do
210
250
  allow(subject).to receive(:database).and_return(double('Database'))
211
251
  allow(subject.database).to receive(:name)
@@ -270,21 +310,4 @@ development:
270
310
  subject.configure_with_uri(connection_uri)
271
311
  end
272
312
  end
273
-
274
- describe 'experimental_features' do
275
- let(:fresh_config) { Guacamole::Configuration.new }
276
-
277
- after do
278
- subject.experimental_features = []
279
- end
280
-
281
- it 'should default to none' do
282
- expect(fresh_config.experimental_features).to be_empty
283
- end
284
-
285
- it 'should accept a list of features to activate' do
286
- subject.experimental_features = [:aql_support]
287
- expect(subject.experimental_features).to include :aql_support
288
- end
289
- end
290
313
  end
@@ -22,6 +22,22 @@ describe Guacamole::DocumentModelMapper do
22
22
  expect(mapper.model_class).to eq FancyModel
23
23
  end
24
24
 
25
+ context 'document mapper instance' do
26
+ subject { Guacamole::DocumentModelMapper.new FancyModel, FakeIdentityMap }
27
+
28
+ let(:model) { double('Model') }
29
+ let(:model_class) { double('ModelClass') }
30
+
31
+ before do
32
+ allow(subject).to receive(:model_class).and_return(model_class)
33
+ allow(model).to receive(:instance_of?).with(model_class).and_return(true)
34
+ end
35
+
36
+ it 'should know if it responsible for a certain model' do
37
+ expect(subject.responsible_for?(model)).to be_truthy
38
+ end
39
+ end
40
+
25
41
  describe 'document_to_model' do
26
42
  subject { Guacamole::DocumentModelMapper.new FancyModel, FakeIdentityMap }
27
43
 
@@ -52,57 +68,41 @@ describe Guacamole::DocumentModelMapper do
52
68
  subject.document_to_model document
53
69
  end
54
70
 
55
- context 'with referenced_by models' do
56
- let(:referenced_by_model_name) { :cupcakes }
57
- let(:referenced_by_models) { [referenced_by_model_name] }
58
- let(:association_proxy) { Guacamole::Proxies::ReferencedBy }
59
- let(:association_proxy_instance) { double('AssociationProxy') }
60
-
61
- before do
62
- allow(subject).to receive(:referenced_by_models).and_return referenced_by_models
63
- allow(association_proxy).to receive(:new)
64
- .with(referenced_by_model_name, model_instance)
65
- .and_return(association_proxy_instance)
71
+ context 'with attributes as edge relations' do
72
+ let(:attribute_with_edge_relation) do
73
+ instance_double('Guacamole::DocumentModelMapper::Attribute', name: 'my_relation')
66
74
  end
75
+ let(:related_edge_class) { instance_double('Guacamole::Edge') }
76
+ let(:relation_proxy_class) { Guacamole::Proxies::Relation }
77
+ let(:relation_proxy) { instance_double('Guacamole::Proxies::Relation') }
67
78
 
68
- it 'should initialize the association proxy with referenced_by model and its name' do
69
- expect(association_proxy).to receive(:new)
70
- .with(referenced_by_model_name, model_instance)
71
- .and_return(association_proxy_instance)
72
-
73
- subject.document_to_model document
79
+ before do
80
+ allow(attribute_with_edge_relation).to receive(:setter).and_return('my_relation=')
81
+ allow(attribute_with_edge_relation).to receive(:edge_class).and_return(related_edge_class)
82
+ allow(subject).to receive(:edge_attributes).and_return([attribute_with_edge_relation])
83
+ allow(relation_proxy_class).to receive(:new).
84
+ with(model_instance, related_edge_class).
85
+ and_return(relation_proxy)
74
86
  end
75
87
 
76
- it 'should set an association proxy' do
77
- expect(model_instance).to receive("#{referenced_by_model_name}=").with(association_proxy_instance)
88
+ it 'should initialize a relation proxy with the model and the appropriate edge class' do
89
+ expect(relation_proxy_class).to receive(:new).
90
+ with(model_instance, related_edge_class).
91
+ and_return(relation_proxy)
78
92
 
79
93
  subject.document_to_model document
80
94
  end
81
- end
82
95
 
83
- context 'with referenced models' do
84
- let(:referenced_model_name) { :pony }
85
- let(:referenced_models) { [referenced_model_name] }
86
- let(:association_proxy) { Guacamole::Proxies::References }
87
- let(:association_proxy_instance) { double('AssociationProxy') }
88
-
89
- before do
90
- allow(subject).to receive(:referenced_models).and_return referenced_models
91
- allow(association_proxy).to receive(:new)
92
- .with(referenced_model_name, document)
93
- .and_return(association_proxy_instance)
94
- end
95
-
96
- it 'should initialize the association proxy with the document and the referenced model name' do
97
- expect(association_proxy).to receive(:new)
98
- .with(referenced_model_name, document)
99
- .and_return(association_proxy_instance)
96
+ it 'should set first the key and rev and after that the proxy' do
97
+ expect(model_instance).to receive(:key=).ordered
98
+ expect(model_instance).to receive(:rev=).ordered
99
+ expect(subject).to receive(:handle_related_documents).ordered
100
100
 
101
101
  subject.document_to_model document
102
102
  end
103
103
 
104
- it 'should set an association proxy' do
105
- expect(model_instance).to receive("#{referenced_model_name}=").with(association_proxy_instance)
104
+ it 'should assign the relation proxy for the appropriate attribute' do
105
+ expect(model_instance).to receive(:my_relation=).with(relation_proxy)
106
106
 
107
107
  subject.document_to_model document
108
108
  end
@@ -171,39 +171,17 @@ describe Guacamole::DocumentModelMapper do
171
171
  end
172
172
  end
173
173
 
174
- context 'with referenced_by models' do
175
- let(:referenced_by_model_name) { :cupcakes }
176
- let(:referenced_by_models) { [referenced_by_model_name] }
177
-
178
- before do
179
- allow(subject).to receive(:referenced_by_models).and_return referenced_by_models
174
+ context 'with attributes as edge relations' do
175
+ let(:attribute_with_edge_relation) do
176
+ instance_double('Guacamole::DocumentModelMapper::Attribute', name: 'my_relation')
180
177
  end
181
178
 
182
- it 'should remove the referenced_by attribute from the document' do
183
- expect(model_attributes).to receive(:delete).with(referenced_by_model_name)
184
-
185
- subject.model_to_document(model)
186
- end
187
- end
188
-
189
- context 'with referenced models' do
190
- let(:referenced_model) { double('ReferencedModel', key: 23) }
191
- let(:referenced_model_name) { :pony }
192
- let(:referenced_models) { [referenced_model_name] }
193
-
194
179
  before do
195
- allow(subject).to receive(:referenced_models).and_return referenced_models
196
- allow(model).to receive(:send).with(referenced_model_name).and_return referenced_model
180
+ allow(subject).to receive(:edge_attributes).and_return([attribute_with_edge_relation])
197
181
  end
198
182
 
199
- it 'should remove the referenced attribute from the document' do
200
- expect(model_attributes).to receive(:delete).with(referenced_model_name)
201
-
202
- subject.model_to_document(model)
203
- end
204
-
205
- it 'should add the key of the referenced model to the document' do
206
- expect(model_attributes).to receive(:[]=).with(:"#{referenced_model_name}_id", referenced_model.key)
183
+ it 'should remove the attributes from the document' do
184
+ expect(model_attributes).to receive(:delete).with('my_relation')
207
185
 
208
186
  subject.model_to_document(model)
209
187
  end
@@ -220,23 +198,52 @@ describe Guacamole::DocumentModelMapper do
220
198
  end
221
199
  end
222
200
 
223
- describe 'referenced_by' do
224
- subject { Guacamole::DocumentModelMapper.new FancyModel, FakeIdentityMap }
201
+ describe 'attribute' do
202
+ describe Guacamole::DocumentModelMapper::Attribute do
203
+ subject { Guacamole::DocumentModelMapper::Attribute.new(:attribute_name) }
204
+
205
+ its(:name) { should eq :attribute_name }
206
+ its(:options) { should eq({}) }
207
+ its(:getter) { should eq :attribute_name }
208
+ its(:setter) { should eq 'attribute_name=' }
225
209
 
226
- it 'should remember which models holding references' do
227
- subject.referenced_by :ponies
210
+ context 'attributes for relations' do
211
+ let(:edge_class) { double('SomeEdgeClass') }
228
212
 
229
- expect(subject.referenced_by_models).to include :ponies
213
+ before do
214
+ subject.options[:via] = edge_class
215
+ end
216
+
217
+ it 'should know if the attribute must be mapped via an edge' do
218
+ expect(subject.map_via_edge?).to be_truthy
219
+ end
220
+
221
+ it 'should hold a reference to the edge class' do
222
+ expect(subject.edge_class).to eq edge_class
223
+ end
224
+ end
230
225
  end
231
- end
232
226
 
233
- describe 'references' do
234
227
  subject { Guacamole::DocumentModelMapper.new FancyModel, FakeIdentityMap }
235
228
 
236
- it 'should remember which models are referenced' do
237
- subject.references :pony
229
+ it 'should add an attribute to be handled differently during the mapping' do
230
+ subject.attribute :special_one
231
+
232
+ expect(subject.attributes).to include Guacamole::DocumentModelMapper::Attribute.new(:special_one)
233
+ end
234
+
235
+ it 'should hold a list of all attributes to be considered during the mapping' do
236
+ subject.attribute :some_attribute
237
+ subject.attribute :another_attribute
238
+
239
+ expect(subject.attributes.count).to eq 2
240
+ end
241
+
242
+ it 'should hold a list of all attributes to be mapped via Edges' do
243
+ subject.attribute :normal_attribute
244
+ subject.attribute :related_model, via: double('EdgeClass')
238
245
 
239
- expect(subject.referenced_models).to include :pony
246
+ expect(subject.edge_attributes).to include(an_object_having_attributes(name: :related_model))
240
247
  end
241
248
  end
242
249
  end
@@ -0,0 +1,174 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+ require 'guacamole/edge_collection'
5
+
6
+ describe Guacamole::EdgeCollection do
7
+ let(:graph) { double('Graph') }
8
+ let(:config) { double('Configuration') }
9
+
10
+ before do
11
+ allow(Guacamole).to receive(:configuration).and_return(config)
12
+ allow(config).to receive(:graph).and_return(graph)
13
+ allow(graph).to receive(:add_edge_definition)
14
+ end
15
+
16
+ context 'the edge collection module' do
17
+ subject { Guacamole::EdgeCollection }
18
+
19
+ context 'with user defined edge collection class' do
20
+ let(:edge_class) { double('EdgeClass', name: 'MyEdge') }
21
+ let(:user_defined_edge_collection) { double('EdgeCollection') }
22
+
23
+ before do
24
+ stub_const('MyEdgesCollection', user_defined_edge_collection)
25
+ allow(user_defined_edge_collection).to receive(:add_edge_definition_to_graph)
26
+ end
27
+
28
+ it 'should return the edge collection for a given edge class' do
29
+ expect(subject.for(edge_class)).to eq user_defined_edge_collection
30
+ end
31
+ end
32
+
33
+ context 'without user defined edge collection class' do
34
+ let(:edge_class) { double('EdgeClass', name: 'AmazingEdge') }
35
+ let(:auto_defined_edge_collection) { double('EdgeCollection') }
36
+
37
+ before do
38
+ stub_const('ExampleEdge', double('Edge').as_null_object)
39
+ allow(auto_defined_edge_collection).to receive(:add_edge_definition_to_graph)
40
+ end
41
+
42
+ it 'should create an edge collection class' do
43
+ edge_collection = subject.create_edge_collection('ExampleEdgesCollection')
44
+
45
+ expect(edge_collection.name).to eq 'ExampleEdgesCollection'
46
+ expect(edge_collection.ancestors).to include Guacamole::EdgeCollection
47
+ end
48
+
49
+ it 'should return the edge collection for a givene edge class' do
50
+ allow(subject).to receive(:create_edge_collection).
51
+ with('AmazingEdgesCollection').
52
+ and_return(auto_defined_edge_collection)
53
+
54
+ expect(subject.for(edge_class)).to eq auto_defined_edge_collection
55
+ end
56
+ end
57
+ end
58
+
59
+ context 'concrete edge collections' do
60
+ subject do
61
+ class SomeEdgesCollection
62
+ include Guacamole::EdgeCollection
63
+ end
64
+ end
65
+
66
+ let(:database) { double('Database') }
67
+ let(:edge_collection_name) { 'some_edges' }
68
+ let(:raw_edge_collection) { double('Ashikawa::Core::EdgeCollection') }
69
+ let(:collection_a) { :a }
70
+ let(:collection_b) { :b }
71
+ let(:edge_class) { double('EdgeClass', name: 'SomeEdge', from: collection_a, to: collection_b) }
72
+ let(:model) { double('Model') }
73
+
74
+ before do
75
+ stub_const('SomeEdge', edge_class)
76
+ allow(graph).to receive(:edge_collection).with(edge_collection_name).and_return(raw_edge_collection)
77
+ allow(subject).to receive(:database).and_return(database)
78
+ allow(graph).to receive(:add_edge_definition)
79
+ end
80
+
81
+ after do
82
+ # This stunt is required to have a fresh subject each time and not running into problems
83
+ # with cached mock doubles that will raise errors upon test execution.
84
+ Object.send(:remove_const, subject.name)
85
+ end
86
+
87
+ its(:edge_class) { should eq edge_class }
88
+
89
+ it 'should be a specialized Guacamole::Collection' do
90
+ expect(subject).to include Guacamole::Collection
91
+ end
92
+
93
+ it 'should map the #connectino to the underlying edge_connection' do
94
+ allow(subject).to receive(:graph).and_return(graph)
95
+
96
+ expect(subject.connection).to eq raw_edge_collection
97
+ end
98
+
99
+ context 'initialize the edge definition' do
100
+ it 'should add the edge definition as soon as the module is included' do
101
+ just_another_edge_collection = Class.new
102
+ expect(just_another_edge_collection).to receive(:add_edge_definition_to_graph)
103
+
104
+ just_another_edge_collection.send(:include, Guacamole::EdgeCollection)
105
+ end
106
+
107
+ it 'should ignore if the the edge definition was already added' do
108
+ expect(graph).to receive(:add_edge_definition).and_raise(Ashikawa::Core::ResourceNotFound)
109
+
110
+ expect { subject.add_edge_definition_to_graph }.not_to raise_error
111
+ end
112
+
113
+ it 'should create the edge definition based on the edge class' do
114
+ expect(graph).to receive(:add_edge_definition).with(edge_collection_name,
115
+ from: [collection_a], to: [collection_b])
116
+
117
+ subject.add_edge_definition_to_graph
118
+ end
119
+ end
120
+
121
+ context 'accessing the mapper' do
122
+ let(:collection_a) { double('Collection') }
123
+ let(:collection_b) { double('Collection') }
124
+ let(:mapper_a) { double('DocumentModelMapper') }
125
+ let(:mapper_b) { double('DocumentModelMapper') }
126
+
127
+ before do
128
+ allow(collection_a).to receive(:mapper).and_return(mapper_a)
129
+ allow(collection_b).to receive(:mapper).and_return(mapper_b)
130
+ allow(edge_class).to receive(:from_collection).and_return(collection_a)
131
+ allow(edge_class).to receive(:to_collection).and_return(collection_b)
132
+ allow(mapper_a).to receive(:responsible_for?).with(model).and_return(true)
133
+ allow(mapper_b).to receive(:responsible_for?).with(model).and_return(false)
134
+ end
135
+
136
+ it 'should provide a method to get the mapper for the :to collection' do
137
+ expect(subject.mapper_for_target(model)).to eq mapper_b
138
+ end
139
+
140
+ it 'should provide a method to get the mapper for the :from collection' do
141
+ expect(subject.mapper_for_start(model)).to eq mapper_a
142
+ end
143
+ end
144
+
145
+ context 'getting neighbors' do
146
+ let(:graph_query) { instance_double('Guacamole::GraphQuery') }
147
+ let(:target_mapper) { double('DocumentModelMapper') }
148
+
149
+ before do
150
+ allow(Guacamole::GraphQuery).to receive(:new).and_return(graph_query)
151
+ allow(subject).to receive(:mapper_for_target).with(model).and_return(target_mapper)
152
+ allow(graph_query).to receive(:neighbors).and_return(graph_query)
153
+ end
154
+
155
+ it 'should return a query object' do
156
+ query = subject.neighbors(model)
157
+
158
+ expect(query).to eq graph_query
159
+ end
160
+
161
+ it 'should initialize the query object with the graph an the appropriate mapper' do
162
+ expect(Guacamole::GraphQuery).to receive(:new).with(graph, target_mapper).and_return(graph_query)
163
+
164
+ subject.neighbors(model)
165
+ end
166
+
167
+ it 'should provide a #neighbors function' do
168
+ expect(graph_query).to receive(:neighbors).with(model, 'some_edges')
169
+
170
+ subject.neighbors(model)
171
+ end
172
+ end
173
+ end
174
+ end