activesupport 3.1.12 → 3.2.0.rc1

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 (78) hide show
  1. data/CHANGELOG.md +1539 -30
  2. data/README.rdoc +2 -2
  3. data/lib/active_support.rb +1 -1
  4. data/lib/active_support/benchmarkable.rb +13 -18
  5. data/lib/active_support/buffered_logger.rb +43 -55
  6. data/lib/active_support/cache.rb +109 -115
  7. data/lib/active_support/cache/file_store.rb +6 -17
  8. data/lib/active_support/cache/mem_cache_store.rb +10 -2
  9. data/lib/active_support/cache/null_store.rb +44 -0
  10. data/lib/active_support/cache/strategy/local_cache.rb +2 -2
  11. data/lib/active_support/callbacks.rb +38 -35
  12. data/lib/active_support/concern.rb +5 -10
  13. data/lib/active_support/core_ext/array.rb +1 -0
  14. data/lib/active_support/core_ext/array/access.rb +1 -1
  15. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  16. data/lib/active_support/core_ext/array/wrap.rb +1 -1
  17. data/lib/active_support/core_ext/class.rb +0 -1
  18. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -2
  19. data/lib/active_support/core_ext/date/calculations.rb +37 -14
  20. data/lib/active_support/core_ext/date/freeze.rb +6 -4
  21. data/lib/active_support/core_ext/date_time/calculations.rb +1 -1
  22. data/lib/active_support/core_ext/enumerable.rb +25 -8
  23. data/lib/active_support/core_ext/file/atomic.rb +1 -2
  24. data/lib/active_support/core_ext/hash/conversions.rb +7 -25
  25. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -1
  26. data/lib/active_support/core_ext/io.rb +15 -0
  27. data/lib/active_support/core_ext/kernel.rb +0 -1
  28. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  29. data/lib/active_support/core_ext/kernel/debugger.rb +1 -7
  30. data/lib/active_support/core_ext/module.rb +2 -2
  31. data/lib/active_support/core_ext/module/attribute_accessors.rb +6 -2
  32. data/lib/active_support/core_ext/module/delegation.rb +32 -21
  33. data/lib/active_support/core_ext/module/qualified_const.rb +64 -0
  34. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  35. data/lib/active_support/core_ext/module/synchronization.rb +2 -0
  36. data/lib/active_support/core_ext/object/blank.rb +14 -2
  37. data/lib/active_support/core_ext/object/inclusion.rb +17 -7
  38. data/lib/active_support/core_ext/object/to_json.rb +2 -2
  39. data/lib/active_support/core_ext/range/conversions.rb +1 -1
  40. data/lib/active_support/core_ext/string/conversions.rb +2 -2
  41. data/lib/active_support/core_ext/string/inflections.rb +44 -6
  42. data/lib/active_support/core_ext/string/multibyte.rb +1 -1
  43. data/lib/active_support/core_ext/string/output_safety.rb +22 -25
  44. data/lib/active_support/core_ext/time/calculations.rb +66 -12
  45. data/lib/active_support/dependencies.rb +51 -52
  46. data/lib/active_support/file_update_checker.rb +100 -15
  47. data/lib/active_support/hash_with_indifferent_access.rb +5 -1
  48. data/lib/active_support/i18n.rb +1 -1
  49. data/lib/active_support/i18n_railtie.rb +9 -4
  50. data/lib/active_support/inflections.rb +3 -3
  51. data/lib/active_support/inflector/inflections.rb +53 -92
  52. data/lib/active_support/inflector/methods.rb +173 -9
  53. data/lib/active_support/json/decoding.rb +3 -17
  54. data/lib/active_support/json/encoding.rb +11 -14
  55. data/lib/active_support/memoizable.rb +12 -1
  56. data/lib/active_support/message_encryptor.rb +52 -20
  57. data/lib/active_support/message_verifier.rb +15 -4
  58. data/lib/active_support/notifications.rb +87 -14
  59. data/lib/active_support/notifications/instrumenter.rb +1 -2
  60. data/lib/active_support/ordered_hash.rb +7 -3
  61. data/lib/active_support/tagged_logging.rb +63 -0
  62. data/lib/active_support/testing/assertions.rb +1 -1
  63. data/lib/active_support/testing/mochaing.rb +2 -2
  64. data/lib/active_support/testing/performance/ruby.rb +1 -1
  65. data/lib/active_support/testing/setup_and_teardown.rb +4 -12
  66. data/lib/active_support/time_with_zone.rb +6 -3
  67. data/lib/active_support/values/time_zone.rb +3 -7
  68. data/lib/active_support/version.rb +3 -3
  69. data/lib/active_support/xml_mini.rb +3 -3
  70. data/lib/active_support/xml_mini/jdom.rb +4 -10
  71. metadata +28 -21
  72. checksums.yaml +0 -7
  73. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  74. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  75. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  76. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  77. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  78. data/lib/active_support/secure_random.rb +0 -6
@@ -1,3 +1,6 @@
1
+ # encoding: utf-8
2
+ require 'active_support/core_ext/string/encoding'
3
+
1
4
  class Object
2
5
  # An object is blank if it's false, empty, or a whitespace string.
3
6
  # For example, "", " ", +nil+, [], and {} are all blank.
@@ -22,7 +25,7 @@ class Object
22
25
  # <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
23
26
  #
24
27
  # This is handy for any representation of objects where blank is the same
25
- # as not present at all. For example, this simplifies a common check for
28
+ # as not present at all. For example, this simplifies a common check for
26
29
  # HTTP POST/query parameters:
27
30
  #
28
31
  # state = params[:state] if params[:state].present?
@@ -86,14 +89,23 @@ class Hash
86
89
  end
87
90
 
88
91
  class String
92
+ # 0x3000: fullwidth whitespace
93
+ NON_WHITESPACE_REGEXP = %r![^\s#{[0x3000].pack("U")}]!
94
+
89
95
  # A string is blank if it's empty or contains whitespaces only:
90
96
  #
91
97
  # "".blank? # => true
92
98
  # " ".blank? # => true
99
+ # " ".blank? # => true
93
100
  # " something here ".blank? # => false
94
101
  #
95
102
  def blank?
96
- self !~ /\S/
103
+ # 1.8 does not takes [:space:] properly
104
+ if encoding_aware?
105
+ self !~ /[^[:space:]]/
106
+ else
107
+ self !~ NON_WHITESPACE_REGEXP
108
+ end
97
109
  end
98
110
  end
99
111
 
@@ -1,15 +1,25 @@
1
1
  class Object
2
- # Returns true if this object is included in the argument. Argument must be
3
- # any object which responds to +#include?+. Usage:
2
+ # Returns true if this object is included in the argument(s). Argument must be
3
+ # any object which responds to +#include?+ or optionally, multiple arguments can be passed in. Usage:
4
4
  #
5
5
  # characters = ["Konata", "Kagami", "Tsukasa"]
6
6
  # "Konata".in?(characters) # => true
7
+ #
8
+ # character = "Konata"
9
+ # character.in?("Konata", "Kagami", "Tsukasa") # => true
7
10
  #
8
- # This will throw an ArgumentError if the argument doesn't respond
11
+ # This will throw an ArgumentError if a single argument is passed in and it doesn't respond
9
12
  # to +#include?+.
10
- def in?(another_object)
11
- another_object.include?(self)
12
- rescue NoMethodError
13
- raise ArgumentError.new("The parameter passed to #in? must respond to #include?")
13
+ def in?(*args)
14
+ if args.length > 1
15
+ args.include? self
16
+ else
17
+ another_object = args.first
18
+ if another_object.respond_to? :include?
19
+ another_object.include? self
20
+ else
21
+ raise ArgumentError.new("The single parameter passed to #in? must respond to #include?")
22
+ end
23
+ end
14
24
  end
15
25
  end
@@ -10,10 +10,10 @@ end
10
10
  # several cases (for instance, the JSON implementation for Hash does not work) with inheritance
11
11
  # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
12
12
  [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass|
13
- klass.class_eval <<-RUBY, __FILE__, __LINE__
13
+ klass.class_eval do
14
14
  # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info.
15
15
  def to_json(options = nil)
16
16
  ActiveSupport::JSON.encode(self, options)
17
17
  end
18
- RUBY
18
+ end
19
19
  end
@@ -7,7 +7,7 @@ class Range
7
7
  #
8
8
  # ==== Example
9
9
  #
10
- # [1..100].to_formatted_s # => "1..100"
10
+ # (1..100).to_formatted_s # => "1..100"
11
11
  def to_formatted_s(format = :default)
12
12
  if formatter = RANGE_FORMATS[format]
13
13
  formatter.call(first, last)
@@ -35,9 +35,9 @@ class String
35
35
  # Form can be either :utc (default) or :local.
36
36
  def to_time(form = :utc)
37
37
  return nil if self.blank?
38
- d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction).map { |arg| arg || 0 }
38
+ d = ::Date._parse(self, false).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset).map { |arg| arg || 0 }
39
39
  d[6] *= 1000000
40
- ::Time.send("#{form}_time", *d)
40
+ ::Time.send("#{form}_time", *d[0..6]) - d[7]
41
41
  end
42
42
 
43
43
  def to_date
@@ -1,5 +1,4 @@
1
1
  require 'active_support/inflector/methods'
2
- require 'active_support/inflector/inflections'
3
2
  require 'active_support/inflector/transliterate'
4
3
 
5
4
  # String inflections define new methods on the String class to transform names for different purposes.
@@ -10,14 +9,25 @@ require 'active_support/inflector/transliterate'
10
9
  class String
11
10
  # Returns the plural form of the word in the string.
12
11
  #
12
+ # If the optional parameter +count+ is specified,
13
+ # the singular form will be returned if <tt>count == 1</tt>.
14
+ # For any other value of +count+ the plural will be returned.
15
+ #
16
+ # ==== Examples
13
17
  # "post".pluralize # => "posts"
14
18
  # "octopus".pluralize # => "octopi"
15
19
  # "sheep".pluralize # => "sheep"
16
20
  # "words".pluralize # => "words"
17
21
  # "the blue mailman".pluralize # => "the blue mailmen"
18
22
  # "CamelOctopus".pluralize # => "CamelOctopi"
19
- def pluralize
20
- ActiveSupport::Inflector.pluralize(self)
23
+ # "apple".pluralize(1) # => "apple"
24
+ # "apple".pluralize(2) # => "apples"
25
+ def pluralize(count = nil)
26
+ if count == 1
27
+ self
28
+ else
29
+ ActiveSupport::Inflector.pluralize(self)
30
+ end
21
31
  end
22
32
 
23
33
  # The reverse of +pluralize+, returns the singular form of a word in a string.
@@ -34,15 +44,28 @@ class String
34
44
 
35
45
  # +constantize+ tries to find a declared constant with the name specified
36
46
  # in the string. It raises a NameError when the name is not in CamelCase
37
- # or is not initialized.
47
+ # or is not initialized. See ActiveSupport::Inflector.constantize
38
48
  #
39
49
  # Examples
40
- # "Module".constantize # => Module
41
- # "Class".constantize # => Class
50
+ # "Module".constantize # => Module
51
+ # "Class".constantize # => Class
52
+ # "blargle".constantize # => NameError: wrong constant name blargle
42
53
  def constantize
43
54
  ActiveSupport::Inflector.constantize(self)
44
55
  end
45
56
 
57
+ # +safe_constantize+ tries to find a declared constant with the name specified
58
+ # in the string. It returns nil when the name is not in CamelCase
59
+ # or is not initialized. See ActiveSupport::Inflector.safe_constantize
60
+ #
61
+ # Examples
62
+ # "Module".safe_constantize # => Module
63
+ # "Class".safe_constantize # => Class
64
+ # "blargle".safe_constantize # => nil
65
+ def safe_constantize
66
+ ActiveSupport::Inflector.safe_constantize(self)
67
+ end
68
+
46
69
  # By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
47
70
  # is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
48
71
  #
@@ -94,10 +117,25 @@ class String
94
117
  #
95
118
  # "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
96
119
  # "Inflections".demodulize # => "Inflections"
120
+ #
121
+ # See also +deconstantize+.
97
122
  def demodulize
98
123
  ActiveSupport::Inflector.demodulize(self)
99
124
  end
100
125
 
126
+ # Removes the rightmost segment from the constant expression in the string.
127
+ #
128
+ # "Net::HTTP".deconstantize # => "Net"
129
+ # "::Net::HTTP".deconstantize # => "::Net"
130
+ # "String".deconstantize # => ""
131
+ # "::String".deconstantize # => ""
132
+ # "".deconstantize # => ""
133
+ #
134
+ # See also +demodulize+.
135
+ def deconstantize
136
+ ActiveSupport::Inflector.deconstantize(self)
137
+ end
138
+
101
139
  # Replaces special characters in a string so that it may be used as part of a 'pretty' URL.
102
140
  #
103
141
  # ==== Examples
@@ -44,7 +44,7 @@ class String
44
44
  end
45
45
  end
46
46
 
47
- def is_utf8? #:nodoc
47
+ def is_utf8?
48
48
  case encoding
49
49
  when Encoding::UTF_8
50
50
  valid_encoding?
@@ -3,10 +3,11 @@ require 'active_support/core_ext/kernel/singleton_class'
3
3
 
4
4
  class ERB
5
5
  module Util
6
- HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#x27;' }
6
+ HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
7
7
  JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
8
8
 
9
- if RUBY_VERSION >= '1.9'
9
+ # Detect whether 1.9 can transcode with XML escaping.
10
+ if '"&gt;&lt;&amp;&quot;"' == ('><&"'.encode('utf-8', :xml => :attr) rescue false)
10
11
  # A utility method for escaping HTML tag characters.
11
12
  # This method is also aliased as <tt>h</tt>.
12
13
  #
@@ -21,7 +22,7 @@ class ERB
21
22
  if s.html_safe?
22
23
  s
23
24
  else
24
- s.gsub(/[&"'><]/, HTML_ESCAPE).html_safe
25
+ s.encode(s.encoding, :xml => :attr)[1...-1].html_safe
25
26
  end
26
27
  end
27
28
  else
@@ -30,7 +31,7 @@ class ERB
30
31
  if s.html_safe?
31
32
  s
32
33
  else
33
- s.gsub(/[&"'><]/n) { |special| HTML_ESCAPE[special] }.html_safe
34
+ s.gsub(/[&"><]/n) { |special| HTML_ESCAPE[special] }.html_safe
34
35
  end
35
36
  end
36
37
  end
@@ -97,39 +98,29 @@ module ActiveSupport #:nodoc:
97
98
  end
98
99
  end
99
100
 
100
- def [](*args)
101
- return super if args.size < 2
102
-
103
- if html_safe?
104
- new_safe_buffer = super
105
- new_safe_buffer.instance_eval { @html_safe = true }
106
- new_safe_buffer
107
- else
108
- to_str[*args]
109
- end
101
+ def[](*args)
102
+ new_safe_buffer = super
103
+ new_safe_buffer.instance_eval { @dirty = false }
104
+ new_safe_buffer
110
105
  end
111
106
 
112
107
  def safe_concat(value)
113
- raise SafeConcatError unless html_safe?
108
+ raise SafeConcatError if dirty?
114
109
  original_concat(value)
115
110
  end
116
111
 
117
112
  def initialize(*)
118
- @html_safe = true
113
+ @dirty = false
119
114
  super
120
115
  end
121
116
 
122
117
  def initialize_copy(other)
123
118
  super
124
- @html_safe = other.html_safe?
125
- end
126
-
127
- def clone_empty
128
- self[0, 0]
119
+ @dirty = other.dirty?
129
120
  end
130
121
 
131
122
  def concat(value)
132
- if !html_safe? || value.html_safe?
123
+ if dirty? || value.html_safe?
133
124
  super(value)
134
125
  else
135
126
  super(ERB::Util.h(value))
@@ -142,7 +133,7 @@ module ActiveSupport #:nodoc:
142
133
  end
143
134
 
144
135
  def html_safe?
145
- defined?(@html_safe) && @html_safe
136
+ !dirty?
146
137
  end
147
138
 
148
139
  def to_s
@@ -162,7 +153,7 @@ module ActiveSupport #:nodoc:
162
153
  to_str.to_yaml(*args)
163
154
  end
164
155
 
165
- for unsafe_method in UNSAFE_STRING_METHODS
156
+ UNSAFE_STRING_METHODS.each do |unsafe_method|
166
157
  if 'String'.respond_to?(unsafe_method)
167
158
  class_eval <<-EOT, __FILE__, __LINE__ + 1
168
159
  def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)
@@ -170,12 +161,18 @@ module ActiveSupport #:nodoc:
170
161
  end # end
171
162
 
172
163
  def #{unsafe_method}!(*args) # def capitalize!(*args)
173
- @html_safe = false # @html_safe = false
164
+ @dirty = true # @dirty = true
174
165
  super # super
175
166
  end # end
176
167
  EOT
177
168
  end
178
169
  end
170
+
171
+ protected
172
+
173
+ def dirty?
174
+ @dirty
175
+ end
179
176
  end
180
177
  end
181
178
 
@@ -161,27 +161,46 @@ class Time
161
161
  months_since(1)
162
162
  end
163
163
 
164
- # Returns a new Time representing the "start" of this week (Monday, 0:00)
165
- def beginning_of_week
166
- days_to_monday = wday!=0 ? wday-1 : 6
167
- (self - days_to_monday.days).midnight
164
+ # Returns number of days to start of this week, week starts on start_day (default is :monday).
165
+ def days_to_week_start(start_day = :monday)
166
+ start_day_number = DAYS_INTO_WEEK[start_day]
167
+ current_day_number = wday != 0 ? wday - 1 : 6
168
+ days_span = current_day_number - start_day_number
169
+ days_span >= 0 ? days_span : 7 + days_span
170
+ end
171
+
172
+ # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00).
173
+ def beginning_of_week(start_day = :monday)
174
+ days_to_start = days_to_week_start(start_day)
175
+ (self - days_to_start.days).midnight
168
176
  end
169
- alias :monday :beginning_of_week
170
177
  alias :at_beginning_of_week :beginning_of_week
171
178
 
172
- # Returns a new Time representing the end of this week, (end of Sunday)
173
- def end_of_week
174
- days_to_sunday = wday!=0 ? 7-wday : 0
175
- (self + days_to_sunday.days).end_of_day
179
+ # Returns a new +Date+/+DateTime+ representing the start of this week. Week is
180
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 0:00.
181
+ def monday
182
+ beginning_of_week
183
+ end
184
+
185
+ # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday).
186
+ def end_of_week(start_day = :monday)
187
+ days_to_end = 6 - days_to_week_start(start_day)
188
+ (self + days_to_end.days).end_of_day
176
189
  end
177
190
  alias :at_end_of_week :end_of_week
178
191
 
179
- # Returns a new Time representing the start of the given day in the previous week (default is Monday).
192
+ # Returns a new +Date+/+DateTime+ representing the end of this week. Week is
193
+ # assumed to start on a Monday. +DateTime+ objects have their time set to 23:59:59.
194
+ def sunday
195
+ end_of_week
196
+ end
197
+
198
+ # Returns a new Time representing the start of the given day in the previous week (default is :monday).
180
199
  def prev_week(day = :monday)
181
200
  ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
182
201
  end
183
202
 
184
- # Returns a new Time representing the start of the given day in next week (default is Monday).
203
+ # Returns a new Time representing the start of the given day in next week (default is :monday).
185
204
  def next_week(day = :monday)
186
205
  since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0)
187
206
  end
@@ -227,7 +246,7 @@ class Time
227
246
  end
228
247
  alias :at_end_of_quarter :end_of_quarter
229
248
 
230
- # Returns a new Time representing the start of the year (1st of january, 0:00)
249
+ # Returns a new Time representing the start of the year (1st of january, 0:00)
231
250
  def beginning_of_year
232
251
  change(:month => 1, :day => 1, :hour => 0)
233
252
  end
@@ -249,6 +268,31 @@ class Time
249
268
  advance(:days => 1)
250
269
  end
251
270
 
271
+ # Returns a Range representing the whole day of the current time.
272
+ def all_day
273
+ beginning_of_day..end_of_day
274
+ end
275
+
276
+ # Returns a Range representing the whole week of the current time.
277
+ def all_week
278
+ beginning_of_week..end_of_week
279
+ end
280
+
281
+ # Returns a Range representing the whole month of the current time.
282
+ def all_month
283
+ beginning_of_month..end_of_month
284
+ end
285
+
286
+ # Returns a Range representing the whole quarter of the current time.
287
+ def all_quarter
288
+ beginning_of_quarter..end_of_quarter
289
+ end
290
+
291
+ # Returns a Range representing the whole year of the current time.
292
+ def all_year
293
+ beginning_of_year..end_of_year
294
+ end
295
+
252
296
  def plus_with_duration(other) #:nodoc:
253
297
  if ActiveSupport::Duration === other
254
298
  other.since(self)
@@ -287,4 +331,14 @@ class Time
287
331
  end
288
332
  alias_method :compare_without_coercion, :<=>
289
333
  alias_method :<=>, :compare_with_coercion
334
+
335
+ # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances
336
+ # can be eql? to an equivalent Time
337
+ def eql_with_coercion(other)
338
+ # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison
339
+ other = other.comparable_time if other.respond_to?(:comparable_time)
340
+ eql_without_coercion(other)
341
+ end
342
+ alias_method :eql_without_coercion, :eql?
343
+ alias_method :eql?, :eql_with_coercion
290
344
  end
@@ -5,7 +5,7 @@ require 'active_support/core_ext/module/aliasing'
5
5
  require 'active_support/core_ext/module/attribute_accessors'
6
6
  require 'active_support/core_ext/module/introspection'
7
7
  require 'active_support/core_ext/module/anonymous'
8
- require 'active_support/core_ext/module/deprecation'
8
+ require 'active_support/core_ext/module/qualified_const'
9
9
  require 'active_support/core_ext/object/blank'
10
10
  require 'active_support/core_ext/load_error'
11
11
  require 'active_support/core_ext/name_error'
@@ -71,14 +71,24 @@ module ActiveSupport #:nodoc:
71
71
  #
72
72
  # This is handled by walking back up the watch stack and adding the constants
73
73
  # found by child.rb to the list of original constants in parent.rb
74
- class WatchStack < Hash
74
+ class WatchStack
75
+ include Enumerable
76
+
75
77
  # @watching is a stack of lists of constants being watched. For instance,
76
78
  # if parent.rb is autoloaded, the stack will look like [[Object]]. If parent.rb
77
79
  # then requires namespace/child.rb, the stack will look like [[Object], [Namespace]].
78
80
 
79
81
  def initialize
80
82
  @watching = []
81
- super { |h,k| h[k] = [] }
83
+ @stack = Hash.new { |h,k| h[k] = [] }
84
+ end
85
+
86
+ def each(&block)
87
+ @stack.each(&block)
88
+ end
89
+
90
+ def watching?
91
+ !@watching.empty?
82
92
  end
83
93
 
84
94
  # return a list of new constants found since the last call to watch_namespaces
@@ -89,7 +99,7 @@ module ActiveSupport #:nodoc:
89
99
  @watching.last.each do |namespace|
90
100
  # Retrieve the constants that were present under the namespace when watch_namespaces
91
101
  # was originally called
92
- original_constants = self[namespace].last
102
+ original_constants = @stack[namespace].last
93
103
 
94
104
  mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
95
105
  next unless mod.is_a?(Module)
@@ -102,7 +112,7 @@ module ActiveSupport #:nodoc:
102
112
  # element of self[Object] will be an Array of the constants that were present
103
113
  # before parent.rb was required. The second element will be an Array of the
104
114
  # constants that were present before child.rb was required.
105
- self[namespace].each do |namespace_constants|
115
+ @stack[namespace].each do |namespace_constants|
106
116
  namespace_constants.concat(new_constants)
107
117
  end
108
118
 
@@ -126,13 +136,14 @@ module ActiveSupport #:nodoc:
126
136
  Inflector.constantize(module_name).local_constant_names : []
127
137
 
128
138
  watching << module_name
129
- self[module_name] << original_constants
139
+ @stack[module_name] << original_constants
130
140
  end
131
141
  @watching << watching
132
142
  end
133
143
 
144
+ private
134
145
  def pop_modules(modules)
135
- modules.each { |mod| self[mod].pop }
146
+ modules.each { |mod| @stack[mod].pop }
136
147
  end
137
148
  end
138
149
 
@@ -219,8 +230,8 @@ module ActiveSupport #:nodoc:
219
230
  end
220
231
 
221
232
  def load_dependency(file)
222
- if Dependencies.load?
223
- Dependencies.new_constants_in(Object) { yield }.presence
233
+ if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
234
+ Dependencies.new_constants_in(Object) { yield }
224
235
  else
225
236
  yield
226
237
  end
@@ -229,13 +240,13 @@ module ActiveSupport #:nodoc:
229
240
  raise
230
241
  end
231
242
 
232
- def load(file, *)
243
+ def load(file, wrap = false)
233
244
  result = false
234
245
  load_dependency(file) { result = super }
235
246
  result
236
247
  end
237
248
 
238
- def require(file, *)
249
+ def require(file)
239
250
  result = false
240
251
  load_dependency(file) { result = super }
241
252
  result
@@ -358,12 +369,13 @@ module ActiveSupport #:nodoc:
358
369
  end
359
370
 
360
371
  # Is the provided constant path defined?
361
- def qualified_const_defined?(path)
362
- names = path.sub(/^::/, '').to_s.split('::')
363
-
364
- names.inject(Object) do |mod, name|
365
- return false unless local_const_defined?(mod, name)
366
- mod.const_get name
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)
367
379
  end
368
380
  end
369
381
 
@@ -422,7 +434,8 @@ module ActiveSupport #:nodoc:
422
434
  end
423
435
 
424
436
  def load_once_path?(path)
425
- autoload_once_paths.any? { |base| path.starts_with? base }
437
+ # to_s works around a ruby1.9 issue where #starts_with?(Pathname) will always return false
438
+ autoload_once_paths.any? { |base| path.starts_with? base.to_s }
426
439
  end
427
440
 
428
441
  # Attempt to autoload the provided module name by searching for a directory
@@ -525,7 +538,7 @@ module ActiveSupport #:nodoc:
525
538
 
526
539
  class ClassCache
527
540
  def initialize
528
- @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) }
541
+ @store = Hash.new
529
542
  end
530
543
 
531
544
  def empty?
@@ -536,40 +549,24 @@ module ActiveSupport #:nodoc:
536
549
  @store.key?(key)
537
550
  end
538
551
 
539
- def []=(key, value)
540
- return unless key.respond_to?(:name)
541
-
542
- raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank?
543
-
544
- @store[key.name] = value
545
- end
546
-
547
- def [](key)
552
+ def get(key)
548
553
  key = key.name if key.respond_to?(:name)
549
-
550
- @store[key]
554
+ @store[key] ||= Inflector.constantize(key)
551
555
  end
552
- alias :get :[]
553
-
554
- class Getter # :nodoc:
555
- def initialize(name)
556
- @name = name
557
- end
556
+ alias :[] :get
558
557
 
559
- def get
560
- Reference.get @name
558
+ def safe_get(key)
559
+ key = key.name if key.respond_to?(:name)
560
+ @store[key] || begin
561
+ klass = Inflector.safe_constantize(key)
562
+ @store[key] = klass
561
563
  end
562
- deprecate :get
563
564
  end
564
565
 
565
- def new(name)
566
- self[name] = name
567
- Getter.new(name)
568
- end
569
- deprecate :new
570
-
571
- def store(name)
572
- self[name] = name
566
+ def store(klass)
567
+ return self unless klass.respond_to?(:name)
568
+ raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
569
+ @store[klass.name] = klass
573
570
  self
574
571
  end
575
572
 
@@ -580,21 +577,23 @@ module ActiveSupport #:nodoc:
580
577
 
581
578
  Reference = ClassCache.new
582
579
 
583
- def ref(name)
584
- Reference.new(name)
585
- end
586
- deprecate :ref
587
-
588
580
  # Store a reference to a class +klass+.
589
581
  def reference(klass)
590
582
  Reference.store klass
591
583
  end
592
584
 
593
585
  # Get the reference for class named +name+.
586
+ # Raises an exception if referenced class does not exist.
594
587
  def constantize(name)
595
588
  Reference.get(name)
596
589
  end
597
590
 
591
+ # Get the reference for class named +name+ if one exists.
592
+ # Otherwise returns nil.
593
+ def safe_constantize(name)
594
+ Reference.safe_get(name)
595
+ end
596
+
598
597
  # Determine if the given constant has been automatically loaded.
599
598
  def autoloaded?(desc)
600
599
  # No name => anonymous module.