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/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
|
+
|