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 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