Class: Couchbase::Model
- Includes:
- ActiveModel
- 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.4'
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 (also: #[])
-
#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 (also: #[]=)
Methods included from ActiveModel
Constructor Details
#initialize(attrs = {}) ⇒ Model
Constructor for all subclasses of Couchbase::Model
Optionally takes a Hash of attribute value pairs.
478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 |
# File 'lib/couchbase/model.rb', line 478 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) if attrs.respond_to?(:with_indifferent_access) attrs = attrs.with_indifferent_access end 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) @previously_changed = nil @changed_attributes.clear unless @changed_attributes.nil? else @raw = attrs end end |
Instance Attribute Details
#errors ⇒ Object (readonly)
124 125 126 |
# File 'lib/couchbase/model.rb', line 124 def errors @errors end |
#id ⇒ Object
Each model must have identifier
109 110 111 |
# File 'lib/couchbase/model.rb', line 109 def id @id end |
#value ⇒ Object (readonly)
115 116 117 |
# File 'lib/couchbase/model.rb', line 115 def value @value end |
Class Method Details
._find(quiet, *ids) ⇒ Object
399 400 401 402 403 404 405 406 407 408 409 |
# File 'lib/couchbase/model.rb', line 399 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
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/couchbase/model.rb', line 318 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.
672 673 674 675 676 677 678 |
# File 'lib/couchbase/model.rb', line 672 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
389 390 391 392 393 394 395 396 |
# File 'lib/couchbase/model.rb', line 389 def self.belongs_to(name, = {}) ref = "#{name}_id" attribute(ref) assoc = ([:class_name] || name).to_s.camelize.constantize define_method(name) do assoc.find(self.send(ref)) end end |
.connect(*options) ⇒ Couchbase::Bucket
Use custom connection options
150 151 152 |
# File 'lib/couchbase/model.rb', line 150 def self.connect(*) self.bucket = Couchbase.connect(*) end |
.couchbase_ancestor ⇒ Object
Returns the first ancestor that is also a Couchbase::Model ancestor.
698 699 700 701 702 |
# File 'lib/couchbase/model.rb', line 698 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
458 459 460 |
# File 'lib/couchbase/model.rb', line 458 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.
467 468 469 |
# File 'lib/couchbase/model.rb', line 467 def self.create!(*args) new(*args).create! end |
.defaults(options = nil) ⇒ Object
188 189 190 191 192 193 194 |
# File 'lib/couchbase/model.rb', line 188 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.
174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/couchbase/model.rb', line 174 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_created_at
, +by_session_id` and ‘total_views`.
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 267 268 269 |
# File 'lib/couchbase/model.rb', line 225 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
651 652 653 |
# File 'lib/couchbase/model.rb', line 651 def self.exists?(id) !!bucket.get(id, :quiet => true) end |
.find(*id) ⇒ Couchbase::Model, Array
Find the model using id
attribute
427 428 429 |
# File 'lib/couchbase/model.rb', line 427 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
448 449 450 |
# File 'lib/couchbase/model.rb', line 448 def self.find_by_id(*id) _find(true, *id) end |
.inspect ⇒ Object
824 825 826 827 828 829 830 |
# File 'lib/couchbase/model.rb', line 824 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
287 288 289 |
# File 'lib/couchbase/model.rb', line 287 def self.uuid_algorithm(algorithm) self.thread_storage[:uuid_algorithm] = algorithm end |
.view(*names) ⇒ Object
Defines a view for the model
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
# File 'lib/couchbase/model.rb', line 353 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.
687 688 689 690 691 692 693 |
# File 'lib/couchbase/model.rb', line 687 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
750 751 752 |
# File 'lib/couchbase/model.rb', line 750 def as_json( = {}) attributes.merge({:id => @id}).as_json() end |
#attributes ⇒ Hash
All the attributes of the current instance
709 710 711 |
# File 'lib/couchbase/model.rb', line 709 def attributes @_attributes || {} end |
#create(options = {}) ⇒ Couchbase::Model, false
Create this model and assign new id if necessary
521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 |
# File 'lib/couchbase/model.rb', line 521 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.
544 545 546 |
# File 'lib/couchbase/model.rb', line 544 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
618 619 620 621 622 623 624 |
# File 'lib/couchbase/model.rb', line 618 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.
661 662 663 |
# File 'lib/couchbase/model.rb', line 661 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
635 636 637 |
# File 'lib/couchbase/model.rb', line 635 def new? !@id end |
#persisted? ⇒ true, false
Returns Where on on this object persisted in the storage.
640 641 642 |
# File 'lib/couchbase/model.rb', line 640 def persisted? !!@id end |
#read_attribute(attr_name) ⇒ Object Also known as: []
291 292 293 |
# File 'lib/couchbase/model.rb', line 291 def read_attribute(attr_name) @_attributes[attr_name] end |
#reload ⇒ Model
Reload all the model attributes from the bucket
736 737 738 739 740 741 742 |
# File 'lib/couchbase/model.rb', line 736 def reload raise Couchbase::Error::MissingId, 'missing id attribute' unless @id pristine = model.find(@id) update_attributes(pristine.attributes) @meta[:cas] = pristine.[:cas] self end |
#save(options = {}) ⇒ Couchbase::Model, false
Create or update this object based on the state of #new?.
568 569 570 571 572 573 574 575 576 577 578 579 |
# File 'lib/couchbase/model.rb', line 568 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, ) @previously_changed = changes @changed_attributes.clear unless @changed_attributes.nil? self end |
#save!(options = {}) ⇒ Object
Creates an object just like Couchbase::Model.{Model{Model#save but raises an exception if the record is invalid.
587 588 589 |
# File 'lib/couchbase/model.rb', line 587 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
846 847 848 849 |
# File 'lib/couchbase/model.rb', line 846 def to_key keys = [id || key] keys.empty? ? nil : keys end |
#to_param ⇒ Object
851 852 853 854 855 856 |
# File 'lib/couchbase/model.rb', line 851 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.
600 601 602 603 |
# File 'lib/couchbase/model.rb', line 600 def update(attrs, = {}) update_attributes(attrs) save() end |
#update_attributes(attrs) ⇒ Object
Update all attributes without persisting the changes.
718 719 720 721 722 723 724 725 726 |
# File 'lib/couchbase/model.rb', line 718 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 Also known as: []=
296 297 298 299 |
# File 'lib/couchbase/model.rb', line 296 def write_attribute(attr_name, value) attribute_will_change!(attr_name) unless @_attributes[attr_name] == value @_attributes[attr_name] = value end |