activesupport 3.0.20 → 3.1.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (110) hide show
  1. data/CHANGELOG +14 -66
  2. data/README.rdoc +1 -1
  3. data/lib/active_support/backtrace_cleaner.rb +1 -1
  4. data/lib/active_support/buffered_logger.rb +4 -11
  5. data/lib/active_support/cache.rb +12 -15
  6. data/lib/active_support/cache/file_store.rb +3 -2
  7. data/lib/active_support/cache/mem_cache_store.rb +11 -3
  8. data/lib/active_support/cache/strategy/local_cache.rb +28 -23
  9. data/lib/active_support/callbacks.rb +195 -175
  10. data/lib/active_support/concern.rb +105 -35
  11. data/lib/active_support/configurable.rb +41 -2
  12. data/lib/active_support/core_ext/array/access.rb +2 -2
  13. data/lib/active_support/core_ext/array/random_access.rb +10 -7
  14. data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -2
  15. data/lib/active_support/core_ext/class/attribute.rb +27 -17
  16. data/lib/active_support/core_ext/class/inheritable_attributes.rb +12 -86
  17. data/lib/active_support/core_ext/class/subclasses.rb +20 -34
  18. data/lib/active_support/core_ext/date/calculations.rb +14 -2
  19. data/lib/active_support/core_ext/date/conversions.rb +6 -0
  20. data/lib/active_support/core_ext/date_time/calculations.rb +26 -9
  21. data/lib/active_support/core_ext/date_time/conversions.rb +1 -1
  22. data/lib/active_support/core_ext/date_time/zones.rb +1 -1
  23. data/lib/active_support/core_ext/enumerable.rb +2 -5
  24. data/lib/active_support/core_ext/float/rounding.rb +1 -1
  25. data/lib/active_support/core_ext/hash.rb +1 -0
  26. data/lib/active_support/core_ext/hash/conversions.rb +23 -36
  27. data/lib/active_support/core_ext/hash/deep_dup.rb +11 -0
  28. data/lib/active_support/core_ext/hash/keys.rb +6 -4
  29. data/lib/active_support/core_ext/hash/reverse_merge.rb +9 -14
  30. data/lib/active_support/core_ext/kernel/reporting.rb +19 -0
  31. data/lib/active_support/core_ext/logger.rb +11 -38
  32. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +11 -12
  33. data/lib/active_support/core_ext/module/attr_internal.rb +13 -6
  34. data/lib/active_support/core_ext/module/deprecation.rb +2 -0
  35. data/lib/active_support/core_ext/object.rb +1 -1
  36. data/lib/active_support/core_ext/object/blank.rb +42 -9
  37. data/lib/active_support/core_ext/object/duplicable.rb +48 -9
  38. data/lib/active_support/core_ext/object/inclusion.rb +15 -0
  39. data/lib/active_support/core_ext/object/instance_variables.rb +1 -35
  40. data/lib/active_support/core_ext/object/to_param.rb +13 -8
  41. data/lib/active_support/core_ext/object/to_query.rb +2 -2
  42. data/lib/active_support/core_ext/object/try.rb +27 -10
  43. data/lib/active_support/core_ext/object/with_options.rb +19 -5
  44. data/lib/active_support/core_ext/range.rb +1 -0
  45. data/lib/active_support/core_ext/range/cover.rb +3 -0
  46. data/lib/active_support/core_ext/string.rb +1 -0
  47. data/lib/active_support/core_ext/string/filters.rb +3 -3
  48. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  49. data/lib/active_support/core_ext/string/inquiry.rb +13 -0
  50. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  51. data/lib/active_support/core_ext/string/output_safety.rb +33 -89
  52. data/lib/active_support/core_ext/time/calculations.rb +14 -13
  53. data/lib/active_support/core_ext/time/conversions.rb +2 -24
  54. data/lib/active_support/core_ext/time/marshal.rb +0 -1
  55. data/lib/active_support/core_ext/time/zones.rb +32 -21
  56. data/lib/active_support/core_ext/uri.rb +8 -0
  57. data/lib/active_support/dependencies.rb +93 -37
  58. data/lib/active_support/deprecation.rb +2 -2
  59. data/lib/active_support/deprecation/behaviors.rb +7 -0
  60. data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
  61. data/lib/active_support/deprecation/reporting.rb +4 -0
  62. data/lib/active_support/duration.rb +4 -0
  63. data/lib/active_support/file_update_checker.rb +1 -1
  64. data/lib/active_support/file_watcher.rb +36 -0
  65. data/lib/active_support/gzip.rb +0 -1
  66. data/lib/active_support/hash_with_indifferent_access.rb +6 -4
  67. data/lib/active_support/i18n.rb +0 -1
  68. data/lib/active_support/i18n_railtie.rb +2 -2
  69. data/lib/active_support/inflector/inflections.rb +1 -1
  70. data/lib/active_support/json/decoding.rb +37 -23
  71. data/lib/active_support/json/encoding.rb +8 -3
  72. data/lib/active_support/lazy_load_hooks.rb +7 -7
  73. data/lib/active_support/log_subscriber.rb +3 -3
  74. data/lib/active_support/log_subscriber/test_helper.rb +4 -3
  75. data/lib/active_support/message_encryptor.rb +1 -1
  76. data/lib/active_support/message_verifier.rb +1 -1
  77. data/lib/active_support/multibyte/chars.rb +4 -3
  78. data/lib/active_support/multibyte/unicode.rb +1 -1
  79. data/lib/active_support/notifications.rb +9 -8
  80. data/lib/active_support/notifications/fanout.rb +3 -3
  81. data/lib/active_support/ordered_hash.rb +19 -2
  82. data/lib/active_support/ordered_options.rb +33 -3
  83. data/lib/active_support/railtie.rb +1 -1
  84. data/lib/active_support/rescuable.rb +1 -0
  85. data/lib/active_support/secure_random.rb +11 -5
  86. data/lib/active_support/test_case.rb +2 -10
  87. data/lib/active_support/testing/assertions.rb +17 -6
  88. data/lib/active_support/testing/mochaing.rb +7 -0
  89. data/lib/active_support/testing/pending.rb +27 -23
  90. data/lib/active_support/testing/setup_and_teardown.rb +8 -11
  91. data/lib/active_support/time_with_zone.rb +6 -3
  92. data/lib/active_support/version.rb +3 -3
  93. data/lib/active_support/whiny_nil.rb +1 -1
  94. data/lib/active_support/xml_mini.rb +4 -2
  95. data/lib/active_support/xml_mini/jdom.rb +9 -16
  96. data/lib/active_support/xml_mini/libxml.rb +1 -0
  97. data/lib/active_support/xml_mini/libxmlsax.rb +2 -1
  98. data/lib/active_support/xml_mini/nokogiri.rb +1 -0
  99. data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
  100. data/lib/active_support/xml_mini/rexml.rb +1 -0
  101. metadata +50 -32
  102. checksums.yaml +0 -7
  103. data/lib/active_support/core_ext/cgi.rb +0 -1
  104. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -19
  105. data/lib/active_support/core_ext/object/returning.rb +0 -43
  106. data/lib/active_support/json/backends/jsongem.rb +0 -47
  107. data/lib/active_support/json/backends/okjson.rb +0 -644
  108. data/lib/active_support/json/backends/yajl.rb +0 -44
  109. data/lib/active_support/json/backends/yaml.rb +0 -19
  110. data/lib/active_support/testing/default.rb +0 -9
data/CHANGELOG CHANGED
@@ -1,83 +1,31 @@
1
- ## Rails 3.0.20 (unreleased)
1
+ *Rails 3.1.0 (unreleased)*
2
2
 
3
- ## Rails 3.0.19 (Jan 8, 2013)
3
+ * Add String#inquiry as a convenience method for turning a string into a StringInquirer object [DHH]
4
4
 
5
- * Hash.from_xml raises when it encounters type="symbol" or type="yaml".
6
- Use Hash.from_trusted_xml to parse this XML.
5
+ * Add Object#in? to test if an object is included in another object [Prem Sichanugrist, Brian Morearty, John Reitano]
7
6
 
8
- CVE-2013-0156
7
+ * LocalCache strategy is now a real middleware class, not an anonymous class
8
+ posing for pictures.
9
9
 
10
- *Jeremy Kemper*
10
+ * ActiveSupport::Dependencies::ClassCache class has been introduced for
11
+ holding references to reloadable classes.
11
12
 
12
- ## Rails 3.0.18 (Jan 2, 2013)
13
+ * ActiveSupport::Dependencies::Reference has been refactored to take direct
14
+ advantage of the new ClassCache.
13
15
 
14
- * No changes.
16
+ * Backports Range#cover? as an alias for Range#include? in Ruby 1.8 [Diego Carrion, fxn]
15
17
 
16
- ## Rails 3.0.17 (Aug 9, 2012)
17
-
18
- * ERB::Util.html_escape now escapes single quotes. [Santiago Pastorino]
19
-
20
- ## Rails 3.0.16 (Jul 26, 2012)
21
-
22
- * No changes.
23
-
24
- ## Rails 3.0.14 (Jun 12, 2012)
25
-
26
- * No changes.
27
-
28
- * Rails 3.0.13 (May 31, 2012)
29
-
30
- * Stop SafeBuffer#clone_empty from issuing warnings
31
-
32
- * Rails 3.0.10 (August 16, 2011)
33
-
34
- * Delayed backtrace scrubbing in `load_missing_constant` until we actually
35
- raise the exception
36
-
37
-
38
- *Rails 3.0.9 (June 16, 2011)*
39
-
40
- *No changes.
41
-
42
-
43
- *Rails 3.0.8 (June 7, 2011)*
44
-
45
- * No changes.
46
-
47
- *Rails 3.0.7 (April 18, 2011)*
48
-
49
- * Hash.from_xml no longer loses attributes on tags containing only whitespace [André Arko]
50
-
51
-
52
- *Rails 3.0.6 (April 5, 2011)
53
-
54
- * No changes.
55
-
56
-
57
- *Rails 3.0.5 (February 26, 2011)*
58
-
59
- * No changes.
60
-
61
-
62
- *Rails 3.0.4 (February 8, 2011)*
63
-
64
- * No changes.
65
-
66
-
67
- *Rails 3.0.3 (November 16, 2010)*
68
-
69
- * No changes.
18
+ * Added weeks_ago and prev_week to Date/DateTime/Time. [Rob Zolkos, fxn]
70
19
 
20
+ * Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
71
21
 
72
- *Rails 3.0.2 (November 15, 2010)*
22
+ *Rails 3.0.2 (unreleased)*
73
23
 
74
24
  * Added before_remove_const callback to ActiveSupport::Dependencies.remove_unloadable_constants! [Andrew White]
75
25
 
76
-
77
26
  *Rails 3.0.1 (October 15, 2010)*
78
27
 
79
- * No changes.
80
-
28
+ * No Changes, just a version bump.
81
29
 
82
30
  *Rails 3.0.0 (August 29, 2010)*
83
31
 
@@ -14,7 +14,7 @@ The latest version of Active Support can be installed with Rubygems:
14
14
 
15
15
  Source code can be downloaded as part of the Rails project on GitHub
16
16
 
17
- * http://github.com/rails/rails/tree/master/activesupport/
17
+ * https://github.com/rails/rails/tree/master/activesupport/
18
18
 
19
19
 
20
20
  == License
@@ -8,7 +8,7 @@ module ActiveSupport
8
8
  # filter or modify the paths of any lines of the backtrace, you can call BacktraceCleaner#remove_filters! These two methods
9
9
  # will give you a completely untouched backtrace.
10
10
  #
11
- # Example:
11
+ # ==== Example:
12
12
  #
13
13
  # bc = BacktraceCleaner.new
14
14
  # bc.add_filter { |line| line.gsub(Rails.root, '') }
@@ -48,10 +48,12 @@ module ActiveSupport
48
48
  if log.respond_to?(:write)
49
49
  @log = log
50
50
  elsif File.exist?(log)
51
- @log = open_log(log, (File::WRONLY | File::APPEND))
51
+ @log = open(log, (File::WRONLY | File::APPEND))
52
+ @log.sync = true
52
53
  else
53
54
  FileUtils.mkdir_p(File.dirname(log))
54
- @log = open_log(log, (File::WRONLY | File::APPEND | File::CREAT))
55
+ @log = open(log, (File::WRONLY | File::APPEND | File::CREAT))
56
+ @log.sync = true
55
57
  end
56
58
  end
57
59
 
@@ -126,14 +128,5 @@ module ActiveSupport
126
128
  def clear_buffer
127
129
  @buffer.delete(Thread.current)
128
130
  end
129
-
130
- private
131
-
132
- def open_log(log, mode)
133
- open(log, mode).tap do |log|
134
- log.set_encoding(Encoding::BINARY) if log.respond_to?(:set_encoding)
135
- log.sync = true
136
- end
137
- end
138
131
  end
139
132
  end
@@ -345,7 +345,7 @@ module ActiveSupport
345
345
  entry = read_entry(key, options)
346
346
  if entry
347
347
  if entry.expired?
348
- delete_entry(key, options)
348
+ delete_entry(key)
349
349
  else
350
350
  results[name] = entry.value
351
351
  end
@@ -484,19 +484,20 @@ module ActiveSupport
484
484
  # object responds to +cache_key+. Otherwise, to_param method will be
485
485
  # called. If the key is a Hash, then keys will be sorted alphabetically.
486
486
  def expanded_key(key) # :nodoc:
487
- if key.respond_to?(:cache_key)
488
- key = key.cache_key.to_s
489
- elsif key.is_a?(Array)
487
+ return key.cache_key.to_s if key.respond_to?(:cache_key)
488
+
489
+ case key
490
+ when Array
490
491
  if key.size > 1
491
- key.collect{|element| expanded_key(element)}.to_param
492
+ key = key.collect{|element| expanded_key(element)}
492
493
  else
493
- key.first.to_param
494
+ key = key.first
494
495
  end
495
- elsif key.is_a?(Hash)
496
- key = key.to_a.sort{|a,b| a.first.to_s <=> b.first.to_s}.collect{|k,v| "#{k}=#{v}"}.to_param
497
- else
498
- key = key.to_param
496
+ when Hash
497
+ key = key.sort_by { |k,_| k.to_s }.collect{|k,v| "#{k}=#{v}"}
499
498
  end
499
+
500
+ key.to_param
500
501
  end
501
502
 
502
503
  # Prefix a key with the namespace. Namespace and key will be delimited with a colon.
@@ -589,11 +590,7 @@ module ActiveSupport
589
590
  # Check if the entry is expired. The +expires_in+ parameter can override the
590
591
  # value set when the entry was created.
591
592
  def expired?
592
- if @expires_in && @created_at + @expires_in <= Time.now.to_f
593
- true
594
- else
595
- false
596
- end
593
+ @expires_in && @created_at + @expires_in <= Time.now.to_f
597
594
  end
598
595
 
599
596
  # Set a new time when the entry will expire.
@@ -1,5 +1,6 @@
1
1
  require 'active_support/core_ext/file/atomic'
2
2
  require 'active_support/core_ext/string/conversions'
3
+ require 'active_support/core_ext/object/inclusion'
3
4
  require 'rack/utils'
4
5
 
5
6
  module ActiveSupport
@@ -20,7 +21,7 @@ module ActiveSupport
20
21
  end
21
22
 
22
23
  def clear(options = nil)
23
- root_dirs = Dir.entries(cache_path).reject{|f| ['.', '..'].include?(f)}
24
+ root_dirs = Dir.entries(cache_path).reject{|f| f.in?(['.', '..'])}
24
25
  FileUtils.rm_r(root_dirs.collect{|f| File.join(cache_path, f)})
25
26
  end
26
27
 
@@ -161,7 +162,7 @@ module ActiveSupport
161
162
  # Delete empty directories in the cache.
162
163
  def delete_empty_directories(dir)
163
164
  return if dir == cache_path
164
- if Dir.entries(dir).reject{|f| ['.', '..'].include?(f)}.empty?
165
+ if Dir.entries(dir).reject{|f| f.in?(['.', '..'])}.empty?
165
166
  File.delete(dir) rescue nil
166
167
  delete_empty_directories(File.dirname(dir))
167
168
  end
@@ -4,7 +4,9 @@ rescue LoadError => e
4
4
  $stderr.puts "You don't have memcache-client installed in your application. Please add it to your Gemfile and run bundle install"
5
5
  raise e
6
6
  end
7
+
7
8
  require 'digest/md5'
9
+ require 'active_support/core_ext/string/encoding'
8
10
 
9
11
  module ActiveSupport
10
12
  module Cache
@@ -29,7 +31,7 @@ module ActiveSupport
29
31
  DELETED = "DELETED\r\n"
30
32
  end
31
33
 
32
- ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/n
34
+ ESCAPE_KEY_CHARS = /[\x00-\x20%\x7F-\xFF]/
33
35
 
34
36
  def self.build_mem_cache(*addresses)
35
37
  addresses = addresses.flatten
@@ -73,7 +75,7 @@ module ActiveSupport
73
75
  def read_multi(*names)
74
76
  options = names.extract_options!
75
77
  options = merged_options(options)
76
- keys_to_names = names.inject({}){|map, name| map[escape_key(namespaced_key(name, options))] = name; map}
78
+ keys_to_names = Hash[names.map{|name| [escape_key(namespaced_key(name, options)), name]}]
77
79
  raw_values = @data.get_multi(keys_to_names.keys, :raw => true)
78
80
  values = {}
79
81
  raw_values.each do |key, value|
@@ -157,8 +159,14 @@ module ActiveSupport
157
159
  end
158
160
 
159
161
  private
162
+
163
+ # Memcache keys are binaries. So we need to force their encoding to binary
164
+ # before applying the regular expression to ensure we are escaping all
165
+ # characters properly.
160
166
  def escape_key(key)
161
- key = key.to_s.gsub(ESCAPE_KEY_CHARS){|match| "%#{match.getbyte(0).to_s(16).upcase}"}
167
+ key = key.to_s.dup
168
+ key = key.force_encoding("BINARY") if key.encoding_aware?
169
+ key = key.gsub(ESCAPE_KEY_CHARS){ |match| "%#{match.getbyte(0).to_s(16).upcase}" }
162
170
  key = "#{key[0, 213]}:md5:#{Digest::MD5.hexdigest(key)}" if key.size > 250
163
171
  key
164
172
  end
@@ -50,34 +50,39 @@ module ActiveSupport
50
50
  end
51
51
  end
52
52
 
53
- # Middleware class can be inserted as a Rack handler to be local cache for the
54
- # duration of request.
55
- def middleware
56
- @middleware ||= begin
57
- klass = Class.new
58
- klass.class_eval(<<-EOS, __FILE__, __LINE__ + 1)
59
- class << self
60
- def name
61
- "ActiveSupport::Cache::Strategy::LocalCache"
62
- end
63
- alias :to_s :name
64
- end
53
+ #--
54
+ # This class wraps up local storage for middlewares. Only the middleware method should
55
+ # construct them.
56
+ class Middleware # :nodoc:
57
+ attr_reader :name, :thread_local_key
65
58
 
66
- def initialize(app)
67
- @app = app
68
- end
59
+ def initialize(name, thread_local_key)
60
+ @name = name
61
+ @thread_local_key = thread_local_key
62
+ @app = nil
63
+ end
69
64
 
70
- def call(env)
71
- Thread.current[:#{thread_local_key}] = LocalStore.new
72
- @app.call(env)
73
- ensure
74
- Thread.current[:#{thread_local_key}] = nil
75
- end
76
- EOS
77
- klass
65
+ def new(app)
66
+ @app = app
67
+ self
68
+ end
69
+
70
+ def call(env)
71
+ Thread.current[thread_local_key] = LocalStore.new
72
+ @app.call(env)
73
+ ensure
74
+ Thread.current[thread_local_key] = nil
78
75
  end
79
76
  end
80
77
 
78
+ # Middleware class can be inserted as a Rack handler to be local cache for the
79
+ # duration of request.
80
+ def middleware
81
+ @middleware ||= Middleware.new(
82
+ "ActiveSupport::Cache::Strategy::LocalCache",
83
+ thread_local_key)
84
+ end
85
+
81
86
  def clear(options = nil) # :nodoc:
82
87
  local_cache.clear(options) if local_cache
83
88
  super
@@ -1,32 +1,34 @@
1
1
  require 'active_support/concern'
2
2
  require 'active_support/descendants_tracker'
3
3
  require 'active_support/core_ext/array/wrap'
4
- require 'active_support/core_ext/class/inheritable_attributes'
4
+ require 'active_support/core_ext/class/attribute'
5
5
  require 'active_support/core_ext/kernel/reporting'
6
6
  require 'active_support/core_ext/kernel/singleton_class'
7
+ require 'active_support/core_ext/object/inclusion'
7
8
 
8
9
  module ActiveSupport
9
- # Callbacks are hooks into the life cycle of an object that allow you to trigger logic
10
- # before or after an alteration of the object state.
10
+ # \Callbacks are code hooks that are run at key points in an object's lifecycle.
11
+ # The typical use case is to have a base class define a set of callbacks relevant
12
+ # to the other functionality it supplies, so that subclasses can install callbacks
13
+ # that enhance or modify the base functionality without needing to override
14
+ # or redefine methods of the base class.
11
15
  #
12
- # Mixing in this module allows you to define callbacks in your class.
16
+ # Mixing in this module allows you to define the events in the object's lifecycle
17
+ # that will support callbacks (via +ClassMethods.define_callbacks+), set the instance
18
+ # methods, procs, or callback objects to be called (via +ClassMethods.set_callback+),
19
+ # and run the installed callbacks at the appropriate times (via +run_callbacks+).
13
20
  #
14
- # Example:
15
- # class Storage
16
- # include ActiveSupport::Callbacks
17
- #
18
- # define_callbacks :save
19
- # end
21
+ # Three kinds of callbacks are supported: before callbacks, run before a certain event;
22
+ # after callbacks, run after the event; and around callbacks, blocks that surround the
23
+ # event, triggering it when they yield. Callback code can be contained in instance
24
+ # methods, procs or lambdas, or callback objects that respond to certain predetermined
25
+ # methods. See +ClassMethods.set_callback+ for details.
20
26
  #
21
- # class ConfigStorage < Storage
22
- # set_callback :save, :before, :saving_message
23
- # def saving_message
24
- # puts "saving..."
25
- # end
27
+ # ==== Example
26
28
  #
27
- # set_callback :save, :after do |object|
28
- # puts "saved"
29
- # end
29
+ # class Record
30
+ # include ActiveSupport::Callbacks
31
+ # define_callbacks :save
30
32
  #
31
33
  # def save
32
34
  # run_callbacks :save do
@@ -35,29 +37,7 @@ module ActiveSupport
35
37
  # end
36
38
  # end
37
39
  #
38
- # config = ConfigStorage.new
39
- # config.save
40
- #
41
- # Output:
42
- # saving...
43
- # - save
44
- # saved
45
- #
46
- # Callbacks from parent classes are inherited.
47
- #
48
- # Example:
49
- # class Storage
50
- # include ActiveSupport::Callbacks
51
- #
52
- # define_callbacks :save
53
- #
54
- # set_callback :save, :before, :prepare
55
- # def prepare
56
- # puts "preparing save"
57
- # end
58
- # end
59
- #
60
- # class ConfigStorage < Storage
40
+ # class PersonRecord < Record
61
41
  # set_callback :save, :before, :saving_message
62
42
  # def saving_message
63
43
  # puts "saving..."
@@ -66,19 +46,12 @@ module ActiveSupport
66
46
  # set_callback :save, :after do |object|
67
47
  # puts "saved"
68
48
  # end
69
- #
70
- # def save
71
- # run_callbacks :save do
72
- # puts "- save"
73
- # end
74
- # end
75
49
  # end
76
50
  #
77
- # config = ConfigStorage.new
78
- # config.save
51
+ # person = PersonRecord.new
52
+ # person.save
79
53
  #
80
54
  # Output:
81
- # preparing save
82
55
  # saving...
83
56
  # - save
84
57
  # saved
@@ -90,16 +63,25 @@ module ActiveSupport
90
63
  extend ActiveSupport::DescendantsTracker
91
64
  end
92
65
 
66
+ # Runs the callbacks for the given event.
67
+ #
68
+ # Calls the before and around callbacks in the order they were set, yields
69
+ # the block (if given one), and then runs the after callbacks in reverse order.
70
+ # Optionally accepts a key, which will be used to compile an optimized callback
71
+ # method for each key. See +ClassMethods.define_callbacks+ for more information.
72
+ #
73
+ # If the callback chain was halted, returns +false+. Otherwise returns the result
74
+ # of the block, or +true+ if no block is given.
75
+ #
76
+ # run_callbacks :save do
77
+ # save
78
+ # end
79
+ #
93
80
  def run_callbacks(kind, *args, &block)
94
81
  send("_run_#{kind}_callbacks", *args, &block)
95
82
  end
96
83
 
97
- def callback(kind)
98
- ActiveSupport::Deprecation.warn("callback is deprecated. Please use run_callbacks")
99
- send("_run_#{kind}_callbacks")
100
- end
101
-
102
- class Callback
84
+ class Callback #:nodoc:#
103
85
  @@_callback_sequence = 0
104
86
 
105
87
  attr_accessor :chain, :filter, :kind, :options, :per_key, :klass, :raw_filter
@@ -184,49 +166,52 @@ module ActiveSupport
184
166
  # options[0] is the compiled form of supplied conditions
185
167
  # options[1] is the "end" for the conditional
186
168
  #
187
- if @kind == :before || @kind == :around
188
- if @kind == :before
189
- # if condition # before_save :filter_name, :if => :condition
190
- # filter_name
191
- # end
192
- filter = <<-RUBY_EVAL
193
- unless halted
194
- result = #{@filter}
195
- halted = (#{chain.config[:terminator]})
196
- end
197
- RUBY_EVAL
198
-
199
- [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
200
- else
201
- # Compile around filters with conditions into proxy methods
202
- # that contain the conditions.
203
- #
204
- # For `around_save :filter_name, :if => :condition':
205
- #
206
- # def _conditional_callback_save_17
207
- # if condition
208
- # filter_name do
209
- # yield self
210
- # end
211
- # else
212
- # yield self
213
- # end
214
- # end
215
- #
216
- name = "_conditional_callback_#{@kind}_#{next_id}"
217
- @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
218
- def #{name}(halted)
219
- #{@compiled_options[0] || "if true"} && !halted
220
- #{@filter} do
221
- yield self
222
- end
223
- else
169
+ case @kind
170
+ when :before
171
+ # if condition # before_save :filter_name, :if => :condition
172
+ # filter_name
173
+ # end
174
+ filter = <<-RUBY_EVAL
175
+ unless halted
176
+ # This double assignment is to prevent warnings in 1.9.3. I would
177
+ # remove the `result` variable, but apparently some other
178
+ # generated code is depending on this variable being set sometimes
179
+ # and sometimes not.
180
+ result = result = #{@filter}
181
+ halted = (#{chain.config[:terminator]})
182
+ end
183
+ RUBY_EVAL
184
+
185
+ [@compiled_options[0], filter, @compiled_options[1]].compact.join("\n")
186
+ when :around
187
+ # Compile around filters with conditions into proxy methods
188
+ # that contain the conditions.
189
+ #
190
+ # For `around_save :filter_name, :if => :condition':
191
+ #
192
+ # def _conditional_callback_save_17
193
+ # if condition
194
+ # filter_name do
195
+ # yield self
196
+ # end
197
+ # else
198
+ # yield self
199
+ # end
200
+ # end
201
+ #
202
+ name = "_conditional_callback_#{@kind}_#{next_id}"
203
+ @klass.class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
204
+ def #{name}(halted)
205
+ #{@compiled_options[0] || "if true"} && !halted
206
+ #{@filter} do
224
207
  yield self
225
208
  end
209
+ else
210
+ yield self
226
211
  end
227
- RUBY_EVAL
228
- "#{name}(halted) do"
229
- end
212
+ end
213
+ RUBY_EVAL
214
+ "#{name}(halted) do"
230
215
  end
231
216
  end
232
217
 
@@ -235,15 +220,17 @@ module ActiveSupport
235
220
  def end(key=nil, object=nil)
236
221
  return if key && !object.send("_one_time_conditions_valid_#{@callback_id}?")
237
222
 
238
- if @kind == :around || @kind == :after
223
+ case @kind
224
+ when :after
239
225
  # if condition # after_save :filter_name, :if => :condition
240
226
  # filter_name
241
227
  # end
242
- if @kind == :after
243
- [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
244
- else
245
- "end"
228
+ [@compiled_options[0], @filter, @compiled_options[1]].compact.join("\n")
229
+ when :around
230
+ <<-RUBY_EVAL
231
+ value
246
232
  end
233
+ RUBY_EVAL
247
234
  end
248
235
  end
249
236
 
@@ -336,7 +323,7 @@ module ActiveSupport
336
323
  end
337
324
 
338
325
  # An Array with a compile method
339
- class CallbackChain < Array
326
+ class CallbackChain < Array #:nodoc:#
340
327
  attr_reader :name, :config
341
328
 
342
329
  def initialize(name, config)
@@ -381,20 +368,9 @@ module ActiveSupport
381
368
  end
382
369
 
383
370
  module ClassMethods
384
- # Make the run_callbacks :save method. The generated method takes
385
- # a block that it'll yield to. It'll call the before and around filters
386
- # in order, yield the block, and then run the after filters.
387
- #
388
- # run_callbacks :save do
389
- # save
390
- # end
391
- #
392
- # The run_callbacks :save method can optionally take a key, which
393
- # will be used to compile an optimized callback method for each
394
- # key. See #define_callbacks for more information.
395
- #
371
+ # Generate the internal runner method called by +run_callbacks+.
396
372
  def __define_runner(symbol) #:nodoc:
397
- body = send("_#{symbol}_callbacks").compile(nil)
373
+ body = send("_#{symbol}_callbacks").compile
398
374
 
399
375
  silence_warnings do
400
376
  undef_method "_run_#{symbol}_callbacks" if method_defined?("_run_#{symbol}_callbacks")
@@ -437,25 +413,56 @@ module ActiveSupport
437
413
  # CallbackChain.
438
414
  #
439
415
  def __update_callbacks(name, filters = [], block = nil) #:nodoc:
440
- type = [:before, :after, :around].include?(filters.first) ? filters.shift : :before
416
+ type = filters.first.in?([:before, :after, :around]) ? filters.shift : :before
441
417
  options = filters.last.is_a?(Hash) ? filters.pop : {}
442
418
  filters.unshift(block) if block
443
419
 
444
- ([self] + ActiveSupport::DescendantsTracker.descendants(self)).each do |target|
420
+ ([self] + ActiveSupport::DescendantsTracker.descendants(self)).reverse.each do |target|
445
421
  chain = target.send("_#{name}_callbacks")
446
- yield chain, type, filters, options
422
+ yield target, chain.dup, type, filters, options
447
423
  target.__define_runner(name)
448
424
  end
449
425
  end
450
426
 
451
- # Set callbacks for a previously defined callback.
427
+ # Install a callback for the given event.
452
428
  #
453
- # Syntax:
454
429
  # set_callback :save, :before, :before_meth
455
430
  # set_callback :save, :after, :after_meth, :if => :condition
456
- # set_callback :save, :around, lambda { |r| stuff; yield; stuff }
431
+ # set_callback :save, :around, lambda { |r| stuff; result = yield; stuff }
432
+ #
433
+ # The second arguments indicates whether the callback is to be run +:before+,
434
+ # +:after+, or +:around+ the event. If omitted, +:before+ is assumed. This
435
+ # means the first example above can also be written as:
436
+ #
437
+ # set_callback :save, :before_meth
438
+ #
439
+ # The callback can specified as a symbol naming an instance method; as a proc,
440
+ # lambda, or block; as a string to be instance evaluated; or as an object that
441
+ # responds to a certain method determined by the <tt>:scope</tt> argument to
442
+ # +define_callback+.
443
+ #
444
+ # If a proc, lambda, or block is given, its body is evaluated in the context
445
+ # of the current object. It can also optionally accept the current object as
446
+ # an argument.
457
447
  #
458
- # Use skip_callback to skip any defined one.
448
+ # Before and around callbacks are called in the order that they are set; after
449
+ # callbacks are called in the reverse order.
450
+ #
451
+ # Around callbacks can access the return value from the event, if it
452
+ # wasn't halted, from the +yield+ call.
453
+ #
454
+ # ===== Options
455
+ #
456
+ # * <tt>:if</tt> - A symbol naming an instance method or a proc; the callback
457
+ # will be called only when it returns a true value.
458
+ # * <tt>:unless</tt> - A symbol naming an instance method or a proc; the callback
459
+ # will be called only when it returns a false value.
460
+ # * <tt>:prepend</tt> - If true, the callback will be prepended to the existing
461
+ # chain rather than appended.
462
+ # * <tt>:per_key</tt> - A hash with <tt>:if</tt> and <tt>:unless</tt> options;
463
+ # see "Per-key conditions" below.
464
+ #
465
+ # ===== Per-key conditions
459
466
  #
460
467
  # When creating or skipping callbacks, you can specify conditions that
461
468
  # are always the same for a given key. For instance, in Action Pack,
@@ -465,12 +472,12 @@ module ActiveSupport
465
472
  #
466
473
  # becomes
467
474
  #
468
- # dispatch_callback :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
475
+ # set_callback :process_action, :before, :authenticate, :per_key => {:unless => proc {|c| c.action_name == "index"}}
469
476
  #
470
- # Per-Key conditions are evaluated only once per use of a given key.
477
+ # Per-key conditions are evaluated only once per use of a given key.
471
478
  # In the case of the above example, you would do:
472
479
  #
473
- # run_callbacks(:dispatch, action_name) { ... dispatch stuff ... }
480
+ # run_callbacks(:process_action, action_name) { ... dispatch stuff ... }
474
481
  #
475
482
  # In that case, each action_name would get its own compiled callback
476
483
  # method that took into consideration the per_key conditions. This
@@ -479,7 +486,7 @@ module ActiveSupport
479
486
  def set_callback(name, *filter_list, &block)
480
487
  mapped = nil
481
488
 
482
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
489
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
483
490
  mapped ||= filters.map do |filter|
484
491
  Callback.new(chain, filter, type, options.dup, self)
485
492
  end
@@ -489,13 +496,20 @@ module ActiveSupport
489
496
  end
490
497
 
491
498
  options[:prepend] ? chain.unshift(*(mapped.reverse)) : chain.push(*mapped)
499
+
500
+ target.send("_#{name}_callbacks=", chain)
492
501
  end
493
502
  end
494
503
 
495
- # Skip a previously defined callback for a given type.
504
+ # Skip a previously set callback. Like +set_callback+, <tt>:if</tt> or <tt>:unless</tt>
505
+ # options may be passed in order to control when the callback is skipped.
506
+ #
507
+ # class Writer < Person
508
+ # skip_callback :validate, :before, :check_membership, :if => lambda { self.age > 18 }
509
+ # end
496
510
  #
497
511
  def skip_callback(name, *filter_list, &block)
498
- __update_callbacks(name, filter_list, block) do |chain, type, filters, options|
512
+ __update_callbacks(name, filter_list, block) do |target, chain, type, filters, options|
499
513
  filters.each do |filter|
500
514
  filter = chain.find {|c| c.matches?(type, filter) }
501
515
 
@@ -507,92 +521,98 @@ module ActiveSupport
507
521
 
508
522
  chain.delete(filter)
509
523
  end
524
+ target.send("_#{name}_callbacks=", chain)
510
525
  end
511
526
  end
512
527
 
513
- # Reset callbacks for a given type.
528
+ # Remove all set callbacks for the given event.
514
529
  #
515
530
  def reset_callbacks(symbol)
516
531
  callbacks = send("_#{symbol}_callbacks")
517
532
 
518
533
  ActiveSupport::DescendantsTracker.descendants(self).each do |target|
519
- chain = target.send("_#{symbol}_callbacks")
534
+ chain = target.send("_#{symbol}_callbacks").dup
520
535
  callbacks.each { |c| chain.delete(c) }
536
+ target.send("_#{symbol}_callbacks=", chain)
521
537
  target.__define_runner(symbol)
522
538
  end
523
539
 
524
- callbacks.clear
540
+ self.send("_#{symbol}_callbacks=", callbacks.dup.clear)
541
+
525
542
  __define_runner(symbol)
526
543
  end
527
544
 
528
- # Defines callbacks types:
545
+ # Define sets of events in the object lifecycle that support callbacks.
529
546
  #
530
547
  # define_callbacks :validate
548
+ # define_callbacks :initialize, :save, :destroy
531
549
  #
532
- # This macro accepts the following options:
550
+ # ===== Options
533
551
  #
534
- # * <tt>:terminator</tt> - Indicates when a before filter is considered
535
- # to be halted.
552
+ # * <tt>:terminator</tt> - Determines when a before filter will halt the callback
553
+ # chain, preventing following callbacks from being called and the event from being
554
+ # triggered. This is a string to be eval'ed. The result of the callback is available
555
+ # in the <tt>result</tt> variable.
536
556
  #
537
- # define_callbacks :validate, :terminator => "result == false"
557
+ # define_callbacks :validate, :terminator => "result == false"
538
558
  #
539
- # In the example above, if any before validate callbacks returns +false+,
540
- # other callbacks are not executed. Defaults to "false", meaning no value
541
- # halts the chain.
559
+ # In this example, if any before validate callbacks returns +false+,
560
+ # other callbacks are not executed. Defaults to "false", meaning no value
561
+ # halts the chain.
542
562
  #
543
563
  # * <tt>:rescuable</tt> - By default, after filters are not executed if
544
- # the given block or a before filter raises an error. Set this option to
545
- # true to change this behavior.
564
+ # the given block or a before filter raises an error. By setting this option
565
+ # to <tt>true</tt> exception raised by given block is stored and after
566
+ # executing all the after callbacks the stored exception is raised.
546
567
  #
547
- # * <tt>:scope</tt> - Indicates which methods should be executed when a class
548
- # is given as callback. Defaults to <tt>[:kind]</tt>.
568
+ # * <tt>:scope</tt> - Indicates which methods should be executed when an object
569
+ # is used as a callback.
549
570
  #
550
- # class Audit
551
- # def before(caller)
552
- # puts 'Audit: before is called'
553
- # end
571
+ # class Audit
572
+ # def before(caller)
573
+ # puts 'Audit: before is called'
574
+ # end
554
575
  #
555
- # def before_save(caller)
556
- # puts 'Audit: before_save is called'
557
- # end
558
- # end
576
+ # def before_save(caller)
577
+ # puts 'Audit: before_save is called'
578
+ # end
579
+ # end
559
580
  #
560
- # class Account
561
- # include ActiveSupport::Callbacks
581
+ # class Account
582
+ # include ActiveSupport::Callbacks
562
583
  #
563
- # define_callbacks :save
564
- # set_callback :save, :before, Audit.new
584
+ # define_callbacks :save
585
+ # set_callback :save, :before, Audit.new
565
586
  #
566
- # def save
567
- # run_callbacks :save do
568
- # puts 'save in main'
569
- # end
570
- # end
571
- # end
587
+ # def save
588
+ # run_callbacks :save do
589
+ # puts 'save in main'
590
+ # end
591
+ # end
592
+ # end
572
593
  #
573
- # In the above case whenever you save an account the method <tt>Audit#before</tt> will
574
- # be called. On the other hand
594
+ # In the above case whenever you save an account the method <tt>Audit#before</tt> will
595
+ # be called. On the other hand
575
596
  #
576
- # define_callbacks :save, :scope => [:kind, :name]
597
+ # define_callbacks :save, :scope => [:kind, :name]
577
598
  #
578
- # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
579
- # <tt>"#{kind}_#{name}"</tt> on the given instance. In this case "kind" is "before" and
580
- # "name" is "save". In this context ":kind" and ":name" have special meanings: ":kind"
581
- # refers to the kind of callback (before/after/around) and ":name" refers to the
582
- # method on which callbacks are being defined.
599
+ # would trigger <tt>Audit#before_save</tt> instead. That's constructed by calling
600
+ # <tt>#{kind}_#{name}</tt> on the given instance. In this case "kind" is "before" and
601
+ # "name" is "save". In this context +:kind+ and +:name+ have special meanings: +:kind+
602
+ # refers to the kind of callback (before/after/around) and +:name+ refers to the
603
+ # method on which callbacks are being defined.
583
604
  #
584
- # A declaration like
605
+ # A declaration like
585
606
  #
586
- # define_callbacks :save, :scope => [:name]
607
+ # define_callbacks :save, :scope => [:name]
587
608
  #
588
- # would call <tt>Audit#save</tt>.
609
+ # would call <tt>Audit#save</tt>.
589
610
  #
590
611
  def define_callbacks(*callbacks)
591
612
  config = callbacks.last.is_a?(Hash) ? callbacks.pop : {}
592
613
  callbacks.each do |callback|
593
- extlib_inheritable_reader("_#{callback}_callbacks") do
594
- CallbackChain.new(callback, config)
595
- end
614
+ class_attribute "_#{callback}_callbacks"
615
+ send("_#{callback}_callbacks=", CallbackChain.new(callback, config))
596
616
  __define_runner(callback)
597
617
  end
598
618
  end