jchris-couchrest 0.12.6 → 0.16
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/README.md +33 -8
- data/Rakefile +1 -1
- data/examples/model/example.rb +19 -13
- data/lib/couchrest.rb +27 -2
- data/lib/couchrest/core/database.rb +113 -41
- data/lib/couchrest/core/document.rb +48 -27
- data/lib/couchrest/core/response.rb +15 -0
- data/lib/couchrest/core/server.rb +47 -10
- data/lib/couchrest/mixins.rb +4 -0
- data/lib/couchrest/mixins/attachments.rb +31 -0
- data/lib/couchrest/mixins/callbacks.rb +442 -0
- data/lib/couchrest/mixins/design_doc.rb +63 -0
- data/lib/couchrest/mixins/document_queries.rb +48 -0
- data/lib/couchrest/mixins/extended_attachments.rb +68 -0
- data/lib/couchrest/mixins/extended_document_mixins.rb +6 -0
- data/lib/couchrest/mixins/properties.rb +120 -0
- data/lib/couchrest/mixins/validation.rb +234 -0
- data/lib/couchrest/mixins/views.rb +168 -0
- data/lib/couchrest/monkeypatches.rb +75 -0
- data/lib/couchrest/more/casted_model.rb +28 -0
- data/lib/couchrest/more/extended_document.rb +215 -0
- data/lib/couchrest/more/property.rb +40 -0
- data/lib/couchrest/support/blank.rb +42 -0
- data/lib/couchrest/support/class.rb +175 -0
- data/lib/couchrest/validation/auto_validate.rb +163 -0
- data/lib/couchrest/validation/contextual_validators.rb +78 -0
- data/lib/couchrest/validation/validation_errors.rb +118 -0
- data/lib/couchrest/validation/validators/absent_field_validator.rb +74 -0
- data/lib/couchrest/validation/validators/confirmation_validator.rb +99 -0
- data/lib/couchrest/validation/validators/format_validator.rb +117 -0
- data/lib/couchrest/validation/validators/formats/email.rb +66 -0
- data/lib/couchrest/validation/validators/formats/url.rb +43 -0
- data/lib/couchrest/validation/validators/generic_validator.rb +120 -0
- data/lib/couchrest/validation/validators/length_validator.rb +134 -0
- data/lib/couchrest/validation/validators/method_validator.rb +89 -0
- data/lib/couchrest/validation/validators/numeric_validator.rb +104 -0
- data/lib/couchrest/validation/validators/required_field_validator.rb +109 -0
- data/spec/couchrest/core/database_spec.rb +183 -67
- data/spec/couchrest/core/design_spec.rb +1 -1
- data/spec/couchrest/core/document_spec.rb +271 -173
- data/spec/couchrest/core/server_spec.rb +35 -0
- data/spec/couchrest/helpers/pager_spec.rb +1 -1
- data/spec/couchrest/more/casted_model_spec.rb +97 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +129 -0
- data/spec/couchrest/more/extended_doc_spec.rb +509 -0
- data/spec/couchrest/more/extended_doc_view_spec.rb +204 -0
- data/spec/couchrest/more/property_spec.rb +129 -0
- data/spec/fixtures/more/article.rb +34 -0
- data/spec/fixtures/more/card.rb +20 -0
- data/spec/fixtures/more/course.rb +14 -0
- data/spec/fixtures/more/event.rb +6 -0
- data/spec/fixtures/more/invoice.rb +17 -0
- data/spec/fixtures/more/person.rb +8 -0
- data/spec/fixtures/more/question.rb +6 -0
- data/spec/fixtures/more/service.rb +12 -0
- data/spec/spec_helper.rb +13 -7
- metadata +76 -3
- data/lib/couchrest/core/model.rb +0 -613
- data/spec/couchrest/core/model_spec.rb +0 -855
@@ -0,0 +1,168 @@
|
|
1
|
+
module CouchRest
|
2
|
+
module Mixins
|
3
|
+
module Views
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
base.send(:class_inheritable_accessor, :design_doc)
|
8
|
+
base.send(:class_inheritable_accessor, :design_doc_slug_cache)
|
9
|
+
base.send(:class_inheritable_accessor, :design_doc_fresh)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
|
14
|
+
# Define a CouchDB view. The name of the view will be the concatenation
|
15
|
+
# of <tt>by</tt> and the keys joined by <tt>_and_</tt>
|
16
|
+
#
|
17
|
+
# ==== Example views:
|
18
|
+
#
|
19
|
+
# class Post
|
20
|
+
# # view with default options
|
21
|
+
# # query with Post.by_date
|
22
|
+
# view_by :date, :descending => true
|
23
|
+
#
|
24
|
+
# # view with compound sort-keys
|
25
|
+
# # query with Post.by_user_id_and_date
|
26
|
+
# view_by :user_id, :date
|
27
|
+
#
|
28
|
+
# # view with custom map/reduce functions
|
29
|
+
# # query with Post.by_tags :reduce => true
|
30
|
+
# view_by :tags,
|
31
|
+
# :map =>
|
32
|
+
# "function(doc) {
|
33
|
+
# if (doc['couchrest-type'] == 'Post' && doc.tags) {
|
34
|
+
# doc.tags.forEach(function(tag){
|
35
|
+
# emit(doc.tag, 1);
|
36
|
+
# });
|
37
|
+
# }
|
38
|
+
# }",
|
39
|
+
# :reduce =>
|
40
|
+
# "function(keys, values, rereduce) {
|
41
|
+
# return sum(values);
|
42
|
+
# }"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# <tt>view_by :date</tt> will create a view defined by this Javascript
|
46
|
+
# function:
|
47
|
+
#
|
48
|
+
# function(doc) {
|
49
|
+
# if (doc['couchrest-type'] == 'Post' && doc.date) {
|
50
|
+
# emit(doc.date, null);
|
51
|
+
# }
|
52
|
+
# }
|
53
|
+
#
|
54
|
+
# It can be queried by calling <tt>Post.by_date</tt> which accepts all
|
55
|
+
# valid options for CouchRest::Database#view. In addition, calling with
|
56
|
+
# the <tt>:raw => true</tt> option will return the view rows
|
57
|
+
# themselves. By default <tt>Post.by_date</tt> will return the
|
58
|
+
# documents included in the generated view.
|
59
|
+
#
|
60
|
+
# CouchRest::Database#view options can be applied at view definition
|
61
|
+
# time as defaults, and they will be curried and used at view query
|
62
|
+
# time. Or they can be overridden at query time.
|
63
|
+
#
|
64
|
+
# Custom views can be queried with <tt>:reduce => true</tt> to return
|
65
|
+
# reduce results. The default for custom views is to query with
|
66
|
+
# <tt>:reduce => false</tt>.
|
67
|
+
#
|
68
|
+
# Views are generated (on a per-model basis) lazily on first-access.
|
69
|
+
# This means that if you are deploying changes to a view, the views for
|
70
|
+
# that model won't be available until generation is complete. This can
|
71
|
+
# take some time with large databases. Strategies are in the works.
|
72
|
+
#
|
73
|
+
# To understand the capabilities of this view system more compeletly,
|
74
|
+
# it is recommended that you read the RSpec file at
|
75
|
+
# <tt>spec/core/model_spec.rb</tt>.
|
76
|
+
|
77
|
+
def view_by(*keys)
|
78
|
+
self.design_doc ||= Design.new(default_design_doc)
|
79
|
+
opts = keys.pop if keys.last.is_a?(Hash)
|
80
|
+
opts ||= {}
|
81
|
+
ducktype = opts.delete(:ducktype)
|
82
|
+
unless ducktype || opts[:map]
|
83
|
+
opts[:guards] ||= []
|
84
|
+
opts[:guards].push "(doc['couchrest-type'] == '#{self.to_s}')"
|
85
|
+
end
|
86
|
+
keys.push opts
|
87
|
+
self.design_doc.view_by(*keys)
|
88
|
+
self.design_doc_fresh = false
|
89
|
+
end
|
90
|
+
|
91
|
+
# returns stored defaults if the there is a view named this in the design doc
|
92
|
+
def has_view?(view)
|
93
|
+
view = view.to_s
|
94
|
+
design_doc && design_doc['views'] && design_doc['views'][view]
|
95
|
+
end
|
96
|
+
|
97
|
+
# Dispatches to any named view.
|
98
|
+
def view(name, query={}, &block)
|
99
|
+
unless design_doc_fresh
|
100
|
+
refresh_design_doc
|
101
|
+
end
|
102
|
+
query[:raw] = true if query[:reduce]
|
103
|
+
raw = query.delete(:raw)
|
104
|
+
fetch_view_with_docs(name, query, raw, &block)
|
105
|
+
end
|
106
|
+
|
107
|
+
def all_design_doc_versions
|
108
|
+
database.documents :startkey => "_design/#{self.to_s}-",
|
109
|
+
:endkey => "_design/#{self.to_s}-\u9999"
|
110
|
+
end
|
111
|
+
|
112
|
+
# Deletes any non-current design docs that were created by this class.
|
113
|
+
# Running this when you're deployed version of your application is steadily
|
114
|
+
# and consistently using the latest code, is the way to clear out old design
|
115
|
+
# docs. Running it to early could mean that live code has to regenerate
|
116
|
+
# potentially large indexes.
|
117
|
+
def cleanup_design_docs!
|
118
|
+
ddocs = all_design_doc_versions
|
119
|
+
ddocs["rows"].each do |row|
|
120
|
+
if (row['id'] != design_doc_id)
|
121
|
+
database.delete_doc({
|
122
|
+
"_id" => row['id'],
|
123
|
+
"_rev" => row['value']['rev']
|
124
|
+
})
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def fetch_view_with_docs(name, opts, raw=false, &block)
|
132
|
+
if raw
|
133
|
+
fetch_view(name, opts, &block)
|
134
|
+
else
|
135
|
+
begin
|
136
|
+
view = fetch_view name, opts.merge({:include_docs => true}), &block
|
137
|
+
view['rows'].collect{|r|new(r['doc'])} if view['rows']
|
138
|
+
rescue
|
139
|
+
# fallback for old versions of couchdb that don't
|
140
|
+
# have include_docs support
|
141
|
+
view = fetch_view(name, opts, &block)
|
142
|
+
view['rows'].collect{|r|new(database.get(r['id']))} if view['rows']
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def fetch_view view_name, opts, &block
|
148
|
+
retryable = true
|
149
|
+
begin
|
150
|
+
design_doc.view(view_name, opts, &block)
|
151
|
+
# the design doc could have been deleted by a rouge process
|
152
|
+
rescue RestClient::ResourceNotFound => e
|
153
|
+
if retryable
|
154
|
+
refresh_design_doc
|
155
|
+
retryable = false
|
156
|
+
retry
|
157
|
+
else
|
158
|
+
raise e
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end # module ClassMethods
|
164
|
+
|
165
|
+
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
@@ -1,3 +1,6 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'support', 'class')
|
2
|
+
require File.join(File.dirname(__FILE__), 'support', 'blank')
|
3
|
+
|
1
4
|
# This file must be loaded after the JSON gem and any other library that beats up the Time class.
|
2
5
|
class Time
|
3
6
|
# This date format sorts lexicographically
|
@@ -23,6 +26,24 @@ class Time
|
|
23
26
|
# end
|
24
27
|
end
|
25
28
|
|
29
|
+
# Monkey patch for faster net/http io
|
30
|
+
if RUBY_VERSION.to_f < 1.9
|
31
|
+
class Net::BufferedIO #:nodoc:
|
32
|
+
alias :old_rbuf_fill :rbuf_fill
|
33
|
+
def rbuf_fill
|
34
|
+
begin
|
35
|
+
@rbuf << @io.read_nonblock(65536)
|
36
|
+
rescue Errno::EWOULDBLOCK
|
37
|
+
if IO.select([@io], nil, nil, @read_timeout)
|
38
|
+
@rbuf << @io.read_nonblock(65536)
|
39
|
+
else
|
40
|
+
raise Timeout::TimeoutError
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
26
47
|
module RestClient
|
27
48
|
def self.copy(url, headers={})
|
28
49
|
Request.execute(:method => :copy,
|
@@ -35,4 +56,58 @@ module RestClient
|
|
35
56
|
:url => url,
|
36
57
|
:headers => headers)
|
37
58
|
end
|
59
|
+
|
60
|
+
# class Request
|
61
|
+
#
|
62
|
+
# def establish_connection(uri)
|
63
|
+
# Thread.current[:connection].finish if (Thread.current[:connection] && Thread.current[:connection].started?)
|
64
|
+
# p net_http_class
|
65
|
+
# net = net_http_class.new(uri.host, uri.port)
|
66
|
+
# net.use_ssl = uri.is_a?(URI::HTTPS)
|
67
|
+
# net.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
68
|
+
# Thread.current[:connection] = net
|
69
|
+
# Thread.current[:connection].start
|
70
|
+
# Thread.current[:connection]
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# def transmit(uri, req, payload)
|
74
|
+
# setup_credentials(req)
|
75
|
+
#
|
76
|
+
# Thread.current[:host] ||= uri.host
|
77
|
+
# Thread.current[:port] ||= uri.port
|
78
|
+
#
|
79
|
+
# if (Thread.current[:connection].nil? || (Thread.current[:host] != uri.host))
|
80
|
+
# p "establishing a connection"
|
81
|
+
# establish_connection(uri)
|
82
|
+
# end
|
83
|
+
#
|
84
|
+
# display_log request_log
|
85
|
+
# http = Thread.current[:connection]
|
86
|
+
# http.read_timeout = @timeout if @timeout
|
87
|
+
#
|
88
|
+
# begin
|
89
|
+
# res = http.request(req, payload)
|
90
|
+
# rescue
|
91
|
+
# p "Net::HTTP connection failed, reconnecting"
|
92
|
+
# establish_connection(uri)
|
93
|
+
# http = Thread.current[:connection]
|
94
|
+
# require 'ruby-debug'
|
95
|
+
# debugger
|
96
|
+
# req.body_stream = nil
|
97
|
+
#
|
98
|
+
# res = http.request(req, payload)
|
99
|
+
# display_log response_log(res)
|
100
|
+
# result res
|
101
|
+
# else
|
102
|
+
# display_log response_log(res)
|
103
|
+
# process_result res
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# rescue EOFError
|
107
|
+
# raise RestClient::ServerBrokeConnection
|
108
|
+
# rescue Timeout::Error
|
109
|
+
# raise RestClient::RequestTimeout
|
110
|
+
# end
|
111
|
+
# end
|
112
|
+
|
38
113
|
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'mixins', 'properties')
|
2
|
+
|
3
|
+
module CouchRest
|
4
|
+
module CastedModel
|
5
|
+
|
6
|
+
def self.included(base)
|
7
|
+
base.send(:include, CouchRest::Mixins::Properties)
|
8
|
+
base.send(:attr_accessor, :casted_by)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(keys={})
|
12
|
+
super
|
13
|
+
keys.each do |k,v|
|
14
|
+
self[k.to_s] = v
|
15
|
+
end if keys
|
16
|
+
apply_defaults # defined in CouchRest::Mixins::Properties
|
17
|
+
# cast_keys # defined in CouchRest::Mixins::Properties
|
18
|
+
end
|
19
|
+
|
20
|
+
def []= key, value
|
21
|
+
super(key.to_s, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
def [] key
|
25
|
+
super(key.to_s)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
begin
|
2
|
+
# still required for Time parsing and pluralization in the validation
|
3
|
+
require 'extlib'
|
4
|
+
rescue
|
5
|
+
puts "CouchRest::ExtendedDocument still requires extlib (not for much longer). This is left out of the gemspec on purpose."
|
6
|
+
raise
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'mime/types'
|
10
|
+
require File.join(File.dirname(__FILE__), "property")
|
11
|
+
require File.join(File.dirname(__FILE__), '..', 'mixins', 'extended_document_mixins')
|
12
|
+
|
13
|
+
module CouchRest
|
14
|
+
|
15
|
+
# Same as CouchRest::Document but with properties and validations
|
16
|
+
class ExtendedDocument < Document
|
17
|
+
include CouchRest::Callbacks
|
18
|
+
include CouchRest::Mixins::DocumentQueries
|
19
|
+
include CouchRest::Mixins::Views
|
20
|
+
include CouchRest::Mixins::DesignDoc
|
21
|
+
include CouchRest::Mixins::ExtendedAttachments
|
22
|
+
|
23
|
+
def self.inherited(subklass)
|
24
|
+
subklass.send(:include, CouchRest::Mixins::Properties)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Callbacks
|
28
|
+
define_callbacks :create
|
29
|
+
define_callbacks :save
|
30
|
+
define_callbacks :update
|
31
|
+
define_callbacks :destroy
|
32
|
+
|
33
|
+
def initialize(keys={})
|
34
|
+
apply_defaults # defined in CouchRest::Mixins::Properties
|
35
|
+
keys ||= {}
|
36
|
+
super
|
37
|
+
cast_keys # defined in CouchRest::Mixins::Properties
|
38
|
+
unless self['_id'] && self['_rev']
|
39
|
+
self['couchrest-type'] = self.class.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# Automatically set <tt>updated_at</tt> and <tt>created_at</tt> fields
|
45
|
+
# on the document whenever saving occurs. CouchRest uses a pretty
|
46
|
+
# decent time format by default. See Time#to_json
|
47
|
+
def self.timestamps!
|
48
|
+
class_eval <<-EOS, __FILE__, __LINE__
|
49
|
+
property(:updated_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
|
50
|
+
property(:created_at, :read_only => true, :cast_as => 'Time', :auto_validation => false)
|
51
|
+
|
52
|
+
save_callback :before do |object|
|
53
|
+
object['updated_at'] = Time.now
|
54
|
+
object['created_at'] = object['updated_at'] if object.new_document?
|
55
|
+
end
|
56
|
+
EOS
|
57
|
+
end
|
58
|
+
|
59
|
+
# Name a method that will be called before the document is first saved,
|
60
|
+
# which returns a string to be used for the document's <tt>_id</tt>.
|
61
|
+
# Because CouchDB enforces a constraint that each id must be unique,
|
62
|
+
# this can be used to enforce eg: uniq usernames. Note that this id
|
63
|
+
# must be globally unique across all document types which share a
|
64
|
+
# database, so if you'd like to scope uniqueness to this class, you
|
65
|
+
# should use the class name as part of the unique id.
|
66
|
+
def self.unique_id method = nil, &block
|
67
|
+
if method
|
68
|
+
define_method :set_unique_id do
|
69
|
+
self['_id'] ||= self.send(method)
|
70
|
+
end
|
71
|
+
elsif block
|
72
|
+
define_method :set_unique_id do
|
73
|
+
uniqid = block.call(self)
|
74
|
+
raise ArgumentError, "unique_id block must not return nil" if uniqid.nil?
|
75
|
+
self['_id'] ||= uniqid
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Temp solution to make the view_by methods available
|
81
|
+
def self.method_missing(m, *args)
|
82
|
+
if has_view?(m)
|
83
|
+
query = args.shift || {}
|
84
|
+
view(m, query, *args)
|
85
|
+
else
|
86
|
+
super
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
### instance methods
|
91
|
+
|
92
|
+
# Returns the Class properties
|
93
|
+
#
|
94
|
+
# ==== Returns
|
95
|
+
# Array:: the list of properties for the instance
|
96
|
+
def properties
|
97
|
+
self.class.properties
|
98
|
+
end
|
99
|
+
|
100
|
+
# Takes a hash as argument, and applies the values by using writer methods
|
101
|
+
# for each key. It doesn't save the document at the end. Raises a NoMethodError if the corresponding methods are
|
102
|
+
# missing. In case of error, no attributes are changed.
|
103
|
+
def update_attributes_without_saving(hash)
|
104
|
+
hash.each do |k, v|
|
105
|
+
raise NoMethodError, "#{k}= method not available, use property :#{k}" unless self.respond_to?("#{k}=")
|
106
|
+
end
|
107
|
+
hash.each do |k, v|
|
108
|
+
self.send("#{k}=",v)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Takes a hash as argument, and applies the values by using writer methods
|
113
|
+
# for each key. Raises a NoMethodError if the corresponding methods are
|
114
|
+
# missing. In case of error, no attributes are changed.
|
115
|
+
def update_attributes(hash)
|
116
|
+
update_attributes_without_saving hash
|
117
|
+
save
|
118
|
+
end
|
119
|
+
|
120
|
+
# for compatibility with old-school frameworks
|
121
|
+
alias :new_record? :new_document?
|
122
|
+
|
123
|
+
# Trigger the callbacks (before, after, around)
|
124
|
+
# and create the document
|
125
|
+
# It's important to have a create callback since you can't check if a document
|
126
|
+
# was new after you saved it
|
127
|
+
#
|
128
|
+
# When creating a document, both the create and the save callbacks will be triggered.
|
129
|
+
def create(bulk = false)
|
130
|
+
caught = catch(:halt) do
|
131
|
+
_run_create_callbacks do
|
132
|
+
_run_save_callbacks do
|
133
|
+
create_without_callbacks(bulk)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# unlike save, create returns the newly created document
|
140
|
+
def create_without_callbacks(bulk =false)
|
141
|
+
raise ArgumentError, "a document requires a database to be created to (The document or the #{self.class} default database were not set)" unless database
|
142
|
+
set_unique_id if new_document? && self.respond_to?(:set_unique_id)
|
143
|
+
result = database.save_doc(self, bulk)
|
144
|
+
(result["ok"] == true) ? self : false
|
145
|
+
end
|
146
|
+
|
147
|
+
# Creates the document in the db. Raises an exception
|
148
|
+
# if the document is not created properly.
|
149
|
+
def create!
|
150
|
+
raise "#{self.inspect} failed to save" unless self.create
|
151
|
+
end
|
152
|
+
|
153
|
+
# Trigger the callbacks (before, after, around)
|
154
|
+
# only if the document isn't new
|
155
|
+
def update(bulk = false)
|
156
|
+
caught = catch(:halt) do
|
157
|
+
if self.new_document?
|
158
|
+
save(bulk)
|
159
|
+
else
|
160
|
+
_run_update_callbacks do
|
161
|
+
_run_save_callbacks do
|
162
|
+
save_without_callbacks(bulk)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
# Trigger the callbacks (before, after, around)
|
170
|
+
# and save the document
|
171
|
+
def save(bulk = false)
|
172
|
+
caught = catch(:halt) do
|
173
|
+
if self.new_document?
|
174
|
+
_run_save_callbacks do
|
175
|
+
save_without_callbacks(bulk)
|
176
|
+
end
|
177
|
+
else
|
178
|
+
update(bulk)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Overridden to set the unique ID.
|
184
|
+
# Returns a boolean value
|
185
|
+
def save_without_callbacks(bulk = false)
|
186
|
+
raise ArgumentError, "a document requires a database to be saved to (The document or the #{self.class} default database were not set)" unless database
|
187
|
+
set_unique_id if new_document? && self.respond_to?(:set_unique_id)
|
188
|
+
result = database.save_doc(self, bulk)
|
189
|
+
result["ok"] == true
|
190
|
+
end
|
191
|
+
|
192
|
+
# Saves the document to the db using save. Raises an exception
|
193
|
+
# if the document is not saved properly.
|
194
|
+
def save!
|
195
|
+
raise "#{self.inspect} failed to save" unless self.save
|
196
|
+
end
|
197
|
+
|
198
|
+
# Deletes the document from the database. Runs the :destroy callbacks.
|
199
|
+
# Removes the <tt>_id</tt> and <tt>_rev</tt> fields, preparing the
|
200
|
+
# document to be saved to a new <tt>_id</tt>.
|
201
|
+
def destroy(bulk=false)
|
202
|
+
caught = catch(:halt) do
|
203
|
+
_run_destroy_callbacks do
|
204
|
+
result = database.delete_doc(self, bulk)
|
205
|
+
if result['ok']
|
206
|
+
self['_rev'] = nil
|
207
|
+
self['_id'] = nil
|
208
|
+
end
|
209
|
+
result['ok']
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
end
|