couchbase-orm 1.1.1 → 2.0.0
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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +45 -0
- data/.gitignore +2 -0
- data/.travis.yml +3 -2
- data/CODEOWNERS +1 -0
- data/Gemfile +5 -3
- data/README.md +237 -31
- data/ci/run_couchbase.sh +22 -0
- data/couchbase-orm.gemspec +26 -20
- data/lib/couchbase-orm/active_record_compat.rb +92 -0
- data/lib/couchbase-orm/associations.rb +119 -0
- data/lib/couchbase-orm/base.rb +143 -166
- data/lib/couchbase-orm/changeable.rb +512 -0
- data/lib/couchbase-orm/connection.rb +28 -8
- data/lib/couchbase-orm/encrypt.rb +48 -0
- data/lib/couchbase-orm/error.rb +17 -2
- data/lib/couchbase-orm/inspectable.rb +37 -0
- data/lib/couchbase-orm/json_schema/json_validation_error.rb +13 -0
- data/lib/couchbase-orm/json_schema/loader.rb +47 -0
- data/lib/couchbase-orm/json_schema/validation.rb +18 -0
- data/lib/couchbase-orm/json_schema/validator.rb +45 -0
- data/lib/couchbase-orm/json_schema.rb +9 -0
- data/lib/couchbase-orm/json_transcoder.rb +27 -0
- data/lib/couchbase-orm/locale/en.yml +5 -0
- data/lib/couchbase-orm/n1ql.rb +133 -0
- data/lib/couchbase-orm/persistence.rb +61 -52
- data/lib/couchbase-orm/proxies/bucket_proxy.rb +36 -0
- data/lib/couchbase-orm/proxies/collection_proxy.rb +52 -0
- data/lib/couchbase-orm/proxies/n1ql_proxy.rb +40 -0
- data/lib/couchbase-orm/proxies/results_proxy.rb +23 -0
- data/lib/couchbase-orm/railtie.rb +6 -17
- data/lib/couchbase-orm/relation.rb +249 -0
- data/lib/couchbase-orm/strict_loading.rb +21 -0
- data/lib/couchbase-orm/timestamps/created.rb +20 -0
- data/lib/couchbase-orm/timestamps/updated.rb +21 -0
- data/lib/couchbase-orm/timestamps.rb +15 -0
- data/lib/couchbase-orm/types/array.rb +32 -0
- data/lib/couchbase-orm/types/date.rb +9 -0
- data/lib/couchbase-orm/types/date_time.rb +14 -0
- data/lib/couchbase-orm/types/encrypted.rb +17 -0
- data/lib/couchbase-orm/types/nested.rb +43 -0
- data/lib/couchbase-orm/types/timestamp.rb +18 -0
- data/lib/couchbase-orm/types.rb +20 -0
- data/lib/couchbase-orm/utilities/enum.rb +13 -1
- data/lib/couchbase-orm/utilities/has_many.rb +72 -36
- data/lib/couchbase-orm/utilities/ignored_properties.rb +15 -0
- data/lib/couchbase-orm/utilities/index.rb +18 -20
- data/lib/couchbase-orm/utilities/properties_always_exists_in_document.rb +16 -0
- data/lib/couchbase-orm/utilities/query_helper.rb +148 -0
- data/lib/couchbase-orm/utils.rb +25 -0
- data/lib/couchbase-orm/version.rb +1 -1
- data/lib/couchbase-orm/views.rb +38 -41
- data/lib/couchbase-orm.rb +44 -9
- data/lib/ext/query_n1ql.rb +124 -0
- data/lib/rails/generators/couchbase_orm/config/templates/couchbase.yml +3 -2
- data/spec/associations_spec.rb +219 -50
- data/spec/base_spec.rb +296 -14
- data/spec/collection_proxy_spec.rb +29 -0
- data/spec/connection_spec.rb +27 -0
- data/spec/couchbase-orm/active_record_compat_spec.rb +24 -0
- data/spec/couchbase-orm/changeable_spec.rb +16 -0
- data/spec/couchbase-orm/json_schema/validation_spec.rb +23 -0
- data/spec/couchbase-orm/json_schema/validator_spec.rb +13 -0
- data/spec/couchbase-orm/timestamps_spec.rb +85 -0
- data/spec/couchbase-orm/timestamps_spec_models.rb +36 -0
- data/spec/empty-json-schema/.gitkeep +0 -0
- data/spec/enum_spec.rb +34 -0
- data/spec/has_many_spec.rb +101 -54
- data/spec/index_spec.rb +13 -9
- data/spec/json-schema/JsonSchemaBaseTest.json +19 -0
- data/spec/json-schema/entity_snakecase.json +20 -0
- data/spec/json-schema/loader_spec.rb +42 -0
- data/spec/json-schema/specific_path.json +20 -0
- data/spec/json_schema_spec.rb +178 -0
- data/spec/n1ql_spec.rb +193 -0
- data/spec/persistence_spec.rb +49 -9
- data/spec/relation_nested_spec.rb +88 -0
- data/spec/relation_spec.rb +430 -0
- data/spec/support.rb +16 -8
- data/spec/type_array_spec.rb +52 -0
- data/spec/type_encrypted_spec.rb +114 -0
- data/spec/type_nested_spec.rb +191 -0
- data/spec/type_spec.rb +317 -0
- data/spec/utilities/ignored_properties_spec.rb +20 -0
- data/spec/utilities/properties_always_exists_in_document_spec.rb +24 -0
- data/spec/views_spec.rb +32 -11
- metadata +192 -29
data/spec/n1ql_spec.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path("../support", __FILE__)
|
4
|
+
|
5
|
+
class N1QLTest < CouchbaseOrm::Base
|
6
|
+
attribute :name, type: String
|
7
|
+
attribute :lastname, type: String
|
8
|
+
enum rating: [:awesome, :good, :okay, :bad], default: :okay
|
9
|
+
|
10
|
+
n1ql :by_custom_rating, emit_key: [:rating], query_fn: proc { |bucket, _values, options|
|
11
|
+
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` WHERE type = 'n1_ql_test' AND rating IN [1, 2] ORDER BY name ASC", options)
|
12
|
+
}
|
13
|
+
n1ql :by_name, emit_key: [:name]
|
14
|
+
n1ql :by_lastname, emit_key: [:lastname]
|
15
|
+
n1ql :by_rating_emit, emit_key: :rating
|
16
|
+
|
17
|
+
n1ql :by_custom_rating_values, emit_key: [:rating], query_fn: proc { |bucket, values, options|
|
18
|
+
cluster.query("SELECT raw meta().id FROM `#{bucket.name}` where type = 'n1_ql_test' AND rating IN #{quote(values[0])} ORDER BY name ASC", options)
|
19
|
+
}
|
20
|
+
n1ql :by_rating_reverse, emit_key: :rating, custom_order: "name DESC"
|
21
|
+
n1ql :by_rating_without_docs, emit_key: :rating, include_docs: false
|
22
|
+
|
23
|
+
# This generates both:
|
24
|
+
# view :by_rating, emit_key: :rating
|
25
|
+
# def self.find_by_rating(rating); end # also provide this helper function
|
26
|
+
index_n1ql :rating
|
27
|
+
end
|
28
|
+
|
29
|
+
describe CouchbaseOrm::N1ql do
|
30
|
+
before(:each) do
|
31
|
+
N1QLTest.delete_all
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should not allow n1ql to override existing methods" do
|
35
|
+
expect { N1QLTest.n1ql :all }.to raise_error(ArgumentError)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should perform a query and return the n1ql" do
|
39
|
+
N1QLTest.create! name: :bob
|
40
|
+
docs = N1QLTest.all.collect { |ob|
|
41
|
+
ob.name
|
42
|
+
}
|
43
|
+
expect(docs).to eq(%w[bob])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should query by non-nil value" do
|
47
|
+
_anonymous = N1QLTest.create!
|
48
|
+
bob = N1QLTest.create! name: :bob
|
49
|
+
|
50
|
+
expect(N1QLTest.by_name(key: 'bob').to_a).to eq [bob]
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should query by nil value" do
|
54
|
+
anonymous = N1QLTest.create! lastname: "Anonymous"
|
55
|
+
anonymous_no_property = N1QLTest.create! lastname: "Anonymous without name property"
|
56
|
+
|
57
|
+
CouchbaseOrm::Connection.bucket.default_collection.mutate_in(anonymous_no_property.id, [
|
58
|
+
Couchbase::MutateInSpec.remove("name"),
|
59
|
+
])
|
60
|
+
|
61
|
+
anonymous_no_property.reload
|
62
|
+
|
63
|
+
_bob = N1QLTest.create! name: :bob
|
64
|
+
|
65
|
+
expect(N1QLTest.by_name(key: nil).to_a).to match_array [anonymous, anonymous_no_property]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should query all when key is not set" do
|
69
|
+
anonymous = N1QLTest.create!
|
70
|
+
bob = N1QLTest.create! name: :bob
|
71
|
+
|
72
|
+
expect(N1QLTest.by_name.to_a).to eq [anonymous, bob]
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
it "should work with other keys" do
|
77
|
+
N1QLTest.create! name: :bob, rating: :good
|
78
|
+
N1QLTest.create! name: :jane, rating: :awesome
|
79
|
+
N1QLTest.create! name: :greg, rating: :bad
|
80
|
+
|
81
|
+
docs = N1QLTest.by_name(descending: true).collect { |ob|
|
82
|
+
ob.name
|
83
|
+
}
|
84
|
+
expect(docs).to eq(%w[jane greg bob])
|
85
|
+
|
86
|
+
docs = N1QLTest.by_rating(descending: true).collect { |ob|
|
87
|
+
ob.rating
|
88
|
+
}
|
89
|
+
expect(docs).to eq([4, 2, 1])
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should return matching results" do
|
93
|
+
N1QLTest.create! name: :bob, rating: :awesome
|
94
|
+
N1QLTest.create! name: :jane, rating: :awesome
|
95
|
+
N1QLTest.create! name: :greg, rating: :bad
|
96
|
+
N1QLTest.create! name: :mel, rating: :good
|
97
|
+
|
98
|
+
docs = N1QLTest.find_by_rating(1).collect { |ob|
|
99
|
+
ob.name
|
100
|
+
}
|
101
|
+
|
102
|
+
expect(Set.new(docs)).to eq(Set.new(%w[bob jane]))
|
103
|
+
|
104
|
+
docs = N1QLTest.by_custom_rating().collect { |ob|
|
105
|
+
ob.name
|
106
|
+
}
|
107
|
+
|
108
|
+
expect(Set.new(docs)).to eq(Set.new(%w[bob jane mel]))
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should return matching results with reverse order" do
|
112
|
+
N1QLTest.create! name: :bob, rating: :awesome
|
113
|
+
N1QLTest.create! name: :jane, rating: :awesome
|
114
|
+
N1QLTest.create! name: :greg, rating: :bad
|
115
|
+
N1QLTest.create! name: :mel, rating: :good
|
116
|
+
|
117
|
+
docs = N1QLTest.by_rating_reverse(key: 1).collect { |ob|
|
118
|
+
ob.name
|
119
|
+
}
|
120
|
+
|
121
|
+
expect(docs).to eq(%w[jane bob])
|
122
|
+
end
|
123
|
+
|
124
|
+
it "should return matching results without full documents" do
|
125
|
+
inst_bob = N1QLTest.create! name: :bob, rating: :awesome
|
126
|
+
inst_jane = N1QLTest.create! name: :jane, rating: :awesome
|
127
|
+
N1QLTest.create! name: :greg, rating: :bad
|
128
|
+
N1QLTest.create! name: :mel, rating: :good
|
129
|
+
|
130
|
+
docs = N1QLTest.by_rating_without_docs(key: 1)
|
131
|
+
|
132
|
+
expect(Set.new(docs)).to eq(Set.new([inst_bob.id, inst_jane.id]))
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should return matching results with nil usage" do
|
136
|
+
N1QLTest.create! name: :bob, lastname: nil
|
137
|
+
N1QLTest.create! name: :jane, lastname: "dupond"
|
138
|
+
|
139
|
+
docs = N1QLTest.by_lastname(key: [nil]).collect { |ob|
|
140
|
+
ob.name
|
141
|
+
}
|
142
|
+
expect(docs).to eq(%w[bob])
|
143
|
+
end
|
144
|
+
|
145
|
+
it "should allow storing quoting chars" do
|
146
|
+
special_name = "O'Leary & Sons \"The best\" \\ between backslash \\"
|
147
|
+
t = N1QLTest.create! name: special_name, rating: :awesome
|
148
|
+
expect(N1QLTest.find(t.id).name).to eq(special_name)
|
149
|
+
expect(N1QLTest.by_name(key: special_name).to_a.first).to eq(t)
|
150
|
+
expect(N1QLTest.where(name: special_name).to_a.first).to eq(t)
|
151
|
+
end
|
152
|
+
it "should return matching results with custom n1ql query" do
|
153
|
+
N1QLTest.create! name: :bob, rating: :awesome
|
154
|
+
N1QLTest.create! name: :jane, rating: :awesome
|
155
|
+
N1QLTest.create! name: :greg, rating: :bad
|
156
|
+
N1QLTest.create! name: :mel, rating: :good
|
157
|
+
|
158
|
+
|
159
|
+
docs = N1QLTest.by_custom_rating().collect { |ob|
|
160
|
+
ob.name
|
161
|
+
}
|
162
|
+
|
163
|
+
expect(Set.new(docs)).to eq(Set.new(%w[bob jane mel]))
|
164
|
+
|
165
|
+
docs = N1QLTest.by_custom_rating_values(key: [[1, 2]]).collect { |ob|
|
166
|
+
ob.name
|
167
|
+
}
|
168
|
+
|
169
|
+
expect(Set.new(docs)).to eq(Set.new(%w[bob jane mel]))
|
170
|
+
end
|
171
|
+
|
172
|
+
it "should log the default scan_consistency when n1ql query is executed" do
|
173
|
+
allow(CouchbaseOrm.logger).to receive(:debug)
|
174
|
+
N1QLTest.by_rating_reverse()
|
175
|
+
expect(CouchbaseOrm.logger).to have_received(:debug).at_least(:once).with("N1QL query: select raw meta().id from `#{CouchbaseOrm::Connection.bucket.name}` where type=\"n1_ql_test\" order by name DESC return 0 rows with scan_consistency : #{described_class::DEFAULT_SCAN_CONSISTENCY}")
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should log the set scan_consistency when n1ql query is executed with a specific scan_consistency" do
|
179
|
+
allow(CouchbaseOrm.logger).to receive(:debug)
|
180
|
+
default_n1ql_config = CouchbaseOrm::N1ql.config
|
181
|
+
CouchbaseOrm::N1ql.config({ scan_consistency: :not_bounded })
|
182
|
+
N1QLTest.by_rating_reverse()
|
183
|
+
expect(CouchbaseOrm.logger).to have_received(:debug).at_least(:once).with("N1QL query: select raw meta().id from `#{CouchbaseOrm::Connection.bucket.name}` where type=\"n1_ql_test\" order by name DESC return 0 rows with scan_consistency : not_bounded")
|
184
|
+
|
185
|
+
CouchbaseOrm::N1ql.config(default_n1ql_config)
|
186
|
+
N1QLTest.by_rating_reverse()
|
187
|
+
expect(CouchbaseOrm.logger).to have_received(:debug).at_least(:once).with("N1QL query: select raw meta().id from `#{CouchbaseOrm::Connection.bucket.name}` where type=\"n1_ql_test\" order by name DESC return 0 rows with scan_consistency : #{described_class::DEFAULT_SCAN_CONSISTENCY}")
|
188
|
+
end
|
189
|
+
|
190
|
+
after(:all) do
|
191
|
+
N1QLTest.delete_all
|
192
|
+
end
|
193
|
+
end
|
data/spec/persistence_spec.rb
CHANGED
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
require File.expand_path("../support", __FILE__)
|
4
4
|
|
5
|
+
require "action_controller"
|
5
6
|
|
6
7
|
class BasicModel < CouchbaseOrm::Base
|
7
|
-
attribute :name
|
8
|
+
attribute :name
|
9
|
+
attribute :address
|
10
|
+
attribute :age
|
8
11
|
end
|
9
12
|
|
10
13
|
class ModelWithDefaults < CouchbaseOrm::Base
|
@@ -14,7 +17,9 @@ class ModelWithDefaults < CouchbaseOrm::Base
|
|
14
17
|
end
|
15
18
|
|
16
19
|
class ModelWithCallbacks < CouchbaseOrm::Base
|
17
|
-
attribute :name
|
20
|
+
attribute :name
|
21
|
+
attribute :address
|
22
|
+
attribute :age
|
18
23
|
|
19
24
|
before_create :update_name
|
20
25
|
before_save :set_address
|
@@ -32,7 +37,8 @@ class ModelWithCallbacks < CouchbaseOrm::Base
|
|
32
37
|
end
|
33
38
|
|
34
39
|
class ModelWithValidations < CouchbaseOrm::Base
|
35
|
-
attribute :name,
|
40
|
+
attribute :name, type: String
|
41
|
+
attribute :address, type: String
|
36
42
|
attribute :age, type: :Integer
|
37
43
|
|
38
44
|
validates :name, presence: true
|
@@ -205,11 +211,8 @@ describe CouchbaseOrm::Persistence do
|
|
205
211
|
expect(model.save!).to be(model)
|
206
212
|
|
207
213
|
# coercion will fail here
|
208
|
-
|
209
|
-
|
210
|
-
expect(false).to be(true)
|
211
|
-
rescue ArgumentError => e
|
212
|
-
end
|
214
|
+
model.age = "a23"
|
215
|
+
expect{ model.save! }.to raise_error(CouchbaseOrm::Error::RecordInvalid)
|
213
216
|
|
214
217
|
model.destroy
|
215
218
|
end
|
@@ -228,12 +231,24 @@ describe CouchbaseOrm::Persistence do
|
|
228
231
|
|
229
232
|
model.reload
|
230
233
|
expect(model.changed?).to be(false)
|
231
|
-
expect(model.id).to
|
234
|
+
expect(model.id).to eq(id)
|
232
235
|
|
233
236
|
model.destroy
|
234
237
|
expect(model.destroyed?).to be(true)
|
235
238
|
end
|
236
239
|
|
240
|
+
it "should update with action controler parameters" do
|
241
|
+
model = BasicModel.create!
|
242
|
+
params = ActionController::Parameters.new({
|
243
|
+
name: 'Francesco',
|
244
|
+
age: 22,
|
245
|
+
foo: 'bar'
|
246
|
+
})
|
247
|
+
model.update!(params.permit(:name, :age))
|
248
|
+
model.reload
|
249
|
+
expect(model.age).to eq(22)
|
250
|
+
end
|
251
|
+
|
237
252
|
it "should update attributes" do
|
238
253
|
model = BasicModel.new
|
239
254
|
|
@@ -254,6 +269,31 @@ describe CouchbaseOrm::Persistence do
|
|
254
269
|
expect(model.destroyed?).to be(true)
|
255
270
|
end
|
256
271
|
|
272
|
+
it "should not allow to update unkown attributes" do
|
273
|
+
model = BasicModel.new
|
274
|
+
|
275
|
+
expect{ model.update_attributes({
|
276
|
+
name: 'bob',
|
277
|
+
age: 34,
|
278
|
+
foo: 'bar'
|
279
|
+
}) }.to raise_error(ActiveModel::UnknownAttributeError)
|
280
|
+
end
|
281
|
+
|
282
|
+
it "should not allow to create with unkown attributes" do
|
283
|
+
expect{ BasicModel.create({
|
284
|
+
name: 'bob',
|
285
|
+
age: 34,
|
286
|
+
foo: 'bar'
|
287
|
+
}) }.to raise_error(ActiveModel::UnknownAttributeError)
|
288
|
+
end
|
289
|
+
|
290
|
+
it "should not allow to update with unkown attributes" do
|
291
|
+
model = BasicModel.create!(name: 'bob', age: 34)
|
292
|
+
expect{ model.update({
|
293
|
+
foo: 'bar'
|
294
|
+
}) }.to raise_error(ActiveModel::UnknownAttributeError)
|
295
|
+
end
|
296
|
+
|
257
297
|
describe BasicModel do
|
258
298
|
it_behaves_like "ActiveModel"
|
259
299
|
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require File.expand_path("../support", __FILE__)
|
4
|
+
|
5
|
+
class NestedModel < CouchbaseOrm::NestedDocument
|
6
|
+
attribute :name, :string
|
7
|
+
attribute :size, :integer
|
8
|
+
attribute :child, :nested, type: NestedModel
|
9
|
+
end
|
10
|
+
|
11
|
+
class RelationParentModel < CouchbaseOrm::Base
|
12
|
+
attribute :name, :string
|
13
|
+
attribute :sub, :nested, type: NestedModel
|
14
|
+
attribute :subs, :array, type: NestedModel
|
15
|
+
end
|
16
|
+
|
17
|
+
describe CouchbaseOrm::Relation do
|
18
|
+
before(:each) do
|
19
|
+
RelationParentModel.delete_all
|
20
|
+
CouchbaseOrm.logger.debug "Cleaned before tests"
|
21
|
+
end
|
22
|
+
|
23
|
+
after(:all) do
|
24
|
+
CouchbaseOrm.logger.debug "Cleanup after all tests"
|
25
|
+
RelationParentModel.delete_all
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should query on nested array attribute" do
|
29
|
+
RelationParentModel.create(name: "parent_without_subs")
|
30
|
+
parent = RelationParentModel.create(name: "parent")
|
31
|
+
parent.subs = [
|
32
|
+
NestedModel.new(name: "sub2"),
|
33
|
+
NestedModel.new(name: "sub3")
|
34
|
+
]
|
35
|
+
parent.save!
|
36
|
+
|
37
|
+
expect(RelationParentModel.where(subs: {name: 'sub2'}).first).to eq parent
|
38
|
+
expect(RelationParentModel.where(subs: {name: ['sub3', 'subX']}).first).to eq parent
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should query on nested array with multiple attributes" do
|
42
|
+
RelationParentModel.create(name: "parent_without_subs")
|
43
|
+
parent = RelationParentModel.create(name: "parent")
|
44
|
+
parent.subs = [
|
45
|
+
NestedModel.new(name: "sub2", size: 2),
|
46
|
+
NestedModel.new(name: "sub3", size: 3)
|
47
|
+
]
|
48
|
+
parent.save!
|
49
|
+
|
50
|
+
expect(RelationParentModel.where(subs: {name: 'sub2', size: 2}).first).to eq parent
|
51
|
+
expect(RelationParentModel.where(subs: {name: 'sub2', size: 3}).first).to eq nil
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should query by gte function" do
|
55
|
+
parent = RelationParentModel.create(name: "parent")
|
56
|
+
parent.subs = [
|
57
|
+
NestedModel.new(name: "sub2", size: 2),
|
58
|
+
NestedModel.new(name: "sub3", size: 3),
|
59
|
+
NestedModel.new(name: "sub4", size: 4)
|
60
|
+
]
|
61
|
+
parent.save!
|
62
|
+
expect(RelationParentModel.where(subs: {size: {_gte: 3, _lt: 4}}).first).to eq parent
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should query by nested attribute" do
|
66
|
+
RelationParentModel.create(name: "parent_without_sub")
|
67
|
+
parent = RelationParentModel.create(name: "parent")
|
68
|
+
parent.sub = NestedModel.new(name: "sub")
|
69
|
+
parent.save!
|
70
|
+
expect(RelationParentModel.where('sub.name': 'sub').first).to eq parent
|
71
|
+
expect(RelationParentModel.where(sub: {name: 'sub'}).first).to eq parent
|
72
|
+
expect(RelationParentModel.where(sub: {name: ['sub', 'subX']}).first).to eq parent
|
73
|
+
expect(RelationParentModel.where(sub: {name: ['subX']}).first).to be_nil
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should query by grand child attribute" do
|
78
|
+
RelationParentModel.create(name: "parent_without_sub")
|
79
|
+
parent = RelationParentModel.create(name: "parent")
|
80
|
+
parent.sub = NestedModel.new(name: "sub", child: NestedModel.new(name: "child"))
|
81
|
+
parent.save!
|
82
|
+
|
83
|
+
expect(RelationParentModel.where(sub: {child: {name: 'child'}}).first).to eq parent
|
84
|
+
expect(RelationParentModel.where(sub: {child: {name: ['child', 'childX']}}).first).to eq parent
|
85
|
+
expect(RelationParentModel.where(sub: {child: {name: ['childX']}}).first).to be_nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|