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.1.0'- @@attributes =
{}
- @@views =
{}
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.
- .bucket ⇒ Object
- .bucket=(connection) ⇒ Object
-
.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
idattribute. -
.find_by_id(*id) ⇒ Couchbase::Model, ...
Find the model using
idattribute. - .inspect ⇒ Object
- .thread_storage ⇒ 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.
- .wrap(bucket, data) ⇒ Model
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.
-
#inspect ⇒ Object
of the record.
- #model ⇒ Object
-
#new? ⇒ true, false
Check if the record have
idattribute. -
#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
Methods included from ActiveModel
#==, #eql?, #hash, included, #to_model
Constructor Details
#initialize(attrs = {}) ⇒ Model
Constructor for all subclasses of Couchbase::Model
Optionally takes a Hash of attribute value pairs.
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 482 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) = attrs.delete(:meta) @raw = attrs.delete(:raw) update_attributes(@doc || attrs) else @raw = attrs end end |
Instance Attribute Details
#errors ⇒ Object (readonly)
131 132 133 |
# File 'lib/couchbase/model.rb', line 131 def errors @errors end |
#id ⇒ Object
Each model must have identifier
116 117 118 |
# File 'lib/couchbase/model.rb', line 116 def id @id end |
#value ⇒ Object (readonly)
122 123 124 |
# File 'lib/couchbase/model.rb', line 122 def value @value end |
Class Method Details
._find(quiet, *ids) ⇒ Object
403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/couchbase/model.rb', line 403 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
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 |
# File 'lib/couchbase/model.rb', line 322 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.
670 671 672 673 674 675 676 |
# File 'lib/couchbase/model.rb', line 670 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
393 394 395 396 397 398 399 400 |
# File 'lib/couchbase/model.rb', line 393 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 |
.bucket ⇒ Object
762 763 764 |
# File 'lib/couchbase/model.rb', line 762 def self.bucket self.thread_storage[:bucket] ||= Couchbase.bucket end |
.bucket=(connection) ⇒ Object
771 772 773 |
# File 'lib/couchbase/model.rb', line 771 def self.bucket=(connection) self.thread_storage[:bucket] = connection end |
.connect(*options) ⇒ Couchbase::Bucket
Use custom connection options
157 158 159 |
# File 'lib/couchbase/model.rb', line 157 def self.connect(*) self.bucket = Couchbase.connect(*) end |
.couchbase_ancestor ⇒ Object
Returns the first ancestor that is also a Couchbase::Model ancestor.
696 697 698 699 700 |
# File 'lib/couchbase/model.rb', line 696 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
462 463 464 |
# File 'lib/couchbase/model.rb', line 462 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.
471 472 473 |
# File 'lib/couchbase/model.rb', line 471 def self.create!(*args) new(*args).create! end |
.defaults(options = nil) ⇒ Object
195 196 197 198 199 200 201 |
# File 'lib/couchbase/model.rb', line 195 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.
181 182 183 184 185 186 187 188 189 190 191 192 193 |
# File 'lib/couchbase/model.rb', line 181 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`.
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 270 271 272 273 274 275 276 |
# File 'lib/couchbase/model.rb', line 232 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
649 650 651 |
# File 'lib/couchbase/model.rb', line 649 def self.exists?(id) !!bucket.get(id, :quiet => true) end |
.find(*id) ⇒ Couchbase::Model, Array
Find the model using id attribute
431 432 433 |
# File 'lib/couchbase/model.rb', line 431 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
452 453 454 |
# File 'lib/couchbase/model.rb', line 452 def self.find_by_id(*id) _find(true, *id) end |
.inspect ⇒ Object
822 823 824 825 826 827 828 |
# File 'lib/couchbase/model.rb', line 822 def self.inspect buf = "#{name}" if self != Couchbase::Model buf << "(#{['id', attributes.map(&:first)].flatten.join(', ')})" end buf end |
.thread_storage ⇒ Object
755 756 757 |
# File 'lib/couchbase/model.rb', line 755 def self.thread_storage Couchbase.thread_storage[self] ||= {:uuid_algorithm => :sequential} end |
.uuid_algorithm(algorithm) ⇒ Symbol
Choose the UUID generation algorithms
294 295 296 |
# File 'lib/couchbase/model.rb', line 294 def self.uuid_algorithm(algorithm) self.thread_storage[:uuid_algorithm] = algorithm end |
.view(*names) ⇒ Object
Defines a view for the model
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/couchbase/model.rb', line 357 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.
685 686 687 688 689 690 691 |
# File 'lib/couchbase/model.rb', line 685 def self.views @views ||= if self == Model @@views.dup else couchbase_ancestor.views.dup end end |
.wrap(bucket, data) ⇒ Model
792 793 794 795 796 797 798 799 800 801 802 803 |
# File 'lib/couchbase/model.rb', line 792 def self.wrap(bucket, data) doc = { :id => data['id'], :key => data['key'], :value => data['value'] } if data['doc'] doc[:meta] = data['doc']['meta'] doc[:doc] = data['doc']['value'] || data['doc']['json'] end new(doc) end |
Instance Method Details
#as_json(options = {}) ⇒ Hash
Format the model for use in a JSON response
748 749 750 |
# File 'lib/couchbase/model.rb', line 748 def as_json( = {}) attributes.merge({:id => @id}).as_json() end |
#attributes ⇒ Hash
All the attributes of the current instance
707 708 709 |
# File 'lib/couchbase/model.rb', line 707 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 = {} if .respond_to?(:with_indifferent_access) = .with_indifferent_access end end ['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
616 617 618 619 620 621 622 |
# File 'lib/couchbase/model.rb', line 616 def delete( = {}) raise Couchbase::Error::MissingId, 'missing id attribute' unless @id model.bucket.delete(@id, ) @id = nil = nil self end |
#exists? ⇒ true, false
Check if this model exists in the bucket.
659 660 661 |
# File 'lib/couchbase/model.rb', line 659 def exists? model.exists?(@id) end |
#inspect ⇒ Object
of the record.
809 810 811 812 813 814 815 816 817 818 819 820 |
# File 'lib/couchbase/model.rb', line 809 def inspect attrs = [] attrs << ['key', @key.inspect] unless @key.nil? attrs << ['value', @value.inspect] unless @value.nil? model.attributes.map do |attr, default| val = read_attribute(attr) attrs << [attr.to_s, val.inspect] unless val.nil? end attrs.sort! attrs.unshift([:id, id]) unless new? sprintf('#<%s %s>', model, attrs.map { |a| a.join(': ') }.join(', ')) end |
#new? ⇒ true, false
true doesn’t mean that record exists in the database
Check if the record have id attribute
633 634 635 |
# File 'lib/couchbase/model.rb', line 633 def new? !@id end |
#persisted? ⇒ true, false
Returns Where on on this object persisted in the storage.
638 639 640 |
# File 'lib/couchbase/model.rb', line 638 def persisted? !!@id end |
#read_attribute(attr_name) ⇒ Object
298 299 300 |
# File 'lib/couchbase/model.rb', line 298 def read_attribute(attr_name) @_attributes[attr_name] end |
#reload ⇒ Model
Reload all the model attributes from the bucket
734 735 736 737 738 739 740 |
# File 'lib/couchbase/model.rb', line 734 def reload raise Couchbase::Error::MissingId, 'missing id attribute' unless @id pristine = model.find(@id) update_attributes(pristine.attributes) [: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 |
# File 'lib/couchbase/model.rb', line 568 def save( = {}) return create() unless if respond_to?(:valid?) && !valid? return false end = model.defaults.merge() value = ([:format] == :plain) ? @raw : attributes_with_values ['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.
585 586 587 |
# File 'lib/couchbase/model.rb', line 585 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
844 845 846 847 |
# File 'lib/couchbase/model.rb', line 844 def to_key keys = [id || key] keys.empty? ? nil : keys end |
#to_param ⇒ Object
849 850 851 852 853 854 |
# File 'lib/couchbase/model.rb', line 849 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.
598 599 600 601 |
# File 'lib/couchbase/model.rb', line 598 def update(attrs, = {}) update_attributes(attrs) save() end |
#update_attributes(attrs) ⇒ Object
Update all attributes without persisting the changes.
716 717 718 719 720 721 722 723 724 |
# File 'lib/couchbase/model.rb', line 716 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
302 303 304 |
# File 'lib/couchbase/model.rb', line 302 def write_attribute(attr_name, value) @_attributes[attr_name] = value end |