openlogic-couchrest_model 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +11 -0
- data/.rspec +4 -0
- data/Gemfile +4 -0
- data/LICENSE +176 -0
- data/README.md +137 -0
- data/Rakefile +38 -0
- data/THANKS.md +21 -0
- data/VERSION +1 -0
- data/benchmarks/dirty.rb +118 -0
- data/couchrest_model.gemspec +36 -0
- data/history.md +309 -0
- data/init.rb +1 -0
- data/lib/couchrest/model.rb +10 -0
- data/lib/couchrest/model/associations.rb +231 -0
- data/lib/couchrest/model/base.rb +129 -0
- data/lib/couchrest/model/callbacks.rb +28 -0
- data/lib/couchrest/model/casted_array.rb +83 -0
- data/lib/couchrest/model/casted_by.rb +33 -0
- data/lib/couchrest/model/casted_hash.rb +84 -0
- data/lib/couchrest/model/class_proxy.rb +135 -0
- data/lib/couchrest/model/collection.rb +273 -0
- data/lib/couchrest/model/configuration.rb +67 -0
- data/lib/couchrest/model/connection.rb +70 -0
- data/lib/couchrest/model/core_extensions/hash.rb +9 -0
- data/lib/couchrest/model/core_extensions/time_parsing.rb +66 -0
- data/lib/couchrest/model/design_doc.rb +128 -0
- data/lib/couchrest/model/designs.rb +91 -0
- data/lib/couchrest/model/designs/view.rb +513 -0
- data/lib/couchrest/model/dirty.rb +39 -0
- data/lib/couchrest/model/document_queries.rb +99 -0
- data/lib/couchrest/model/embeddable.rb +78 -0
- data/lib/couchrest/model/errors.rb +25 -0
- data/lib/couchrest/model/extended_attachments.rb +83 -0
- data/lib/couchrest/model/persistence.rb +178 -0
- data/lib/couchrest/model/properties.rb +228 -0
- data/lib/couchrest/model/property.rb +114 -0
- data/lib/couchrest/model/property_protection.rb +71 -0
- data/lib/couchrest/model/proxyable.rb +183 -0
- data/lib/couchrest/model/support/couchrest_database.rb +13 -0
- data/lib/couchrest/model/support/couchrest_design.rb +33 -0
- data/lib/couchrest/model/typecast.rb +154 -0
- data/lib/couchrest/model/validations.rb +80 -0
- data/lib/couchrest/model/validations/casted_model.rb +16 -0
- data/lib/couchrest/model/validations/locale/en.yml +5 -0
- data/lib/couchrest/model/validations/uniqueness.rb +69 -0
- data/lib/couchrest/model/views.rb +151 -0
- data/lib/couchrest/railtie.rb +24 -0
- data/lib/couchrest_model.rb +66 -0
- data/lib/rails/generators/couchrest_model.rb +16 -0
- data/lib/rails/generators/couchrest_model/config/config_generator.rb +18 -0
- data/lib/rails/generators/couchrest_model/config/templates/couchdb.yml +21 -0
- data/lib/rails/generators/couchrest_model/model/model_generator.rb +27 -0
- data/lib/rails/generators/couchrest_model/model/templates/model.rb +2 -0
- data/spec/.gitignore +1 -0
- data/spec/fixtures/attachments/README +3 -0
- data/spec/fixtures/attachments/couchdb.png +0 -0
- data/spec/fixtures/attachments/test.html +11 -0
- data/spec/fixtures/config/couchdb.yml +10 -0
- data/spec/fixtures/models/article.rb +36 -0
- data/spec/fixtures/models/base.rb +164 -0
- data/spec/fixtures/models/card.rb +19 -0
- data/spec/fixtures/models/cat.rb +23 -0
- data/spec/fixtures/models/client.rb +6 -0
- data/spec/fixtures/models/course.rb +27 -0
- data/spec/fixtures/models/event.rb +8 -0
- data/spec/fixtures/models/invoice.rb +14 -0
- data/spec/fixtures/models/key_chain.rb +5 -0
- data/spec/fixtures/models/membership.rb +4 -0
- data/spec/fixtures/models/person.rb +11 -0
- data/spec/fixtures/models/project.rb +6 -0
- data/spec/fixtures/models/question.rb +7 -0
- data/spec/fixtures/models/sale_entry.rb +9 -0
- data/spec/fixtures/models/sale_invoice.rb +14 -0
- data/spec/fixtures/models/service.rb +10 -0
- data/spec/fixtures/models/user.rb +22 -0
- data/spec/fixtures/views/lib.js +3 -0
- data/spec/fixtures/views/test_view/lib.js +3 -0
- data/spec/fixtures/views/test_view/only-map.js +4 -0
- data/spec/fixtures/views/test_view/test-map.js +3 -0
- data/spec/fixtures/views/test_view/test-reduce.js +3 -0
- data/spec/functional/validations_spec.rb +8 -0
- data/spec/spec_helper.rb +60 -0
- data/spec/unit/active_model_lint_spec.rb +30 -0
- data/spec/unit/assocations_spec.rb +242 -0
- data/spec/unit/attachment_spec.rb +176 -0
- data/spec/unit/base_spec.rb +537 -0
- data/spec/unit/casted_spec.rb +72 -0
- data/spec/unit/class_proxy_spec.rb +167 -0
- data/spec/unit/collection_spec.rb +86 -0
- data/spec/unit/configuration_spec.rb +77 -0
- data/spec/unit/connection_spec.rb +148 -0
- data/spec/unit/core_extensions/time_parsing.rb +77 -0
- data/spec/unit/design_doc_spec.rb +241 -0
- data/spec/unit/designs/view_spec.rb +831 -0
- data/spec/unit/designs_spec.rb +134 -0
- data/spec/unit/dirty_spec.rb +436 -0
- data/spec/unit/embeddable_spec.rb +498 -0
- data/spec/unit/inherited_spec.rb +33 -0
- data/spec/unit/persistence_spec.rb +481 -0
- data/spec/unit/property_protection_spec.rb +192 -0
- data/spec/unit/property_spec.rb +481 -0
- data/spec/unit/proxyable_spec.rb +376 -0
- data/spec/unit/subclass_spec.rb +85 -0
- data/spec/unit/typecast_spec.rb +521 -0
- data/spec/unit/validations_spec.rb +140 -0
- data/spec/unit/view_spec.rb +367 -0
- metadata +301 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
I18n.load_path << File.join(
|
|
4
|
+
File.dirname(__FILE__), "validations", "locale", "en.yml"
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
module CouchRest
|
|
8
|
+
module Model
|
|
9
|
+
|
|
10
|
+
# This applies to both Model::Base and Model::CastedModel
|
|
11
|
+
module Dirty
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
include ActiveModel::Dirty
|
|
14
|
+
|
|
15
|
+
included do
|
|
16
|
+
# internal dirty setting - overrides global setting.
|
|
17
|
+
# this is used to temporarily disable dirty tracking when setting
|
|
18
|
+
# attributes directly, for performance reasons.
|
|
19
|
+
self.send(:attr_accessor, :disable_dirty)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def use_dirty?
|
|
23
|
+
doc = base_doc
|
|
24
|
+
doc && !doc.disable_dirty
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def couchrest_attribute_will_change!(attr)
|
|
28
|
+
return if attr.nil? || !use_dirty?
|
|
29
|
+
attribute_will_change!(attr)
|
|
30
|
+
couchrest_parent_will_change!
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def couchrest_parent_will_change!
|
|
34
|
+
casted_by.couchrest_attribute_will_change!(casted_by_property.name) if casted_by_property
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
module DocumentQueries
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
module ClassMethods
|
|
7
|
+
|
|
8
|
+
# Load all documents that have the model_type_key's field equal to the
|
|
9
|
+
# name of the current class. Take the standard set of
|
|
10
|
+
# CouchRest::Database#view options.
|
|
11
|
+
def all(opts = {}, &block)
|
|
12
|
+
view(:all, opts, &block)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the number of documents that have the model_type_key's field
|
|
16
|
+
# equal to the name of the current class. Takes the standard set of
|
|
17
|
+
# CouchRest::Database#view options
|
|
18
|
+
def count(opts = {}, &block)
|
|
19
|
+
all({:raw => true, :limit => 0}.merge(opts), &block)['total_rows']
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Load the first document that have the model_type_key's field equal to
|
|
23
|
+
# the name of the current class.
|
|
24
|
+
#
|
|
25
|
+
# ==== Returns
|
|
26
|
+
# Object:: The first object instance available
|
|
27
|
+
# or
|
|
28
|
+
# Nil:: if no instances available
|
|
29
|
+
#
|
|
30
|
+
# ==== Parameters
|
|
31
|
+
# opts<Hash>::
|
|
32
|
+
# View options, see <tt>CouchRest::Database#view</tt> options for more info.
|
|
33
|
+
def first(opts = {})
|
|
34
|
+
first_instance = self.all(opts.merge!(:limit => 1))
|
|
35
|
+
first_instance.empty? ? nil : first_instance.first
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Load the last document that have the model_type_key's field equal to
|
|
39
|
+
# the name of the current class.
|
|
40
|
+
# It's similar to method first, just adds :descending => true
|
|
41
|
+
#
|
|
42
|
+
# ==== Returns
|
|
43
|
+
# Object:: The last object instance available
|
|
44
|
+
# or
|
|
45
|
+
# Nil:: if no instances available
|
|
46
|
+
#
|
|
47
|
+
# ==== Parameters
|
|
48
|
+
# opts<Hash>::
|
|
49
|
+
# View options, see <tt>CouchRest::Database#view</tt> options for more info.
|
|
50
|
+
def last(opts = {})
|
|
51
|
+
first(opts.merge!(:descending => true))
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Load a document from the database by id
|
|
55
|
+
# No exceptions will be raised if the document isn't found
|
|
56
|
+
#
|
|
57
|
+
# ==== Returns
|
|
58
|
+
# Object:: if the document was found
|
|
59
|
+
# or
|
|
60
|
+
# Nil::
|
|
61
|
+
#
|
|
62
|
+
# === Parameters
|
|
63
|
+
# id<String, Integer>:: Document ID
|
|
64
|
+
# db<Database>:: optional option to pass a custom database to use
|
|
65
|
+
def get(id, db = database)
|
|
66
|
+
begin
|
|
67
|
+
get!(id, db)
|
|
68
|
+
rescue
|
|
69
|
+
nil
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
alias :find :get
|
|
73
|
+
|
|
74
|
+
# Load a document from the database by id
|
|
75
|
+
# An exception will be raised if the document isn't found
|
|
76
|
+
#
|
|
77
|
+
# ==== Returns
|
|
78
|
+
# Object:: if the document was found
|
|
79
|
+
# or
|
|
80
|
+
# Exception
|
|
81
|
+
#
|
|
82
|
+
# === Parameters
|
|
83
|
+
# id<String, Integer>:: Document ID
|
|
84
|
+
# db<Database>:: optional option to pass a custom database to use
|
|
85
|
+
def get!(id, db = database)
|
|
86
|
+
raise CouchRest::Model::DocumentNotFound if id.blank?
|
|
87
|
+
|
|
88
|
+
doc = db.get id
|
|
89
|
+
build_from_database(doc)
|
|
90
|
+
rescue RestClient::ResourceNotFound
|
|
91
|
+
raise CouchRest::Model::DocumentNotFound
|
|
92
|
+
end
|
|
93
|
+
alias :find! :get!
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
module CouchRest::Model
|
|
2
|
+
module Embeddable
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
# Include Attributes early to ensure super() will work
|
|
6
|
+
include CouchRest::Attributes
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
include CouchRest::Model::Configuration
|
|
10
|
+
include CouchRest::Model::Properties
|
|
11
|
+
include CouchRest::Model::PropertyProtection
|
|
12
|
+
include CouchRest::Model::Associations
|
|
13
|
+
include CouchRest::Model::Validations
|
|
14
|
+
include CouchRest::Model::Callbacks
|
|
15
|
+
include CouchRest::Model::CastedBy
|
|
16
|
+
include CouchRest::Model::Dirty
|
|
17
|
+
include CouchRest::Model::Callbacks
|
|
18
|
+
|
|
19
|
+
class_eval do
|
|
20
|
+
# Override CastedBy's base_doc?
|
|
21
|
+
def base_doc?
|
|
22
|
+
false # Can never be base doc!
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Initialize a new Casted Model. Accepts the same
|
|
29
|
+
# options as CouchRest::Model::Base for preparing and initializing
|
|
30
|
+
# attributes.
|
|
31
|
+
def initialize(keys = {}, options = {})
|
|
32
|
+
super()
|
|
33
|
+
prepare_all_attributes(keys, options)
|
|
34
|
+
run_callbacks(:initialize) { self }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# False if the casted model has already
|
|
38
|
+
# been saved in the containing document
|
|
39
|
+
def new?
|
|
40
|
+
casted_by.nil? ? true : casted_by.new?
|
|
41
|
+
end
|
|
42
|
+
alias :new_record? :new?
|
|
43
|
+
|
|
44
|
+
def persisted?
|
|
45
|
+
!new?
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The to_param method is needed for rails to generate resourceful routes.
|
|
49
|
+
# In your controller, remember that it's actually the id of the document.
|
|
50
|
+
def id
|
|
51
|
+
return nil if base_doc.nil?
|
|
52
|
+
base_doc.id
|
|
53
|
+
end
|
|
54
|
+
alias :to_key :id
|
|
55
|
+
alias :to_param :id
|
|
56
|
+
|
|
57
|
+
# Sets the attributes from a hash
|
|
58
|
+
def update_attributes_without_saving(hash)
|
|
59
|
+
hash.each do |k, v|
|
|
60
|
+
raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=")
|
|
61
|
+
end
|
|
62
|
+
hash.each do |k, v|
|
|
63
|
+
self.send("#{k}=",v)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
alias :attributes= :update_attributes_without_saving
|
|
67
|
+
|
|
68
|
+
end # End Embeddable
|
|
69
|
+
|
|
70
|
+
# Provide backwards compatability with previous versions (pre 1.1.0)
|
|
71
|
+
module CastedModel
|
|
72
|
+
extend ActiveSupport::Concern
|
|
73
|
+
included do
|
|
74
|
+
include CouchRest::Model::Embeddable
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
module CouchRest
|
|
3
|
+
module Model
|
|
4
|
+
module Errors
|
|
5
|
+
|
|
6
|
+
class CouchRestModelError < StandardError; end
|
|
7
|
+
|
|
8
|
+
# Raised when a persisence method ending in ! fails validation. The message
|
|
9
|
+
# will contain the full error messages from the +Document+ in question.
|
|
10
|
+
#
|
|
11
|
+
# Example:
|
|
12
|
+
#
|
|
13
|
+
# <tt>Validations.new(person.errors)</tt>
|
|
14
|
+
class Validations < CouchRestModelError
|
|
15
|
+
attr_reader :document
|
|
16
|
+
def initialize(document)
|
|
17
|
+
@document = document
|
|
18
|
+
super("Validation Failed: #{@document.errors.full_messages.join(", ")}")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class DocumentNotFound < Errors::CouchRestModelError; end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
module ExtendedAttachments
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Add a file attachment to the current document. Expects
|
|
7
|
+
# :file and :name to be included in the arguments.
|
|
8
|
+
def create_attachment(args={})
|
|
9
|
+
raise ArgumentError unless args[:file] && args[:name]
|
|
10
|
+
return if has_attachment?(args[:name])
|
|
11
|
+
set_attachment_attr(args)
|
|
12
|
+
rescue ArgumentError => e
|
|
13
|
+
raise ArgumentError, 'You must specify :file and :name'
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# return all attachments
|
|
17
|
+
def attachments
|
|
18
|
+
self['_attachments'] ||= {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# reads the data from an attachment
|
|
22
|
+
def read_attachment(attachment_name)
|
|
23
|
+
database.fetch_attachment(self, attachment_name)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# modifies a file attachment on the current doc
|
|
27
|
+
def update_attachment(args={})
|
|
28
|
+
raise ArgumentError unless args[:file] && args[:name]
|
|
29
|
+
return unless has_attachment?(args[:name])
|
|
30
|
+
delete_attachment(args[:name])
|
|
31
|
+
set_attachment_attr(args)
|
|
32
|
+
rescue ArgumentError => e
|
|
33
|
+
raise ArgumentError, 'You must specify :file and :name'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# deletes a file attachment from the current doc
|
|
37
|
+
def delete_attachment(attachment_name)
|
|
38
|
+
return unless attachments
|
|
39
|
+
if attachments.include?(attachment_name)
|
|
40
|
+
attribute_will_change!("_attachments")
|
|
41
|
+
attachments.delete attachment_name
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# returns true if attachment_name exists
|
|
46
|
+
def has_attachment?(attachment_name)
|
|
47
|
+
!!(attachments && attachments[attachment_name] && !attachments[attachment_name].empty?)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# returns URL to fetch the attachment from
|
|
51
|
+
def attachment_url(attachment_name)
|
|
52
|
+
return unless has_attachment?(attachment_name)
|
|
53
|
+
"#{database.root}/#{self.id}/#{attachment_name}"
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# returns URI to fetch the attachment from
|
|
57
|
+
def attachment_uri(attachment_name)
|
|
58
|
+
return unless has_attachment?(attachment_name)
|
|
59
|
+
"#{database.uri}/#{self.id}/#{attachment_name}"
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def get_mime_type(path)
|
|
65
|
+
return nil if path.nil?
|
|
66
|
+
type = ::MIME::Types.type_for(path)
|
|
67
|
+
type.empty? ? nil : type.first.content_type
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def set_attachment_attr(args)
|
|
71
|
+
content_type = args[:content_type] ? args[:content_type] : get_mime_type(args[:file].path)
|
|
72
|
+
content_type ||= (get_mime_type(args[:name]) || 'text/plain')
|
|
73
|
+
|
|
74
|
+
attribute_will_change!("_attachments")
|
|
75
|
+
attachments[args[:name]] = {
|
|
76
|
+
'content_type' => content_type,
|
|
77
|
+
'data' => args[:file].read
|
|
78
|
+
}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
end # module ExtendedAttachments
|
|
82
|
+
end
|
|
83
|
+
end
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
module CouchRest
|
|
2
|
+
module Model
|
|
3
|
+
module Persistence
|
|
4
|
+
extend ActiveSupport::Concern
|
|
5
|
+
|
|
6
|
+
# Create the document. Validation is enabled by default and will return
|
|
7
|
+
# false if the document is not valid. If all goes well, the document will
|
|
8
|
+
# be returned.
|
|
9
|
+
def create(options = {})
|
|
10
|
+
return false unless perform_validations(options)
|
|
11
|
+
_run_create_callbacks do
|
|
12
|
+
_run_save_callbacks do
|
|
13
|
+
set_unique_id if new? && self.respond_to?(:set_unique_id)
|
|
14
|
+
result = database.save_doc(self)
|
|
15
|
+
ret = (result["ok"] == true) ? self : false
|
|
16
|
+
@changed_attributes.clear if ret && @changed_attributes
|
|
17
|
+
ret
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Creates the document in the db. Raises an exception
|
|
23
|
+
# if the document is not created properly.
|
|
24
|
+
def create!(options = {})
|
|
25
|
+
self.class.fail_validate!(self) unless self.create(options)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Trigger the callbacks (before, after, around)
|
|
29
|
+
# only if the document isn't new
|
|
30
|
+
def update(options = {})
|
|
31
|
+
raise "Cannot save a destroyed document!" if destroyed?
|
|
32
|
+
raise "Calling #{self.class.name}#update on document that has not been created!" if new?
|
|
33
|
+
return false unless perform_validations(options)
|
|
34
|
+
return true if !self.disable_dirty && !self.changed?
|
|
35
|
+
_run_update_callbacks do
|
|
36
|
+
_run_save_callbacks do
|
|
37
|
+
result = database.save_doc(self)
|
|
38
|
+
ret = result["ok"] == true
|
|
39
|
+
@changed_attributes.clear if ret && @changed_attributes
|
|
40
|
+
ret
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Trigger the callbacks (before, after, around) and save the document
|
|
46
|
+
def save(options = {})
|
|
47
|
+
self.new? ? create(options) : update(options)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Saves the document to the db using save. Raises an exception
|
|
51
|
+
# if the document is not saved properly.
|
|
52
|
+
def save!
|
|
53
|
+
self.class.fail_validate!(self) unless self.save
|
|
54
|
+
true
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Deletes the document from the database. Runs the :destroy callbacks.
|
|
58
|
+
def destroy
|
|
59
|
+
_run_destroy_callbacks do
|
|
60
|
+
result = database.delete_doc(self)
|
|
61
|
+
if result['ok']
|
|
62
|
+
@_destroyed = true
|
|
63
|
+
self.freeze
|
|
64
|
+
end
|
|
65
|
+
result['ok']
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def destroyed?
|
|
70
|
+
!!@_destroyed
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def persisted?
|
|
74
|
+
!new? && !destroyed?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Update the document's attributes and save. For example:
|
|
78
|
+
#
|
|
79
|
+
# doc.update_attributes :name => "Fred"
|
|
80
|
+
# Is the equivilent of doing the following:
|
|
81
|
+
#
|
|
82
|
+
# doc.attributes = { :name => "Fred" }
|
|
83
|
+
# doc.save
|
|
84
|
+
#
|
|
85
|
+
def update_attributes(hash)
|
|
86
|
+
update_attributes_without_saving hash
|
|
87
|
+
save
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Reloads the attributes of this object from the database.
|
|
91
|
+
# It doesn't override custom instance variables.
|
|
92
|
+
#
|
|
93
|
+
# Returns self.
|
|
94
|
+
def reload
|
|
95
|
+
prepare_all_attributes(database.get(id), :directly_set_attributes => true)
|
|
96
|
+
self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
protected
|
|
100
|
+
|
|
101
|
+
def perform_validations(options = {})
|
|
102
|
+
perform_validation = case options
|
|
103
|
+
when Hash
|
|
104
|
+
options[:validate] != false
|
|
105
|
+
else
|
|
106
|
+
options
|
|
107
|
+
end
|
|
108
|
+
perform_validation ? valid? : true
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
module ClassMethods
|
|
113
|
+
|
|
114
|
+
# Creates a new instance, bypassing attribute protection and
|
|
115
|
+
# uses the type field to determine which model to use to instanatiate
|
|
116
|
+
# the new object.
|
|
117
|
+
#
|
|
118
|
+
# ==== Returns
|
|
119
|
+
# a document instance
|
|
120
|
+
#
|
|
121
|
+
def build_from_database(doc = {}, options = {}, &block)
|
|
122
|
+
src = doc[model_type_key]
|
|
123
|
+
base = (src.blank? || src == self.to_s) ? self : src.constantize
|
|
124
|
+
base.new(doc, options.merge(:directly_set_attributes => true), &block)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Defines an instance and save it directly to the database
|
|
128
|
+
#
|
|
129
|
+
# ==== Returns
|
|
130
|
+
# returns the reloaded document
|
|
131
|
+
def create(attributes = {}, &block)
|
|
132
|
+
instance = new(attributes, &block)
|
|
133
|
+
instance.create
|
|
134
|
+
instance
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
# Defines an instance and save it directly to the database
|
|
138
|
+
#
|
|
139
|
+
# ==== Returns
|
|
140
|
+
# returns the reloaded document or raises an exception
|
|
141
|
+
def create!(attributes = {}, &block)
|
|
142
|
+
instance = new(attributes, &block)
|
|
143
|
+
instance.create!
|
|
144
|
+
instance
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
# Name a method that will be called before the document is first saved,
|
|
148
|
+
# which returns a string to be used for the document's <tt>_id</tt>.
|
|
149
|
+
#
|
|
150
|
+
# Because CouchDB enforces a constraint that each id must be unique,
|
|
151
|
+
# this can be used to enforce eg: uniq usernames. Note that this id
|
|
152
|
+
# must be globally unique across all document types which share a
|
|
153
|
+
# database, so if you'd like to scope uniqueness to this class, you
|
|
154
|
+
# should use the class name as part of the unique id.
|
|
155
|
+
def unique_id(method = nil, &block)
|
|
156
|
+
if method
|
|
157
|
+
define_method :set_unique_id do
|
|
158
|
+
self['_id'] ||= self.send(method)
|
|
159
|
+
end
|
|
160
|
+
elsif block
|
|
161
|
+
define_method :set_unique_id do
|
|
162
|
+
uniqid = block.call(self)
|
|
163
|
+
raise ArgumentError, "unique_id block must not return nil" if uniqid.nil?
|
|
164
|
+
self['_id'] ||= uniqid
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Raise an error if validation failed.
|
|
170
|
+
def fail_validate!(document)
|
|
171
|
+
raise Errors::Validations.new(document)
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|