couchbase-orm 0.0.1
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 +7 -0
- data/.gitignore +18 -0
- data/.rspec +1 -0
- data/.travis.yml +34 -0
- data/Gemfile +4 -0
- data/LICENSE +24 -0
- data/README.md +131 -0
- data/Rakefile +75 -0
- data/couchbase-orm.gemspec +27 -0
- data/lib/couchbase-orm.rb +43 -0
- data/lib/couchbase-orm/associations.rb +87 -0
- data/lib/couchbase-orm/base.rb +254 -0
- data/lib/couchbase-orm/connection.rb +22 -0
- data/lib/couchbase-orm/error.rb +15 -0
- data/lib/couchbase-orm/id_generator.rb +30 -0
- data/lib/couchbase-orm/persistence.rb +240 -0
- data/lib/couchbase-orm/railtie.rb +96 -0
- data/lib/couchbase-orm/utilities/ensure_unique.rb +18 -0
- data/lib/couchbase-orm/utilities/enum.rb +49 -0
- data/lib/couchbase-orm/utilities/has_many.rb +73 -0
- data/lib/couchbase-orm/utilities/index.rb +117 -0
- data/lib/couchbase-orm/utilities/join.rb +68 -0
- data/lib/couchbase-orm/version.rb +5 -0
- data/lib/couchbase-orm/views.rb +148 -0
- data/lib/rails/generators/couchbase_orm/config/config_generator.rb +42 -0
- data/lib/rails/generators/couchbase_orm/config/templates/couchbase.yml +18 -0
- data/lib/rails/generators/couchbase_orm_generator.rb +42 -0
- data/spec/associations_spec.rb +80 -0
- data/spec/base_spec.rb +72 -0
- data/spec/has_many_spec.rb +82 -0
- data/spec/id_generator_spec.rb +48 -0
- data/spec/index_spec.rb +73 -0
- data/spec/persistence_spec.rb +237 -0
- data/spec/support.rb +28 -0
- data/spec/views_spec.rb +73 -0
- metadata +184 -0
@@ -0,0 +1,254 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
|
4
|
+
require 'active_model'
|
5
|
+
require 'active_support/hash_with_indifferent_access'
|
6
|
+
require 'couchbase-orm/error'
|
7
|
+
require 'couchbase-orm/views'
|
8
|
+
require 'couchbase-orm/persistence'
|
9
|
+
require 'couchbase-orm/associations'
|
10
|
+
require 'couchbase-orm/utilities/join'
|
11
|
+
require 'couchbase-orm/utilities/enum'
|
12
|
+
require 'couchbase-orm/utilities/index'
|
13
|
+
require 'couchbase-orm/utilities/has_many'
|
14
|
+
require 'couchbase-orm/utilities/ensure_unique'
|
15
|
+
|
16
|
+
|
17
|
+
module CouchbaseOrm
|
18
|
+
class Base
|
19
|
+
include ::ActiveModel::Model
|
20
|
+
include ::ActiveModel::Dirty
|
21
|
+
include ::ActiveModel::Serializers::JSON
|
22
|
+
|
23
|
+
extend ::ActiveModel::Callbacks
|
24
|
+
define_model_callbacks :initialize, :only => :after
|
25
|
+
define_model_callbacks :create, :destroy, :save, :update
|
26
|
+
|
27
|
+
include Persistence
|
28
|
+
include Associations
|
29
|
+
include Views
|
30
|
+
|
31
|
+
extend Join
|
32
|
+
extend Enum
|
33
|
+
extend EnsureUnique
|
34
|
+
extend HasMany
|
35
|
+
extend Index
|
36
|
+
|
37
|
+
|
38
|
+
Metadata = Struct.new(:key, :cas)
|
39
|
+
|
40
|
+
|
41
|
+
class << self
|
42
|
+
def connect(**options)
|
43
|
+
@bucket = ::Libcouchbase::Bucket.new(**options)
|
44
|
+
end
|
45
|
+
|
46
|
+
def bucket=(bucket)
|
47
|
+
@bucket = bucket
|
48
|
+
end
|
49
|
+
|
50
|
+
def bucket
|
51
|
+
@bucket ||= Connection.bucket
|
52
|
+
end
|
53
|
+
|
54
|
+
at_exit do
|
55
|
+
# This will disconnect the database connection
|
56
|
+
@bucket = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def uuid_generator
|
60
|
+
@uuid_generator ||= IdGenerator
|
61
|
+
end
|
62
|
+
|
63
|
+
def uuid_generator=(generator)
|
64
|
+
@uuid_generator = generator
|
65
|
+
end
|
66
|
+
|
67
|
+
def attribute(*names, **options)
|
68
|
+
@attributes ||= {}
|
69
|
+
names.each do |name|
|
70
|
+
name = name.to_sym
|
71
|
+
|
72
|
+
@attributes[name] = options
|
73
|
+
next if self.instance_methods.include?(name)
|
74
|
+
|
75
|
+
define_method(name) do
|
76
|
+
read_attribute(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
define_method(:"#{name}=") do |value|
|
80
|
+
value = yield(value) if block_given?
|
81
|
+
write_attribute(name, value)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def attributes
|
87
|
+
@attributes ||= {}
|
88
|
+
end
|
89
|
+
|
90
|
+
def find(*ids, **options)
|
91
|
+
options[:extended] = true
|
92
|
+
options[:quiet] ||= false
|
93
|
+
|
94
|
+
ids = ids.flatten
|
95
|
+
records = bucket.get(*ids, **options)
|
96
|
+
|
97
|
+
records = records.is_a?(Array) ? records : [records]
|
98
|
+
records.map! { |record|
|
99
|
+
if record
|
100
|
+
self.new(record)
|
101
|
+
else
|
102
|
+
false
|
103
|
+
end
|
104
|
+
}
|
105
|
+
records.select! { |rec| rec }
|
106
|
+
ids.length > 1 ? records : records[0]
|
107
|
+
end
|
108
|
+
|
109
|
+
def find_by_id(*ids, **options)
|
110
|
+
options[:quiet] = true
|
111
|
+
find *ids, **options
|
112
|
+
end
|
113
|
+
alias_method :[], :find_by_id
|
114
|
+
|
115
|
+
def exists?(id)
|
116
|
+
!bucket.get(id, quiet: true).nil?
|
117
|
+
end
|
118
|
+
alias_method :has_key?, :exists?
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
# Add support for libcouchbase response objects
|
123
|
+
def initialize(model = nil, ignore_doc_type: false, **attributes)
|
124
|
+
@__metadata__ = Metadata.new
|
125
|
+
|
126
|
+
# Assign default values
|
127
|
+
@__attributes__ = ::ActiveSupport::HashWithIndifferentAccess.new({type: self.class.design_document})
|
128
|
+
self.class.attributes.each do |key, options|
|
129
|
+
default = options[:default]
|
130
|
+
if default.respond_to?(:call)
|
131
|
+
@__attributes__[key] = default.call
|
132
|
+
else
|
133
|
+
@__attributes__[key] = default
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
if model
|
138
|
+
case model
|
139
|
+
when ::Libcouchbase::Response
|
140
|
+
doc = model.value || raise('empty response provided')
|
141
|
+
type = doc.delete(:type)
|
142
|
+
doc.delete(:id)
|
143
|
+
|
144
|
+
if type && !ignore_doc_type && type.to_s != self.class.design_document
|
145
|
+
raise "document type mismatch, #{type} != #{self.class.design_document}"
|
146
|
+
end
|
147
|
+
|
148
|
+
@__metadata__.key = model.key
|
149
|
+
@__metadata__.cas = model.cas
|
150
|
+
|
151
|
+
# This ensures that defaults are applied
|
152
|
+
super(**doc)
|
153
|
+
when CouchbaseOrm::Base
|
154
|
+
attributes = model.attributes
|
155
|
+
attributes.delete(:id)
|
156
|
+
super(**attributes)
|
157
|
+
else
|
158
|
+
super(**attributes.merge(Hash(model)))
|
159
|
+
end
|
160
|
+
else
|
161
|
+
super(**attributes)
|
162
|
+
end
|
163
|
+
|
164
|
+
yield self if block_given?
|
165
|
+
|
166
|
+
run_callbacks :initialize
|
167
|
+
end
|
168
|
+
|
169
|
+
|
170
|
+
# Document ID is a special case as it is not stored in the document
|
171
|
+
def id
|
172
|
+
@__metadata__.key || @id
|
173
|
+
end
|
174
|
+
|
175
|
+
def id=(value)
|
176
|
+
raise 'ID cannot be changed' if @__metadata__.cas
|
177
|
+
attribute_will_change!(:id)
|
178
|
+
@id = value.to_s
|
179
|
+
end
|
180
|
+
|
181
|
+
def read_attribute(attr_name)
|
182
|
+
@__attributes__[attr_name]
|
183
|
+
end
|
184
|
+
alias_method :[], :read_attribute
|
185
|
+
|
186
|
+
def write_attribute(attr_name, value)
|
187
|
+
unless value.nil?
|
188
|
+
coerce = self.class.attributes[attr_name][:type]
|
189
|
+
value = Kernel.send(coerce.to_s, value) if coerce
|
190
|
+
end
|
191
|
+
attribute_will_change!(attr_name) unless @__attributes__[attr_name] == value
|
192
|
+
@__attributes__[attr_name] = value
|
193
|
+
end
|
194
|
+
alias_method :[]=, :write_attribute
|
195
|
+
|
196
|
+
#
|
197
|
+
# Add support for Serialization:
|
198
|
+
# http://guides.rubyonrails.org/active_model_basics.html#serialization
|
199
|
+
#
|
200
|
+
|
201
|
+
def attributes
|
202
|
+
copy = @__attributes__.merge({id: id})
|
203
|
+
copy.delete(:type)
|
204
|
+
copy
|
205
|
+
end
|
206
|
+
|
207
|
+
def attributes=(attributes)
|
208
|
+
attributes.each do |key, value|
|
209
|
+
setter = :"#{key}="
|
210
|
+
send(setter, value) if respond_to?(setter)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
|
215
|
+
#
|
216
|
+
# Add support for comparisons
|
217
|
+
#
|
218
|
+
|
219
|
+
# Public: Allows for access to ActiveModel functionality.
|
220
|
+
#
|
221
|
+
# Returns self.
|
222
|
+
def to_model
|
223
|
+
self
|
224
|
+
end
|
225
|
+
|
226
|
+
# Public: Hashes identifying properties of the instance
|
227
|
+
#
|
228
|
+
# Ruby normally hashes an object to be used in comparisons. In our case
|
229
|
+
# we may have two techincally different objects referencing the same entity id.
|
230
|
+
#
|
231
|
+
# Returns a string representing the unique key.
|
232
|
+
def hash
|
233
|
+
"#{self.class.name}-#{self.id}-#{@__metadata__.cas}-#{@__attributes__.hash}".hash
|
234
|
+
end
|
235
|
+
|
236
|
+
# Public: Overrides eql? to use == in the comparison.
|
237
|
+
#
|
238
|
+
# other - Another object to compare to
|
239
|
+
#
|
240
|
+
# Returns a boolean.
|
241
|
+
def eql?(other)
|
242
|
+
self == other
|
243
|
+
end
|
244
|
+
|
245
|
+
# Public: Overrides == to compare via class and entity id.
|
246
|
+
#
|
247
|
+
# other - Another object to compare to
|
248
|
+
#
|
249
|
+
# Returns a boolean.
|
250
|
+
def ==(other)
|
251
|
+
hash == other.hash
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'libcouchbase'
|
4
|
+
|
5
|
+
module CouchbaseOrm
|
6
|
+
class Connection
|
7
|
+
@options = {}
|
8
|
+
class << self
|
9
|
+
attr_accessor :options
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.bucket
|
13
|
+
@bucket ||= ::Libcouchbase::Bucket.new(**@options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# This will disconnect the database connection,
|
17
|
+
# allowing the application to exit
|
18
|
+
at_exit do
|
19
|
+
@bucket = nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
module CouchbaseOrm
|
4
|
+
class Error < ::StandardError
|
5
|
+
attr_reader :record
|
6
|
+
|
7
|
+
def initialize(message = nil, record = nil)
|
8
|
+
@record = record
|
9
|
+
super(message)
|
10
|
+
end
|
11
|
+
|
12
|
+
class RecordInvalid < Error; end
|
13
|
+
class RecordExists < Error; end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'radix/base'
|
4
|
+
|
5
|
+
module CouchbaseOrm
|
6
|
+
class IdGenerator
|
7
|
+
# Using base 65 as a form of compression (reduced length of ID string)
|
8
|
+
# No escape characters are required to display these in a URL
|
9
|
+
B65 = ::Radix::Base.new(::Radix::BASE::B62 + ['-', '_', '~'])
|
10
|
+
B10 = ::Radix::Base.new(10)
|
11
|
+
|
12
|
+
# We don't really care about dates before this library was created
|
13
|
+
# This reduces the length of the ID significantly
|
14
|
+
Skip46Years = 1451649600 # 46.years.to_i
|
15
|
+
|
16
|
+
# Generate a unique, orderable, ID using minimal bytes
|
17
|
+
def self.next(model)
|
18
|
+
# We are unlikely to see a clash here
|
19
|
+
now = Time.now
|
20
|
+
time = (now.to_i - Skip46Years) * 1_000_000 + now.usec
|
21
|
+
|
22
|
+
# This makes it very very improbable that there will ever be an ID clash
|
23
|
+
# Distributed system safe!
|
24
|
+
prefix = time.to_s
|
25
|
+
tail = (rand(9999) + 1).to_s.rjust(4, '0')
|
26
|
+
|
27
|
+
"#{model.class.design_document}-#{Radix.convert("#{prefix}#{tail}", B10, B65)}"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true, encoding: ASCII-8BIT
|
2
|
+
|
3
|
+
require 'active_model'
|
4
|
+
require 'active_support/hash_with_indifferent_access'
|
5
|
+
|
6
|
+
module CouchbaseOrm
|
7
|
+
module Persistence
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def create(attributes = nil, &block)
|
13
|
+
if attributes.is_a?(Array)
|
14
|
+
attributes.collect { |attr| create(attr, &block) }
|
15
|
+
else
|
16
|
+
instance = new(attributes, &block)
|
17
|
+
instance.save
|
18
|
+
instance
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def create!(attributes = nil, &block)
|
23
|
+
if attributes.is_a?(Array)
|
24
|
+
attributes.collect { |attr| create!(attr, &block) }
|
25
|
+
else
|
26
|
+
instance = new(attributes, &block)
|
27
|
+
instance.save!
|
28
|
+
instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Raise an error if validation failed.
|
33
|
+
def fail_validate!(document)
|
34
|
+
raise Error::RecordInvalid.new("Failed to save the record", document)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Allow classes to overwrite the default document name
|
38
|
+
# extend ActiveModel::Naming (included by ActiveModel::Model)
|
39
|
+
def design_document(name = nil)
|
40
|
+
return @design_document unless name
|
41
|
+
@design_document = name.to_s
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set a default design document
|
45
|
+
def inherited(child)
|
46
|
+
super
|
47
|
+
child.instance_eval do
|
48
|
+
@design_document = child.name.underscore
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
# Returns true if this object hasn't been saved yet -- that is, a record
|
55
|
+
# for the object doesn't exist in the database yet; otherwise, returns false.
|
56
|
+
def new_record?
|
57
|
+
@__metadata__.cas.nil? && @__metadata__.key.nil?
|
58
|
+
end
|
59
|
+
alias_method :new?, :new_record?
|
60
|
+
|
61
|
+
# Returns true if this object has been destroyed, otherwise returns false.
|
62
|
+
def destroyed?
|
63
|
+
!!(@__metadata__.cas && @__metadata__.key.nil?)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns true if the record is persisted, i.e. it's not a new record and it was
|
67
|
+
# not destroyed, otherwise returns false.
|
68
|
+
def persisted?
|
69
|
+
# Changed? is provided by ActiveModel::Dirty
|
70
|
+
!!@__metadata__.key
|
71
|
+
end
|
72
|
+
alias_method :exists?, :persisted?
|
73
|
+
|
74
|
+
# Saves the model.
|
75
|
+
#
|
76
|
+
# If the model is new, a record gets created in the database, otherwise
|
77
|
+
# the existing record gets updated.
|
78
|
+
def save(**options)
|
79
|
+
raise "Cannot save a destroyed document!" if destroyed?
|
80
|
+
self.new_record? ? _create_record(**options) : _update_record(**options)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Saves the model.
|
84
|
+
#
|
85
|
+
# If the model is new, a record gets created in the database, otherwise
|
86
|
+
# the existing record gets updated.
|
87
|
+
#
|
88
|
+
# By default, #save! always runs validations. If any of them fail
|
89
|
+
# CouchbaseOrm::Error::RecordInvalid gets raised, and the record won't be saved.
|
90
|
+
def save!
|
91
|
+
self.class.fail_validate!(self) unless self.save
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
# Deletes the record in the database and freezes this instance to
|
96
|
+
# reflect that no changes should be made (since they can't be
|
97
|
+
# persisted). Returns the frozen instance.
|
98
|
+
#
|
99
|
+
# The record is simply removed, no callbacks are executed.
|
100
|
+
def delete(with_cas: false, **options)
|
101
|
+
options[:cas] = @__metadata__.cas if with_cas
|
102
|
+
self.class.bucket.delete(@__metadata__.key, options)
|
103
|
+
|
104
|
+
@__metadata__.key = nil
|
105
|
+
@id = nil
|
106
|
+
|
107
|
+
clear_changes_information
|
108
|
+
self.freeze
|
109
|
+
self
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deletes the record in the database and freezes this instance to reflect
|
113
|
+
# that no changes should be made (since they can't be persisted).
|
114
|
+
#
|
115
|
+
# There's a series of callbacks associated with #destroy.
|
116
|
+
def destroy(with_cas: false, **options)
|
117
|
+
return self if destroyed?
|
118
|
+
raise 'model not persisted' unless persisted?
|
119
|
+
|
120
|
+
run_callbacks :destroy do
|
121
|
+
destroy_associations!
|
122
|
+
|
123
|
+
options[:cas] = @__metadata__.cas if with_cas
|
124
|
+
self.class.bucket.delete(@__metadata__.key, options)
|
125
|
+
|
126
|
+
@__metadata__.key = nil
|
127
|
+
@id = nil
|
128
|
+
|
129
|
+
clear_changes_information
|
130
|
+
freeze
|
131
|
+
end
|
132
|
+
end
|
133
|
+
alias_method :destroy!, :destroy
|
134
|
+
|
135
|
+
# Updates a single attribute and saves the record.
|
136
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
137
|
+
#
|
138
|
+
# * Validation is skipped.
|
139
|
+
# * \Callbacks are invoked.
|
140
|
+
def update_attribute(name, value)
|
141
|
+
public_send(:"#{name}=", value)
|
142
|
+
changed? ? save(validate: false) : true
|
143
|
+
end
|
144
|
+
|
145
|
+
# Updates the attributes of the model from the passed-in hash and saves the
|
146
|
+
# record. If the object is invalid, the saving will fail and false will be returned.
|
147
|
+
def update(hash)
|
148
|
+
assign_attributes(hash)
|
149
|
+
save
|
150
|
+
end
|
151
|
+
alias_method :update_attributes, :update
|
152
|
+
|
153
|
+
# Updates its receiver just like #update but calls #save! instead
|
154
|
+
# of +save+, so an exception is raised if the record is invalid and saving will fail.
|
155
|
+
def update!(hash)
|
156
|
+
assign_attributes(hash) # Assign attributes is provided by ActiveModel::AttributeAssignment
|
157
|
+
save!
|
158
|
+
end
|
159
|
+
alias_method :update_attributes!, :update!
|
160
|
+
|
161
|
+
# Reloads the record from the database.
|
162
|
+
#
|
163
|
+
# This method finds record by its key and modifies the receiver in-place:
|
164
|
+
def reload
|
165
|
+
key = @__metadata__.key
|
166
|
+
raise "unable to reload, model not persisted" unless key
|
167
|
+
|
168
|
+
resp = self.class.bucket.get(key, quiet: false, extended: true)
|
169
|
+
@__attributes__ = ::ActiveSupport::HashWithIndifferentAccess.new(resp.value)
|
170
|
+
@__metadata__.key = resp.key
|
171
|
+
@__metadata__.cas = resp.cas
|
172
|
+
|
173
|
+
reset_associations
|
174
|
+
clear_changes_information
|
175
|
+
self
|
176
|
+
end
|
177
|
+
|
178
|
+
# Updates the TTL of the document
|
179
|
+
def touch(**options)
|
180
|
+
res = self.class.bucket.touch(@__metadata__.key, async: false, **options)
|
181
|
+
@__metadata__.cas = resp.cas
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
protected
|
187
|
+
|
188
|
+
|
189
|
+
def _update_record(with_cas: false, **options)
|
190
|
+
return false unless perform_validations(options)
|
191
|
+
return true unless changed?
|
192
|
+
|
193
|
+
run_callbacks :update do
|
194
|
+
run_callbacks :save do
|
195
|
+
# Ensure the type is set
|
196
|
+
@__attributes__[:type] = self.class.design_document
|
197
|
+
@__attributes__.delete(:id)
|
198
|
+
|
199
|
+
_id = @__metadata__.key
|
200
|
+
options[:cas] = @__metadata__.cas if with_cas
|
201
|
+
resp = self.class.bucket.replace(_id, @__attributes__, **options)
|
202
|
+
|
203
|
+
# Ensure the model is up to date
|
204
|
+
@__metadata__.key = resp.key
|
205
|
+
@__metadata__.cas = resp.cas
|
206
|
+
|
207
|
+
clear_changes_information
|
208
|
+
true
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def _create_record(**options)
|
214
|
+
return false unless perform_validations(options)
|
215
|
+
|
216
|
+
run_callbacks :create do
|
217
|
+
run_callbacks :save do
|
218
|
+
# Ensure the type is set
|
219
|
+
@__attributes__[:type] = self.class.design_document
|
220
|
+
@__attributes__.delete(:id)
|
221
|
+
|
222
|
+
_id = @id || self.class.uuid_generator.next(self)
|
223
|
+
resp = self.class.bucket.add(_id, @__attributes__, **options)
|
224
|
+
|
225
|
+
# Ensure the model is up to date
|
226
|
+
@__metadata__.key = resp.key
|
227
|
+
@__metadata__.cas = resp.cas
|
228
|
+
|
229
|
+
clear_changes_information
|
230
|
+
true
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def perform_validations(options = {})
|
236
|
+
return valid? if options[:validate] != false
|
237
|
+
true
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|