mattetti-couchrest 0.33 → 0.34
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 +29 -10
- data/Rakefile +1 -0
- data/THANKS.md +1 -0
- data/lib/couchrest.rb +2 -1
- data/lib/couchrest/core/database.rb +0 -18
- data/lib/couchrest/core/document.rb +3 -2
- data/lib/couchrest/mixins/callbacks.rb +187 -138
- data/lib/couchrest/mixins/collection.rb +3 -16
- data/lib/couchrest/mixins/extended_attachments.rb +1 -1
- data/lib/couchrest/mixins/properties.rb +71 -44
- data/lib/couchrest/mixins/validation.rb +18 -29
- data/lib/couchrest/more/casted_model.rb +29 -1
- data/lib/couchrest/more/extended_document.rb +56 -17
- data/lib/couchrest/more/property.rb +19 -0
- data/lib/couchrest/support/class.rb +81 -67
- data/lib/couchrest/support/rails.rb +12 -5
- data/spec/couchrest/core/database_spec.rb +2 -2
- data/spec/couchrest/more/casted_extended_doc_spec.rb +2 -4
- data/spec/couchrest/more/casted_model_spec.rb +229 -0
- data/spec/couchrest/more/extended_doc_attachment_spec.rb +2 -2
- data/spec/couchrest/more/extended_doc_spec.rb +167 -16
- data/spec/couchrest/more/extended_doc_view_spec.rb +17 -6
- data/spec/couchrest/more/property_spec.rb +71 -3
- data/spec/fixtures/more/article.rb +2 -2
- data/spec/fixtures/more/cat.rb +1 -0
- data/spec/fixtures/more/event.rb +3 -0
- data/spec/fixtures/more/person.rb +1 -0
- metadata +3 -3
data/README.md
CHANGED
@@ -103,27 +103,45 @@ Check spec/couchrest/more and spec/fixtures/more for more examples
|
|
103
103
|
save_callback :before, :generate_slug_from_title
|
104
104
|
|
105
105
|
def generate_slug_from_title
|
106
|
-
self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if
|
106
|
+
self['slug'] = title.downcase.gsub(/[^a-z0-9]/,'-').squeeze('-').gsub(/^\-|\-$/,'') if new?
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
110
|
### Callbacks
|
111
111
|
|
112
|
-
`CouchRest::ExtendedDocuments` instances have
|
113
|
-
|
112
|
+
`CouchRest::ExtendedDocuments` instances have 4 callbacks already defined for you:
|
113
|
+
`:validate`, `:create`, `:save`, `:update` and `:destroy`
|
114
114
|
|
115
|
-
|
115
|
+
`CouchRest::CastedModel` instances have 1 callback already defined for you:
|
116
|
+
`:validate`
|
117
|
+
|
118
|
+
Define your callback as follows:
|
116
119
|
|
117
|
-
|
120
|
+
set_callback :save, :before, :generate_slug_from_name
|
118
121
|
|
119
122
|
CouchRest uses a mixin you can find in lib/mixins/callbacks which is extracted from Rails 3, here are some simple usage examples:
|
120
123
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
+
set_callback :save, :before, :before_method
|
125
|
+
set_callback :save, :after, :after_method, :if => :condition
|
126
|
+
set_callback :save, :around {|r| stuff; yield; stuff }
|
127
|
+
|
128
|
+
Or the aliased short version:
|
129
|
+
|
130
|
+
before_save :before_method, :another_method
|
131
|
+
after_save :after_method, :another_method, :if => :condition
|
132
|
+
around_save {|r| stuff; yield; stuff }
|
133
|
+
|
134
|
+
To halt the callback, simply return a :halt symbol in your callback method.
|
124
135
|
|
125
136
|
Check the mixin or the ExtendedDocument class to see how to implement your own callbacks.
|
126
137
|
|
138
|
+
### Properties
|
139
|
+
|
140
|
+
property :last_name, :alias => :family_name
|
141
|
+
property :read_only_value, :read_only => true
|
142
|
+
property :name, :length => 4...20
|
143
|
+
property :price, :type => Integer
|
144
|
+
|
127
145
|
### Casting
|
128
146
|
|
129
147
|
Often, you will want to store multiple objects within a document, to be able to retrieve your objects when you load the document,
|
@@ -131,6 +149,8 @@ you can define some casting rules.
|
|
131
149
|
|
132
150
|
property :casted_attribute, :cast_as => 'WithCastedModelMixin'
|
133
151
|
property :keywords, :cast_as => ["String"]
|
152
|
+
property :occurs_at, :cast_as => 'Time', :send => 'parse
|
153
|
+
property :end_date, :cast_as => 'Date', :send => 'parse
|
134
154
|
|
135
155
|
If you want to cast an array of instances from a specific Class, use the trick shown above ["ClassName"]
|
136
156
|
|
@@ -157,9 +177,8 @@ Low level usage:
|
|
157
177
|
Article.paginate(:design_doc => 'Article', :view_name => 'by_date',
|
158
178
|
:per_page => 3, :page => 2, :descending => true, :key => Date.today, :include_docs => true)
|
159
179
|
|
160
|
-
|
161
180
|
## Ruby on Rails
|
162
181
|
|
163
182
|
CouchRest is compatible with rails and can even be used a Rails plugin.
|
164
183
|
However, you might be interested in the CouchRest companion rails project:
|
165
|
-
[http://github.com/hpoydar/couchrest-rails](http://github.com/hpoydar/couchrest-rails)
|
184
|
+
[http://github.com/hpoydar/couchrest-rails](http://github.com/hpoydar/couchrest-rails)
|
data/Rakefile
CHANGED
data/THANKS.md
CHANGED
@@ -12,6 +12,7 @@ changes. A list of these people is included below.
|
|
12
12
|
* [Jonathan S. Katz](http://github.com/jkatz)
|
13
13
|
* [Matt Lyon](http://mattly.tumblr.com/)
|
14
14
|
* Simon Rozet (simon /at/ rozet /dot/ name)
|
15
|
+
* [Marcos Tapajós](http://tapajos.me)
|
15
16
|
|
16
17
|
Patches are welcome. The primary source for this software project is [on Github](http://github.com/jchris/couchrest/tree/master)
|
17
18
|
|
data/lib/couchrest.rb
CHANGED
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
|
|
28
28
|
|
29
29
|
# = CouchDB, close to the metal
|
30
30
|
module CouchRest
|
31
|
-
VERSION = '0.
|
31
|
+
VERSION = '0.34' unless self.const_defined?("VERSION")
|
32
32
|
|
33
33
|
autoload :Server, 'couchrest/core/server'
|
34
34
|
autoload :Database, 'couchrest/core/database'
|
@@ -48,6 +48,7 @@ module CouchRest
|
|
48
48
|
require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'rest_api')
|
49
49
|
require File.join(File.dirname(__FILE__), 'couchrest', 'core', 'http_abstraction')
|
50
50
|
require File.join(File.dirname(__FILE__), 'couchrest', 'mixins')
|
51
|
+
require File.join(File.dirname(__FILE__), 'couchrest', 'support', 'rails') if defined?(Rails)
|
51
52
|
|
52
53
|
# we extend CouchRest with the RestAPI module which gives us acess to
|
53
54
|
# the get, post, put, delete and copy
|
@@ -175,12 +175,6 @@ module CouchRest
|
|
175
175
|
result
|
176
176
|
end
|
177
177
|
|
178
|
-
### DEPRECATION NOTICE
|
179
|
-
def save(doc, bulk=false)
|
180
|
-
puts "CouchRest::Database's save method is being deprecated, please use save_doc instead"
|
181
|
-
save_doc(doc, bulk)
|
182
|
-
end
|
183
|
-
|
184
178
|
|
185
179
|
# POST an array of documents to CouchDB. If any of the documents are
|
186
180
|
# missing ids, supply one from the uuid cache.
|
@@ -219,12 +213,6 @@ module CouchRest
|
|
219
213
|
CouchRest.delete "#{@root}/#{slug}?rev=#{doc['_rev']}"
|
220
214
|
end
|
221
215
|
|
222
|
-
### DEPRECATION NOTICE
|
223
|
-
def delete(doc, bulk=false)
|
224
|
-
puts "CouchRest::Database's delete method is being deprecated, please use delete_doc instead"
|
225
|
-
delete_doc(doc, bulk)
|
226
|
-
end
|
227
|
-
|
228
216
|
# COPY an existing document to a new id. If the destination id currently exists, a rev must be provided.
|
229
217
|
# <tt>dest</tt> can take one of two forms if overwriting: "id_to_overwrite?rev=revision" or the actual doc
|
230
218
|
# hash with a '_rev' key
|
@@ -239,12 +227,6 @@ module CouchRest
|
|
239
227
|
CouchRest.copy "#{@root}/#{slug}", destination
|
240
228
|
end
|
241
229
|
|
242
|
-
### DEPRECATION NOTICE
|
243
|
-
def copy(doc, dest)
|
244
|
-
puts "CouchRest::Database's copy method is being deprecated, please use copy_doc instead"
|
245
|
-
copy_doc(doc, dest)
|
246
|
-
end
|
247
|
-
|
248
230
|
# Compact the database, removing old document revisions and optimizing space use.
|
249
231
|
def compact!
|
250
232
|
CouchRest.post "#{@root}/_compact"
|
@@ -23,9 +23,10 @@ module CouchRest
|
|
23
23
|
end
|
24
24
|
|
25
25
|
# returns true if the document has never been saved
|
26
|
-
def
|
26
|
+
def new?
|
27
27
|
!rev
|
28
28
|
end
|
29
|
+
alias :new_document? :new?
|
29
30
|
|
30
31
|
# Saves the document to the db using create or update. Also runs the :save
|
31
32
|
# callbacks. Sets the <tt>_id</tt> and <tt>_rev</tt> fields based on
|
@@ -63,7 +64,7 @@ module CouchRest
|
|
63
64
|
|
64
65
|
# Returns the CouchDB uri for the document
|
65
66
|
def uri(append_rev = false)
|
66
|
-
return nil if
|
67
|
+
return nil if new?
|
67
68
|
couch_uri = "http://#{database.root}/#{CGI.escape(id)}"
|
68
69
|
if append_rev == true
|
69
70
|
couch_uri << "?rev=#{rev}"
|
@@ -1,8 +1,29 @@
|
|
1
|
-
|
1
|
+
# Copyright (c) 2006-2009 David Heinemeier Hansson
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
#
|
22
|
+
# Extracted from ActiveSupport::NewCallbacks written by Yehuda Katz
|
23
|
+
# http://github.com/rails/rails/raw/d6e4113c83a9d55be6f2af247da2cecaa855f43b/activesupport/lib/active_support/new_callbacks.rb
|
24
|
+
# http://github.com/rails/rails/commit/1126a85aed576402d978e6f76eb393b6baaa9541
|
2
25
|
|
3
|
-
|
4
|
-
# http://github.com/wycats/rails/raw/abstract_controller/activesupport/lib/active_support/new_callbacks.rb
|
5
|
-
# http://github.com/wycats/rails/raw/18b405f154868204a8f332888871041a7bad95e1/activesupport/lib/active_support/callbacks.rb
|
26
|
+
require File.join(File.dirname(__FILE__), '..', 'support', 'class')
|
6
27
|
|
7
28
|
module CouchRest
|
8
29
|
# Callbacks are hooks into the lifecycle of an object that allow you to trigger logic
|
@@ -85,19 +106,18 @@ module CouchRest
|
|
85
106
|
def self.included(klass)
|
86
107
|
klass.extend ClassMethods
|
87
108
|
end
|
88
|
-
|
109
|
+
|
89
110
|
def run_callbacks(kind, options = {}, &blk)
|
90
111
|
send("_run_#{kind}_callbacks", &blk)
|
91
112
|
end
|
92
|
-
|
113
|
+
|
93
114
|
class Callback
|
94
115
|
@@_callback_sequence = 0
|
95
|
-
|
116
|
+
|
96
117
|
attr_accessor :filter, :kind, :name, :options, :per_key, :klass
|
97
|
-
def initialize(filter, kind, options, klass
|
118
|
+
def initialize(filter, kind, options, klass)
|
98
119
|
@kind, @klass = kind, klass
|
99
|
-
|
100
|
-
|
120
|
+
|
101
121
|
normalize_options!(options)
|
102
122
|
|
103
123
|
@per_key = options.delete(:per_key)
|
@@ -108,7 +128,7 @@ module CouchRest
|
|
108
128
|
|
109
129
|
_compile_per_key_options
|
110
130
|
end
|
111
|
-
|
131
|
+
|
112
132
|
def clone(klass)
|
113
133
|
obj = super()
|
114
134
|
obj.klass = klass
|
@@ -120,23 +140,22 @@ module CouchRest
|
|
120
140
|
obj.options[:unless] = @options[:unless].dup
|
121
141
|
obj
|
122
142
|
end
|
123
|
-
|
143
|
+
|
124
144
|
def normalize_options!(options)
|
125
|
-
options[:if] = Array(options[:if])
|
126
|
-
options[:unless] = Array(options[:unless])
|
145
|
+
options[:if] = Array.wrap(options[:if])
|
146
|
+
options[:unless] = Array.wrap(options[:unless])
|
127
147
|
|
128
148
|
options[:per_key] ||= {}
|
129
|
-
options[:per_key][:if] = Array(options[:per_key][:if])
|
130
|
-
options[:per_key][:unless] = Array(options[:per_key][:unless])
|
149
|
+
options[:per_key][:if] = Array.wrap(options[:per_key][:if])
|
150
|
+
options[:per_key][:unless] = Array.wrap(options[:per_key][:unless])
|
131
151
|
end
|
132
|
-
|
152
|
+
|
133
153
|
def next_id
|
134
154
|
@@_callback_sequence += 1
|
135
155
|
end
|
136
|
-
|
137
|
-
def matches?(_kind,
|
156
|
+
|
157
|
+
def matches?(_kind, _filter)
|
138
158
|
@kind == _kind &&
|
139
|
-
@name == _name &&
|
140
159
|
@filter == _filter
|
141
160
|
end
|
142
161
|
|
@@ -144,11 +163,11 @@ module CouchRest
|
|
144
163
|
filter_options[:if].push(new_options[:unless]) if new_options.key?(:unless)
|
145
164
|
filter_options[:unless].push(new_options[:if]) if new_options.key?(:if)
|
146
165
|
end
|
147
|
-
|
166
|
+
|
148
167
|
def recompile!(_options, _per_key)
|
149
168
|
_update_filter(self.options, _options)
|
150
169
|
_update_filter(self.per_key, _per_key)
|
151
|
-
|
170
|
+
|
152
171
|
@callback_id = next_id
|
153
172
|
@filter = _compile_filter(@raw_filter)
|
154
173
|
@compiled_options = _compile_options(@options)
|
@@ -164,19 +183,19 @@ module CouchRest
|
|
164
183
|
end
|
165
184
|
RUBY_EVAL
|
166
185
|
end
|
167
|
-
|
186
|
+
|
168
187
|
# This will supply contents for before and around filters, and no
|
169
188
|
# contents for after filters (for the forward pass).
|
170
189
|
def start(key = nil, options = {})
|
171
190
|
object, terminator = (options || {}).values_at(:object, :terminator)
|
172
|
-
|
191
|
+
|
173
192
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
174
|
-
|
193
|
+
|
175
194
|
terminator ||= false
|
176
|
-
|
195
|
+
|
177
196
|
# options[0] is the compiled form of supplied conditions
|
178
197
|
# options[1] is the "end" for the conditional
|
179
|
-
|
198
|
+
|
180
199
|
if @kind == :before || @kind == :around
|
181
200
|
if @kind == :before
|
182
201
|
# if condition # before_save :filter_name, :if => :condition
|
@@ -185,9 +204,10 @@ module CouchRest
|
|
185
204
|
filter = <<-RUBY_EVAL
|
186
205
|
unless halted
|
187
206
|
result = #{@filter}
|
188
|
-
halted
|
207
|
+
halted = (#{terminator})
|
189
208
|
end
|
190
209
|
RUBY_EVAL
|
210
|
+
|
191
211
|
[@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
|
192
212
|
else
|
193
213
|
# Compile around filters with conditions into proxy methods
|
@@ -204,9 +224,9 @@ module CouchRest
|
|
204
224
|
# yield self
|
205
225
|
# end
|
206
226
|
# end
|
207
|
-
|
227
|
+
|
208
228
|
name = "_conditional_callback_#{@kind}_#{next_id}"
|
209
|
-
txt = <<-RUBY_EVAL
|
229
|
+
txt, line = <<-RUBY_EVAL, __LINE__ + 1
|
210
230
|
def #{name}(halted)
|
211
231
|
#{@compiled_options[0] || "if true"} && !halted
|
212
232
|
#{@filter} do
|
@@ -217,19 +237,19 @@ module CouchRest
|
|
217
237
|
end
|
218
238
|
end
|
219
239
|
RUBY_EVAL
|
220
|
-
@klass.class_eval(txt)
|
240
|
+
@klass.class_eval(txt, __FILE__, line)
|
221
241
|
"#{name}(halted) do"
|
222
242
|
end
|
223
243
|
end
|
224
244
|
end
|
225
|
-
|
245
|
+
|
226
246
|
# This will supply contents for around and after filters, but not
|
227
247
|
# before filters (for the backward pass).
|
228
248
|
def end(key = nil, options = {})
|
229
249
|
object = (options || {})[:object]
|
230
|
-
|
250
|
+
|
231
251
|
return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
|
232
|
-
|
252
|
+
|
233
253
|
if @kind == :around || @kind == :after
|
234
254
|
# if condition # after_save :filter_name, :if => :condition
|
235
255
|
# filter_name
|
@@ -241,27 +261,27 @@ module CouchRest
|
|
241
261
|
end
|
242
262
|
end
|
243
263
|
end
|
244
|
-
|
264
|
+
|
245
265
|
private
|
246
266
|
# Options support the same options as filters themselves (and support
|
247
267
|
# symbols, string, procs, and objects), so compile a conditional
|
248
268
|
# expression based on the options
|
249
269
|
def _compile_options(options)
|
250
270
|
return [] if options[:if].empty? && options[:unless].empty?
|
251
|
-
|
271
|
+
|
252
272
|
conditions = []
|
253
|
-
|
273
|
+
|
254
274
|
unless options[:if].empty?
|
255
|
-
conditions << Array(_compile_filter(options[:if]))
|
275
|
+
conditions << Array.wrap(_compile_filter(options[:if]))
|
256
276
|
end
|
257
|
-
|
277
|
+
|
258
278
|
unless options[:unless].empty?
|
259
|
-
conditions << Array(_compile_filter(options[:unless])).map {|f| "!#{f}"}
|
279
|
+
conditions << Array.wrap(_compile_filter(options[:unless])).map {|f| "!#{f}"}
|
260
280
|
end
|
261
|
-
|
281
|
+
|
262
282
|
["if #{conditions.flatten.join(" && ")}", "end"]
|
263
283
|
end
|
264
|
-
|
284
|
+
|
265
285
|
# Filters support:
|
266
286
|
# Arrays:: Used in conditions. This is used to specify
|
267
287
|
# multiple conditions. Used internally to
|
@@ -287,63 +307,72 @@ module CouchRest
|
|
287
307
|
filter.map {|f| _compile_filter(f)}
|
288
308
|
when Symbol
|
289
309
|
filter
|
310
|
+
when String
|
311
|
+
"(#{filter})"
|
290
312
|
when Proc
|
291
313
|
@klass.send(:define_method, method_name, &filter)
|
292
|
-
method_name
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
314
|
+
return method_name if filter.arity == 0
|
315
|
+
|
316
|
+
method_name << (filter.arity == 1 ? "(self)" : " self, Proc.new ")
|
317
|
+
else
|
318
|
+
@klass.send(:define_method, "#{method_name}_object") { filter }
|
319
|
+
|
320
|
+
_normalize_legacy_filter(kind, filter)
|
321
|
+
|
322
|
+
@klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
323
|
+
def #{method_name}(&blk)
|
324
|
+
#{method_name}_object.send(:#{kind}, self, &blk)
|
297
325
|
end
|
298
326
|
RUBY_EVAL
|
299
|
-
|
300
|
-
else
|
301
|
-
kind, name = @kind, @name
|
302
|
-
@klass.send(:define_method, method_name) do
|
303
|
-
filter.send("#{kind}_#{name}", self)
|
304
|
-
end
|
327
|
+
|
305
328
|
method_name
|
306
329
|
end
|
307
330
|
end
|
308
|
-
end
|
309
331
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
332
|
+
def _normalize_legacy_filter(kind, filter)
|
333
|
+
if !filter.respond_to?(kind) && filter.respond_to?(:filter)
|
334
|
+
filter.class_eval(
|
335
|
+
"def #{kind}(context, &block) filter(context, &block) end",
|
336
|
+
__FILE__, __LINE__ - 1)
|
337
|
+
elsif filter.respond_to?(:before) && filter.respond_to?(:after) && kind == :around
|
338
|
+
def filter.around(context)
|
339
|
+
should_continue = before(context)
|
340
|
+
yield if should_continue
|
341
|
+
after(context)
|
342
|
+
end
|
343
|
+
end
|
315
344
|
end
|
316
|
-
|
345
|
+
|
317
346
|
end
|
318
|
-
|
347
|
+
|
319
348
|
# An Array with a compile method
|
320
349
|
class CallbackChain < Array
|
321
350
|
def initialize(symbol)
|
322
351
|
@symbol = symbol
|
323
352
|
end
|
324
|
-
|
353
|
+
|
325
354
|
def compile(key = nil, options = {})
|
326
355
|
method = []
|
327
356
|
method << "halted = false"
|
328
357
|
each do |callback|
|
329
358
|
method << callback.start(key, options)
|
330
359
|
end
|
331
|
-
method << "yield self if block_given?"
|
360
|
+
method << "yield self if block_given? && !halted"
|
332
361
|
reverse_each do |callback|
|
333
362
|
method << callback.end(key, options)
|
334
363
|
end
|
335
364
|
method.compact.join("\n")
|
336
365
|
end
|
337
|
-
|
366
|
+
|
338
367
|
def clone(klass)
|
339
368
|
chain = CallbackChain.new(@symbol)
|
340
369
|
chain.push(*map {|c| c.clone(klass)})
|
341
370
|
end
|
342
371
|
end
|
343
|
-
|
372
|
+
|
344
373
|
module ClassMethods
|
345
|
-
CHAINS = {:before => :before, :around => :before, :after => :after}
|
346
|
-
|
374
|
+
#CHAINS = {:before => :before, :around => :before, :after => :after}
|
375
|
+
|
347
376
|
# Make the _run_save_callbacks method. The generated method takes
|
348
377
|
# a block that it'll yield to. It'll call the before and around filters
|
349
378
|
# in order, yield the block, and then run the after filters.
|
@@ -355,43 +384,45 @@ module CouchRest
|
|
355
384
|
# The _run_save_callbacks method can optionally take a key, which
|
356
385
|
# will be used to compile an optimized callback method for each
|
357
386
|
# key. See #define_callbacks for more information.
|
358
|
-
def _define_runner(symbol
|
359
|
-
|
360
|
-
|
387
|
+
def _define_runner(symbol)
|
388
|
+
body = send("_#{symbol}_callback").
|
389
|
+
compile(nil, :terminator => send("_#{symbol}_terminator"))
|
390
|
+
|
391
|
+
body, line = <<-RUBY_EVAL, __LINE__ + 1
|
392
|
+
def _run_#{symbol}_callbacks(key = nil, &blk)
|
361
393
|
if key
|
362
|
-
|
394
|
+
name = "_run__\#{self.class.name.hash.abs}__#{symbol}__\#{key.hash.abs}__callbacks"
|
395
|
+
|
396
|
+
unless respond_to?(name)
|
397
|
+
self.class._create_keyed_callback(name, :#{symbol}, self, &blk)
|
398
|
+
end
|
399
|
+
|
400
|
+
send(name, &blk)
|
363
401
|
else
|
364
|
-
#{
|
402
|
+
#{body}
|
365
403
|
end
|
366
404
|
end
|
367
405
|
RUBY_EVAL
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
before_name, around_name, after_name =
|
372
|
-
options.values_at(:before, :after, :around)
|
406
|
+
|
407
|
+
undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
|
408
|
+
class_eval body, __FILE__, line
|
373
409
|
end
|
374
|
-
|
410
|
+
|
375
411
|
# This is called the first time a callback is called with a particular
|
376
412
|
# key. It creates a new callback method for the key, calculating
|
377
413
|
# which callbacks can be omitted because of per_key conditions.
|
378
|
-
def
|
414
|
+
def _create_keyed_callback(name, kind, obj, &blk)
|
379
415
|
@_keyed_callbacks ||= {}
|
380
|
-
@_keyed_callbacks[
|
381
|
-
str =
|
416
|
+
@_keyed_callbacks[name] ||= begin
|
417
|
+
str = send("_#{kind}_callback").
|
418
|
+
compile(name, :object => obj, :terminator => send("_#{kind}_terminator"))
|
419
|
+
|
420
|
+
class_eval "def #{name}() #{str} end", __FILE__, __LINE__
|
382
421
|
|
383
|
-
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
384
|
-
def _run__#{klass.split("::").last}__#{kind}__#{key}__callbacks
|
385
|
-
#{str}
|
386
|
-
end
|
387
|
-
RUBY_EVAL
|
388
|
-
|
389
422
|
true
|
390
423
|
end
|
391
|
-
|
392
|
-
obj.send("_run__#{klass.split("::").last}__#{kind}__#{key}__callbacks", &blk)
|
393
424
|
end
|
394
|
-
|
425
|
+
|
395
426
|
# Define callbacks.
|
396
427
|
#
|
397
428
|
# Creates a <name>_callback method that you can use to add callbacks.
|
@@ -423,59 +454,77 @@ module CouchRest
|
|
423
454
|
# In that case, each action_name would get its own compiled callback
|
424
455
|
# method that took into consideration the per_key conditions. This
|
425
456
|
# is a speed improvement for ActionPack.
|
457
|
+
def _update_callbacks(name, filters = CallbackChain.new(name), block = nil)
|
458
|
+
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
459
|
+
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
460
|
+
filters.unshift(block) if block
|
461
|
+
|
462
|
+
callbacks = send("_#{name}_callback")
|
463
|
+
yield callbacks, type, filters, options if block_given?
|
464
|
+
|
465
|
+
_define_runner(name)
|
466
|
+
end
|
467
|
+
|
468
|
+
alias_method :_reset_callbacks, :_update_callbacks
|
469
|
+
|
470
|
+
def set_callback(name, *filters, &block)
|
471
|
+
_update_callbacks(name, filters, block) do |callbacks, type, filters, options|
|
472
|
+
filters.map! do |filter|
|
473
|
+
# overrides parent class
|
474
|
+
callbacks.delete_if {|c| c.matches?(type, filter) }
|
475
|
+
Callback.new(filter, type, options.dup, self)
|
476
|
+
end
|
477
|
+
|
478
|
+
options[:prepend] ? callbacks.unshift(*filters) : callbacks.push(*filters)
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
def skip_callback(name, *filters, &block)
|
483
|
+
_update_callbacks(name, filters, block) do |callbacks, type, filters, options|
|
484
|
+
filters.each do |filter|
|
485
|
+
callbacks = send("_#{name}_callback=", callbacks.clone(self))
|
486
|
+
|
487
|
+
filter = callbacks.find {|c| c.matches?(type, filter) }
|
488
|
+
|
489
|
+
if filter && options.any?
|
490
|
+
filter.recompile!(options, options[:per_key] || {})
|
491
|
+
else
|
492
|
+
callbacks.delete(filter)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
426
498
|
def define_callbacks(*symbols)
|
427
499
|
terminator = symbols.pop if symbols.last.is_a?(String)
|
428
500
|
symbols.each do |symbol|
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
end
|
445
|
-
self._#{symbol}_callbacks.push(*filters)
|
446
|
-
_define_runner(:#{symbol},
|
447
|
-
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
448
|
-
options)
|
449
|
-
end
|
450
|
-
|
451
|
-
def self.skip_#{symbol}_callback(*filters, &blk)
|
452
|
-
type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
|
453
|
-
options = filters.last.is_a?(Hash) ? filters.pop : {}
|
454
|
-
filters.unshift(blk) if block_given?
|
455
|
-
filters.each do |filter|
|
456
|
-
self._#{symbol}_callbacks = self._#{symbol}_callbacks.clone(self)
|
457
|
-
|
458
|
-
filter = self._#{symbol}_callbacks.find {|c| c.matches?(type, :#{symbol}, filter) }
|
459
|
-
per_key = options[:per_key] || {}
|
460
|
-
if filter
|
461
|
-
filter.recompile!(options, per_key)
|
462
|
-
else
|
463
|
-
self._#{symbol}_callbacks.delete(filter)
|
501
|
+
extlib_inheritable_accessor("_#{symbol}_terminator") { terminator }
|
502
|
+
|
503
|
+
extlib_inheritable_accessor("_#{symbol}_callback") do
|
504
|
+
CallbackChain.new(symbol)
|
505
|
+
end
|
506
|
+
|
507
|
+
_define_runner(symbol)
|
508
|
+
|
509
|
+
# Define more convenient callback methods
|
510
|
+
# set_callback(:save, :before) becomes before_save
|
511
|
+
[:before, :after, :around].each do |filter|
|
512
|
+
self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
513
|
+
def self.#{filter}_#{symbol}(*symbols, &blk)
|
514
|
+
_alias_callbacks(symbols, blk) do |callback, options|
|
515
|
+
set_callback(:#{symbol}, :#{filter}, callback, options)
|
464
516
|
end
|
465
|
-
_define_runner(:#{symbol},
|
466
|
-
self._#{symbol}_callbacks.compile(nil, :terminator => _#{symbol}_terminator),
|
467
|
-
options)
|
468
517
|
end
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
518
|
+
RUBY_EVAL
|
519
|
+
end
|
520
|
+
end
|
521
|
+
end
|
522
|
+
|
523
|
+
def _alias_callbacks(callbacks, block)
|
524
|
+
options = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
|
525
|
+
callbacks.push(block) if block
|
526
|
+
callbacks.each do |callback|
|
527
|
+
yield callback, options
|
479
528
|
end
|
480
529
|
end
|
481
530
|
end
|