Class: CouchRest::ExtendedDocument

Inherits:
Document
  • Object
show all
Includes:
Mixins::AttributeProtection, Mixins::Attributes, Mixins::Callbacks, Mixins::ClassProxy, Mixins::Collection, Mixins::DesignDoc, Mixins::DocumentQueries, Mixins::ExtendedAttachments, Mixins::Views
Defined in:
lib/couchrest/extended_document.rb

Overview

Same as CouchRest::Document but with properties and validations

Constant Summary collapse

VERSION =
"1.0.0"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mixins::Attributes

#prepare_all_attributes, #update_attributes, #update_attributes_without_saving

Methods included from Mixins::AttributeProtection

#accessible_properties, included, #protected_properties, #remove_protected_attributes

Methods included from Mixins::Collection

included

Methods included from Mixins::ClassProxy

included

Methods included from Mixins::ExtendedAttachments

#attachment_uri, #attachment_url, #create_attachment, #delete_attachment, #has_attachment?, #read_attachment, #update_attachment

Methods included from Mixins::DesignDoc

included

Methods included from Mixins::Views

included

Methods included from Mixins::DocumentQueries

included

Methods included from Mixins::Callbacks

included, #run_callbacks

Constructor Details

#initialize(doc = {}, options = {}) ⇒ ExtendedDocument

Instantiate a new ExtendedDocument by preparing all properties using the provided document hash.

Options supported:

  • :directly_set_attributes: true when data comes directly from database



70
71
72
73
74
75
76
77
# File 'lib/couchrest/extended_document.rb', line 70

def initialize(doc = {}, options = {})
  prepare_all_attributes(doc, options) # defined in CouchRest::Mixins::Attributes
  super(doc)
  unless self['_id'] && self['_rev']
    self[CouchRest.type_field] = self.class.to_s
  end
  after_initialize if respond_to?(:after_initialize)
end

Instance Attribute Details

#casted_byObject

Accessors



43
44
45
# File 'lib/couchrest/extended_document.rb', line 43

def casted_by
  @casted_by
end

Class Method Details

.create(options) ⇒ Object

Defines an instance and save it directly to the database

Returns

returns the reloaded document


83
84
85
86
87
# File 'lib/couchrest/extended_document.rb', line 83

def self.create(options)
  instance = new(options)
  instance.create
  instance
end

.create!(options) ⇒ Object

Defines an instance and save it directly to the database

Returns

returns the reloaded document or raises an exception


93
94
95
96
97
# File 'lib/couchrest/extended_document.rb', line 93

def self.create!(options)
  instance = new(options)
  instance.create!
  instance
end

.create_from_database(doc = {}) ⇒ Object

Creates a new instance, bypassing attribute protection

Returns

a document instance


56
57
58
59
60
# File 'lib/couchrest/extended_document.rb', line 56

def self.create_from_database(doc = {})
  type_field = CouchRest.type_field
  base = (doc[type_field].blank? || doc[type_field] == self.to_s) ? self : doc[type_field].constantize
  base.new(doc, :directly_set_attributes => true)      
end

.inherited(subklass) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/couchrest/extended_document.rb', line 30

def self.inherited(subklass)
  super
  subklass.send(:include, CouchRest::Mixins::Properties)
  subklass.class_eval <<-EOS, __FILE__, __LINE__ + 1
    def self.inherited(subklass)
      super
      subklass.properties = self.properties.dup
    end
  EOS
  subclasses << subklass
end

.method_missing(m, *args, &block) ⇒ Object

Temp solution to make the view_by methods available



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/couchrest/extended_document.rb', line 136

def self.method_missing(m, *args, &block)
  if has_view?(m)
    query = args.shift || {}
    return view(m, query, *args, &block)
  elsif m.to_s =~ /^find_(by_.+)/
    view_name = $1
    if has_view?(view_name)
      query = {:key => args.first, :limit => 1}
      return view(view_name, query).first
    end
  end
  super
end

.subclassesObject

Including validation here does not work due to the way inheritance is handled. include CouchRest::Validation



26
27
28
# File 'lib/couchrest/extended_document.rb', line 26

def self.subclasses
  @subclasses ||= []
end

.timestamps!Object

Automatically set updated_at and created_at fields on the document whenever saving occurs. CouchRest uses a pretty decent time format by default. See Time#to_json



102
103
104
105
106
107
108
109
110
111
112
# File 'lib/couchrest/extended_document.rb', line 102

def self.timestamps!
  class_eval <<-EOS, __FILE__, __LINE__
    property(:updated_at, Time, :read_only => true, :protected => true, :auto_validation => false)
    property(:created_at, Time, :read_only => true, :protected => true, :auto_validation => false)
    
    set_callback :save, :before do |object|
      write_attribute('updated_at', Time.now)
      write_attribute('created_at', Time.now) if object.new?
    end
  EOS
end

.unique_id(method = nil, &block) ⇒ Object

Name a method that will be called before the document is first saved, which returns a string to be used for the document’s _id. Because CouchDB enforces a constraint that each id must be unique, this can be used to enforce eg: uniq usernames. Note that this id must be globally unique across all document types which share a database, so if you’d like to scope uniqueness to this class, you should use the class name as part of the unique id.



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/couchrest/extended_document.rb', line 121

def self.unique_id method = nil, &block
  if method
    define_method :set_unique_id do
      self['_id'] ||= self.send(method)
    end
  elsif block
    define_method :set_unique_id do
      uniqid = block.call(self)
      raise ArgumentError, "unique_id block must not return nil" if uniqid.nil?
      self['_id'] ||= uniqid
    end
  end
end

Instance Method Details

#base_docObject

Gets a reference to the actual document in the DB Calls up to the next document if there is one, Otherwise we’re at the top and we return self



155
156
157
158
# File 'lib/couchrest/extended_document.rb', line 155

def base_doc
  return self if base_doc?
  @casted_by.base_doc
end

#base_doc?Boolean

Checks if we’re the top document

Returns:

  • (Boolean)


161
162
163
# File 'lib/couchrest/extended_document.rb', line 161

def base_doc?
  !@casted_by
end

#create(bulk = false) ⇒ Object

Trigger the callbacks (before, after, around) and create the document It’s important to have a create callback since you can’t check if a document was new after you saved it

When creating a document, both the create and the save callbacks will be triggered.



175
176
177
178
179
180
181
182
183
# File 'lib/couchrest/extended_document.rb', line 175

def create(bulk = false)
  caught = catch(:halt)  do
    _run_create_callbacks do
        _run_save_callbacks do
          create_without_callbacks(bulk)
      end
    end
  end
end

#create!Object

Creates the document in the db. Raises an exception if the document is not created properly.



195
196
197
# File 'lib/couchrest/extended_document.rb', line 195

def create!
  raise "#{self.inspect} failed to save" unless self.create
end

#create_without_callbacks(bulk = false) ⇒ Object

unlike save, create returns the newly created document

Raises:

  • (ArgumentError)


186
187
188
189
190
191
# File 'lib/couchrest/extended_document.rb', line 186

def create_without_callbacks(bulk =false)
  raise ArgumentError, "a document requires a database to be created to (The document or the #{self.class} default database were not set)" unless database
  set_unique_id if new? && self.respond_to?(:set_unique_id)
  result = database.save_doc(self, bulk)
  (result["ok"] == true) ? self : false
end

#destroy(bulk = false) ⇒ Object

Deletes the document from the database. Runs the :destroy callbacks. Removes the _id and _rev fields, preparing the document to be saved to a new _id.



248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/couchrest/extended_document.rb', line 248

def destroy(bulk=false)
  caught = catch(:halt)  do
    _run_destroy_callbacks do
      result = database.delete_doc(self, bulk)
      if result['ok']
        self.delete('_rev')
        self.delete('_id')
      end
      result['ok']
    end
  end
end

#save(bulk = false) ⇒ Object

Trigger the callbacks (before, after, around) and save the document



217
218
219
220
221
222
223
224
225
226
227
# File 'lib/couchrest/extended_document.rb', line 217

def save(bulk = false)
  caught = catch(:halt)  do
    if self.new?
      _run_save_callbacks do
        save_without_callbacks(bulk)
      end
    else
      update(bulk)
    end
  end
end

#save!Object

Saves the document to the db using save. Raises an exception if the document is not saved properly.



240
241
242
243
# File 'lib/couchrest/extended_document.rb', line 240

def save!
  raise "#{self.inspect} failed to save" unless self.save
  true
end

#save_without_callbacks(bulk = false) ⇒ Object

Overridden to set the unique ID. Returns a boolean value

Raises:

  • (ArgumentError)


231
232
233
234
235
236
# File 'lib/couchrest/extended_document.rb', line 231

def save_without_callbacks(bulk = false)
  raise ArgumentError, "a document requires a database to be saved to (The document or the #{self.class} default database were not set)" unless database
  set_unique_id if new? && self.respond_to?(:set_unique_id)
  result = database.save_doc(self, bulk)
  result["ok"] == true
end

#update(bulk = false) ⇒ Object

Trigger the callbacks (before, after, around) only if the document isn’t new



201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/couchrest/extended_document.rb', line 201

def update(bulk = false)
  caught = catch(:halt)  do
    if self.new?
      save(bulk)
    else
      _run_update_callbacks do
        _run_save_callbacks do
          save_without_callbacks(bulk)
        end
      end
    end
  end
end