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.3'
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
- ._find(quiet, *ids) ⇒ Object
-
.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.
-
.couchbase_ancestor ⇒ Object
Returns the first ancestor that is also a Couchbase::Model ancestor.
-
.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, Array
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
-
#as_json(options = {}) ⇒ Hash
Format the model for use in a JSON response.
-
#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.
472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 |
# File 'lib/couchbase/model.rb', line 472 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
._find(quiet, *ids) ⇒ Object
393 394 395 396 397 398 399 400 401 402 403 |
# File 'lib/couchbase/model.rb', line 393 def _find(quiet, *ids) wants_array = ids.first.kind_of?(Array) ids = ids.flatten.compact.uniq unless ids.empty? res = bucket.get(ids, :quiet => quiet, :extended => true).map do |id, (obj, flags, cas)| obj = {:raw => obj} unless obj.is_a?(Hash) new({:id => id, :meta => {'flags' => flags, 'cas' => cas}}.merge(obj)) end wants_array ? res : res.first end end |
.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.
660 661 662 663 664 665 666 |
# File 'lib/couchbase/model.rb', line 660 def self.attributes @attributes ||= if self == Model @@attributes.dup else couchbase_ancestor.attributes.dup end 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 |
.couchbase_ancestor ⇒ Object
Returns the first ancestor that is also a Couchbase::Model ancestor.
686 687 688 689 690 |
# File 'lib/couchbase/model.rb', line 686 def self.couchbase_ancestor ancestors[1..-1].each do |ancestor| return ancestor if ancestor.ancestors.include?(Couchbase::Model) end end |
.create(*args) ⇒ Couchbase::Model, false
Create the model with given attributes
452 453 454 |
# File 'lib/couchbase/model.rb', line 452 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.
461 462 463 |
# File 'lib/couchbase/model.rb', line 461 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).gsub(/^\s*\/\/.*$\n\r?/, '').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
639 640 641 |
# File 'lib/couchbase/model.rb', line 639 def self.exists?(id) !!bucket.get(id, :quiet => true) end |
.find(*id) ⇒ Couchbase::Model, Array
Find the model using id
attribute
421 422 423 |
# File 'lib/couchbase/model.rb', line 421 def self.find(*id) _find(false, *id) end |
.find_by_id(*id) ⇒ Couchbase::Model, ...
Find the model using id
attribute
Unlike find, this method won’t raise Error::NotFound error when key doesn’t exist in the bucket
442 443 444 |
# File 'lib/couchbase/model.rb', line 442 def self.find_by_id(*id) _find(true, *id) end |
.inspect ⇒ Object
811 812 813 814 815 816 817 |
# File 'lib/couchbase/model.rb', line 811 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.
675 676 677 678 679 680 681 |
# File 'lib/couchbase/model.rb', line 675 def self.views @views ||= if self == Model @@views.dup else couchbase_ancestor.views.dup end end |
Instance Method Details
#as_json(options = {}) ⇒ Hash
Format the model for use in a JSON response
737 738 739 |
# File 'lib/couchbase/model.rb', line 737 def as_json( = {}) attributes.merge({:id => @id}).as_json() end |
#attributes ⇒ Hash
All the attributes of the current instance
697 698 699 |
# File 'lib/couchbase/model.rb', line 697 def attributes @_attributes end |
#create(options = {}) ⇒ Couchbase::Model, false
Create this model and assign new id if necessary
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 |
# File 'lib/couchbase/model.rb', line 511 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.
534 535 536 |
# File 'lib/couchbase/model.rb', line 534 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
606 607 608 609 610 611 612 |
# File 'lib/couchbase/model.rb', line 606 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.
649 650 651 |
# File 'lib/couchbase/model.rb', line 649 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
623 624 625 |
# File 'lib/couchbase/model.rb', line 623 def new? !@id end |
#persisted? ⇒ true, false
Returns Where on on this object persisted in the storage.
628 629 630 |
# File 'lib/couchbase/model.rb', line 628 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
724 725 726 727 728 729 |
# File 'lib/couchbase/model.rb', line 724 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?.
558 559 560 561 562 563 564 565 566 567 |
# File 'lib/couchbase/model.rb', line 558 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.
575 576 577 |
# File 'lib/couchbase/model.rb', line 575 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
852 853 854 855 |
# File 'lib/couchbase/model.rb', line 852 def to_key keys = [id || key] keys.empty? ? nil : keys end |
#to_param ⇒ Object
857 858 859 860 861 862 |
# File 'lib/couchbase/model.rb', line 857 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.
588 589 590 591 |
# File 'lib/couchbase/model.rb', line 588 def update(attrs, = {}) update_attributes(attrs) save() end |
#update_attributes(attrs) ⇒ Object
Update all attributes without persisting the changes.
706 707 708 709 710 711 712 713 714 |
# File 'lib/couchbase/model.rb', line 706 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 |