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.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +45 -0
- data/.gitignore +2 -0
- data/.travis.yml +5 -4
- 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 +67 -50
- 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 +23 -8
- 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 +55 -8
- 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 +34 -13
- metadata +192 -29
@@ -32,6 +32,8 @@ module CouchbaseOrm
|
|
32
32
|
val = if options[:polymorphic]
|
33
33
|
::CouchbaseOrm.try_load(self.send(ref))
|
34
34
|
else
|
35
|
+
raise CouchbaseOrm::StrictLoadingViolationError, "#{self.class} is marked as strict_loading and #{assoc} cannot be lazily loaded." if strict_loading?
|
36
|
+
|
35
37
|
assoc.constantize.find(self.send(ref), quiet: true)
|
36
38
|
end
|
37
39
|
instance_variable_set(instance_var, val)
|
@@ -55,11 +57,126 @@ module CouchbaseOrm
|
|
55
57
|
end
|
56
58
|
end
|
57
59
|
|
60
|
+
def has_and_belongs_to_many(name, **options)
|
61
|
+
@associations ||= []
|
62
|
+
@associations << [name.to_sym, options[:dependent]]
|
63
|
+
|
64
|
+
ref = options[:foreign_key] || :"#{name.to_s.singularize}_ids"
|
65
|
+
ref_ass = :"#{ref}="
|
66
|
+
instance_var = :"@__assoc_#{name}"
|
67
|
+
|
68
|
+
# Class reference
|
69
|
+
assoc = (options[:class_name] || name.to_s.singularize.camelize).to_s
|
70
|
+
|
71
|
+
# Create the local setter / getter
|
72
|
+
attribute(ref) { |value|
|
73
|
+
remove_instance_variable(instance_var) if instance_variable_defined?(instance_var)
|
74
|
+
value
|
75
|
+
}
|
76
|
+
|
77
|
+
# Define reader
|
78
|
+
define_method(name) do
|
79
|
+
return instance_variable_get(instance_var) if instance_variable_defined?(instance_var)
|
80
|
+
ref_value = self.send(ref)
|
81
|
+
ref_value = nil if ref_value.respond_to?(:empty?) && ref_value.empty?
|
82
|
+
|
83
|
+
val = if options[:polymorphic]
|
84
|
+
::CouchbaseOrm.try_load(ref_value) if ref_value
|
85
|
+
else
|
86
|
+
raise CouchbaseOrm::StrictLoadingViolationError, "#{self.class} is marked as strict_loading and #{assoc} cannot be lazily loaded." if strict_loading?
|
87
|
+
|
88
|
+
assoc.constantize.find(ref_value) if ref_value
|
89
|
+
end
|
90
|
+
val = Array.wrap(val || [])
|
91
|
+
instance_variable_set(instance_var, val)
|
92
|
+
val
|
93
|
+
end
|
94
|
+
|
95
|
+
# Define writer
|
96
|
+
attr_writer name
|
97
|
+
define_method(:"#{name}=") do |value|
|
98
|
+
if value
|
99
|
+
if !options[:polymorphic]
|
100
|
+
klass = assoc.constantize
|
101
|
+
value.each do |v|
|
102
|
+
raise ArgumentError, "type mismatch on association: #{klass.design_document} != #{v.class.design_document}" if klass.design_document != v.class.design_document
|
103
|
+
end
|
104
|
+
end
|
105
|
+
self.send(ref_ass, value.map(&:id))
|
106
|
+
else
|
107
|
+
self.send(ref_ass, nil)
|
108
|
+
end
|
109
|
+
|
110
|
+
instance_variable_set(instance_var, value)
|
111
|
+
end
|
112
|
+
|
113
|
+
return unless options[:autosave]
|
114
|
+
|
115
|
+
save_method = :"autosave_associated_records_for_#{name}"
|
116
|
+
|
117
|
+
define_non_cyclic_method(save_method) do
|
118
|
+
old, new = previous_changes[ref]
|
119
|
+
adds = (new || []) - (old || [])
|
120
|
+
subs = (old || []) - (new || [])
|
121
|
+
update_has_and_belongs_to_many_reverse_association(assoc, adds, true, **options) if adds.any?
|
122
|
+
update_has_and_belongs_to_many_reverse_association(assoc, subs, false, **options) if subs.any?
|
123
|
+
end
|
124
|
+
|
125
|
+
after_create save_method
|
126
|
+
after_update save_method
|
127
|
+
end
|
128
|
+
|
58
129
|
def associations
|
59
130
|
@associations || []
|
60
131
|
end
|
132
|
+
|
133
|
+
def define_non_cyclic_method(name, &block)
|
134
|
+
return if method_defined?(name)
|
135
|
+
|
136
|
+
define_method(name) do |*args|
|
137
|
+
result = true; @_already_called ||= {}
|
138
|
+
# Loop prevention for validation of associations
|
139
|
+
unless @_already_called[name]
|
140
|
+
begin
|
141
|
+
@_already_called[name] = true
|
142
|
+
result = instance_eval(&block)
|
143
|
+
ensure
|
144
|
+
@_already_called[name] = false
|
145
|
+
end
|
146
|
+
end
|
147
|
+
result
|
148
|
+
end
|
149
|
+
end
|
61
150
|
end
|
62
151
|
|
152
|
+
def update_has_and_belongs_to_many_reverse_association(assoc, keys, is_add, **options)
|
153
|
+
remote_method = options[:inverse_of] || self.class.to_s.pluralize.underscore.to_sym
|
154
|
+
return if keys.empty?
|
155
|
+
|
156
|
+
models = if options[:polymorphic]
|
157
|
+
::CouchbaseOrm.try_load(keys)
|
158
|
+
else
|
159
|
+
assoc.constantize.find(keys, quiet: true)
|
160
|
+
end
|
161
|
+
models = Array.wrap(models)
|
162
|
+
models.each do |v|
|
163
|
+
next unless v.respond_to?(remote_method)
|
164
|
+
|
165
|
+
tab = v.__send__(remote_method) || []
|
166
|
+
index = tab.find_index(self)
|
167
|
+
if is_add && !index
|
168
|
+
tab = tab.dup
|
169
|
+
tab.push(self)
|
170
|
+
elsif !is_add && index
|
171
|
+
tab = tab.dup
|
172
|
+
tab.delete_at(index)
|
173
|
+
else
|
174
|
+
next
|
175
|
+
end
|
176
|
+
v[remote_method] = tab
|
177
|
+
v.save!
|
178
|
+
end
|
179
|
+
end
|
63
180
|
|
64
181
|
def destroy_associations!
|
65
182
|
assoc = self.class.associations
|
@@ -72,6 +189,8 @@ module CouchbaseOrm
|
|
72
189
|
when :destroy, :delete
|
73
190
|
if model.respond_to?(:stream)
|
74
191
|
model.stream { |mod| mod.__send__(dependent) }
|
192
|
+
elsif model.is_a?(Array) || model.is_a?(CouchbaseOrm::ResultsProxy)
|
193
|
+
model.each { |m| m.__send__(dependent) }
|
75
194
|
else
|
76
195
|
model.__send__(dependent)
|
77
196
|
end
|
data/lib/couchbase-orm/base.rb
CHANGED
@@ -3,110 +3,198 @@
|
|
3
3
|
|
4
4
|
require 'active_model'
|
5
5
|
require 'active_support/hash_with_indifferent_access'
|
6
|
+
require 'couchbase'
|
7
|
+
require 'couchbase-orm/changeable'
|
8
|
+
require 'couchbase-orm/inspectable'
|
6
9
|
require 'couchbase-orm/error'
|
7
10
|
require 'couchbase-orm/views'
|
11
|
+
require 'couchbase-orm/n1ql'
|
8
12
|
require 'couchbase-orm/persistence'
|
9
13
|
require 'couchbase-orm/associations'
|
14
|
+
require 'couchbase-orm/types'
|
15
|
+
require 'couchbase-orm/relation'
|
16
|
+
require 'couchbase-orm/proxies/bucket_proxy'
|
17
|
+
require 'couchbase-orm/proxies/collection_proxy'
|
10
18
|
require 'couchbase-orm/utilities/join'
|
11
19
|
require 'couchbase-orm/utilities/enum'
|
12
20
|
require 'couchbase-orm/utilities/index'
|
13
21
|
require 'couchbase-orm/utilities/has_many'
|
14
22
|
require 'couchbase-orm/utilities/ensure_unique'
|
15
|
-
|
23
|
+
require 'couchbase-orm/utilities/query_helper'
|
24
|
+
require 'couchbase-orm/utilities/ignored_properties'
|
25
|
+
require 'couchbase-orm/json_transcoder'
|
26
|
+
require 'couchbase-orm/timestamps'
|
27
|
+
require 'couchbase-orm/active_record_compat'
|
28
|
+
require 'couchbase-orm/strict_loading'
|
29
|
+
require 'couchbase-orm/json_schema/validation'
|
30
|
+
require 'couchbase-orm/utilities/properties_always_exists_in_document'
|
16
31
|
|
17
32
|
module CouchbaseOrm
|
18
|
-
class
|
33
|
+
class Document
|
34
|
+
include Inspectable
|
19
35
|
include ::ActiveModel::Model
|
20
36
|
include ::ActiveModel::Dirty
|
37
|
+
include Changeable # override some methods from ActiveModel::Dirty (keep it included after)
|
38
|
+
include ::ActiveModel::Attributes
|
21
39
|
include ::ActiveModel::Serializers::JSON
|
22
40
|
|
23
41
|
include ::ActiveModel::Validations
|
24
42
|
include ::ActiveModel::Validations::Callbacks
|
43
|
+
|
44
|
+
include ActiveRecordCompat
|
45
|
+
include StrictLoading
|
46
|
+
include Encrypt
|
47
|
+
|
48
|
+
extend Enum
|
49
|
+
|
25
50
|
define_model_callbacks :initialize, :only => :after
|
26
|
-
define_model_callbacks :create, :destroy, :save, :update
|
27
51
|
|
52
|
+
Metadata = Struct.new(:cas)
|
53
|
+
|
54
|
+
class MismatchTypeError < RuntimeError; end
|
55
|
+
|
56
|
+
def initialize(model = nil, ignore_doc_type: false, **attributes)
|
57
|
+
CouchbaseOrm.logger.debug { "Initialize model #{model} with #{attributes.to_s.truncate(200)}" }
|
58
|
+
@__metadata__ = Metadata.new
|
59
|
+
|
60
|
+
super()
|
61
|
+
|
62
|
+
if model
|
63
|
+
case model
|
64
|
+
when Couchbase::Collection::GetResult
|
65
|
+
doc = HashWithIndifferentAccess.new(model.content) || raise('empty response provided')
|
66
|
+
type = doc.delete(:type)
|
67
|
+
doc.delete(:id)
|
68
|
+
|
69
|
+
if type && !ignore_doc_type && type.to_s != self.class.design_document
|
70
|
+
raise CouchbaseOrm::Error::TypeMismatchError.new("document type mismatch, #{type} != #{self.class.design_document}", self)
|
71
|
+
end
|
72
|
+
|
73
|
+
self.id = attributes[:id] if attributes[:id].present?
|
74
|
+
@__metadata__.cas = model.cas
|
75
|
+
|
76
|
+
assign_attributes(decode_encrypted_attributes(doc))
|
77
|
+
when CouchbaseOrm::Base
|
78
|
+
clear_changes_information
|
79
|
+
super(model.attributes.except(:id, 'type'))
|
80
|
+
else
|
81
|
+
clear_changes_information
|
82
|
+
assign_attributes(decode_encrypted_attributes(**attributes.merge(Hash(model)).symbolize_keys))
|
83
|
+
end
|
84
|
+
else
|
85
|
+
clear_changes_information
|
86
|
+
super(attributes)
|
87
|
+
end
|
88
|
+
|
89
|
+
yield self if block_given?
|
90
|
+
|
91
|
+
init_strict_loading
|
92
|
+
run_callbacks :initialize
|
93
|
+
end
|
94
|
+
|
95
|
+
def [](key)
|
96
|
+
send(key)
|
97
|
+
end
|
98
|
+
|
99
|
+
def []=(key, value)
|
100
|
+
send(:"#{key}=", value)
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
|
105
|
+
def serialized_attributes
|
106
|
+
encode_encrypted_attributes.map { |k, v|
|
107
|
+
[k, self.class.attribute_types[k].serialize(v)]
|
108
|
+
}.to_h
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
class NestedDocument < Document
|
113
|
+
def initialize(*args, **kwargs)
|
114
|
+
super
|
115
|
+
if respond_to?(:id) && id.nil?
|
116
|
+
assign_attributes(id: SecureRandom.hex)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Base < Document
|
122
|
+
define_model_callbacks :create, :destroy, :save, :update
|
28
123
|
include Persistence
|
124
|
+
|
29
125
|
include Associations
|
30
126
|
include Views
|
127
|
+
include QueryHelper
|
128
|
+
include N1ql
|
129
|
+
include Relation
|
130
|
+
include Timestamps
|
31
131
|
|
32
132
|
extend Join
|
33
133
|
extend Enum
|
34
134
|
extend EnsureUnique
|
35
135
|
extend HasMany
|
36
136
|
extend Index
|
137
|
+
extend IgnoredProperties
|
138
|
+
extend JsonSchema::Validation
|
139
|
+
extend PropertiesAlwaysExistsInDocument
|
37
140
|
|
38
141
|
|
39
|
-
|
142
|
+
class << self
|
40
143
|
|
144
|
+
def attribute(name, ...)
|
145
|
+
super
|
146
|
+
create_dirty_methods(name, name)
|
147
|
+
create_setters(name)
|
148
|
+
end
|
41
149
|
|
42
|
-
class << self
|
43
150
|
def connect(**options)
|
44
|
-
@bucket = ::
|
151
|
+
@bucket = BucketProxy.new(::MTLibcouchbase::Bucket.new(**options))
|
45
152
|
end
|
46
153
|
|
47
154
|
def bucket=(bucket)
|
48
|
-
@bucket = bucket
|
155
|
+
@bucket = bucket.is_a?(BucketProxy) ? bucket : BucketProxy.new(bucket)
|
49
156
|
end
|
50
157
|
|
51
158
|
def bucket
|
52
|
-
@bucket ||= Connection.bucket
|
159
|
+
@bucket ||= BucketProxy.new(Connection.bucket)
|
53
160
|
end
|
54
161
|
|
55
|
-
def
|
56
|
-
|
162
|
+
def cluster
|
163
|
+
Connection.cluster
|
57
164
|
end
|
58
165
|
|
59
|
-
def
|
60
|
-
|
166
|
+
def collection
|
167
|
+
CollectionProxy.new(bucket.default_collection)
|
61
168
|
end
|
62
169
|
|
63
|
-
def
|
64
|
-
@
|
65
|
-
names.each do |name|
|
66
|
-
name = name.to_sym
|
67
|
-
|
68
|
-
@attributes[name] = options
|
69
|
-
|
70
|
-
unless self.instance_methods.include?(name)
|
71
|
-
define_method(name) do
|
72
|
-
read_attribute(name)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
eq_meth = :"#{name}="
|
77
|
-
unless self.instance_methods.include?(eq_meth)
|
78
|
-
define_method(eq_meth) do |value|
|
79
|
-
value = yield(value) if block_given?
|
80
|
-
write_attribute(name, value)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
170
|
+
def uuid_generator
|
171
|
+
@uuid_generator ||= IdGenerator
|
84
172
|
end
|
85
173
|
|
86
|
-
def
|
87
|
-
@
|
174
|
+
def uuid_generator=(generator)
|
175
|
+
@uuid_generator = generator
|
88
176
|
end
|
89
177
|
|
90
|
-
def find(*ids,
|
91
|
-
|
92
|
-
options[:quiet] ||= false
|
178
|
+
def find(*ids, quiet: false, with_strict_loading: false)
|
179
|
+
CouchbaseOrm.logger.debug { "Base.find(l##{ids.length}) #{ids}" }
|
93
180
|
|
94
181
|
ids = ids.flatten.select { |id| id.present? }
|
95
182
|
if ids.empty?
|
96
|
-
|
97
|
-
raise Libcouchbase::Error::EmptyKey, 'no id(s) provided'
|
183
|
+
raise CouchbaseOrm::Error::EmptyNotAllowed, 'no id(s) provided'
|
98
184
|
end
|
99
185
|
|
100
|
-
|
101
|
-
records =
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
186
|
+
transcoder = CouchbaseOrm::JsonTranscoder.new(ignored_properties: ignored_properties)
|
187
|
+
records = quiet ? collection.get_multi(ids, transcoder: transcoder) : collection.get_multi!(ids, transcoder: transcoder)
|
188
|
+
CouchbaseOrm.logger.debug { "Base.find found(#{records})" }
|
189
|
+
records = records.zip(ids).map { |record, id|
|
190
|
+
next unless record
|
191
|
+
next if record.error
|
192
|
+
new(record, id: id).tap do |instance|
|
193
|
+
if with_strict_loading
|
194
|
+
instance.strict_loading!
|
195
|
+
end
|
196
|
+
end.tap(&:reset_object!)
|
197
|
+
}.compact
|
110
198
|
ids.length > 1 ? records : records[0]
|
111
199
|
end
|
112
200
|
|
@@ -117,124 +205,18 @@ module CouchbaseOrm
|
|
117
205
|
alias_method :[], :find_by_id
|
118
206
|
|
119
207
|
def exists?(id)
|
120
|
-
|
208
|
+
CouchbaseOrm.logger.debug { "Data - Exists? #{id}" }
|
209
|
+
collection.exists(id).exists
|
121
210
|
end
|
122
211
|
alias_method :has_key?, :exists?
|
123
212
|
end
|
124
213
|
|
125
|
-
|
126
|
-
# Add support for libcouchbase response objects
|
127
|
-
def initialize(model = nil, ignore_doc_type: false, **attributes)
|
128
|
-
@__metadata__ = Metadata.new
|
129
|
-
|
130
|
-
# Assign default values
|
131
|
-
@__attributes__ = ::ActiveSupport::HashWithIndifferentAccess.new({type: self.class.design_document})
|
132
|
-
self.class.attributes.each do |key, options|
|
133
|
-
default = options[:default]
|
134
|
-
if default.respond_to?(:call)
|
135
|
-
write_attribute key, default.call
|
136
|
-
else
|
137
|
-
write_attribute key, default
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
if model
|
142
|
-
case model
|
143
|
-
when ::Libcouchbase::Response
|
144
|
-
doc = model.value || raise('empty response provided')
|
145
|
-
type = doc.delete(:type)
|
146
|
-
doc.delete(:id)
|
147
|
-
|
148
|
-
if type && !ignore_doc_type && type.to_s != self.class.design_document
|
149
|
-
raise "document type mismatch, #{type} != #{self.class.design_document}"
|
150
|
-
end
|
151
|
-
|
152
|
-
@__metadata__.key = model.key
|
153
|
-
@__metadata__.cas = model.cas
|
154
|
-
|
155
|
-
# This ensures that defaults are applied
|
156
|
-
@__attributes__.merge! doc
|
157
|
-
clear_changes_information
|
158
|
-
when CouchbaseOrm::Base
|
159
|
-
clear_changes_information
|
160
|
-
attributes = model.attributes
|
161
|
-
attributes.delete(:id)
|
162
|
-
super(attributes)
|
163
|
-
else
|
164
|
-
clear_changes_information
|
165
|
-
super(attributes.merge(Hash(model)))
|
166
|
-
end
|
167
|
-
else
|
168
|
-
clear_changes_information
|
169
|
-
super(attributes)
|
170
|
-
end
|
171
|
-
|
172
|
-
yield self if block_given?
|
173
|
-
|
174
|
-
run_callbacks :initialize
|
175
|
-
end
|
176
|
-
|
177
|
-
|
178
|
-
# Document ID is a special case as it is not stored in the document
|
179
|
-
def id
|
180
|
-
@__metadata__.key || @id
|
181
|
-
end
|
182
|
-
|
183
214
|
def id=(value)
|
184
|
-
raise 'ID cannot be changed' if @__metadata__.cas
|
215
|
+
raise RuntimeError, 'ID cannot be changed' if @__metadata__.cas && value
|
185
216
|
attribute_will_change!(:id)
|
186
|
-
|
187
|
-
end
|
188
|
-
|
189
|
-
def read_attribute(attr_name)
|
190
|
-
@__attributes__[attr_name]
|
191
|
-
end
|
192
|
-
alias_method :[], :read_attribute
|
193
|
-
|
194
|
-
def write_attribute(attr_name, value)
|
195
|
-
unless value.nil?
|
196
|
-
coerce = self.class.attributes[attr_name][:type]
|
197
|
-
value = Kernel.send(coerce.to_s, value) if coerce
|
198
|
-
end
|
199
|
-
attribute_will_change!(attr_name) unless @__attributes__[attr_name] == value
|
200
|
-
@__attributes__[attr_name] = value
|
201
|
-
end
|
202
|
-
alias_method :[]=, :write_attribute
|
203
|
-
|
204
|
-
#
|
205
|
-
# Add support for Serialization:
|
206
|
-
# http://guides.rubyonrails.org/active_model_basics.html#serialization
|
207
|
-
#
|
208
|
-
|
209
|
-
def attributes
|
210
|
-
copy = @__attributes__.merge({id: id})
|
211
|
-
copy.delete(:type)
|
212
|
-
copy
|
213
|
-
end
|
214
|
-
|
215
|
-
def attributes=(attributes)
|
216
|
-
attributes.each do |key, value|
|
217
|
-
setter = :"#{key}="
|
218
|
-
send(setter, value) if respond_to?(setter)
|
219
|
-
end
|
220
|
-
end
|
221
|
-
|
222
|
-
ID_LOOKUP = ['id', :id].freeze
|
223
|
-
def attribute(name)
|
224
|
-
return self.id if ID_LOOKUP.include?(name)
|
225
|
-
@__attributes__[name]
|
226
|
-
end
|
227
|
-
alias_method :read_attribute_for_serialization, :attribute
|
228
|
-
|
229
|
-
def attribute=(name, value)
|
230
|
-
__send__(:"#{name}=", value)
|
217
|
+
_write_attribute("id", value)
|
231
218
|
end
|
232
219
|
|
233
|
-
|
234
|
-
#
|
235
|
-
# Add support for comparisons
|
236
|
-
#
|
237
|
-
|
238
220
|
# Public: Allows for access to ActiveModel functionality.
|
239
221
|
#
|
240
222
|
# Returns self.
|
@@ -267,12 +249,7 @@ module CouchbaseOrm
|
|
267
249
|
#
|
268
250
|
# Returns a boolean.
|
269
251
|
def ==(other)
|
270
|
-
|
271
|
-
when self.class
|
272
|
-
hash == other.hash
|
273
|
-
else
|
274
|
-
false
|
275
|
-
end
|
252
|
+
super || other.instance_of?(self.class) && !id.nil? && other.id == id
|
276
253
|
end
|
277
254
|
end
|
278
255
|
end
|