couchbase-orm 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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/base_spec.rb
CHANGED
@@ -2,20 +2,71 @@
|
|
2
2
|
|
3
3
|
require File.expand_path("../support", __FILE__)
|
4
4
|
|
5
|
-
|
6
5
|
class BaseTest < CouchbaseOrm::Base
|
7
|
-
attribute :name, :
|
6
|
+
attribute :name, :string
|
7
|
+
attribute :job, :string
|
8
8
|
end
|
9
9
|
|
10
10
|
class CompareTest < CouchbaseOrm::Base
|
11
|
-
attribute :age
|
11
|
+
attribute :age, :integer
|
12
|
+
end
|
13
|
+
|
14
|
+
class TimestampTest < CouchbaseOrm::Base
|
15
|
+
attribute :created_at, :datetime, precision: 6
|
16
|
+
attribute :updated_at, :datetime, precision: 6
|
17
|
+
end
|
18
|
+
|
19
|
+
class BaseTestWithIgnoredProperties < CouchbaseOrm::Base
|
20
|
+
self.ignored_properties += [:deprecated_property]
|
21
|
+
attribute :name, :string
|
22
|
+
attribute :job, :string
|
23
|
+
end
|
24
|
+
|
25
|
+
class BaseTestWithPropertiesAlwaysExistsInDocument < CouchbaseOrm::Base
|
26
|
+
self.properties_always_exists_in_document = true
|
27
|
+
attribute :name, :string
|
12
28
|
end
|
13
29
|
|
30
|
+
class BaseTestWithTimeframe < CouchbaseOrm::Base
|
31
|
+
attribute :name, :string
|
32
|
+
attribute :start_date, :datetime
|
33
|
+
attribute :age, :integer
|
34
|
+
end
|
35
|
+
|
36
|
+
class DocInvalidOnUpdate < CouchbaseOrm::Base
|
37
|
+
attribute :title
|
38
|
+
validate :foo, on: :update
|
14
39
|
|
40
|
+
def foo
|
41
|
+
errors.add(:title, 'should not be updated')
|
42
|
+
end
|
43
|
+
end
|
15
44
|
|
16
45
|
describe CouchbaseOrm::Base do
|
46
|
+
|
47
|
+
it 'should have clean model after find' do
|
48
|
+
base = BaseTest.create!(name: 'joe')
|
49
|
+
base = BaseTest.find base.id
|
50
|
+
expect(base.changed?).to be false
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'stores not stringified changes' do
|
54
|
+
compare = CompareTest.create!
|
55
|
+
compare.age = '42'
|
56
|
+
compare.save!
|
57
|
+
expect(compare.saved_change_to_age).to eq [nil, 42]
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'should update changed_attributes after update' do
|
61
|
+
base = BaseTest.create!(name: 'joe')
|
62
|
+
base = BaseTest.find base.id
|
63
|
+
base.name = 'toto'
|
64
|
+
base.save!
|
65
|
+
expect(base.saved_change_to_name?).to be true
|
66
|
+
expect(base.saved_change_to_name).to eq ["joe", "toto"]
|
67
|
+
end
|
17
68
|
it "should be comparable to other objects" do
|
18
|
-
base
|
69
|
+
base = BaseTest.create!(name: 'joe')
|
19
70
|
base2 = BaseTest.create!(name: 'joe')
|
20
71
|
base3 = BaseTest.create!(ActiveSupport::HashWithIndifferentAccess.new(name: 'joe'))
|
21
72
|
|
@@ -33,14 +84,19 @@ describe CouchbaseOrm::Base do
|
|
33
84
|
base3.delete
|
34
85
|
end
|
35
86
|
|
87
|
+
it "should be inspectable" do
|
88
|
+
base = BaseTest.create!(name: 'joe')
|
89
|
+
expect(base.inspect).to eq("#<BaseTest id: \"#{base.id}\", name: \"joe\", job: nil>")
|
90
|
+
end
|
91
|
+
|
36
92
|
it "should load database responses" do
|
37
93
|
base = BaseTest.create!(name: 'joe')
|
38
|
-
resp = BaseTest.bucket.get(base.id
|
94
|
+
resp = BaseTest.bucket.default_collection.get(base.id)
|
39
95
|
|
40
|
-
|
96
|
+
base_loaded = BaseTest.new(resp, id: base.id)
|
41
97
|
|
42
|
-
base_loaded
|
43
|
-
expect(base_loaded).to
|
98
|
+
expect(base_loaded.id).to eq(base.id)
|
99
|
+
expect(base_loaded).to eq(base)
|
44
100
|
expect(base_loaded).not_to be(base)
|
45
101
|
|
46
102
|
base.destroy
|
@@ -49,17 +105,44 @@ describe CouchbaseOrm::Base do
|
|
49
105
|
it "should not load objects if there is a type mismatch" do
|
50
106
|
base = BaseTest.create!(name: 'joe')
|
51
107
|
|
52
|
-
expect { CompareTest.find_by_id(base.id) }.to raise_error(
|
108
|
+
expect { CompareTest.find_by_id(base.id) }.to raise_error(CouchbaseOrm::Error::TypeMismatchError)
|
53
109
|
|
54
110
|
base.destroy
|
55
111
|
end
|
56
112
|
|
113
|
+
it "raises ActiveModel::UnknownAttributeError on loading objects with unexpected properties" do
|
114
|
+
too_much_properties_doc = {
|
115
|
+
type: BaseTest.design_document,
|
116
|
+
name: 'Pierre',
|
117
|
+
job: 'dev',
|
118
|
+
age: '42'
|
119
|
+
}
|
120
|
+
BaseTest.bucket.default_collection.upsert 'doc_1', too_much_properties_doc
|
121
|
+
|
122
|
+
expect { BaseTest.find_by_id('doc_1') }.to raise_error(ActiveModel::UnknownAttributeError)
|
123
|
+
|
124
|
+
BaseTest.bucket.default_collection.remove 'doc_1'
|
125
|
+
end
|
126
|
+
|
127
|
+
it "loads objects even if there is a missing property in doc" do
|
128
|
+
missing_properties_doc = {
|
129
|
+
type: BaseTest.design_document,
|
130
|
+
name: 'Pierre'
|
131
|
+
}
|
132
|
+
BaseTest.bucket.default_collection.upsert 'doc_1', missing_properties_doc
|
133
|
+
base = BaseTest.find('doc_1')
|
134
|
+
|
135
|
+
expect(base.name).to eq('Pierre')
|
136
|
+
expect(base.job).to be_nil
|
137
|
+
base.destroy
|
138
|
+
end
|
139
|
+
|
57
140
|
it "should support serialisation" do
|
58
141
|
base = BaseTest.create!(name: 'joe')
|
59
142
|
|
60
143
|
base_id = base.id
|
61
|
-
expect(base.to_json).to eq({name: 'joe', job: nil
|
62
|
-
expect(base.to_json(only: :name)).to eq({name: 'joe'}.to_json)
|
144
|
+
expect(base.to_json).to eq({ id: base_id, name: 'joe', job: nil }.to_json)
|
145
|
+
expect(base.to_json(only: :name)).to eq({ name: 'joe' }.to_json)
|
63
146
|
|
64
147
|
base.destroy
|
65
148
|
end
|
@@ -73,21 +156,27 @@ describe CouchbaseOrm::Base do
|
|
73
156
|
base.name = 'change'
|
74
157
|
expect(base.changes.empty?).to be(false)
|
75
158
|
|
76
|
-
|
159
|
+
# Attributes are set by key
|
160
|
+
base = BaseTest.new
|
161
|
+
base[:name] = 'bob'
|
162
|
+
expect(base.changes.empty?).to be(false)
|
163
|
+
|
164
|
+
# Attributes are set by initializer from hash
|
165
|
+
base = BaseTest.new({ name: 'bob' })
|
77
166
|
expect(base.changes.empty?).to be(false)
|
78
167
|
expect(base.previous_changes.empty?).to be(true)
|
79
168
|
|
80
169
|
# A saved model should have no changes
|
81
170
|
base = BaseTest.create!(name: 'joe')
|
82
171
|
expect(base.changes.empty?).to be(true)
|
83
|
-
expect(base.previous_changes.empty?).to be(
|
172
|
+
expect(base.previous_changes.empty?).to be(true)
|
84
173
|
|
85
174
|
# Attributes are copied from the existing model
|
86
175
|
base = BaseTest.new(base)
|
87
176
|
expect(base.changes.empty?).to be(false)
|
88
177
|
expect(base.previous_changes.empty?).to be(true)
|
89
178
|
ensure
|
90
|
-
base.destroy if base.
|
179
|
+
base.destroy if base.persisted?
|
91
180
|
end
|
92
181
|
end
|
93
182
|
|
@@ -101,6 +190,85 @@ describe CouchbaseOrm::Base do
|
|
101
190
|
end
|
102
191
|
end
|
103
192
|
|
193
|
+
it "should be able to create model with a custom ID" do
|
194
|
+
begin
|
195
|
+
base = BaseTest.create!(id: 'custom_id', name: 'joe')
|
196
|
+
expect(base.id).to eq('custom_id')
|
197
|
+
|
198
|
+
base = BaseTest.find('custom_id')
|
199
|
+
expect(base.id).to eq('custom_id')
|
200
|
+
ensure
|
201
|
+
base&.destroy
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
|
206
|
+
it "should try to load a model with nothing but single-multiple ID" do
|
207
|
+
begin
|
208
|
+
bases = [BaseTest.create!(name: 'joe')]
|
209
|
+
objs = CouchbaseOrm.try_load(bases.map(&:id))
|
210
|
+
expect(objs).to match_array(bases)
|
211
|
+
ensure
|
212
|
+
bases.each(&:destroy)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should try to load a model with nothing but multiple ID" do
|
217
|
+
begin
|
218
|
+
bases = [BaseTest.create!(name: 'joe'), CompareTest.create!(age: 12)]
|
219
|
+
objs = CouchbaseOrm.try_load(bases.map(&:id))
|
220
|
+
expect(objs).to match_array(bases)
|
221
|
+
ensure
|
222
|
+
bases.each(&:destroy)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should set the attribute on creation" do
|
227
|
+
base = BaseTest.create!(name: 'joe')
|
228
|
+
expect(base.name).to eq('joe')
|
229
|
+
ensure
|
230
|
+
base.destroy
|
231
|
+
end
|
232
|
+
|
233
|
+
it "should support getting the attribute by key" do
|
234
|
+
base = BaseTest.create!(name: 'joe')
|
235
|
+
expect(base[:name]).to eq('joe')
|
236
|
+
ensure
|
237
|
+
base.destroy
|
238
|
+
end
|
239
|
+
|
240
|
+
it "cannot change the id of a loaded object" do
|
241
|
+
base = BaseTest.create!(name: 'joe')
|
242
|
+
expect(base.id).to_not be_nil
|
243
|
+
expect{base.id = "foo"}.to raise_error(RuntimeError, 'ID cannot be changed')
|
244
|
+
end
|
245
|
+
|
246
|
+
it "should generate a timestamp on creation" do
|
247
|
+
base = TimestampTest.create!
|
248
|
+
expect(base.created_at).to be_a(Time)
|
249
|
+
end
|
250
|
+
|
251
|
+
it "should find multiple ids at same time" do
|
252
|
+
base1 = BaseTest.create!(name: 'joe1')
|
253
|
+
base2 = BaseTest.create!(name: 'joe2')
|
254
|
+
base3 = BaseTest.create!(name: 'joe3')
|
255
|
+
expect(BaseTest.find([base1.id, base2.id, base3.id])).to eq([base1, base2, base3])
|
256
|
+
end
|
257
|
+
|
258
|
+
it "should find multiple ids at same time with a not found id with exception" do
|
259
|
+
base1 = BaseTest.create!(name: 'joe1')
|
260
|
+
base2 = BaseTest.create!(name: 'joe2')
|
261
|
+
base3 = BaseTest.create!(name: 'joe3')
|
262
|
+
expect { BaseTest.find([base1.id, 't', base3.id]) }.to raise_error(Couchbase::Error::DocumentNotFound)
|
263
|
+
end
|
264
|
+
|
265
|
+
it "should find multiple ids at same time with a not found id without exception" do
|
266
|
+
base1 = BaseTest.create!(name: 'joe1')
|
267
|
+
base2 = BaseTest.create!(name: 'joe2')
|
268
|
+
base3 = BaseTest.create!(name: 'joe3')
|
269
|
+
expect(BaseTest.find([base1.id, 't', 't', base2.id, base3.id], quiet: true)).to eq([base1, base2, base3])
|
270
|
+
end
|
271
|
+
|
104
272
|
describe BaseTest do
|
105
273
|
it_behaves_like "ActiveModel"
|
106
274
|
end
|
@@ -108,4 +276,118 @@ describe CouchbaseOrm::Base do
|
|
108
276
|
describe CompareTest do
|
109
277
|
it_behaves_like "ActiveModel"
|
110
278
|
end
|
279
|
+
|
280
|
+
it 'does not expose callbacks for nested that wont never be called' do
|
281
|
+
expect{
|
282
|
+
class InvalidNested < CouchbaseOrm::NestedDocument
|
283
|
+
before_save {p "this should raise on loading class"}
|
284
|
+
end
|
285
|
+
|
286
|
+
}.to raise_error NoMethodError
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'should unassign attributes on validation error' do
|
290
|
+
doc = DocInvalidOnUpdate.new(title: 'Test')
|
291
|
+
doc.save
|
292
|
+
expect(doc.title).to eq('Test')
|
293
|
+
expect { doc.update!(title: 'changed wich assignation should not stay after raise') }.to raise_error(CouchbaseOrm::Error::RecordInvalid)
|
294
|
+
expect(doc.title_was).to eq('Test') # raising in master with "changed wich assignation should not stay after raise"
|
295
|
+
expect(doc.title).not_to eq(doc.title_was)
|
296
|
+
expect(doc.title).to eq('changed wich assignation should not stay after raise')
|
297
|
+
end
|
298
|
+
|
299
|
+
describe '.ignored_properties' do
|
300
|
+
it 'returns an array of ignored properties' do
|
301
|
+
expect(BaseTestWithIgnoredProperties.ignored_properties).to eq(['deprecated_property'])
|
302
|
+
end
|
303
|
+
|
304
|
+
context 'given a document with ignored properties' do
|
305
|
+
let(:doc_id) { 'doc_1' }
|
306
|
+
let(:document_properties) do
|
307
|
+
{
|
308
|
+
'type' => BaseTestWithIgnoredProperties.design_document,
|
309
|
+
'name' => 'Pierre',
|
310
|
+
'job' => 'dev',
|
311
|
+
'deprecated_property' => 'depracted that could be removed on next save'
|
312
|
+
}
|
313
|
+
end
|
314
|
+
let(:loaded_model) { BaseTestWithIgnoredProperties.find(doc_id) }
|
315
|
+
|
316
|
+
before { BaseTestWithIgnoredProperties.bucket.default_collection.upsert doc_id, document_properties }
|
317
|
+
after { BaseTestWithIgnoredProperties.bucket.default_collection.remove doc_id }
|
318
|
+
|
319
|
+
it 'ignores the ignored properties on load from db (and dont raise)' do
|
320
|
+
expect(loaded_model.attributes.keys).not_to include('deprecated_property')
|
321
|
+
expect(loaded_model.name).to eq('Pierre')
|
322
|
+
expect(BaseTestWithIgnoredProperties.bucket.default_collection.get(doc_id).content).to include(document_properties)
|
323
|
+
end
|
324
|
+
|
325
|
+
it 'delete the ignored properties on save' do
|
326
|
+
loaded_model.name = 'Updated Name'
|
327
|
+
expect{ loaded_model.save }.to change { BaseTestWithIgnoredProperties.bucket.default_collection.get(doc_id).content.keys.sort }.
|
328
|
+
from(%w[deprecated_property job name type]).
|
329
|
+
to(%w[job name type])
|
330
|
+
end
|
331
|
+
|
332
|
+
it 'does not raise for reload' do
|
333
|
+
expect{ loaded_model.reload }.not_to raise_error
|
334
|
+
end
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
describe '.properties_always_exists_in_document' do
|
339
|
+
it 'Uses NOT VALUED when properties_always_exists_in_document = false' do
|
340
|
+
where_clause = BaseTest.where(name: nil)
|
341
|
+
expect(where_clause.to_n1ql).to include("AND name IS NOT VALUED")
|
342
|
+
end
|
343
|
+
|
344
|
+
it 'Uses VALUED when properties_always_exists_in_document = false' do
|
345
|
+
where_clause = BaseTest.where.not(name: nil)
|
346
|
+
expect(where_clause.to_n1ql).to include("AND name IS VALUED")
|
347
|
+
end
|
348
|
+
|
349
|
+
it 'Uses IS NULL when properties_always_exists_in_document = true' do
|
350
|
+
where_clause = BaseTestWithPropertiesAlwaysExistsInDocument.where(name: nil)
|
351
|
+
expect(where_clause.to_n1ql).to include("AND name IS NULL")
|
352
|
+
end
|
353
|
+
|
354
|
+
it 'Uses IS NOT NULL when properties_always_exists_in_document = true' do
|
355
|
+
where_clause = BaseTestWithPropertiesAlwaysExistsInDocument.where.not(name: nil)
|
356
|
+
expect(where_clause.to_n1ql).to include("AND name IS NOT NULL")
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
describe 'With a range in the where clause' do
|
361
|
+
before do
|
362
|
+
BaseTestWithTimeframe.delete_all
|
363
|
+
BaseTestWithTimeframe.create!(name: 'january', start_date: DateTime.new(2020, 1, 1, 10, 0, 0), age: 10)
|
364
|
+
BaseTestWithTimeframe.create!(name: 'midjanuary', start_date: DateTime.new(2020, 1, 15,11, 0, 0), age: 15)
|
365
|
+
BaseTestWithTimeframe.create!(name: 'february', start_date: DateTime.new(2020, 2, 1,15, 0, 0), age: 20)
|
366
|
+
end
|
367
|
+
|
368
|
+
it 'manages the datetime range correctly' do
|
369
|
+
result = BaseTestWithTimeframe.where(start_date: DateTime.new(2020, 1, 1, 11, 0,0)..DateTime.new(2020, 1, 31)).pluck(:name)
|
370
|
+
expect(result).to eq(%w[midjanuary])
|
371
|
+
end
|
372
|
+
|
373
|
+
it 'manages the date range correctly' do
|
374
|
+
result = BaseTestWithTimeframe.where(start_date: Date.new(2020, 1, 1)..Date.new(2020, 1, 31)).pluck(:name)
|
375
|
+
expect(result).to eq(%w[january midjanuary])
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'manages the time range correctly' do
|
379
|
+
result = BaseTestWithTimeframe.where(start_date: Time.new(2020, 1, 1, 14, 35, 0)..Time.new(2020, 1, 31, 16, 35, 0)).pluck(:name)
|
380
|
+
expect(result).to eq(%w[midjanuary])
|
381
|
+
end
|
382
|
+
|
383
|
+
it 'manages the integer range correctly' do
|
384
|
+
result = BaseTestWithTimeframe.where(age: 12..25).pluck(:name)
|
385
|
+
expect(result).to eq(%w[midjanuary february])
|
386
|
+
end
|
387
|
+
|
388
|
+
it 'manages the exclude end correctly' do
|
389
|
+
result = BaseTestWithTimeframe.where(age: 10...15).pluck(:name)
|
390
|
+
expect(result).to eq(%w[january])
|
391
|
+
end
|
392
|
+
end
|
111
393
|
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.expand_path("../support", __FILE__)
|
2
|
+
require File.expand_path("../../lib/couchbase-orm/proxies/collection_proxy", __FILE__)
|
3
|
+
|
4
|
+
class Proxyfied
|
5
|
+
def get(key, options = nil)
|
6
|
+
raise Couchbase::Error::DocumentNotFound
|
7
|
+
end
|
8
|
+
def remove(key, options = nil)
|
9
|
+
raise Couchbase::Error::DocumentNotFound
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe CouchbaseOrm::CollectionProxy do
|
14
|
+
it "should raise an error when get is called with bang version" do
|
15
|
+
expect { CouchbaseOrm::CollectionProxy.new(Proxyfied.new).get!('key') }.to raise_error(Couchbase::Error::DocumentNotFound)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should not raise an error when get is called with non bang version" do
|
19
|
+
expect { CouchbaseOrm::CollectionProxy.new(Proxyfied.new).get('key') }.to_not raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should raise an error when remove is called with bang version" do
|
23
|
+
expect { CouchbaseOrm::CollectionProxy.new(Proxyfied.new).remove!('key') }.to raise_error(Couchbase::Error::DocumentNotFound)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should not raise an error when remove is called with non bang version" do
|
27
|
+
expect { CouchbaseOrm::CollectionProxy.new(Proxyfied.new).remove('key') }.to_not raise_error
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require File.expand_path("../support", __FILE__)
|
4
|
+
|
5
|
+
class ConnectedModel < CouchbaseOrm::Base
|
6
|
+
attribute :name, :string
|
7
|
+
end
|
8
|
+
|
9
|
+
# disabled by default because a little hacky
|
10
|
+
# and test couchbase ruby client not couchbase orm
|
11
|
+
|
12
|
+
return unless ENV["TEST_DOCKER_CONTAINER"]
|
13
|
+
|
14
|
+
describe CouchbaseOrm::Base do
|
15
|
+
it "should reconnect after a disconnection" do
|
16
|
+
s = ConnectedModel.create!(name: "foo")
|
17
|
+
`docker stop #{ENV["TEST_DOCKER_CONTAINER"]}`
|
18
|
+
sleep 3
|
19
|
+
expect {ConnectedModel.find(s.id)}.to raise_error(Couchbase::Error::UnambiguousTimeout)
|
20
|
+
`docker start #{ENV["TEST_DOCKER_CONTAINER"]}`
|
21
|
+
sleep 10
|
22
|
+
s2 = ConnectedModel.find(s.id)
|
23
|
+
expect(s2.name).to eq (s.name)
|
24
|
+
ensure
|
25
|
+
`docker start #{ENV["TEST_DOCKER_CONTAINER"]}`
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require './lib/couchbase-orm/active_record_compat'
|
2
|
+
|
3
|
+
class Foo
|
4
|
+
include CouchbaseOrm::ActiveRecordCompat
|
5
|
+
|
6
|
+
def compute_age
|
7
|
+
10 + 32
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe CouchbaseOrm::ActiveRecordCompat do
|
12
|
+
let(:foo) { Foo.new }
|
13
|
+
describe '#slice' do
|
14
|
+
it 'creates a hash with method names as keys and results as values' do
|
15
|
+
expect(foo.slice(:compute_age).to_h).to eq(HashWithIndifferentAccess.new({ compute_age: 42 }))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#values_at' do
|
20
|
+
it 'creates an array of results from given method names' do
|
21
|
+
expect(foo.values_at([:compute_age])).to eq([42])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'couchbase-orm'
|
4
|
+
|
5
|
+
class Doc < CouchbaseOrm::Base
|
6
|
+
attribute :title
|
7
|
+
end
|
8
|
+
|
9
|
+
describe 'CouchbaseOrm::Changeable' do
|
10
|
+
it 'should have empty changes after loading a record from db' do
|
11
|
+
doc = Doc.create(title: 'Test')
|
12
|
+
expect(doc.changes).to be_empty
|
13
|
+
doc = Doc.find(doc.id)
|
14
|
+
expect(doc.changes).to be_empty
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'couchbase-orm/json_schema/validation'
|
2
|
+
|
3
|
+
class DummyClass
|
4
|
+
extend CouchbaseOrm::JsonSchema::Validation
|
5
|
+
validate_json_schema
|
6
|
+
end
|
7
|
+
|
8
|
+
class DummyClass2
|
9
|
+
extend CouchbaseOrm::JsonSchema::Validation
|
10
|
+
end
|
11
|
+
|
12
|
+
RSpec.describe CouchbaseOrm::JsonSchema::Validation do
|
13
|
+
describe '#validate_json_schema' do
|
14
|
+
it 'sets @validate_json_schema to true' do
|
15
|
+
expect(DummyClass.json_validation_config[:enabled]).to be_truthy
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'does not mixup @validate_json_schema between classes' do
|
19
|
+
expect(DummyClass.json_validation_config[:enabled]).to be_truthy
|
20
|
+
expect(DummyClass2.json_validation_config[:enabled]).to be_falsey
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'couchbase-orm/json_schema/validator'
|
2
|
+
|
3
|
+
RSpec.describe CouchbaseOrm::JsonSchema::Validator do
|
4
|
+
describe '#validate_entity' do
|
5
|
+
context 'when mode is set to an unknown value' do
|
6
|
+
let(:json_validation_config) { { mode: :unknown } }
|
7
|
+
let(:validator) { CouchbaseOrm::JsonSchema::Validator.new(json_validation_config) }
|
8
|
+
it 'raises an error' do
|
9
|
+
expect { validator.validate_entity({}, '') }.to raise_error('Unknown validation mode unknown')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'couchbase-orm'
|
3
|
+
require 'couchbase-orm/timestamps_spec_models'
|
4
|
+
|
5
|
+
describe CouchbaseOrm::Timestamps do
|
6
|
+
context 'simple document' do
|
7
|
+
describe 'without any related attributes' do
|
8
|
+
let(:doc) { DocWithoutTimestamps.new }
|
9
|
+
it 'does not run timestamp callbacks on create' do
|
10
|
+
expect(doc).not_to receive(:updated_at=)
|
11
|
+
expect(doc).not_to receive(:created_at=)
|
12
|
+
doc.save
|
13
|
+
end
|
14
|
+
it 'does not run timestamp callbacks on update' do
|
15
|
+
doc.save
|
16
|
+
expect(doc).not_to receive(:updated_at=)
|
17
|
+
expect(doc).not_to receive(:created_at=)
|
18
|
+
doc.title = 'New title'
|
19
|
+
doc.save
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe 'with only created_at attribute' do
|
24
|
+
let(:doc) { DocWithCreatedAt.new }
|
25
|
+
it 'runs created_at timestamp callback on create' do
|
26
|
+
expect(doc).to receive(:created_at=)
|
27
|
+
expect(doc).not_to receive(:updated_at=)
|
28
|
+
doc.save
|
29
|
+
end
|
30
|
+
it 'does not run timestamp callback on update' do
|
31
|
+
doc.save
|
32
|
+
expect(doc).not_to receive(:created_at=)
|
33
|
+
expect(doc).not_to receive(:updated_at=)
|
34
|
+
doc.title = 'New title'
|
35
|
+
doc.save
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'with only updated_at attribute' do
|
40
|
+
let(:doc) { DocWithUpdatedAt.new }
|
41
|
+
it 'does run timestamp callback on create only for updated_at' do
|
42
|
+
expect(doc).not_to receive(:created_at=)
|
43
|
+
expect(doc).to receive(:updated_at=)
|
44
|
+
doc.save
|
45
|
+
end
|
46
|
+
it 'runs updated_at timestamp callback on update' do
|
47
|
+
doc.save
|
48
|
+
expect(doc).to receive(:updated_at=)
|
49
|
+
expect(doc).not_to receive(:created_at=)
|
50
|
+
doc.title = 'New title'
|
51
|
+
doc.save
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'with both created_at and updated_at attributes' do
|
56
|
+
let(:doc) { DocWithBothTimestampsAttributes.new }
|
57
|
+
it 'runs created_at timestamp callback on create' do
|
58
|
+
expect(doc).to receive(:created_at=)
|
59
|
+
expect(doc).to receive(:updated_at=)
|
60
|
+
doc.save
|
61
|
+
end
|
62
|
+
it 'runs updated_at timestamp callback on update' do
|
63
|
+
doc.save
|
64
|
+
expect(doc).to receive(:updated_at=)
|
65
|
+
doc.title = 'New title'
|
66
|
+
doc.save
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'with nested documents' do
|
72
|
+
let(:nested_doc) { SimpleNestedDoc.new }
|
73
|
+
describe 'when parent document has both timestamp attributes' do
|
74
|
+
let(:doc) { DocWithBothTimestampsAttributesAndNested.new }
|
75
|
+
# nested document is not saved, so it does not have created_at
|
76
|
+
it 'runs created_at timestamp callback on create only for parent document' do
|
77
|
+
expect(doc).to receive(:created_at=)
|
78
|
+
expect(doc).to receive(:updated_at=)
|
79
|
+
doc.sub = nested_doc
|
80
|
+
expect(nested_doc).not_to receive(:created_at=)
|
81
|
+
doc.save
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'couchbase-orm/base'
|
3
|
+
|
4
|
+
class DocWithoutTimestamps < CouchbaseOrm::Base
|
5
|
+
attribute :title
|
6
|
+
end
|
7
|
+
|
8
|
+
class DocWithCreatedAt < CouchbaseOrm::Base
|
9
|
+
attribute :title
|
10
|
+
attribute :created_at, :datetime
|
11
|
+
end
|
12
|
+
|
13
|
+
class DocWithUpdatedAt < CouchbaseOrm::Base
|
14
|
+
attribute :title
|
15
|
+
attribute :updated_at, :datetime
|
16
|
+
end
|
17
|
+
|
18
|
+
class DocWithBothTimestampsAttributes < CouchbaseOrm::Base
|
19
|
+
attribute :title
|
20
|
+
attribute :created_at, :datetime
|
21
|
+
attribute :updated_at, :datetime
|
22
|
+
end
|
23
|
+
|
24
|
+
class SimpleNestedDoc < CouchbaseOrm::NestedDocument
|
25
|
+
attribute :sub_title
|
26
|
+
attribute :created_at, :datetime
|
27
|
+
attribute :updated_at, :datetime
|
28
|
+
end
|
29
|
+
|
30
|
+
class DocWithBothTimestampsAttributesAndNested < CouchbaseOrm::Base
|
31
|
+
attribute :title
|
32
|
+
attribute :created_at, :datetime
|
33
|
+
attribute :updated_at, :datetime
|
34
|
+
attribute :sub, :nested, type: SimpleNestedDoc
|
35
|
+
attribute :subs, :array, type: SimpleNestedDoc
|
36
|
+
end
|
File without changes
|
data/spec/enum_spec.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require File.expand_path("../support", __FILE__)
|
4
|
+
|
5
|
+
class EnumTest < CouchbaseOrm::Base
|
6
|
+
enum rating: [:awesome, :good, :okay, :bad], default: :okay
|
7
|
+
enum color: [:red, :green, :blue]
|
8
|
+
end
|
9
|
+
|
10
|
+
describe CouchbaseOrm::Base do
|
11
|
+
it "should create an attribute" do
|
12
|
+
base = EnumTest.create!(rating: :good, color: :red)
|
13
|
+
expect(base.attribute_names).to eq(["id", "rating", "color"])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should set the attribute" do
|
17
|
+
base = EnumTest.create!(rating: :good, color: :red)
|
18
|
+
expect(base.rating).to_not be_nil
|
19
|
+
expect(base.color).to_not be_nil
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should convert it to an int" do
|
23
|
+
base = EnumTest.create!(rating: :good, color: :red)
|
24
|
+
expect(base.rating).to eq 2
|
25
|
+
expect(base.color).to eq 1
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should use default value" do
|
29
|
+
base = EnumTest.create!
|
30
|
+
expect(base.rating).to eq 3
|
31
|
+
expect(base.color).to eq 1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|