mattetti-couchrest 0.33 → 0.34

Sign up to get free protection for your applications and to get access to all the features.
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 new_document?
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 2 callbacks already defined for you:
113
- `create_callback`, `save_callback`, `update_callback` and `destroy_callback`
112
+ `CouchRest::ExtendedDocuments` instances have 4 callbacks already defined for you:
113
+ `:validate`, `:create`, `:save`, `:update` and `:destroy`
114
114
 
115
- In your document inherits from `CouchRest::ExtendedDocument`, define your callback as follows:
115
+ `CouchRest::CastedModel` instances have 1 callback already defined for you:
116
+ `:validate`
117
+
118
+ Define your callback as follows:
116
119
 
117
- save_callback :before, :generate_slug_from_name
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
- save_callback :before, :before_method
122
- save_callback :after, :after_method, :if => :condition
123
- save_callback :around {|r| stuff; yield; stuff }
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
@@ -53,6 +53,7 @@ end
53
53
 
54
54
  desc "Run all specs"
55
55
  Spec::Rake::SpecTask.new('spec') do |t|
56
+ t.spec_opts = ["--color"]
56
57
  t.spec_files = FileList['spec/**/*_spec.rb']
57
58
  end
58
59
 
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
 
@@ -28,7 +28,7 @@ require 'couchrest/monkeypatches'
28
28
 
29
29
  # = CouchDB, close to the metal
30
30
  module CouchRest
31
- VERSION = '0.33' unless self.const_defined?("VERSION")
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 new_document?
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 new_document?
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
- require File.join(File.dirname(__FILE__), '..', 'support', 'class')
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
- # Extracted from ActiveSupport::Callbacks written by Yehuda Katz
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, name)
118
+ def initialize(filter, kind, options, klass)
98
119
  @kind, @klass = kind, klass
99
- @name = name
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, _name, _filter)
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 ||= (#{terminator})
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 << (filter.arity == 1 ? "(self)" : "")
293
- when String
294
- @klass.class_eval <<-RUBY_EVAL
295
- def #{method_name}
296
- #{filter}
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
- method_name
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
- # This method_missing is supplied to catch callbacks with keys and create
311
- # the appropriate callback for future use.
312
- def method_missing(meth, *args, &blk)
313
- if meth.to_s =~ /_run__([\w:]+)__(\w+)__(\w+)__callbacks/
314
- return self.class._create_and_run_keyed_callback($1, $2.to_sym, $3.to_sym, self, &blk)
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
- super
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} unless self.const_defined?("CHAINS")
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, str, options)
359
- str = <<-RUBY_EVAL
360
- def _run_#{symbol}_callbacks(key = nil)
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
- send("_run__\#{self.class.name.split("::").last}__#{symbol}__\#{key}__callbacks") { yield if block_given? }
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
- #{str}
402
+ #{body}
365
403
  end
366
404
  end
367
405
  RUBY_EVAL
368
-
369
- class_eval str, __FILE__, __LINE__ + 1
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 _create_and_run_keyed_callback(klass, kind, key, obj, &blk)
414
+ def _create_keyed_callback(name, kind, obj, &blk)
379
415
  @_keyed_callbacks ||= {}
380
- @_keyed_callbacks[[kind, key]] ||= begin
381
- str = self.send("_#{kind}_callbacks").compile(key, :object => obj, :terminator => self.send("_#{kind}_terminator"))
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
- self.extlib_inheritable_accessor("_#{symbol}_terminator")
430
- self.send("_#{symbol}_terminator=", terminator)
431
- self.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
432
- extlib_inheritable_accessor :_#{symbol}_callbacks
433
- self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
434
-
435
- def self.#{symbol}_callback(*filters, &blk)
436
- type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
437
- options = filters.last.is_a?(Hash) ? filters.pop : {}
438
- filters.unshift(blk) if block_given?
439
-
440
- filters.map! do |filter|
441
- # overrides parent class
442
- self._#{symbol}_callbacks.delete_if {|c| c.matches?(type, :#{symbol}, filter)}
443
- Callback.new(filter, type, options.dup, self, :#{symbol})
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
- end
471
-
472
- def self.reset_#{symbol}_callbacks
473
- self._#{symbol}_callbacks = CallbackChain.new(:#{symbol})
474
- _define_runner(:#{symbol}, self._#{symbol}_callbacks.compile, {})
475
- end
476
-
477
- self.#{symbol}_callback(:before)
478
- RUBY_EVAL
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