activesupport 3.2.22.5 → 4.0.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 (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -136
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/active_support.rb +8 -21
  6. data/lib/active_support/backtrace_cleaner.rb +33 -25
  7. data/lib/active_support/basic_object.rb +7 -17
  8. data/lib/active_support/benchmarkable.rb +19 -15
  9. data/lib/active_support/buffered_logger.rb +9 -113
  10. data/lib/active_support/cache.rb +203 -171
  11. data/lib/active_support/cache/file_store.rb +12 -12
  12. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  13. data/lib/active_support/cache/memory_store.rb +2 -0
  14. data/lib/active_support/callbacks.rb +195 -247
  15. data/lib/active_support/concern.rb +16 -23
  16. data/lib/active_support/concurrency/latch.rb +27 -0
  17. data/lib/active_support/configurable.rb +69 -12
  18. data/lib/active_support/core_ext.rb +1 -0
  19. data/lib/active_support/core_ext/array.rb +0 -1
  20. data/lib/active_support/core_ext/array/access.rb +17 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +113 -55
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  24. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  25. data/lib/active_support/core_ext/array/wrap.rb +11 -14
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +12 -8
  28. data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
  29. data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -5
  31. data/lib/active_support/core_ext/date.rb +6 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +34 -188
  33. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  34. data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
  35. data/lib/active_support/core_ext/date/zones.rb +25 -2
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  37. data/lib/active_support/core_ext/date_time.rb +5 -0
  38. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  39. data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
  40. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  41. data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
  42. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  43. data/lib/active_support/core_ext/enumerable.rb +26 -73
  44. data/lib/active_support/core_ext/file.rb +0 -1
  45. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  46. data/lib/active_support/core_ext/hash.rb +0 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  49. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  50. data/lib/active_support/core_ext/hash/except.rb +1 -9
  51. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  52. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  53. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  54. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  55. data/lib/active_support/core_ext/infinite_comparable.rb +35 -0
  56. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  57. data/lib/active_support/core_ext/integer/time.rb +17 -12
  58. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  59. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  60. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  61. data/lib/active_support/core_ext/load_error.rb +7 -5
  62. data/lib/active_support/core_ext/logger.rb +7 -23
  63. data/lib/active_support/core_ext/marshal.rb +19 -0
  64. data/lib/active_support/core_ext/module.rb +1 -3
  65. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  66. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  67. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  68. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  69. data/lib/active_support/core_ext/module/delegation.rb +57 -40
  70. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  71. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  72. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  73. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  74. data/lib/active_support/core_ext/numeric.rb +2 -0
  75. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  76. data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
  77. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  78. data/lib/active_support/core_ext/object.rb +1 -0
  79. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  80. data/lib/active_support/core_ext/object/blank.rb +7 -23
  81. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  82. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  83. data/lib/active_support/core_ext/object/inclusion.rb +6 -6
  84. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  85. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  86. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  87. data/lib/active_support/core_ext/object/try.rb +46 -25
  88. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  89. data/lib/active_support/core_ext/proc.rb +3 -0
  90. data/lib/active_support/core_ext/range.rb +0 -2
  91. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  92. data/lib/active_support/core_ext/range/include_range.rb +1 -1
  93. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  94. data/lib/active_support/core_ext/string.rb +2 -2
  95. data/lib/active_support/core_ext/string/access.rb +95 -90
  96. data/lib/active_support/core_ext/string/conversions.rb +29 -38
  97. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  98. data/lib/active_support/core_ext/string/filters.rb +24 -18
  99. data/lib/active_support/core_ext/string/indent.rb +43 -0
  100. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  101. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  102. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  103. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  104. data/lib/active_support/core_ext/string/zones.rb +13 -0
  105. data/lib/active_support/core_ext/struct.rb +6 -0
  106. data/lib/active_support/core_ext/thread.rb +74 -0
  107. data/lib/active_support/core_ext/time.rb +6 -0
  108. data/lib/active_support/core_ext/time/calculations.rb +105 -193
  109. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  110. data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
  111. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  112. data/lib/active_support/core_ext/time/zones.rb +27 -17
  113. data/lib/active_support/core_ext/uri.rb +13 -17
  114. data/lib/active_support/dependencies.rb +160 -141
  115. data/lib/active_support/dependencies/autoload.rb +47 -20
  116. data/lib/active_support/deprecation.rb +39 -14
  117. data/lib/active_support/deprecation/behaviors.rb +44 -30
  118. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  119. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  121. data/lib/active_support/deprecation/reporting.rb +40 -11
  122. data/lib/active_support/descendants_tracker.rb +34 -19
  123. data/lib/active_support/duration.rb +6 -8
  124. data/lib/active_support/file_update_checker.rb +63 -47
  125. data/lib/active_support/gzip.rb +11 -5
  126. data/lib/active_support/hash_with_indifferent_access.rb +112 -37
  127. data/lib/active_support/i18n.rb +4 -0
  128. data/lib/active_support/i18n_railtie.rb +5 -22
  129. data/lib/active_support/inflections.rb +14 -12
  130. data/lib/active_support/inflector/inflections.rb +108 -71
  131. data/lib/active_support/inflector/methods.rb +181 -160
  132. data/lib/active_support/inflector/transliterate.rb +16 -17
  133. data/lib/active_support/json/decoding.rb +18 -17
  134. data/lib/active_support/json/encoding.rb +93 -39
  135. data/lib/active_support/json/variable.rb +10 -1
  136. data/lib/active_support/key_generator.rb +75 -0
  137. data/lib/active_support/lazy_load_hooks.rb +21 -19
  138. data/lib/active_support/locale/en.yml +100 -3
  139. data/lib/active_support/log_subscriber.rb +56 -36
  140. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  141. data/lib/active_support/logger.rb +57 -0
  142. data/lib/active_support/logger_silence.rb +24 -0
  143. data/lib/active_support/message_encryptor.rb +32 -29
  144. data/lib/active_support/message_verifier.rb +8 -14
  145. data/lib/active_support/multibyte.rb +5 -28
  146. data/lib/active_support/multibyte/chars.rb +80 -333
  147. data/lib/active_support/multibyte/unicode.rb +74 -64
  148. data/lib/active_support/notifications.rb +57 -25
  149. data/lib/active_support/notifications/fanout.rb +105 -18
  150. data/lib/active_support/notifications/instrumenter.rb +32 -13
  151. data/lib/active_support/number_helper.rb +636 -0
  152. data/lib/active_support/ordered_hash.rb +8 -190
  153. data/lib/active_support/ordered_options.rb +21 -23
  154. data/lib/active_support/proxy_object.rb +13 -0
  155. data/lib/active_support/rails.rb +27 -0
  156. data/lib/active_support/railtie.rb +12 -32
  157. data/lib/active_support/rescuable.rb +9 -4
  158. data/lib/active_support/string_inquirer.rb +13 -8
  159. data/lib/active_support/tagged_logging.rb +51 -73
  160. data/lib/active_support/test_case.rb +46 -17
  161. data/lib/active_support/testing/assertions.rb +56 -26
  162. data/lib/active_support/testing/autorun.rb +5 -0
  163. data/lib/active_support/testing/constant_lookup.rb +52 -0
  164. data/lib/active_support/testing/declarative.rb +1 -1
  165. data/lib/active_support/testing/deprecation.rb +0 -19
  166. data/lib/active_support/testing/isolation.rb +25 -58
  167. data/lib/active_support/testing/pending.rb +5 -43
  168. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  169. data/lib/active_support/testing/tagged_logging.rb +25 -0
  170. data/lib/active_support/time.rb +6 -21
  171. data/lib/active_support/time_with_zone.rb +78 -43
  172. data/lib/active_support/values/time_zone.rb +77 -58
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/version.rb +4 -4
  175. data/lib/active_support/xml_mini.rb +35 -17
  176. data/lib/active_support/xml_mini/jdom.rb +9 -17
  177. data/lib/active_support/xml_mini/libxml.rb +1 -2
  178. data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
  179. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  180. data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
  181. data/lib/active_support/xml_mini/rexml.rb +6 -8
  182. metadata +107 -77
  183. data/lib/active_support/base64.rb +0 -54
  184. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  185. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  186. data/lib/active_support/core_ext/exception.rb +0 -3
  187. data/lib/active_support/core_ext/file/path.rb +0 -5
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  190. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  191. data/lib/active_support/core_ext/io.rb +0 -15
  192. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  193. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  196. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  197. data/lib/active_support/core_ext/range/cover.rb +0 -3
  198. data/lib/active_support/core_ext/rexml.rb +0 -46
  199. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  200. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  201. data/lib/active_support/memoizable.rb +0 -116
  202. data/lib/active_support/multibyte/exceptions.rb +0 -8
  203. data/lib/active_support/multibyte/utils.rb +0 -60
  204. data/lib/active_support/ruby/shim.rb +0 -22
  205. data/lib/active_support/security_utils.rb +0 -27
  206. data/lib/active_support/testing/mochaing.rb +0 -7
  207. data/lib/active_support/testing/performance.rb +0 -317
  208. data/lib/active_support/testing/performance/jruby.rb +0 -115
  209. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  210. data/lib/active_support/testing/performance/ruby.rb +0 -152
  211. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  212. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. data/lib/active_support/whiny_nil.rb +0 -24
@@ -1,35 +1,39 @@
1
1
  require 'active_support/inflector/methods'
2
- require 'active_support/core_ext/time/publicize_conversion_methods'
3
2
  require 'active_support/values/time_zone'
4
3
 
5
4
  class Time
6
5
  DATE_FORMATS = {
7
- :db => "%Y-%m-%d %H:%M:%S",
8
- :number => "%Y%m%d%H%M%S",
9
- :time => "%H:%M",
10
- :short => "%d %b %H:%M",
11
- :long => "%B %d, %Y %H:%M",
12
- :long_ordinal => lambda { |time| time.strftime("%B #{ActiveSupport::Inflector.ordinalize(time.day)}, %Y %H:%M") },
13
- :rfc822 => lambda { |time| time.strftime("%a, %d %b %Y %H:%M:%S #{time.formatted_offset(false)}") }
6
+ :db => '%Y-%m-%d %H:%M:%S',
7
+ :number => '%Y%m%d%H%M%S',
8
+ :nsec => '%Y%m%d%H%M%S%9N',
9
+ :time => '%H:%M',
10
+ :short => '%d %b %H:%M',
11
+ :long => '%B %d, %Y %H:%M',
12
+ :long_ordinal => lambda { |time|
13
+ day_format = ActiveSupport::Inflector.ordinalize(time.day)
14
+ time.strftime("%B #{day_format}, %Y %H:%M")
15
+ },
16
+ :rfc822 => lambda { |time|
17
+ offset_format = time.formatted_offset(false)
18
+ time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
19
+ }
14
20
  }
15
21
 
16
- DATE_FORMATS[:nsec] = '%Y%m%d%H%M%S%9N' if RUBY_VERSION >= '1.9'
17
-
18
22
  # Converts to a formatted string. See DATE_FORMATS for builtin formats.
19
23
  #
20
24
  # This method is aliased to <tt>to_s</tt>.
21
25
  #
22
- # time = Time.now # => Thu Jan 18 06:10:17 CST 2007
26
+ # time = Time.now # => Thu Jan 18 06:10:17 CST 2007
23
27
  #
24
- # time.to_formatted_s(:time) # => "06:10"
25
- # time.to_s(:time) # => "06:10"
28
+ # time.to_formatted_s(:time) # => "06:10"
29
+ # time.to_s(:time) # => "06:10"
26
30
  #
27
- # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
28
- # time.to_formatted_s(:number) # => "20070118061017"
29
- # time.to_formatted_s(:short) # => "18 Jan 06:10"
30
- # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
31
- # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
32
- # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
31
+ # time.to_formatted_s(:db) # => "2007-01-18 06:10:17"
32
+ # time.to_formatted_s(:number) # => "20070118061017"
33
+ # time.to_formatted_s(:short) # => "18 Jan 06:10"
34
+ # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
35
+ # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
36
+ # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
33
37
  #
34
38
  # == Adding your own time formats to +to_formatted_s+
35
39
  # You can add your own formats to the Time::DATE_FORMATS hash.
@@ -37,8 +41,8 @@ class Time
37
41
  # or Proc instance that takes a time argument as the value.
38
42
  #
39
43
  # # config/initializers/time_formats.rb
40
- # Time::DATE_FORMATS[:month_and_year] = "%B %Y"
41
- # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
44
+ # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
45
+ # Time::DATE_FORMATS[:short_ordinal] = ->(time) { time.strftime("%B #{time.day.ordinalize}") }
42
46
  def to_formatted_s(format = :default)
43
47
  if formatter = DATE_FORMATS[format]
44
48
  formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
@@ -51,37 +55,9 @@ class Time
51
55
 
52
56
  # Returns the UTC offset as an +HH:MM formatted string.
53
57
  #
54
- # Time.local(2000).formatted_offset # => "-06:00"
55
- # Time.local(2000).formatted_offset(false) # => "-0600"
58
+ # Time.local(2000).formatted_offset # => "-06:00"
59
+ # Time.local(2000).formatted_offset(false) # => "-0600"
56
60
  def formatted_offset(colon = true, alternate_utc_string = nil)
57
61
  utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
58
62
  end
59
-
60
- # Converts a Time object to a Date, dropping hour, minute, and second precision.
61
- #
62
- # my_time = Time.now # => Mon Nov 12 22:59:51 -0500 2007
63
- # my_time.to_date # => Mon, 12 Nov 2007
64
- #
65
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
66
- # your_time.to_date # => Tue, 13 Jan 2009
67
- def to_date
68
- ::Date.new(year, month, day)
69
- end unless method_defined?(:to_date)
70
-
71
- # A method to keep Time, Date and DateTime instances interchangeable on conversions.
72
- # In this case, it simply returns +self+.
73
- def to_time
74
- self
75
- end unless method_defined?(:to_time)
76
-
77
- # Converts a Time instance to a Ruby DateTime instance, preserving UTC offset.
78
- #
79
- # my_time = Time.now # => Mon Nov 12 23:04:21 -0500 2007
80
- # my_time.to_datetime # => Mon, 12 Nov 2007 23:04:21 -0500
81
- #
82
- # your_time = Time.parse("1/13/2009 1:13:03 P.M.") # => Tue Jan 13 13:13:03 -0500 2009
83
- # your_time.to_datetime # => Tue, 13 Jan 2009 13:13:03 -0500
84
- def to_datetime
85
- ::DateTime.civil(year, month, day, hour, min, sec, Rational(utc_offset, 86400))
86
- end unless method_defined?(:to_datetime)
87
63
  end
@@ -0,0 +1,5 @@
1
+ require 'active_support/core_ext/infinite_comparable'
2
+
3
+ class Time
4
+ include InfiniteComparable
5
+ end
@@ -1,30 +1,3 @@
1
- # Pre-1.9 versions of Ruby have a bug with marshaling Time instances, where utc instances are
2
- # unmarshalled in the local zone, instead of utc. We're layering behavior on the _dump and _load
3
- # methods so that utc instances can be flagged on dump, and coerced back to utc on load.
4
- if !Marshal.load(Marshal.dump(Time.now.utc)).utc?
5
- class Time
6
- class << self
7
- alias_method :_load_without_utc_flag, :_load
8
- def _load(marshaled_time)
9
- time = _load_without_utc_flag(marshaled_time)
10
- time.instance_eval do
11
- if defined?(@marshal_with_utc_coercion)
12
- val = remove_instance_variable("@marshal_with_utc_coercion")
13
- end
14
- val ? utc : self
15
- end
16
- end
17
- end
18
-
19
- alias_method :_dump_without_utc_flag, :_dump
20
- def _dump(*args)
21
- obj = dup
22
- obj.instance_variable_set('@marshal_with_utc_coercion', utc?)
23
- obj.send :_dump_without_utc_flag, *args
24
- end
25
- end
26
- end
27
-
28
1
  # Ruby 1.9.2 adds utc_offset and zone to Time, but marshaling only
29
2
  # preserves utc_offset. Preserve zone also, even though it may not
30
3
  # work in some edge cases.
@@ -26,11 +26,11 @@ class Time
26
26
  # around_filter :set_time_zone
27
27
  #
28
28
  # def set_time_zone
29
- # old_time_zone = Time.zone
30
- # Time.zone = current_user.time_zone if logged_in?
31
- # yield
32
- # ensure
33
- # Time.zone = old_time_zone
29
+ # if logged_in?
30
+ # Time.use_zone(current_user.time_zone) { yield }
31
+ # else
32
+ # yield
33
+ # end
34
34
  # end
35
35
  # end
36
36
  def zone=(time_zone)
@@ -50,13 +50,21 @@ class Time
50
50
 
51
51
  # Returns a TimeZone instance or nil, or raises an ArgumentError for invalid timezones.
52
52
  def find_zone!(time_zone)
53
- return time_zone if time_zone.nil? || time_zone.is_a?(ActiveSupport::TimeZone)
54
- # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
55
- unless time_zone.respond_to?(:period_for_local)
56
- time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
53
+ if !time_zone || time_zone.is_a?(ActiveSupport::TimeZone)
54
+ time_zone
55
+ else
56
+ # lookup timezone based on identifier (unless we've been passed a TZInfo::Timezone)
57
+ unless time_zone.respond_to?(:period_for_local)
58
+ time_zone = ActiveSupport::TimeZone[time_zone] || TZInfo::Timezone.get(time_zone)
59
+ end
60
+
61
+ # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
62
+ if time_zone.is_a?(ActiveSupport::TimeZone)
63
+ time_zone
64
+ else
65
+ ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
66
+ end
57
67
  end
58
- # Return if a TimeZone instance, or wrap in a TimeZone instance if a TZInfo::Timezone
59
- time_zone.is_a?(ActiveSupport::TimeZone) ? time_zone : ActiveSupport::TimeZone.create(time_zone.name, nil, time_zone)
60
68
  rescue TZInfo::InvalidTimezoneIdentifier
61
69
  raise ArgumentError, "Invalid Timezone: #{time_zone}"
62
70
  end
@@ -68,8 +76,8 @@ class Time
68
76
 
69
77
  # Returns the simultaneous time in <tt>Time.zone</tt>.
70
78
  #
71
- # Time.zone = 'Hawaii' # => 'Hawaii'
72
- # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
79
+ # Time.zone = 'Hawaii' # => 'Hawaii'
80
+ # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
73
81
  #
74
82
  # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
75
83
  # instead of the operating system's time zone.
@@ -77,10 +85,12 @@ class Time
77
85
  # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
78
86
  # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
79
87
  #
80
- # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
88
+ # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
81
89
  def in_time_zone(zone = ::Time.zone)
82
- return self unless zone
83
-
84
- ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
90
+ if zone
91
+ ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
92
+ else
93
+ self
94
+ end
85
95
  end
86
96
  end
@@ -1,22 +1,18 @@
1
1
  # encoding: utf-8
2
2
 
3
- if RUBY_VERSION >= '1.9'
4
- require 'uri'
3
+ require 'uri'
4
+ str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
5
+ parser = URI::Parser.new
5
6
 
6
- str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
7
-
8
- parser = URI::Parser.new
9
-
10
- unless str == parser.unescape(parser.escape(str))
11
- URI::Parser.class_eval do
12
- remove_method :unescape
13
- def unescape(str, escaped = /%[a-fA-F\d]{2}/)
14
- # TODO: Are we actually sure that ASCII == UTF-8?
15
- # YK: My initial experiments say yes, but let's be sure please
16
- enc = str.encoding
17
- enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
18
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
19
- end
7
+ unless str == parser.unescape(parser.escape(str))
8
+ URI::Parser.class_eval do
9
+ remove_method :unescape
10
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
11
+ # TODO: Are we actually sure that ASCII == UTF-8?
12
+ # YK: My initial experiments say yes, but let's be sure please
13
+ enc = str.encoding
14
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
15
+ str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
20
16
  end
21
17
  end
22
18
  end
@@ -24,7 +20,7 @@ end
24
20
  module URI
25
21
  class << self
26
22
  def parser
27
- @parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
23
+ @parser ||= URI::Parser.new
28
24
  end
29
25
  end
30
26
  end
@@ -1,5 +1,6 @@
1
1
  require 'set'
2
2
  require 'thread'
3
+ require 'thread_safe'
3
4
  require 'pathname'
4
5
  require 'active_support/core_ext/module/aliasing'
5
6
  require 'active_support/core_ext/module/attribute_accessors'
@@ -43,8 +44,9 @@ module ActiveSupport #:nodoc:
43
44
  mattr_accessor :autoload_once_paths
44
45
  self.autoload_once_paths = []
45
46
 
46
- # An array of qualified constant names that have been loaded. Adding a name to
47
- # this array will cause it to be unloaded the next time Dependencies are cleared.
47
+ # An array of qualified constant names that have been loaded. Adding a name
48
+ # to this array will cause it to be unloaded the next time Dependencies are
49
+ # cleared.
48
50
  mattr_accessor :autoloaded_constants
49
51
  self.autoloaded_constants = []
50
52
 
@@ -53,30 +55,32 @@ module ActiveSupport #:nodoc:
53
55
  mattr_accessor :explicitly_unloadable_constants
54
56
  self.explicitly_unloadable_constants = []
55
57
 
56
- # The logger is used for generating information on the action run-time (including benchmarking) if available.
57
- # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
58
+ # The logger is used for generating information on the action run-time
59
+ # (including benchmarking) if available. Can be set to nil for no logging.
60
+ # Compatible with both Ruby's own Logger and Log4r loggers.
58
61
  mattr_accessor :logger
59
62
 
60
- # Set to true to enable logging of const_missing and file loads
63
+ # Set to +true+ to enable logging of const_missing and file loads.
61
64
  mattr_accessor :log_activity
62
65
  self.log_activity = false
63
66
 
64
- # The WatchStack keeps a stack of the modules being watched as files are loaded.
65
- # If a file in the process of being loaded (parent.rb) triggers the load of
66
- # another file (child.rb) the stack will ensure that child.rb handles the new
67
- # constants.
67
+ # The WatchStack keeps a stack of the modules being watched as files are
68
+ # loaded. If a file in the process of being loaded (parent.rb) triggers the
69
+ # load of another file (child.rb) the stack will ensure that child.rb
70
+ # handles the new constants.
68
71
  #
69
72
  # If child.rb is being autoloaded, its constants will be added to
70
73
  # autoloaded_constants. If it was being `require`d, they will be discarded.
71
74
  #
72
75
  # This is handled by walking back up the watch stack and adding the constants
73
- # found by child.rb to the list of original constants in parent.rb
76
+ # found by child.rb to the list of original constants in parent.rb.
74
77
  class WatchStack
75
78
  include Enumerable
76
79
 
77
80
  # @watching is a stack of lists of constants being watched. For instance,
78
- # if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
79
- # then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
81
+ # if parent.rb is autoloaded, the stack will look like [[Object]]. If
82
+ # parent.rb then requires namespace/child.rb, the stack will look like
83
+ # [[Object], [Namespace]].
80
84
 
81
85
  def initialize
82
86
  @watching = []
@@ -91,7 +95,8 @@ module ActiveSupport #:nodoc:
91
95
  !@watching.empty?
92
96
  end
93
97
 
94
- # return a list of new constants found since the last call to watch_namespaces
98
+ # Returns a list of new constants found since the last call to
99
+ # <tt>watch_namespaces</tt>.
95
100
  def new_constants
96
101
  constants = []
97
102
 
@@ -105,7 +110,7 @@ module ActiveSupport #:nodoc:
105
110
  next unless mod.is_a?(Module)
106
111
 
107
112
  # Get a list of the constants that were added
108
- new_constants = mod.local_constant_names - original_constants
113
+ new_constants = mod.local_constants - original_constants
109
114
 
110
115
  # self[namespace] returns an Array of the constants that are being evaluated
111
116
  # for that namespace. For instance, if parent.rb requires child.rb, the first
@@ -127,18 +132,17 @@ module ActiveSupport #:nodoc:
127
132
  pop_modules(@watching.pop)
128
133
  end
129
134
 
130
- # Add a set of modules to the watch stack, remembering the initial constants
135
+ # Add a set of modules to the watch stack, remembering the initial
136
+ # constants.
131
137
  def watch_namespaces(namespaces)
132
- watching = []
133
- namespaces.map do |namespace|
138
+ @watching << namespaces.map do |namespace|
134
139
  module_name = Dependencies.to_constant_name(namespace)
135
140
  original_constants = Dependencies.qualified_const_defined?(module_name) ?
136
- Inflector.constantize(module_name).local_constant_names : []
141
+ Inflector.constantize(module_name).local_constants : []
137
142
 
138
- watching << module_name
139
143
  @stack[module_name] << original_constants
144
+ module_name
140
145
  end
141
- @watching << watching
142
146
  end
143
147
 
144
148
  private
@@ -151,7 +155,7 @@ module ActiveSupport #:nodoc:
151
155
  mattr_accessor :constant_watch_stack
152
156
  self.constant_watch_stack = WatchStack.new
153
157
 
154
- # Module includes this module
158
+ # Module includes this module.
155
159
  module ModuleConstMissing #:nodoc:
156
160
  def self.append_features(base)
157
161
  base.class_eval do
@@ -170,36 +174,13 @@ module ActiveSupport #:nodoc:
170
174
  end
171
175
  end
172
176
 
173
- # Use const_missing to autoload associations so we don't have to
174
- # require_association when using single-table inheritance.
175
- def const_missing(const_name, nesting = nil)
176
- klass_name = name.presence || "Object"
177
-
178
- unless nesting
179
- # We'll assume that the nesting of Foo::Bar is ["Foo::Bar", "Foo"]
180
- # even though it might not be, such as in the case of
181
- # class Foo::Bar; Baz; end
182
- nesting = []
183
- klass_name.to_s.scan(/::|$/) { nesting.unshift $` }
184
- end
185
-
186
- # If there are multiple levels of nesting to search under, the top
187
- # level is the one we want to report as the lookup fail.
188
- error = nil
189
-
190
- nesting.each do |namespace|
191
- begin
192
- return Dependencies.load_missing_constant Inflector.constantize(namespace), const_name
193
- rescue NoMethodError then raise
194
- rescue NameError => e
195
- error ||= e
196
- end
197
- end
198
-
199
- # Raise the first error for this set. If this const_missing came from an
200
- # earlier const_missing, this will result in the real error bubbling
201
- # all the way up
202
- raise error
177
+ def const_missing(const_name)
178
+ # The interpreter does not pass nesting information, and in the
179
+ # case of anonymous modules we cannot even make the trade-off of
180
+ # assuming their name reflects the nesting. Resort to Object as
181
+ # the only meaningful guess we can make.
182
+ from_mod = anonymous? ? ::Object : self
183
+ Dependencies.load_missing_constant(from_mod, const_name)
203
184
  end
204
185
 
205
186
  def unloadable(const_desc = self)
@@ -207,7 +188,7 @@ module ActiveSupport #:nodoc:
207
188
  end
208
189
  end
209
190
 
210
- # Object includes this module
191
+ # Object includes this module.
211
192
  module Loadable #:nodoc:
212
193
  def self.exclude_from(base)
213
194
  base.class_eval { define_method(:load, Kernel.instance_method(:load)) }
@@ -222,11 +203,7 @@ module ActiveSupport #:nodoc:
222
203
  raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
223
204
  end
224
205
 
225
- Dependencies.depend_on(file_name, false, message)
226
- end
227
-
228
- def require_association(file_name)
229
- Dependencies.associate_with(file_name)
206
+ Dependencies.depend_on(file_name, message)
230
207
  end
231
208
 
232
209
  def load_dependency(file)
@@ -252,25 +229,25 @@ module ActiveSupport #:nodoc:
252
229
  result
253
230
  end
254
231
 
255
- # Mark the given constant as unloadable. Unloadable constants are removed each
256
- # time dependencies are cleared.
232
+ # Mark the given constant as unloadable. Unloadable constants are removed
233
+ # each time dependencies are cleared.
257
234
  #
258
235
  # Note that marking a constant for unloading need only be done once. Setup
259
236
  # or init scripts may list each unloadable constant that may need unloading;
260
- # each constant will be removed for every subsequent clear, as opposed to for
261
- # the first clear.
237
+ # each constant will be removed for every subsequent clear, as opposed to
238
+ # for the first clear.
262
239
  #
263
240
  # The provided constant descriptor may be a (non-anonymous) module or class,
264
241
  # or a qualified constant name as a string or symbol.
265
242
  #
266
- # Returns true if the constant was not previously marked for unloading, false
267
- # otherwise.
243
+ # Returns +true+ if the constant was not previously marked for unloading,
244
+ # +false+ otherwise.
268
245
  def unloadable(const_desc)
269
246
  Dependencies.mark_for_unload const_desc
270
247
  end
271
248
  end
272
249
 
273
- # Exception file-blaming
250
+ # Exception file-blaming.
274
251
  module Blamable #:nodoc:
275
252
  def blame_file!(file)
276
253
  (@blamed_files ||= []).unshift file
@@ -295,33 +272,26 @@ module ActiveSupport #:nodoc:
295
272
  Object.class_eval { include Loadable }
296
273
  Module.class_eval { include ModuleConstMissing }
297
274
  Exception.class_eval { include Blamable }
298
- true
299
275
  end
300
276
 
301
277
  def unhook!
302
278
  ModuleConstMissing.exclude_from(Module)
303
279
  Loadable.exclude_from(Object)
304
- true
305
280
  end
306
281
 
307
282
  def load?
308
283
  mechanism == :load
309
284
  end
310
285
 
311
- def depend_on(file_name, swallow_load_errors = false, message = "No such file to load -- %s.rb")
286
+ def depend_on(file_name, message = "No such file to load -- %s.rb")
312
287
  path = search_for_file(file_name)
313
288
  require_or_load(path || file_name)
314
289
  rescue LoadError => load_error
315
- unless swallow_load_errors
316
- if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
317
- raise LoadError.new(message % file_name).copy_blame!(load_error)
318
- end
319
- raise
290
+ if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
291
+ load_error.message.replace(message % file_name)
292
+ load_error.copy_blame!(load_error)
320
293
  end
321
- end
322
-
323
- def associate_with(file_name)
324
- depend_on(file_name, true)
294
+ raise
325
295
  end
326
296
 
327
297
  def clear
@@ -332,7 +302,7 @@ module ActiveSupport #:nodoc:
332
302
 
333
303
  def require_or_load(file_name, const_path = nil)
334
304
  log_call file_name, const_path
335
- file_name = $1 if file_name =~ /^(.*)\.rb$/
305
+ file_name = $` if file_name =~ /\.rb\z/
336
306
  expanded = File.expand_path(file_name)
337
307
  return if loaded.include?(expanded)
338
308
 
@@ -365,36 +335,19 @@ module ActiveSupport #:nodoc:
365
335
 
366
336
  # Record history *after* loading so first load gets warnings.
367
337
  history << expanded
368
- return result
338
+ result
369
339
  end
370
340
 
371
341
  # Is the provided constant path defined?
372
- if Module.method(:const_defined?).arity == 1
373
- def qualified_const_defined?(path)
374
- Object.qualified_const_defined?(path.sub(/^::/, ''))
375
- end
376
- else
377
- def qualified_const_defined?(path)
378
- Object.qualified_const_defined?(path.sub(/^::/, ''), false)
379
- end
380
- end
381
-
382
- if Module.method(:const_defined?).arity == 1
383
- # Does this module define this constant?
384
- # Wrapper to accommodate changing Module#const_defined? in Ruby 1.9
385
- def local_const_defined?(mod, const)
386
- mod.const_defined?(const)
387
- end
388
- else
389
- def local_const_defined?(mod, const) #:nodoc:
390
- mod.const_defined?(const, false)
391
- end
342
+ def qualified_const_defined?(path)
343
+ Object.qualified_const_defined?(path.sub(/^::/, ''), false)
392
344
  end
393
345
 
394
- # Given +path+, a filesystem path to a ruby file, return an array of constant
395
- # paths which would cause Dependencies to attempt to load this file.
346
+ # Given +path+, a filesystem path to a ruby file, return an array of
347
+ # constant paths which would cause Dependencies to attempt to load this
348
+ # file.
396
349
  def loadable_constants_for_path(path, bases = autoload_paths)
397
- path = $1 if path =~ /\A(.*)\.rb\Z/
350
+ path = $` if path =~ /\.rb\z/
398
351
  expanded_path = File.expand_path(path)
399
352
  paths = []
400
353
 
@@ -425,7 +378,8 @@ module ActiveSupport #:nodoc:
425
378
  end
426
379
 
427
380
  # Does the provided path_suffix correspond to an autoloadable module?
428
- # Instead of returning a boolean, the autoload base for this module is returned.
381
+ # Instead of returning a boolean, the autoload base for this module is
382
+ # returned.
429
383
  def autoloadable_module?(path_suffix)
430
384
  autoload_paths.each do |load_path|
431
385
  return load_path if File.directory? File.join(load_path, path_suffix)
@@ -439,16 +393,16 @@ module ActiveSupport #:nodoc:
439
393
  end
440
394
 
441
395
  # Attempt to autoload the provided module name by searching for a directory
442
- # matching the expected path suffix. If found, the module is created and assigned
443
- # to +into+'s constants with the name +const_name+. Provided that the directory
444
- # was loaded from a reloadable base path, it is added to the set of constants
445
- # that are to be unloaded.
396
+ # matching the expected path suffix. If found, the module is created and
397
+ # assigned to +into+'s constants with the name +const_name+. Provided that
398
+ # the directory was loaded from a reloadable base path, it is added to the
399
+ # set of constants that are to be unloaded.
446
400
  def autoload_module!(into, const_name, qualified_name, path_suffix)
447
401
  return nil unless base_path = autoloadable_module?(path_suffix)
448
402
  mod = Module.new
449
403
  into.const_set const_name, mod
450
404
  autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
451
- return mod
405
+ mod
452
406
  end
453
407
 
454
408
  # Load the file at the provided path. +const_paths+ is a set of qualified
@@ -456,13 +410,13 @@ module ActiveSupport #:nodoc:
456
410
  # addition of these constants. Each that is defined will be marked as
457
411
  # autoloaded, and will be removed when Dependencies.clear is next called.
458
412
  #
459
- # If the second parameter is left off, then Dependencies will construct a set
460
- # of names that the file at +path+ may define. See
413
+ # If the second parameter is left off, then Dependencies will construct a
414
+ # set of names that the file at +path+ may define. See
461
415
  # +loadable_constants_for_path+ for more details.
462
416
  def load_file(path, const_paths = loadable_constants_for_path(path))
463
417
  log_call path, const_paths
464
418
  const_paths = [const_paths].compact unless const_paths.is_a? Array
465
- parent_paths = const_paths.collect { |const_path| /(.*)::[^:]+\Z/ =~ const_path ? $1 : :Object }
419
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
466
420
 
467
421
  result = nil
468
422
  newly_defined_paths = new_constants_in(*parent_paths) do
@@ -472,18 +426,18 @@ module ActiveSupport #:nodoc:
472
426
  autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
473
427
  autoloaded_constants.uniq!
474
428
  log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
475
- return result
429
+ result
476
430
  end
477
431
 
478
- # Return the constant path for the provided parent and constant name.
432
+ # Returns the constant path for the provided parent and constant name.
479
433
  def qualified_name_for(mod, name)
480
434
  mod_name = to_constant_name mod
481
435
  mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
482
436
  end
483
437
 
484
438
  # Load the constant named +const_name+ which is missing from +from_mod+. If
485
- # it is not possible to load the constant into from_mod, try its parent module
486
- # using const_missing.
439
+ # it is not possible to load the constant into from_mod, try its parent
440
+ # module using +const_missing+.
487
441
  def load_missing_constant(from_mod, const_name)
488
442
  log_call from_mod, const_name
489
443
 
@@ -491,26 +445,52 @@ module ActiveSupport #:nodoc:
491
445
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
492
446
  end
493
447
 
494
- raise NameError, "#{from_mod} is not missing constant #{const_name}!" if local_const_defined?(from_mod, const_name)
448
+ raise NameError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name, false)
495
449
 
496
450
  qualified_name = qualified_name_for from_mod, const_name
497
451
  path_suffix = qualified_name.underscore
498
452
 
499
453
  file_path = search_for_file(path_suffix)
500
454
 
501
- if file_path && ! loaded.include?(File.expand_path(file_path).sub(/\.rb\z/, '')) # We found a matching file to load
502
- require_or_load file_path
503
- raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name)
504
- return from_mod.const_get(const_name)
455
+ if file_path
456
+ expanded = File.expand_path(file_path)
457
+ expanded.sub!(/\.rb\z/, '')
458
+
459
+ if loaded.include?(expanded)
460
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
461
+ else
462
+ require_or_load(expanded)
463
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
464
+ return from_mod.const_get(const_name)
465
+ end
505
466
  elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
506
467
  return mod
507
468
  elsif (parent = from_mod.parent) && parent != from_mod &&
508
- ! from_mod.parents.any? { |p| local_const_defined?(p, const_name) }
469
+ ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
509
470
  # If our parents do not have a constant named +const_name+ then we are free
510
471
  # to attempt to load upwards. If they do have such a constant, then this
511
472
  # const_missing must be due to from_mod::const_name, which should not
512
473
  # return constants from from_mod's parents.
513
474
  begin
475
+ # Since Ruby does not pass the nesting at the point the unknown
476
+ # constant triggered the callback we cannot fully emulate constant
477
+ # name lookup and need to make a trade-off: we are going to assume
478
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
479
+ # though it might not be. Counterexamples are
480
+ #
481
+ # class Foo::Bar
482
+ # Module.nesting # => [Foo::Bar]
483
+ # end
484
+ #
485
+ # or
486
+ #
487
+ # module M::N
488
+ # module S::T
489
+ # Module.nesting # => [S::T, M::N]
490
+ # end
491
+ # end
492
+ #
493
+ # for example.
514
494
  return parent.const_missing(const_name)
515
495
  rescue NameError => e
516
496
  raise unless e.missing_name? qualified_name_for(parent, const_name)
@@ -519,7 +499,7 @@ module ActiveSupport #:nodoc:
519
499
 
520
500
  raise NameError,
521
501
  "uninitialized constant #{qualified_name}",
522
- caller.reject {|l| l.starts_with? __FILE__ }
502
+ caller.reject { |l| l.starts_with? __FILE__ }
523
503
  end
524
504
 
525
505
  # Remove the constants that have been autoloaded, and those that have been
@@ -538,7 +518,7 @@ module ActiveSupport #:nodoc:
538
518
 
539
519
  class ClassCache
540
520
  def initialize
541
- @store = Hash.new
521
+ @store = ThreadSafe::Cache.new
542
522
  end
543
523
 
544
524
  def empty?
@@ -557,10 +537,7 @@ module ActiveSupport #:nodoc:
557
537
 
558
538
  def safe_get(key)
559
539
  key = key.name if key.respond_to?(:name)
560
- @store[key] || begin
561
- klass = Inflector.safe_constantize(key)
562
- @store[key] = klass
563
- end
540
+ @store[key] ||= Inflector.safe_constantize(key)
564
541
  end
565
542
 
566
543
  def store(klass)
@@ -589,14 +566,13 @@ module ActiveSupport #:nodoc:
589
566
  end
590
567
 
591
568
  # Get the reference for class named +name+ if one exists.
592
- # Otherwise returns nil.
569
+ # Otherwise returns +nil+.
593
570
  def safe_constantize(name)
594
571
  Reference.safe_get(name)
595
572
  end
596
573
 
597
574
  # Determine if the given constant has been automatically loaded.
598
575
  def autoloaded?(desc)
599
- # No name => anonymous module.
600
576
  return false if desc.is_a?(Module) && desc.anonymous?
601
577
  name = to_constant_name desc
602
578
  return false unless qualified_const_defined? name
@@ -614,10 +590,10 @@ module ActiveSupport #:nodoc:
614
590
  def mark_for_unload(const_desc)
615
591
  name = to_constant_name const_desc
616
592
  if explicitly_unloadable_constants.include? name
617
- return false
593
+ false
618
594
  else
619
595
  explicitly_unloadable_constants << name
620
- return true
596
+ true
621
597
  end
622
598
  end
623
599
 
@@ -645,10 +621,10 @@ module ActiveSupport #:nodoc:
645
621
  return new_constants unless aborting
646
622
 
647
623
  log "Error during loading, removing partially loaded constants "
648
- new_constants.each {|c| remove_constant(c) }.clear
624
+ new_constants.each { |c| remove_constant(c) }.clear
649
625
  end
650
626
 
651
- return []
627
+ []
652
628
  end
653
629
 
654
630
  # Convert the provided const desc to a qualified constant name (as a string).
@@ -665,19 +641,62 @@ module ActiveSupport #:nodoc:
665
641
  end
666
642
 
667
643
  def remove_constant(const) #:nodoc:
668
- return false unless qualified_const_defined? const
644
+ # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
645
+ normalized = const.to_s.sub(/\A::/, '')
646
+ normalized.sub!(/\A(Object::)+/, '')
669
647
 
670
- # Normalize ::Foo, Foo, Object::Foo, and ::Object::Foo to Object::Foo
671
- names = const.to_s.sub(/^::(Object)?/, 'Object::').split("::")
672
- to_remove = names.pop
673
- parent = Inflector.constantize(names * '::')
648
+ constants = normalized.split('::')
649
+ to_remove = constants.pop
650
+
651
+ if constants.empty?
652
+ parent = Object
653
+ else
654
+ # This method is robust to non-reachable constants.
655
+ #
656
+ # Non-reachable constants may be passed if some of the parents were
657
+ # autoloaded and already removed. It is easier to do a sanity check
658
+ # here than require the caller to be clever. We check the parent
659
+ # rather than the very const argument because we do not want to
660
+ # trigger Kernel#autoloads, see the comment below.
661
+ parent_name = constants.join('::')
662
+ return unless qualified_const_defined?(parent_name)
663
+ parent = constantize(parent_name)
664
+ end
674
665
 
675
666
  log "removing constant #{const}"
676
- constantized = constantize(const)
677
- constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
678
- parent.instance_eval { remove_const to_remove }
679
667
 
680
- return true
668
+ # In an autoloaded user.rb like this
669
+ #
670
+ # autoload :Foo, 'foo'
671
+ #
672
+ # class User < ActiveRecord::Base
673
+ # end
674
+ #
675
+ # we correctly register "Foo" as being autoloaded. But if the app does
676
+ # not use the "Foo" constant we need to be careful not to trigger
677
+ # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
678
+ # require the file, #autoload? and #remove_const don't.
679
+ #
680
+ # We are going to remove the constant nonetheless ---which exists as
681
+ # far as Ruby is concerned--- because if the user removes the macro
682
+ # call from a class or module that were not autoloaded, as in the
683
+ # example above with Object, accessing to that constant must err.
684
+ unless parent.autoload?(to_remove)
685
+ begin
686
+ constantized = parent.const_get(to_remove, false)
687
+ rescue NameError
688
+ log "the constant #{const} is not reachable anymore, skipping"
689
+ return
690
+ else
691
+ constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
692
+ end
693
+ end
694
+
695
+ begin
696
+ parent.instance_eval { remove_const to_remove }
697
+ rescue NameError
698
+ log "the constant #{const} is not reachable anymore, skipping"
699
+ end
681
700
  end
682
701
 
683
702
  protected