couchbase-orm 1.1.0 → 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.
Files changed (87) hide show
  1. checksums.yaml +5 -5
  2. data/.github/workflows/test.yml +45 -0
  3. data/.gitignore +2 -0
  4. data/.travis.yml +5 -4
  5. data/CODEOWNERS +1 -0
  6. data/Gemfile +5 -3
  7. data/README.md +237 -31
  8. data/ci/run_couchbase.sh +22 -0
  9. data/couchbase-orm.gemspec +26 -20
  10. data/lib/couchbase-orm/active_record_compat.rb +92 -0
  11. data/lib/couchbase-orm/associations.rb +119 -0
  12. data/lib/couchbase-orm/base.rb +143 -166
  13. data/lib/couchbase-orm/changeable.rb +512 -0
  14. data/lib/couchbase-orm/connection.rb +28 -8
  15. data/lib/couchbase-orm/encrypt.rb +48 -0
  16. data/lib/couchbase-orm/error.rb +17 -2
  17. data/lib/couchbase-orm/inspectable.rb +37 -0
  18. data/lib/couchbase-orm/json_schema/json_validation_error.rb +13 -0
  19. data/lib/couchbase-orm/json_schema/loader.rb +47 -0
  20. data/lib/couchbase-orm/json_schema/validation.rb +18 -0
  21. data/lib/couchbase-orm/json_schema/validator.rb +45 -0
  22. data/lib/couchbase-orm/json_schema.rb +9 -0
  23. data/lib/couchbase-orm/json_transcoder.rb +27 -0
  24. data/lib/couchbase-orm/locale/en.yml +5 -0
  25. data/lib/couchbase-orm/n1ql.rb +133 -0
  26. data/lib/couchbase-orm/persistence.rb +67 -50
  27. data/lib/couchbase-orm/proxies/bucket_proxy.rb +36 -0
  28. data/lib/couchbase-orm/proxies/collection_proxy.rb +52 -0
  29. data/lib/couchbase-orm/proxies/n1ql_proxy.rb +40 -0
  30. data/lib/couchbase-orm/proxies/results_proxy.rb +23 -0
  31. data/lib/couchbase-orm/railtie.rb +6 -17
  32. data/lib/couchbase-orm/relation.rb +249 -0
  33. data/lib/couchbase-orm/strict_loading.rb +21 -0
  34. data/lib/couchbase-orm/timestamps/created.rb +20 -0
  35. data/lib/couchbase-orm/timestamps/updated.rb +21 -0
  36. data/lib/couchbase-orm/timestamps.rb +15 -0
  37. data/lib/couchbase-orm/types/array.rb +32 -0
  38. data/lib/couchbase-orm/types/date.rb +9 -0
  39. data/lib/couchbase-orm/types/date_time.rb +14 -0
  40. data/lib/couchbase-orm/types/encrypted.rb +17 -0
  41. data/lib/couchbase-orm/types/nested.rb +43 -0
  42. data/lib/couchbase-orm/types/timestamp.rb +18 -0
  43. data/lib/couchbase-orm/types.rb +20 -0
  44. data/lib/couchbase-orm/utilities/enum.rb +13 -1
  45. data/lib/couchbase-orm/utilities/has_many.rb +72 -36
  46. data/lib/couchbase-orm/utilities/ignored_properties.rb +15 -0
  47. data/lib/couchbase-orm/utilities/index.rb +23 -8
  48. data/lib/couchbase-orm/utilities/properties_always_exists_in_document.rb +16 -0
  49. data/lib/couchbase-orm/utilities/query_helper.rb +148 -0
  50. data/lib/couchbase-orm/utils.rb +25 -0
  51. data/lib/couchbase-orm/version.rb +1 -1
  52. data/lib/couchbase-orm/views.rb +38 -41
  53. data/lib/couchbase-orm.rb +44 -9
  54. data/lib/ext/query_n1ql.rb +124 -0
  55. data/lib/rails/generators/couchbase_orm/config/templates/couchbase.yml +3 -2
  56. data/spec/associations_spec.rb +219 -50
  57. data/spec/base_spec.rb +296 -14
  58. data/spec/collection_proxy_spec.rb +29 -0
  59. data/spec/connection_spec.rb +27 -0
  60. data/spec/couchbase-orm/active_record_compat_spec.rb +24 -0
  61. data/spec/couchbase-orm/changeable_spec.rb +16 -0
  62. data/spec/couchbase-orm/json_schema/validation_spec.rb +23 -0
  63. data/spec/couchbase-orm/json_schema/validator_spec.rb +13 -0
  64. data/spec/couchbase-orm/timestamps_spec.rb +85 -0
  65. data/spec/couchbase-orm/timestamps_spec_models.rb +36 -0
  66. data/spec/empty-json-schema/.gitkeep +0 -0
  67. data/spec/enum_spec.rb +34 -0
  68. data/spec/has_many_spec.rb +101 -54
  69. data/spec/index_spec.rb +55 -8
  70. data/spec/json-schema/JsonSchemaBaseTest.json +19 -0
  71. data/spec/json-schema/entity_snakecase.json +20 -0
  72. data/spec/json-schema/loader_spec.rb +42 -0
  73. data/spec/json-schema/specific_path.json +20 -0
  74. data/spec/json_schema_spec.rb +178 -0
  75. data/spec/n1ql_spec.rb +193 -0
  76. data/spec/persistence_spec.rb +49 -9
  77. data/spec/relation_nested_spec.rb +88 -0
  78. data/spec/relation_spec.rb +430 -0
  79. data/spec/support.rb +16 -8
  80. data/spec/type_array_spec.rb +52 -0
  81. data/spec/type_encrypted_spec.rb +114 -0
  82. data/spec/type_nested_spec.rb +191 -0
  83. data/spec/type_spec.rb +317 -0
  84. data/spec/utilities/ignored_properties_spec.rb +20 -0
  85. data/spec/utilities/properties_always_exists_in_document_spec.rb +24 -0
  86. data/spec/views_spec.rb +34 -13
  87. 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, :job
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 = BaseTest.create!(name: 'joe')
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, extended: true)
94
+ resp = BaseTest.bucket.default_collection.get(base.id)
39
95
 
40
- expect(resp.key).to eq(base.id)
96
+ base_loaded = BaseTest.new(resp, id: base.id)
41
97
 
42
- base_loaded = BaseTest.new(resp)
43
- expect(base_loaded).to eq(base)
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(RuntimeError)
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, id: base_id}.to_json)
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
- base = BaseTest.new({name: 'bob'})
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(false)
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.id
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
+