/
persistence.rb
175 lines (155 loc) · 5.36 KB
/
persistence.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
module CouchRest
module Model
module Persistence
extend ActiveSupport::Concern
# Create the document. Validation is enabled by default and will return
# false if the document is not valid. If all goes well, the document will
# be returned.
def create(options = {})
return false unless perform_validations(options)
_run_create_callbacks do
_run_save_callbacks do
set_unique_id if new? && self.respond_to?(:set_unique_id)
result = database.save_doc(self)
ret = (result["ok"] == true) ? self : false
@changed_attributes.clear if ret && @changed_attributes
ret
end
end
end
# Creates the document in the db. Raises an exception
# if the document is not created properly.
def create!
self.class.fail_validate!(self) unless self.create
end
# Trigger the callbacks (before, after, around)
# only if the document isn't new
def update(options = {})
raise "Calling #{self.class.name}#update on document that has not been created!" if self.new?
return false unless perform_validations(options)
return true if !self.changed?
_run_update_callbacks do
_run_save_callbacks do
result = database.save_doc(self)
ret = result["ok"] == true
@changed_attributes.clear if ret && @changed_attributes
ret
end
end
end
# Trigger the callbacks (before, after, around) and save the document
def save(options = {})
self.new? ? create(options) : update(options)
end
# Saves the document to the db using save. Raises an exception
# if the document is not saved properly.
def save!
self.class.fail_validate!(self) unless self.save
true
end
# Deletes the document from the database. Runs the :destroy callbacks.
# Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
# document to be saved to a new <tt>_id</tt> if required.
def destroy
_run_destroy_callbacks do
result = database.delete_doc(self)
if result['ok']
self.delete('_rev')
self.delete('_id')
end
result['ok']
end
end
# Update the document's attributes and save. For example:
#
# doc.update_attributes :name => "Fred"
# Is the equivilent of doing the following:
#
# doc.attributes = { :name => "Fred" }
# doc.save
#
def update_attributes(hash)
update_attributes_without_saving hash
save
end
# Reloads the attributes of this object from the database.
# It doesn't override custom instance variables.
#
# Returns self.
def reload
merge!(self.class.get(id))
self
end
protected
def perform_validations(options = {})
perform_validation = case options
when Hash
options[:validate] != false
else
options
end
perform_validation ? valid? : true
end
module ClassMethods
# Creates a new instance, bypassing attribute protection
#
#
# ==== Returns
# a document instance
def create_from_database(doc = {})
base = (doc[model_type_key].blank? || doc[model_type_key] == self.to_s) ? self : doc[model_type_key].constantize
base.new(doc, :directly_set_attributes => true)
end
# Defines an instance and save it directly to the database
#
# ==== Returns
# returns the reloaded document
def create(attributes = {})
instance = new(attributes)
instance.create
instance
end
# Defines an instance and save it directly to the database
#
# ==== Returns
# returns the reloaded document or raises an exception
def create!(attributes = {})
instance = new(attributes)
instance.create!
instance
end
# Name a method that will be called before the document is first saved,
# which returns a string to be used for the document's <tt>_id</tt>.
#
# 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.
def unique_id method = nil, &block
if method
define_method :get_unique_id do
self.send(method)
end
define_method :set_unique_id do
self['_id'] ||= get_unique_id
end
elsif block
define_method :get_unique_id do
block.call(self)
end
define_method :set_unique_id do
uniqid = get_unique_id
raise ArgumentError, "unique_id block must not return nil" if uniqid.nil?
self['_id'] ||= uniqid
end
end
end
# Raise an error if validation failed.
def fail_validate!(document)
raise Errors::Validations.new(document)
end
end
end
end
end