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.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +45 -0
  3. data/.gitignore +2 -0
  4. data/.travis.yml +3 -2
  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 +61 -52
  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 +18 -20
  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 +13 -9
  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 +32 -11
  87. metadata +192 -29
@@ -0,0 +1,512 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/concern'
4
+
5
+ module CouchbaseOrm
6
+ # Defines behavior for dirty tracking.
7
+ module Changeable
8
+ extend ActiveSupport::Concern
9
+
10
+ # Get the changed attributes for the document.
11
+ #
12
+ # @example Get the changed attributes.
13
+ # model.changed
14
+ #
15
+ # @return [ Array<String> ] The changed attributes.
16
+ def changed
17
+ changed_attributes.keys.select { |attr| attribute_change(attr) }
18
+ end
19
+
20
+ # Has the document changed?
21
+ #
22
+ # @example Has the document changed?
23
+ # model.changed?
24
+ #
25
+ # @return [ true | false ] If the document is changed.
26
+ def changed?
27
+ changes.values.any? { |val| val } || children_changed?
28
+ end
29
+
30
+ def _children
31
+ attributes.select { |name, _value| self.class.type_for_attribute(name) == CouchbaseOrm::NestedDocument }
32
+ end
33
+
34
+ # Have any children (embedded documents) of this document changed?
35
+ #
36
+ # @note This intentionally only considers children and not descendants.
37
+ #
38
+ # @return [ true | false ] If any children have changed.
39
+ def children_changed?
40
+ _children.any?(&:changed?)
41
+ end
42
+
43
+ # Get the attribute changes.
44
+ #
45
+ # @example Get the attribute changes.
46
+ # model.changed_attributes
47
+ #
48
+ # @return [ Hash<String, Object> ] The attribute changes.
49
+ def changed_attributes
50
+ @changed_attributes ||= {}
51
+ end
52
+
53
+ # Get all the changes for the document.
54
+ #
55
+ # @example Get all the changes.
56
+ # model.changes
57
+ #
58
+ # @return [ Hash<String, Array<Object, Object> ] The changes.
59
+ def changes
60
+ changed.each_with_object({}) do |attr, changes|
61
+ change = attribute_change(attr)
62
+ changes[attr] = change if change
63
+ end.with_indifferent_access
64
+ end
65
+
66
+ # Call this method after save, so the changes can be properly switched.
67
+ #
68
+ # This will unset the memoized children array, set new record flag to
69
+ # false, set the document as validated, and move the dirty changes.
70
+ #
71
+ # @example Move the changes to previous.
72
+ # person.move_changes
73
+ def move_changes
74
+ @changes_before_last_save = @previous_changes
75
+ @previous_changes = changes
76
+ @attributes_before_last_save = @previous_attributes
77
+ @previous_attributes = attributes.dup
78
+ changed_attributes.clear
79
+ end
80
+
81
+ def changes_applied
82
+ move_changes
83
+ super
84
+ end
85
+
86
+ def reset_object!
87
+ # @attributes = attributes
88
+ # @attributes_before_type_cast = @attributes.dup
89
+ @changed_attributes = {}
90
+ @previous_changes = {}
91
+ @previous_attributes = {}
92
+ end
93
+ # for AR compatibility
94
+ # TODO add coverage and move it in AR compat module
95
+ alias clear_changes_information reset_object!
96
+
97
+ # Get the previous changes on the document.
98
+ #
99
+ # @example Get the previous changes.
100
+ # model.previous_changes
101
+ #
102
+ # @return [ Hash<String, Array<Object, Object> ] The previous changes.
103
+ def previous_changes
104
+ @previous_changes ||= {}
105
+ end
106
+
107
+ # Gets all the new values for each of the changed fields, to be passed to
108
+ # a CouchbaseOrm $set modifier.
109
+ #
110
+ # @example Get the setters for the atomic updates.
111
+ # person = Person.new(:title => "Sir")
112
+ # person.title = "Madam"
113
+ # person.setters # returns { "title" => "Madam" }
114
+ #
115
+ # @return [ Hash ] A +Hash+ of atomic setters.
116
+ def setters
117
+ mods = {}
118
+ changes.each_pair do |name, changes|
119
+ next unless changes
120
+
121
+ old, new = changes
122
+ field = fields[name]
123
+ key = atomic_attribute_name(name)
124
+ if field&.resizable?
125
+ field.add_atomic_changes(self, name, key, mods, new, old)
126
+ else
127
+ mods[key] = new unless atomic_unsets.include?(key)
128
+ end
129
+ end
130
+ mods
131
+ end
132
+
133
+ # Returns the original value of an attribute before the last save.
134
+ #
135
+ # This method is useful in after callbacks to get the original value of
136
+ # an attribute before the save that triggered the callbacks to run.
137
+ #
138
+ # @param [ Symbol | String ] attr The name of the attribute.
139
+ #
140
+ # @return [ Object ] Value of the attribute before the last save.
141
+ def attribute_before_last_save(attr)
142
+ attributes_before_last_save[attr]
143
+ end
144
+
145
+ # Returns the change to an attribute during the last save.
146
+ #
147
+ # @param [ Symbol | String ] attr The name of the attribute.
148
+ #
149
+ # @return [ Array<Object> | nil ] If the attribute was changed, returns
150
+ # an array containing the original value and the saved value, otherwise nil.
151
+ def saved_change_to_attribute(attr)
152
+ previous_changes[attr]
153
+ end
154
+
155
+ # Returns whether this attribute changed during the last save.
156
+ #
157
+ # This method is useful in after callbacks, to see the change
158
+ # in an attribute during the save that triggered the callbacks to run.
159
+ #
160
+ # @param [ String ] attr The name of the attribute.
161
+ # @param [ Object ] from The object the attribute was changed from (optional).
162
+ # @param [ Object ] to The object the attribute was changed to (optional).
163
+ #
164
+ # @return [ true | false ] Whether the attribute has changed during the last save.
165
+ def saved_change_to_attribute?(attr, from: Utils::PLACEHOLDER, to: Utils::PLACEHOLDER)
166
+ changes = saved_change_to_attribute(attr)
167
+ return false unless changes.is_a?(Array)
168
+
169
+ return true if Utils.placeholder?(from) && Utils.placeholder?(to)
170
+ return changes.first == from if Utils.placeholder?(to)
171
+ return changes.last == to if Utils.placeholder?(from)
172
+
173
+ changes.first == from && changes.last == to
174
+ end
175
+
176
+ # Returns whether this attribute change the next time we save.
177
+ #
178
+ # This method is useful in validations and before callbacks to determine
179
+ # if the next call to save will change a particular attribute.
180
+ #
181
+ # @param [ String ] attr The name of the attribute.
182
+ # @param **kwargs The optional keyword arguments.
183
+ #
184
+ # @option **kwargs [ Object ] :from The object the attribute was changed from.
185
+ # @option **kwargs [ Object ] :to The object the attribute was changed to.
186
+ #
187
+ # @return [ true | false ] Whether the attribute change the next time we save.
188
+ def will_save_change_to_attribute?(attr, **kwargs)
189
+ attribute_changed?(attr, **kwargs)
190
+ end
191
+
192
+ private
193
+
194
+ # Get attributes of the document before the document was saved.
195
+ #
196
+ # @return [ Hash ] Previous attributes
197
+ def previous_attributes
198
+ @previous_attributes ||= {}
199
+ end
200
+
201
+ def changes_before_last_save
202
+ @changes_before_last_save ||= {}
203
+ end
204
+
205
+ def attributes_before_last_save
206
+ @attributes_before_last_save ||= {}
207
+ end
208
+
209
+ # Get the old and new value for the provided attribute.
210
+ #
211
+ # @example Get the attribute change.
212
+ # model.attribute_change("name")
213
+ #
214
+ # @param [ String ] attr The name of the attribute.
215
+ #
216
+ # @return [ Array<Object> ] The old and new values.
217
+ def attribute_change(attr)
218
+ changed_attributes[attr] if attribute_changed?(attr)
219
+ end
220
+
221
+ # A class for representing the default value that an attribute was changed
222
+ # from or to.
223
+ #
224
+ # @api private
225
+ class Anything
226
+ # `Anything` objects are always equal to everything. This simplifies
227
+ # the logic for asking whether an attribute has changed or not. If the
228
+ # `from` or `to` value is a `Anything` (because it was not
229
+ # explicitly given), any comparison with it will suggest the value has
230
+ # not changed.
231
+ #
232
+ # @param [ Object ] _other The object being compared with this object.
233
+ #
234
+ # @return [ true ] Always returns true.
235
+ def ==(_other)
236
+ true
237
+ end
238
+ end
239
+
240
+ # a singleton object to represent an optional `to` or `from` value
241
+ # that was not explicitly provided to #attribute_changed?
242
+ ATTRIBUTE_UNCHANGED = Anything.new
243
+
244
+ # Determine if a specific attribute has changed.
245
+ #
246
+ # @example Has the attribute changed?
247
+ # model.attribute_changed?("name")
248
+ #
249
+ # @param [ String ] attr The name of the attribute.
250
+ # @param [ Object ] from The object the attribute was changed from (optional).
251
+ # @param [ Object ] to The object the attribute was changed to (optional).
252
+ #
253
+ # @return [ true | false ] Whether the attribute has changed.
254
+ def attribute_changed?(attr, from: ATTRIBUTE_UNCHANGED, to: ATTRIBUTE_UNCHANGED)
255
+ return false unless changed_attributes.key?(attr)
256
+ return false if changed_attributes[attr] == attributes[attr]
257
+ return false if from != changed_attributes[attr]
258
+ return false if to != attributes[attr]
259
+
260
+ true
261
+ end
262
+
263
+ # Get whether or not the field has a different value from the default.
264
+ #
265
+ # @example Is the field different from the default?
266
+ # model.attribute_changed_from_default?
267
+ #
268
+ # @param [ String ] attr The name of the attribute.
269
+ #
270
+ # @return [ true | false ] If the attribute differs.
271
+ def attribute_changed_from_default?(attr)
272
+ return false unless (field = fields[attr])
273
+
274
+ attributes[attr] != field.eval_default(self)
275
+ end
276
+
277
+ # Get the previous value for the attribute.
278
+ #
279
+ # @example Get the previous value.
280
+ # model.attribute_was("name")
281
+ #
282
+ # @param [ String ] attr The attribute name.
283
+ def attribute_was(attr)
284
+ attribute_changed?(attr) ? changed_attributes[attr].first : attributes[attr]
285
+ end
286
+
287
+ # Get the previous attribute value that was changed
288
+ # before the document was saved.
289
+ #
290
+ # It the document has not been saved yet, or was just loaded from database,
291
+ # this method returns nil for all attributes.
292
+ #
293
+ # @param [ String ] attr The attribute name.
294
+ #
295
+ # @return [ Object | nil ] Attribute value before the document was saved,
296
+ # or nil if the document has not been saved yet.
297
+ def attribute_previously_was(attr)
298
+ if previous_changes.key?(attr)
299
+ previous_changes[attr].first
300
+ else
301
+ previous_attributes[attr]
302
+ end
303
+ end
304
+
305
+ # Flag an attribute as going to change.
306
+ #
307
+ # @example Flag the attribute.
308
+ # model.attribute_will_change!("name")
309
+ #
310
+ # @param [ String ] attr The name of the attribute.
311
+ #
312
+ # @return [ Object ] The old value.
313
+ def attribute_will_change!(attr)
314
+ return if changed_attributes.key?(attr)
315
+
316
+ changed_attributes[attr] = attributes[attr]&.__deep_copy__
317
+ end
318
+
319
+ # Set the attribute back to its old value.
320
+ #
321
+ # @example Reset the attribute.
322
+ # model.reset_attribute!("name")
323
+ #
324
+ # @param [ String ] attr The name of the attribute.
325
+ #
326
+ # @return [ Object ] The old value.
327
+ def reset_attribute!(attr)
328
+ attributes[attr] = changed_attributes.delete(attr) if attribute_changed?(attr)
329
+ end
330
+
331
+ def reset_attribute_to_default!(attr)
332
+ if (field = fields[attr])
333
+ __send__("#{attr}=", field.eval_default(self))
334
+ else
335
+ __send__("#{attr}=", nil)
336
+ end
337
+ end
338
+
339
+ def reset_attributes_before_type_cast
340
+ @attributes_before_type_cast = @attributes.dup
341
+ end
342
+
343
+ # Class-level methods for changeable objects.
344
+ module ClassMethods
345
+ private
346
+
347
+ # Generate all the dirty methods needed for the attribute.
348
+ #
349
+ # @example Generate the dirty methods.
350
+ # Model.create_dirty_methods("name", "name")
351
+ #
352
+ # @param [ String ] name The name of the field.
353
+ # @param [ String ] meth The name of the accessor.
354
+ #
355
+ # @return [ Module ] The fields module.
356
+ def create_dirty_methods(name, meth)
357
+ create_dirty_change_accessor(name, meth)
358
+ create_dirty_change_check(name, meth)
359
+ create_dirty_change_flag(name, meth)
360
+ create_dirty_default_change_check(name, meth)
361
+ create_dirty_previous_value_accessor(name, meth)
362
+ create_dirty_reset(name, meth)
363
+ create_dirty_reset_to_default(name, meth)
364
+ create_dirty_previously_changed?(name, meth)
365
+ create_dirty_previous_change(name, meth)
366
+ end
367
+
368
+ def create_setters(name)
369
+ define_method("#{name}=") do |new_attribute_value|
370
+ previous_value = attributes[name.to_s]
371
+ ret = super(new_attribute_value)
372
+ if previous_value != attributes[name.to_s]
373
+ changed_attributes.merge!(Hash[name, [previous_value, attributes[name.to_s]]])
374
+ end
375
+ ret
376
+ end
377
+ end
378
+
379
+ # Creates the dirty change accessor.
380
+ #
381
+ # @example Create the accessor.
382
+ # Model.create_dirty_change_accessor("name", "alias")
383
+ #
384
+ # @param [ String ] name The attribute name.
385
+ # @param [ String ] meth The name of the accessor.
386
+ def create_dirty_change_accessor(name, meth)
387
+ define_method("#{meth}_change") do
388
+ attribute_change(name)
389
+ end
390
+ end
391
+
392
+ # Creates the dirty change check.
393
+ #
394
+ # @example Create the check.
395
+ # Model.create_dirty_change_check("name", "alias")
396
+ #
397
+ # @param [ String ] name The attribute name.
398
+ # @param [ String ] meth The name of the accessor.
399
+ def create_dirty_change_check(name, meth)
400
+ define_method("#{meth}_changed?") do |**kwargs|
401
+ attribute_changed?(name, **kwargs)
402
+ end
403
+ define_method("will_save_change_to_#{meth}?") do |**kwargs|
404
+ will_save_change_to_attribute?(name, **kwargs)
405
+ end
406
+ end
407
+
408
+ # Creates the dirty default change check.
409
+ #
410
+ # @example Create the check.
411
+ # Model.create_dirty_default_change_check("name", "alias")
412
+ #
413
+ # @param [ String ] name The attribute name.
414
+ # @param [ String ] meth The name of the accessor.
415
+ def create_dirty_default_change_check(name, meth)
416
+ define_method("#{meth}_changed_from_default?") do
417
+ attribute_changed_from_default?(name)
418
+ end
419
+ end
420
+
421
+ # Creates the dirty change previous value accessors.
422
+ #
423
+ # @example Create the accessor.
424
+ # Model.create_dirty_previous_value_accessor("name", "alias")
425
+ #
426
+ # @param [ String ] name The attribute name.
427
+ # @param [ String ] meth The name of the accessor.
428
+ def create_dirty_previous_value_accessor(name, meth)
429
+ define_method("#{meth}_was") do
430
+ attribute_was(name)
431
+ end
432
+ define_method("#{meth}_previously_was") do
433
+ attribute_previously_was(name)
434
+ end
435
+ define_method("#{meth}_before_last_save") do
436
+ attribute_before_last_save(name)
437
+ end
438
+ define_method("saved_change_to_#{meth}") do
439
+ saved_change_to_attribute(name)
440
+ end
441
+ define_method("saved_change_to_#{meth}?") do |**kwargs|
442
+ saved_change_to_attribute?(name, **kwargs)
443
+ end
444
+ end
445
+
446
+ # Creates the dirty change flag.
447
+ #
448
+ # @example Create the flag.
449
+ # Model.create_dirty_change_flag("name", "alias")
450
+ #
451
+ # @param [ String ] name The attribute name.
452
+ # @param [ String ] meth The name of the accessor.
453
+ def create_dirty_change_flag(name, meth)
454
+ define_method("#{meth}_will_change!") do
455
+ attribute_will_change!(name)
456
+ end
457
+ end
458
+
459
+ # Creates the dirty change reset.
460
+ #
461
+ # @example Create the reset.
462
+ # Model.create_dirty_reset("name", "alias")
463
+ #
464
+ # @param [ String ] name The attribute name.
465
+ # @param [ String ] meth The name of the accessor.
466
+ def create_dirty_reset(name, meth)
467
+ define_method("reset_#{meth}!") do
468
+ reset_attribute!(name)
469
+ end
470
+ end
471
+
472
+ # Creates the dirty change reset to default.
473
+ #
474
+ # @example Create the reset.
475
+ # Model.create_dirty_reset_to_default("name", "alias")
476
+ #
477
+ # @param [ String ] name The attribute name.
478
+ # @param [ String ] meth The name of the accessor.
479
+ def create_dirty_reset_to_default(name, meth)
480
+ define_method("reset_#{meth}_to_default!") do
481
+ reset_attribute_to_default!(name)
482
+ end
483
+ end
484
+
485
+ # Creates the dirty change check.
486
+ #
487
+ # @example Create the dirty change check.
488
+ # Model.create_dirty_previously_changed?("name", "alias")
489
+ #
490
+ # @param [ String ] name The attribute name.
491
+ # @param [ String ] meth The name of the accessor.
492
+ def create_dirty_previously_changed?(name, meth)
493
+ define_method("#{meth}_previously_changed?") do
494
+ previous_changes.key?(name)
495
+ end
496
+ end
497
+
498
+ # Creates the dirty change accessor.
499
+ #
500
+ # @example Create the dirty change accessor.
501
+ # Model.create_dirty_previous_change("name", "alias")
502
+ #
503
+ # @param [ String ] name The attribute name.
504
+ # @param [ String ] meth The name of the accessor.
505
+ def create_dirty_previous_change(name, meth)
506
+ define_method("#{meth}_previous_change") do
507
+ previous_changes[name]
508
+ end
509
+ end
510
+ end
511
+ end
512
+ end
@@ -1,16 +1,36 @@
1
- # frozen_string_literal: true, encoding: ASCII-8BIT
2
-
3
- require 'libcouchbase'
1
+ require 'couchbase'
4
2
 
5
3
  module CouchbaseOrm
6
4
  class Connection
7
- @options = {}
8
- class << self
9
- attr_accessor :options
5
+ @@config = nil
6
+ def self.config
7
+ @@config || {
8
+ :connection_string => "couchbase://#{ENV['COUCHBASE_HOST'] || '127.0.0.1'}",
9
+ :username => ENV['COUCHBASE_USER'],
10
+ :password => ENV['COUCHBASE_PASSWORD'],
11
+ :bucket => ENV['COUCHBASE_BUCKET']
12
+ }
13
+ end
14
+
15
+ def self.config=(config)
16
+ @@config = config
17
+ end
18
+
19
+ def self.cluster
20
+ @cluster ||= begin
21
+ cb_config = Couchbase::Configuration.new
22
+ cb_config.connection_string = config[:connection_string] || raise(CouchbaseOrm::Error, 'Missing CouchbaseOrm connection string')
23
+ cb_config.username = config[:username] || raise(CouchbaseOrm::Error, 'Missing CouchbaseOrm username')
24
+ cb_config.password = config[:password] || raise(CouchbaseOrm::Error, 'Missing CouchbaseOrm password')
25
+ Couchbase::Cluster.connect(cb_config)
26
+ end
10
27
  end
11
28
 
12
29
  def self.bucket
13
- @bucket ||= ::Libcouchbase::Bucket.new(**@options)
30
+ @bucket ||= begin
31
+ bucket_name = config[:bucket] || raise(CouchbaseOrm::Error, 'Missing CouchbaseOrm bucket name')
32
+ cluster.bucket(bucket_name)
33
+ end
14
34
  end
15
35
  end
16
- end
36
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true, encoding: ASCII-8BIT
2
+
3
+ module CouchbaseOrm
4
+ module Encrypt
5
+ def encode_encrypted_attributes
6
+ attributes.map do |key, value|
7
+ type = self.class.attribute_types[key.to_s]
8
+ if type.is_a?(CouchbaseOrm::Types::Encrypted)
9
+ next unless value
10
+ raise "Can not serialize value #{value} of type '#{value.class}' for Tanker encrypted attribute" unless value.is_a?(String)
11
+ ["encrypted$#{key}", {
12
+ alg: type.alg,
13
+ ciphertext: value
14
+ }]
15
+ else
16
+ [key,value]
17
+ end
18
+ end.compact.to_h
19
+ end
20
+
21
+ def decode_encrypted_attributes(attributes)
22
+ attributes.map do |key, value|
23
+ key = key.to_s
24
+ if key.start_with?('encrypted$')
25
+ key = key.gsub('encrypted$', '')
26
+ value = value.with_indifferent_access[:ciphertext]
27
+ end
28
+ [key, value]
29
+ end.to_h
30
+ end
31
+
32
+
33
+ def to_json(*args, **kwargs)
34
+ as_json.to_json(*args, **kwargs)
35
+ end
36
+
37
+ def as_json(*args, **kwargs)
38
+ super(*args, **kwargs).map do |key, value|
39
+ type = self.class.attribute_types[key.to_s]
40
+ if type.is_a?(CouchbaseOrm::Types::Encrypted) && value
41
+ raise "Can not serialize value #{value} of type '#{value.class}' for encrypted attribute" unless value.is_a?(String)
42
+ end
43
+ [key, value]
44
+ end.to_h.with_indifferent_access
45
+ end
46
+
47
+ end
48
+ end
@@ -3,13 +3,28 @@
3
3
  module CouchbaseOrm
4
4
  class Error < ::StandardError
5
5
  attr_reader :record
6
-
6
+
7
7
  def initialize(message = nil, record = nil)
8
8
  @record = record
9
9
  super(message)
10
10
  end
11
11
 
12
- class RecordInvalid < Error; end
12
+ class RecordInvalid < Error
13
+ def initialize(message = nil, record = nil)
14
+ if record
15
+ errors = record.errors.full_messages.join(", ")
16
+ message = I18n.t(
17
+ :"couchbase.#{record.class.design_document}.errors.messages.record_invalid",
18
+ errors: errors,
19
+ default: :"couchbase.errors.messages.record_invalid"
20
+ )
21
+ end
22
+ super(message, record)
23
+ end
24
+ end
25
+ class TypeMismatchError < Error; end
13
26
  class RecordExists < Error; end
27
+ class CouchbaseOrm::Error::EmptyNotAllowed < Error; end
14
28
  end
29
+ class StrictLoadingViolationError < Error; end
15
30
  end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CouchbaseOrm
4
+
5
+ # Contains the behavior around inspecting documents via inspect.
6
+ module Inspectable
7
+
8
+ # Returns the class name plus its attributes.
9
+ #
10
+ # @example Inspect the document.
11
+ # person.inspect
12
+ #
13
+ # @return [ String ] A nice pretty string to look at.
14
+ def inspect
15
+ inspection = []
16
+ inspection.concat(inspect_attributes)
17
+ "#<#{self.class.name} id: #{respond_to?(:id)? id.inspect : 'no id'}, #{inspection * ', '}>"
18
+ end
19
+
20
+ private
21
+
22
+ # Get an array of inspected fields for the document.
23
+ #
24
+ # @api private
25
+ #
26
+ # @example Inspect the defined fields.
27
+ # document.inspect_attributes
28
+ #
29
+ # @return [ String ] An array of pretty printed field values.
30
+ def inspect_attributes
31
+ attributes.map do |name, value|
32
+ next if name.to_s == "id"
33
+ "#{name}: #{value.inspect}"
34
+ end.compact
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CouchbaseOrm
4
+ module JsonSchema
5
+ class JsonValidationError < StandardError
6
+
7
+ def initialize(class_name, errors)
8
+ super("[COUCHBASEORM]: Invalid document #{class_name} with errors : #{errors}")
9
+ end
10
+
11
+ end
12
+ end
13
+ end