Class: Couchbase::Model
- Extended by:
- ActiveModel::Callbacks, ActiveModel::Naming
- Includes:
- ActiveModel::Conversion, ActiveModel::Validations
- Defined in:
- lib/couchbase/model.rb,
lib/couchbase/model/uuid.rb,
lib/couchbase/model/version.rb,
lib/couchbase/model/configuration.rb
Overview
Declarative layer for Couchbase gem
You can also let the library generate the unique identifier for you:
p = Post.create(:title => 'How to generate ID',
:body => 'Open up the editor...')
p.id #=> "74f43c3116e788d09853226603000809"
There are several algorithms available. By default it use ‘:sequential` algorithm, but you can change it to more suitable one for you:
class Post < Couchbase::Model
attribute :title
attribute :body
attribute :draft
uuid_algorithm :random
end
You can define connection options on per model basis:
class Post < Couchbase::Model
attribute :title
attribute :body
attribute :draft
connect :port => 80, :bucket => 'blog'
end
Defined Under Namespace
Modules: Configuration Classes: UUID
Constant Summary collapse
- VERSION =
"0.5.1"
Instance Attribute Summary collapse
- #doc ⇒ Object readonly
- #errors ⇒ Object readonly
-
#id ⇒ Object
Each model must have identifier.
- #key ⇒ Object readonly
- #meta ⇒ Object readonly
- #raw ⇒ Object readonly
- #value ⇒ Object readonly
Class Method Summary collapse
-
.attribute(*names) ⇒ Object
Defines an attribute for the model.
-
.attributes ⇒ Hash
All defined attributes within a class.
-
.belongs_to(name, options = {}) ⇒ Object
Defines a belongs_to association for the model.
-
.connect(*options) ⇒ Couchbase::Bucket
Use custom connection options.
-
.create(*args) ⇒ Couchbase::Model, false
Create the model with given attributes.
-
.create!(*args) ⇒ Object
Creates an object just like {Model{Model.create but raises an exception if the record is invalid..
- .defaults(options = nil) ⇒ Object
-
.design_document(name = nil) ⇒ String
Associate custom design document with the model.
-
.ensure_design_document! ⇒ Object
Ensure that design document is up to date.
-
.exists?(id) ⇒ true, false
Check if the key exists in the bucket.
-
.find(id) ⇒ Couchbase::Model
Find the model using
id
attribute. -
.find_by_id(id) ⇒ Couchbase::Model?
Find the model using
id
attribute. - .inspect ⇒ Object
-
.uuid_algorithm(algorithm) ⇒ Symbol
Choose the UUID generation algorithms.
-
.view(*names) ⇒ Object
Defines a view for the model.
-
.views ⇒ Array
All defined views within a class.
Instance Method Summary collapse
-
#attributes ⇒ Hash
All the attributes of the current instance.
-
#create(options = {}) ⇒ Couchbase::Model, false
Create this model and assign new id if necessary.
-
#create!(options = {}) ⇒ Object
Creates an object just like {Model{Model#create but raises an exception if the record is invalid..
-
#delete(options = {}) ⇒ Couchbase::Model
Delete this object from the bucket.
-
#exists? ⇒ true, false
Check if this model exists in the bucket.
-
#initialize(attrs = {}) ⇒ Model
constructor
Constructor for all subclasses of Couchbase::Model.
-
#new? ⇒ true, false
Check if the record have
id
attribute. -
#persisted? ⇒ true, false
Where on on this object persisted in the storage.
- #read_attribute(attr_name) ⇒ Object
-
#reload ⇒ Model
Reload all the model attributes from the bucket.
-
#save(options = {}) ⇒ Couchbase::Model, false
Create or update this object based on the state of #new?.
-
#save!(options = {}) ⇒ Object
Creates an object just like {Model{Model#save but raises an exception if the record is invalid..
-
#to_key ⇒ Object
Redefine (if exists) #to_key to use #key if #id is missing.
- #to_param ⇒ Object
-
#update(attrs, options = {}) ⇒ Couchbase::Model
Update this object, optionally accepting new attributes.
-
#update_attributes(attrs) ⇒ Object
Update all attributes without persisting the changes.
- #write_attribute(attr_name, value) ⇒ Object
Constructor Details
#initialize(attrs = {}) ⇒ Model
Constructor for all subclasses of Couchbase::Model
Optionally takes a Hash of attribute value pairs.
454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 |
# File 'lib/couchbase/model.rb', line 454 def initialize(attrs = {}) @errors = ::ActiveModel::Errors.new(self) if defined?(::ActiveModel) @_attributes = ::Hash.new do |h, k| default = self.class.attributes[k] h[k] = if default.respond_to?(:call) default.call else default end end case attrs when Hash if defined?(HashWithIndifferentAccess) && !attrs.is_a?(HashWithIndifferentAccess) attrs = attrs.with_indifferent_access end @id = attrs.delete(:id) @key = attrs.delete(:key) @value = attrs.delete(:value) @doc = attrs.delete(:doc) @meta = attrs.delete(:meta) @raw = attrs.delete(:raw) update_attributes(@doc || attrs) else @raw = attrs end end |
Instance Attribute Details
#errors ⇒ Object (readonly)
121 122 123 |
# File 'lib/couchbase/model.rb', line 121 def errors @errors end |
#id ⇒ Object
Each model must have identifier
106 107 108 |
# File 'lib/couchbase/model.rb', line 106 def id @id end |
#value ⇒ Object (readonly)
112 113 114 |
# File 'lib/couchbase/model.rb', line 112 def value @value end |
Class Method Details
.attribute(*names) ⇒ Object
Defines an attribute for the model
312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/couchbase/model.rb', line 312 def self.attribute(*names) = {} if names.last.is_a?(Hash) = names.pop end names.each do |name| name = name.to_sym attributes[name] = [:default] next if self.instance_methods.include?(name) define_method(name) do read_attribute(name) end define_method(:"#{name}=") do |value| write_attribute(name, value) end end end |
.attributes ⇒ Hash
All defined attributes within a class.
642 643 644 |
# File 'lib/couchbase/model.rb', line 642 def self.attributes @@attributes[self] end |
.belongs_to(name, options = {}) ⇒ Object
Defines a belongs_to association for the model
383 384 385 386 387 388 389 390 |
# File 'lib/couchbase/model.rb', line 383 def self.belongs_to(name, = {}) ref = "#{name}_id" attribute(ref) assoc = name.to_s.camelize.constantize define_method(name) do assoc.find(self.send(ref)) end end |
.connect(*options) ⇒ Couchbase::Bucket
Use custom connection options
147 148 149 |
# File 'lib/couchbase/model.rb', line 147 def self.connect(*) self.bucket = Couchbase.connect(*) end |
.create(*args) ⇒ Couchbase::Model, false
Create the model with given attributes
434 435 436 |
# File 'lib/couchbase/model.rb', line 434 def self.create(*args) new(*args).create end |
.create!(*args) ⇒ Object
Creates an object just like {Model{Model.create but raises an exception if the record is invalid.
443 444 445 |
# File 'lib/couchbase/model.rb', line 443 def self.create!(*args) new(*args).create! end |
.defaults(options = nil) ⇒ Object
185 186 187 188 189 190 191 |
# File 'lib/couchbase/model.rb', line 185 def self.defaults( = nil) if @_defaults = else @_defaults || {} end end |
.design_document(name = nil) ⇒ String
Associate custom design document with the model
Design document is the special document which contains views, the chunks of code for building map/reduce indexes. When this method called without argument, it just returns the effective design document name.
171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/couchbase/model.rb', line 171 def self.design_document(name = nil) if name @_design_doc = name.to_s else @_design_doc ||= begin name = self.name.dup name.gsub!(/::/, '_') name.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2') name.gsub!(/([a-z\d])([A-Z])/,'\1_\2') name.downcase! end end end |
.ensure_design_document! ⇒ Object
Ensure that design document is up to date.
This method also cares about organizing view in separate javascript files. The general structure is the following ([root]
is the directory, one of the Couchbase::Model::Configuration#design_documents_paths):
[root]
|
`- link
| |
| `- by_created_at
| | |
| | `- map.js
| |
| `- by_session_id
| | |
| | `- map.js
| |
| `- total_views
| | |
| | `- map.js
| | |
| | `- reduce.js
The directory structure above demonstrate layout for design document with id _design/link
and three views: by_create_at
, +by_session_id` and ‘total_views`.
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 |
# File 'lib/couchbase/model.rb', line 222 def self.ensure_design_document! unless Configuration.design_documents_paths raise "Configuration.design_documents_path must be directory" end doc = {'_id' => "_design/#{design_document}", 'views' => {}} digest = Digest::MD5.new mtime = 0 views.each do |name, _| doc['views'][name] = {} doc['spatial'] = {} ['map', 'reduce', 'spatial'].each do |type| Configuration.design_documents_paths.each do |path| ff = File.join(path, design_document.to_s, name.to_s, "#{type}.js") if File.file?(ff) contents = File.read(ff).strip next if contents.empty? mtime = [mtime, File.mtime(ff).to_i].max digest << contents case type when 'map', 'reduce' doc['views'][name][type] = contents when 'spatial' doc['spatial'][name] = contents end break # pick first matching file end end end end doc['views'].delete_if {|_, v| v.empty? } doc.delete('spatial') if doc['spatial'] && doc['spatial'].empty? doc['signature'] = digest.to_s doc['timestamp'] = mtime if doc['signature'] != thread_storage[:signature] && doc['timestamp'] > thread_storage[:timestamp].to_i current_doc = bucket.design_docs[design_document.to_s] if current_doc.nil? || (current_doc['signature'] != doc['signature'] && doc['timestamp'] > current_doc[:timestamp].to_i) bucket.save_design_doc(doc) current_doc = doc end thread_storage[:signature] = current_doc['signature'] thread_storage[:timestamp] = current_doc['timestamp'].to_i end end |
.exists?(id) ⇒ true, false
Check if the key exists in the bucket
621 622 623 |
# File 'lib/couchbase/model.rb', line 621 def self.exists?(id) !!bucket.get(id, :quiet => true) end |
.find(id) ⇒ Couchbase::Model
Find the model using id
attribute
402 403 404 405 406 407 408 |
# File 'lib/couchbase/model.rb', line 402 def self.find(id) if id && (res = bucket.get(id, :quiet => false, :extended => true)) obj, flags, cas = res obj = {:raw => obj} unless obj.is_a?(Hash) new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj)) end end |
.find_by_id(id) ⇒ Couchbase::Model?
Find the model using id
attribute
420 421 422 423 424 425 426 |
# File 'lib/couchbase/model.rb', line 420 def self.find_by_id(id) if id && (res = bucket.get(id, :quiet => true, :extended => true)) obj, flags, cas = res obj = {:raw => obj} unless obj.is_a?(Hash) new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj)) end end |
.inspect ⇒ Object
766 767 768 769 770 771 772 |
# File 'lib/couchbase/model.rb', line 766 def self.inspect buf = "#{name}" if self != Couchbase::Model buf << "(#{['id', attributes.map(&:first)].flatten.join(', ')})" end buf end |
.uuid_algorithm(algorithm) ⇒ Symbol
Choose the UUID generation algorithms
284 285 286 |
# File 'lib/couchbase/model.rb', line 284 def self.uuid_algorithm(algorithm) self.thread_storage[:uuid_algorithm] = algorithm end |
.view(*names) ⇒ Object
Defines a view for the model
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 |
# File 'lib/couchbase/model.rb', line 347 def self.view(*names) = {:wrapper_class => self, :include_docs => true} if names.last.is_a?(Hash) .update(names.pop) end is_spatial = .delete(:spatial) names.each do |name| path = "_design/%s/_%s/%s" % [design_document, is_spatial ? "spatial" : "view", name] views[name] = lambda do |*params| params = .merge(params.first || {}) View.new(bucket, path, params) end singleton_class.send(:define_method, name, &views[name]) end end |
.views ⇒ Array
All defined views within a class.
653 654 655 |
# File 'lib/couchbase/model.rb', line 653 def self.views @@views[self] end |
Instance Method Details
#attributes ⇒ Hash
All the attributes of the current instance
662 663 664 |
# File 'lib/couchbase/model.rb', line 662 def attributes @_attributes end |
#create(options = {}) ⇒ Couchbase::Model, false
Create this model and assign new id if necessary
493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 |
# File 'lib/couchbase/model.rb', line 493 def create( = {}) @id ||= Couchbase::Model::UUID.generator.next(1, model.thread_storage[:uuid_algorithm]) if respond_to?(:valid?) && !valid? return false end = model.defaults.merge() value = ([:format] == :plain) ? @raw : attributes_with_values unless @meta @meta = {} if @meta.respond_to?(:with_indifferent_access) @meta = @meta.with_indifferent_access end end @meta['cas'] = model.bucket.add(@id, value, ) self end |
#create!(options = {}) ⇒ Object
Creates an object just like Couchbase::Model.{Model{Model#create but raises an exception if the record is invalid.
516 517 518 |
# File 'lib/couchbase/model.rb', line 516 def create!( = {}) create() || raise(Couchbase::Error::RecordInvalid.new(self)) end |
#delete(options = {}) ⇒ Couchbase::Model
This method will reset id
attribute
Delete this object from the bucket
588 589 590 591 592 593 594 |
# File 'lib/couchbase/model.rb', line 588 def delete( = {}) raise Couchbase::Error::MissingId, "missing id attribute" unless @id model.bucket.delete(@id, ) @id = nil @meta = nil self end |
#exists? ⇒ true, false
Check if this model exists in the bucket.
631 632 633 |
# File 'lib/couchbase/model.rb', line 631 def exists? model.exists?(@id) end |
#new? ⇒ true, false
true
doesn’t mean that record exists in the database
Check if the record have id
attribute
605 606 607 |
# File 'lib/couchbase/model.rb', line 605 def new? !@id end |
#persisted? ⇒ true, false
Returns Where on on this object persisted in the storage.
610 611 612 |
# File 'lib/couchbase/model.rb', line 610 def persisted? !!@id end |
#read_attribute(attr_name) ⇒ Object
288 289 290 |
# File 'lib/couchbase/model.rb', line 288 def read_attribute(attr_name) @_attributes[attr_name] end |
#reload ⇒ Model
Reload all the model attributes from the bucket
689 690 691 692 693 694 |
# File 'lib/couchbase/model.rb', line 689 def reload raise Couchbase::Error::MissingId, "missing id attribute" unless @id attrs = model.find(@id).attributes update_attributes(attrs) self end |
#save(options = {}) ⇒ Couchbase::Model, false
Create or update this object based on the state of #new?.
540 541 542 543 544 545 546 547 548 549 |
# File 'lib/couchbase/model.rb', line 540 def save( = {}) return create() unless @meta if respond_to?(:valid?) && !valid? return false end = model.defaults.merge() value = ([:format] == :plain) ? @raw : attributes_with_values @meta['cas'] = model.bucket.replace(@id, value, ) self end |
#save!(options = {}) ⇒ Object
Creates an object just like Couchbase::Model.{Model{Model#save but raises an exception if the record is invalid.
557 558 559 |
# File 'lib/couchbase/model.rb', line 557 def save!( = {}) save() || raise(Couchbase::Error::RecordInvalid.new(self)) end |
#to_key ⇒ Object
Redefine (if exists) #to_key to use #key if #id is missing
807 808 809 810 |
# File 'lib/couchbase/model.rb', line 807 def to_key keys = [id || key] keys.empty? ? nil : keys end |
#to_param ⇒ Object
812 813 814 815 816 817 |
# File 'lib/couchbase/model.rb', line 812 def to_param keys = to_key if keys && !keys.empty? keys.join("-") end end |
#update(attrs, options = {}) ⇒ Couchbase::Model
Update this object, optionally accepting new attributes.
570 571 572 573 |
# File 'lib/couchbase/model.rb', line 570 def update(attrs, = {}) update_attributes(attrs) save() end |
#update_attributes(attrs) ⇒ Object
Update all attributes without persisting the changes.
671 672 673 674 675 676 677 678 679 |
# File 'lib/couchbase/model.rb', line 671 def update_attributes(attrs) if id = attrs.delete(:id) @id = id end attrs.each do |key, value| setter = :"#{key}=" send(setter, value) if respond_to?(setter) end end |
#write_attribute(attr_name, value) ⇒ Object
292 293 294 |
# File 'lib/couchbase/model.rb', line 292 def write_attribute(attr_name, value) @_attributes[attr_name] = value end |