activesupport 4.0.13 → 4.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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +283 -508
  3. data/README.rdoc +1 -1
  4. data/lib/active_support.rb +7 -1
  5. data/lib/active_support/backtrace_cleaner.rb +5 -5
  6. data/lib/active_support/benchmarkable.rb +0 -10
  7. data/lib/active_support/cache.rb +62 -26
  8. data/lib/active_support/cache/file_store.rb +27 -22
  9. data/lib/active_support/cache/mem_cache_store.rb +2 -2
  10. data/lib/active_support/cache/memory_store.rb +1 -0
  11. data/lib/active_support/cache/strategy/local_cache.rb +3 -0
  12. data/lib/active_support/callbacks.rb +416 -245
  13. data/lib/active_support/concern.rb +13 -5
  14. data/lib/active_support/core_ext.rb +0 -1
  15. data/lib/active_support/core_ext/array.rb +0 -1
  16. data/lib/active_support/core_ext/array/access.rb +2 -0
  17. data/lib/active_support/core_ext/array/conversions.rb +2 -17
  18. data/lib/active_support/core_ext/array/grouping.rb +24 -12
  19. data/lib/active_support/core_ext/array/prepend_and_append.rb +2 -2
  20. data/lib/active_support/core_ext/class.rb +0 -1
  21. data/lib/active_support/core_ext/class/attribute.rb +1 -2
  22. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  23. data/lib/active_support/core_ext/date/calculations.rb +10 -0
  24. data/lib/active_support/core_ext/date/conversions.rb +5 -6
  25. data/lib/active_support/core_ext/date/zones.rb +2 -33
  26. data/lib/active_support/core_ext/date_and_time/calculations.rb +30 -11
  27. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  28. data/lib/active_support/core_ext/date_time/calculations.rb +12 -25
  29. data/lib/active_support/core_ext/date_time/conversions.rb +2 -0
  30. data/lib/active_support/core_ext/date_time/zones.rb +3 -21
  31. data/lib/active_support/core_ext/hash.rb +0 -1
  32. data/lib/active_support/core_ext/hash/conversions.rb +6 -3
  33. data/lib/active_support/core_ext/hash/deep_merge.rb +11 -22
  34. data/lib/active_support/core_ext/hash/indifferent_access.rb +1 -0
  35. data/lib/active_support/core_ext/hash/keys.rb +27 -47
  36. data/lib/active_support/core_ext/kernel/reporting.rb +2 -6
  37. data/lib/active_support/core_ext/module.rb +1 -0
  38. data/lib/active_support/core_ext/module/attribute_accessors.rb +160 -14
  39. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  40. data/lib/active_support/core_ext/module/delegation.rb +14 -4
  41. data/lib/active_support/core_ext/module/deprecation.rb +0 -2
  42. data/lib/active_support/core_ext/module/introspection.rb +0 -16
  43. data/lib/active_support/core_ext/module/method_transplanting.rb +11 -0
  44. data/lib/active_support/core_ext/numeric/time.rb +8 -0
  45. data/lib/active_support/core_ext/object.rb +1 -1
  46. data/lib/active_support/core_ext/object/blank.rb +1 -1
  47. data/lib/active_support/core_ext/object/deep_dup.rb +6 -6
  48. data/lib/active_support/core_ext/object/inclusion.rb +4 -15
  49. data/lib/active_support/core_ext/object/json.rb +197 -0
  50. data/lib/active_support/core_ext/object/to_json.rb +4 -26
  51. data/lib/active_support/core_ext/object/to_param.rb +58 -1
  52. data/lib/active_support/core_ext/object/to_query.rb +7 -56
  53. data/lib/active_support/core_ext/object/try.rb +1 -1
  54. data/lib/active_support/core_ext/range/each.rb +2 -1
  55. data/lib/active_support/core_ext/string/access.rb +31 -31
  56. data/lib/active_support/core_ext/string/conversions.rb +9 -8
  57. data/lib/active_support/core_ext/string/exclude.rb +3 -3
  58. data/lib/active_support/core_ext/string/filters.rb +14 -4
  59. data/lib/active_support/core_ext/string/inflections.rb +11 -9
  60. data/lib/active_support/core_ext/string/output_safety.rb +65 -24
  61. data/lib/active_support/core_ext/string/zones.rb +1 -0
  62. data/lib/active_support/core_ext/thread.rb +4 -4
  63. data/lib/active_support/core_ext/time/calculations.rb +10 -57
  64. data/lib/active_support/core_ext/time/conversions.rb +3 -1
  65. data/lib/active_support/core_ext/time/zones.rb +2 -21
  66. data/lib/active_support/dependencies.rb +29 -13
  67. data/lib/active_support/deprecation.rb +4 -4
  68. data/lib/active_support/deprecation/behaviors.rb +3 -3
  69. data/lib/active_support/duration.rb +5 -7
  70. data/lib/active_support/file_update_checker.rb +1 -1
  71. data/lib/active_support/hash_with_indifferent_access.rb +4 -9
  72. data/lib/active_support/i18n.rb +4 -4
  73. data/lib/active_support/i18n_railtie.rb +2 -6
  74. data/lib/active_support/inflections.rb +0 -1
  75. data/lib/active_support/inflector/inflections.rb +17 -17
  76. data/lib/active_support/inflector/methods.rb +34 -17
  77. data/lib/active_support/json/decoding.rb +14 -21
  78. data/lib/active_support/json/encoding.rb +113 -285
  79. data/lib/active_support/key_generator.rb +1 -1
  80. data/lib/active_support/lazy_load_hooks.rb +1 -1
  81. data/lib/active_support/log_subscriber/test_helper.rb +1 -1
  82. data/lib/active_support/logger.rb +1 -1
  83. data/lib/active_support/message_encryptor.rb +3 -3
  84. data/lib/active_support/message_verifier.rb +6 -1
  85. data/lib/active_support/multibyte/chars.rb +1 -2
  86. data/lib/active_support/multibyte/unicode.rb +27 -39
  87. data/lib/active_support/notifications.rb +3 -3
  88. data/lib/active_support/notifications/instrumenter.rb +2 -1
  89. data/lib/active_support/number_helper.rb +20 -311
  90. data/lib/active_support/number_helper/number_converter.rb +182 -0
  91. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  92. data/lib/active_support/number_helper/number_to_delimited_converter.rb +21 -0
  93. data/lib/active_support/number_helper/number_to_human_converter.rb +66 -0
  94. data/lib/active_support/number_helper/number_to_human_size_converter.rb +58 -0
  95. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  96. data/lib/active_support/number_helper/number_to_phone_converter.rb +49 -0
  97. data/lib/active_support/number_helper/number_to_rounded_converter.rb +62 -0
  98. data/lib/active_support/option_merger.rb +1 -1
  99. data/lib/active_support/ordered_hash.rb +0 -8
  100. data/lib/active_support/ordered_options.rb +8 -0
  101. data/lib/active_support/per_thread_registry.rb +9 -8
  102. data/lib/active_support/subscriber.rb +26 -3
  103. data/lib/active_support/test_case.rb +9 -10
  104. data/lib/active_support/testing/assertions.rb +0 -30
  105. data/lib/active_support/testing/autorun.rb +2 -2
  106. data/lib/active_support/testing/declarative.rb +18 -8
  107. data/lib/active_support/testing/isolation.rb +13 -65
  108. data/lib/active_support/testing/setup_and_teardown.rb +17 -2
  109. data/lib/active_support/testing/tagged_logging.rb +1 -1
  110. data/lib/active_support/testing/time_helpers.rb +55 -0
  111. data/lib/active_support/time_with_zone.rb +4 -4
  112. data/lib/active_support/values/time_zone.rb +18 -15
  113. data/lib/active_support/version.rb +1 -1
  114. data/lib/active_support/xml_mini.rb +2 -4
  115. metadata +71 -61
  116. data/lib/active_support/basic_object.rb +0 -11
  117. data/lib/active_support/buffered_logger.rb +0 -21
  118. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  119. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  120. data/lib/active_support/core_ext/logger.rb +0 -67
  121. data/lib/active_support/core_ext/proc.rb +0 -17
  122. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  123. data/lib/active_support/json/variable.rb +0 -18
  124. data/lib/active_support/testing/pending.rb +0 -14
@@ -16,7 +16,8 @@ class Time
16
16
  :rfc822 => lambda { |time|
17
17
  offset_format = time.formatted_offset(false)
18
18
  time.strftime("%a, %d %b %Y %H:%M:%S #{offset_format}")
19
- }
19
+ },
20
+ :iso8601 => lambda { |time| time.iso8601 }
20
21
  }
21
22
 
22
23
  # Converts to a formatted string. See DATE_FORMATS for builtin formats.
@@ -34,6 +35,7 @@ class Time
34
35
  # time.to_formatted_s(:long) # => "January 18, 2007 06:10"
35
36
  # time.to_formatted_s(:long_ordinal) # => "January 18th, 2007 06:10"
36
37
  # time.to_formatted_s(:rfc822) # => "Thu, 18 Jan 2007 06:10:17 -0600"
38
+ # time.to_formatted_s(:iso8601) # => "2007-01-18T06:10:17-06:00"
37
39
  #
38
40
  # == Adding your own time formats to +to_formatted_s+
39
41
  # You can add your own formats to the Time::DATE_FORMATS hash.
@@ -1,7 +1,8 @@
1
1
  require 'active_support/time_with_zone'
2
- require 'active_support/core_ext/time/acts_like'
2
+ require 'active_support/core_ext/date_and_time/zones'
3
3
 
4
4
  class Time
5
+ include DateAndTime::Zones
5
6
  class << self
6
7
  attr_accessor :zone_default
7
8
 
@@ -74,24 +75,4 @@ class Time
74
75
  find_zone!(time_zone) rescue nil
75
76
  end
76
77
  end
77
-
78
- # Returns the simultaneous time in <tt>Time.zone</tt>.
79
- #
80
- # Time.zone = 'Hawaii' # => 'Hawaii'
81
- # Time.utc(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
82
- #
83
- # This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt> as the local zone
84
- # instead of the operating system's time zone.
85
- #
86
- # You can also pass in a TimeZone instance or string that identifies a TimeZone as an argument,
87
- # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
88
- #
89
- # Time.utc(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
90
- def in_time_zone(zone = ::Time.zone)
91
- if zone
92
- ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
93
- else
94
- self
95
- end
96
- end
97
78
  end
@@ -176,14 +176,22 @@ module ActiveSupport #:nodoc:
176
176
  end
177
177
 
178
178
  def const_missing(const_name)
179
- # The interpreter does not pass nesting information, and in the
180
- # case of anonymous modules we cannot even make the trade-off of
181
- # assuming their name reflects the nesting. Resort to Object as
182
- # the only meaningful guess we can make.
183
- from_mod = anonymous? ? ::Object : self
179
+ from_mod = anonymous? ? guess_for_anonymous(const_name) : self
184
180
  Dependencies.load_missing_constant(from_mod, const_name)
185
181
  end
186
182
 
183
+ # Dependencies assumes the name of the module reflects the nesting (unless
184
+ # it can be proven that is not the case), and the path to the file that
185
+ # defines the constant. Anonymous modules cannot follow these conventions
186
+ # and we assume therefore the user wants to refer to a top-level constant.
187
+ def guess_for_anonymous(const_name)
188
+ if Object.const_defined?(const_name)
189
+ raise NameError, "#{const_name} cannot be autoloaded from an anonymous class or module"
190
+ else
191
+ Object
192
+ end
193
+ end
194
+
187
195
  def unloadable(const_desc = self)
188
196
  super(const_desc)
189
197
  end
@@ -199,9 +207,19 @@ module ActiveSupport #:nodoc:
199
207
  Dependencies.require_or_load(file_name)
200
208
  end
201
209
 
210
+ # Interprets a file using <tt>mechanism</tt> and marks its defined
211
+ # constants as autoloaded. <tt>file_name</tt> can be either a string or
212
+ # respond to <tt>to_path</tt>.
213
+ #
214
+ # Use this method in code that absolutely needs a certain constant to be
215
+ # defined at that point. A typical use case is to make constant name
216
+ # resolution deterministic for constants with the same relative name in
217
+ # different namespaces whose evaluation would depend on load order
218
+ # otherwise.
202
219
  def require_dependency(file_name, message = "No such file to load -- %s")
220
+ file_name = file_name.to_path if file_name.respond_to?(:to_path)
203
221
  unless file_name.is_a?(String)
204
- raise ArgumentError, "the file name must be a String -- you passed #{file_name.inspect}"
222
+ raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
205
223
  end
206
224
 
207
225
  Dependencies.depend_on(file_name, message)
@@ -417,7 +435,7 @@ module ActiveSupport #:nodoc:
417
435
  def load_file(path, const_paths = loadable_constants_for_path(path))
418
436
  log_call path, const_paths
419
437
  const_paths = [const_paths].compact unless const_paths.is_a? Array
420
- parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || :Object }
438
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
421
439
 
422
440
  result = nil
423
441
  newly_defined_paths = new_constants_in(*parent_paths) do
@@ -446,8 +464,6 @@ module ActiveSupport #:nodoc:
446
464
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
447
465
  end
448
466
 
449
- raise NameError.new("#{from_mod} is not missing constant #{const_name}!", const_name) if from_mod.const_defined?(const_name, false)
450
-
451
467
  qualified_name = qualified_name_for from_mod, const_name
452
468
  path_suffix = qualified_name.underscore
453
469
 
@@ -498,9 +514,9 @@ module ActiveSupport #:nodoc:
498
514
  end
499
515
  end
500
516
 
501
- name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
502
- name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
503
- raise name_error
517
+ raise NameError,
518
+ "uninitialized constant #{qualified_name}",
519
+ caller.reject { |l| l.starts_with? __FILE__ }
504
520
  end
505
521
 
506
522
  # Remove the constants that have been autoloaded, and those that have been
@@ -635,7 +651,7 @@ module ActiveSupport #:nodoc:
635
651
  when String then desc.sub(/^::/, '')
636
652
  when Symbol then desc.to_s
637
653
  when Module
638
- desc.name.presence ||
654
+ desc.name ||
639
655
  raise(ArgumentError, "Anonymous modules have no name to be referenced by")
640
656
  else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
641
657
  end
@@ -25,14 +25,14 @@ module ActiveSupport
25
25
  include Reporting
26
26
  include MethodWrapper
27
27
 
28
- # The version the deprecated behavior will be removed, by default.
28
+ # The version number in which the deprecated behavior will be removed, by default.
29
29
  attr_accessor :deprecation_horizon
30
30
 
31
- # It accepts two parameters on initialization. The first is an version of library
32
- # and the second is an library name
31
+ # It accepts two parameters on initialization. The first is a version of library
32
+ # and the second is a library name
33
33
  #
34
34
  # ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
35
- def initialize(deprecation_horizon = '4.1', gem_name = 'Rails')
35
+ def initialize(deprecation_horizon = '4.2', gem_name = 'Rails')
36
36
  self.gem_name = gem_name
37
37
  self.deprecation_horizon = deprecation_horizon
38
38
  # By default, warnings are not silenced and debugging is off.
@@ -17,7 +17,7 @@ module ActiveSupport
17
17
  $stderr.puts(message)
18
18
  $stderr.puts callstack.join("\n ") if debug
19
19
  },
20
-
20
+
21
21
  log: ->(message, callstack) {
22
22
  logger =
23
23
  if defined?(Rails) && Rails.logger
@@ -29,12 +29,12 @@ module ActiveSupport
29
29
  logger.warn message
30
30
  logger.debug callstack.join("\n ") if debug
31
31
  },
32
-
32
+
33
33
  notify: ->(message, callstack) {
34
34
  ActiveSupport::Notifications.instrument("deprecation.rails",
35
35
  :message => message, :callstack => callstack)
36
36
  },
37
-
37
+
38
38
  silence: ->(message, callstack) {},
39
39
  }
40
40
 
@@ -70,13 +70,11 @@ module ActiveSupport
70
70
  alias :until :ago
71
71
 
72
72
  def inspect #:nodoc:
73
- consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
74
- parts = [:years, :months, :days, :minutes, :seconds].map do |length|
75
- n = consolidated[length]
76
- "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
77
- end.compact
78
- parts = ["0 seconds"] if parts.empty?
79
- parts.to_sentence(:locale => :en)
73
+ parts.
74
+ reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
75
+ sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
76
+ map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
77
+ to_sentence(:locale => :en)
80
78
  end
81
79
 
82
80
  def as_json(options = nil) #:nodoc:
@@ -115,7 +115,7 @@ module ActiveSupport
115
115
  end
116
116
 
117
117
  def compile_glob(hash)
118
- hash.freeze # Freeze so changes aren't accidently pushed
118
+ hash.freeze # Freeze so changes aren't accidentally pushed
119
119
  return if hash.empty?
120
120
 
121
121
  globs = hash.map do |key, value|
@@ -72,7 +72,6 @@ module ActiveSupport
72
72
  end
73
73
 
74
74
  def self.new_from_hash_copying_default(hash)
75
- hash = hash.to_hash
76
75
  new(hash).tap do |new_hash|
77
76
  new_hash.default = hash.default
78
77
  end
@@ -126,7 +125,7 @@ module ActiveSupport
126
125
  if other_hash.is_a? HashWithIndifferentAccess
127
126
  super(other_hash)
128
127
  else
129
- other_hash.to_hash.each_pair do |key, value|
128
+ other_hash.each_pair do |key, value|
130
129
  if block_given? && key?(key)
131
130
  value = yield(convert_key(key), self[key], value)
132
131
  end
@@ -208,7 +207,7 @@ module ActiveSupport
208
207
  # Replaces the contents of this hash with other_hash.
209
208
  #
210
209
  # h = { "a" => 100, "b" => 200 }
211
- # h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
210
+ # h.replace({ "c" => 300, "d" => 400 }) # => {"c"=>300, "d"=>400}
212
211
  def replace(other_hash)
213
212
  super(self.class.new_from_hash_copying_default(other_hash))
214
213
  end
@@ -225,15 +224,11 @@ module ActiveSupport
225
224
  undef :symbolize_keys!
226
225
  undef :deep_symbolize_keys!
227
226
  def symbolize_keys; to_hash.symbolize_keys! end
228
- def deep_symbolize_keys; to_hash.deep_symbolize_keys end
227
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys! end
229
228
  def to_options!; self end
230
229
 
231
230
  def select(*args, &block)
232
- dup.tap { |hash| hash.select!(*args, &block) }
233
- end
234
-
235
- def reject(*args, &block)
236
- dup.tap { |hash| hash.reject!(*args, &block) }
231
+ dup.tap {|hash| hash.select!(*args, &block)}
237
232
  end
238
233
 
239
234
  # Convert to a regular hash with string keys.
@@ -1,13 +1,13 @@
1
+ require 'active_support/core_ext/hash/deep_merge'
2
+ require 'active_support/core_ext/hash/except'
3
+ require 'active_support/core_ext/hash/slice'
1
4
  begin
2
- require 'active_support/core_ext/hash/deep_merge'
3
- require 'active_support/core_ext/hash/except'
4
- require 'active_support/core_ext/hash/slice'
5
5
  require 'i18n'
6
- require 'active_support/lazy_load_hooks'
7
6
  rescue LoadError => e
8
7
  $stderr.puts "The i18n gem is not available. Please add it to your Gemfile and run bundle install"
9
8
  raise e
10
9
  end
10
+ require 'active_support/lazy_load_hooks'
11
11
 
12
12
  ActiveSupport.run_load_hooks(:i18n)
13
13
  I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
@@ -36,11 +36,7 @@ module I18n
36
36
  # Avoid issues with setting the default_locale by disabling available locales
37
37
  # check while configuring.
38
38
  enforce_available_locales = app.config.i18n.delete(:enforce_available_locales)
39
-
40
- if enforce_available_locales.nil?
41
- enforce_available_locales = I18n.enforce_available_locales
42
- end
43
-
39
+ enforce_available_locales = I18n.enforce_available_locales unless I18n.enforce_available_locales.nil?
44
40
  I18n.enforce_available_locales = false
45
41
 
46
42
  app.config.i18n.each do |setting, value|
@@ -56,7 +52,7 @@ module I18n
56
52
 
57
53
  init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
58
54
 
59
- # Restore available locales check so it will take place from now on.
55
+ # Restore avalable locales check so it will take place from now on.
60
56
  I18n.enforce_available_locales = enforce_available_locales
61
57
 
62
58
  reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
@@ -57,7 +57,6 @@ module ActiveSupport
57
57
  inflect.irregular('child', 'children')
58
58
  inflect.irregular('sex', 'sexes')
59
59
  inflect.irregular('move', 'moves')
60
- inflect.irregular('cow', 'kine')
61
60
  inflect.irregular('zombie', 'zombies')
62
61
 
63
62
  inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
@@ -52,21 +52,21 @@ module ActiveSupport
52
52
  # into a non-delimited single lowercase word when passed to +underscore+.
53
53
  #
54
54
  # acronym 'HTML'
55
- # titleize 'html' #=> 'HTML'
56
- # camelize 'html' #=> 'HTML'
57
- # underscore 'MyHTML' #=> 'my_html'
55
+ # titleize 'html' # => 'HTML'
56
+ # camelize 'html' # => 'HTML'
57
+ # underscore 'MyHTML' # => 'my_html'
58
58
  #
59
59
  # The acronym, however, must occur as a delimited unit and not be part of
60
60
  # another word for conversions to recognize it:
61
61
  #
62
62
  # acronym 'HTTP'
63
- # camelize 'my_http_delimited' #=> 'MyHTTPDelimited'
64
- # camelize 'https' #=> 'Https', not 'HTTPs'
65
- # underscore 'HTTPS' #=> 'http_s', not 'https'
63
+ # camelize 'my_http_delimited' # => 'MyHTTPDelimited'
64
+ # camelize 'https' # => 'Https', not 'HTTPs'
65
+ # underscore 'HTTPS' # => 'http_s', not 'https'
66
66
  #
67
67
  # acronym 'HTTPS'
68
- # camelize 'https' #=> 'HTTPS'
69
- # underscore 'HTTPS' #=> 'https'
68
+ # camelize 'https' # => 'HTTPS'
69
+ # underscore 'HTTPS' # => 'https'
70
70
  #
71
71
  # Note: Acronyms that are passed to +pluralize+ will no longer be
72
72
  # recognized, since the acronym will not occur as a delimited unit in the
@@ -74,25 +74,25 @@ module ActiveSupport
74
74
  # form as an acronym as well:
75
75
  #
76
76
  # acronym 'API'
77
- # camelize(pluralize('api')) #=> 'Apis'
77
+ # camelize(pluralize('api')) # => 'Apis'
78
78
  #
79
79
  # acronym 'APIs'
80
- # camelize(pluralize('api')) #=> 'APIs'
80
+ # camelize(pluralize('api')) # => 'APIs'
81
81
  #
82
82
  # +acronym+ may be used to specify any word that contains an acronym or
83
83
  # otherwise needs to maintain a non-standard capitalization. The only
84
84
  # restriction is that the word must begin with a capital letter.
85
85
  #
86
86
  # acronym 'RESTful'
87
- # underscore 'RESTful' #=> 'restful'
88
- # underscore 'RESTfulController' #=> 'restful_controller'
89
- # titleize 'RESTfulController' #=> 'RESTful Controller'
90
- # camelize 'restful' #=> 'RESTful'
91
- # camelize 'restful_controller' #=> 'RESTfulController'
87
+ # underscore 'RESTful' # => 'restful'
88
+ # underscore 'RESTfulController' # => 'restful_controller'
89
+ # titleize 'RESTfulController' # => 'RESTful Controller'
90
+ # camelize 'restful' # => 'RESTful'
91
+ # camelize 'restful_controller' # => 'RESTfulController'
92
92
  #
93
93
  # acronym 'McDonald'
94
- # underscore 'McDonald' #=> 'mcdonald'
95
- # camelize 'mcdonald' #=> 'McDonald'
94
+ # underscore 'McDonald' # => 'mcdonald'
95
+ # camelize 'mcdonald' # => 'McDonald'
96
96
  def acronym(word)
97
97
  @acronyms[word.downcase] = word
98
98
  @acronym_regex = /#{@acronyms.values.join("|")}/
@@ -1,6 +1,5 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'active_support/inflector/inflections'
4
3
  require 'active_support/inflections'
5
4
 
6
5
  module ActiveSupport
@@ -37,7 +36,7 @@ module ActiveSupport
37
36
  # string.
38
37
  #
39
38
  # If passed an optional +locale+ parameter, the word will be
40
- # pluralized using rules defined for that language. By default,
39
+ # singularized using rules defined for that language. By default,
41
40
  # this parameter is set to <tt>:en</tt>.
42
41
  #
43
42
  # 'posts'.singularize # => "post"
@@ -73,7 +72,9 @@ module ActiveSupport
73
72
  else
74
73
  string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[A-Z_])|\w)/) { $&.downcase }
75
74
  end
76
- string.gsub(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }.gsub('/', '::')
75
+ string.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{inflections.acronyms[$2] || $2.capitalize}" }
76
+ string.gsub!('/', '::')
77
+ string
77
78
  end
78
79
 
79
80
  # Makes an underscored, lowercase form from the expression in the string.
@@ -88,8 +89,7 @@ module ActiveSupport
88
89
  #
89
90
  # 'SSLError'.underscore.camelize # => "SslError"
90
91
  def underscore(camel_cased_word)
91
- word = camel_cased_word.to_s.dup
92
- word.gsub!('::', '/')
92
+ word = camel_cased_word.to_s.gsub('::', '/')
93
93
  word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
94
94
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
95
95
  word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
@@ -98,20 +98,27 @@ module ActiveSupport
98
98
  word
99
99
  end
100
100
 
101
- # Capitalizes the first word and turns underscores into spaces and strips a
102
- # trailing "_id", if any. Like +titleize+, this is meant for creating pretty
103
- # output.
104
- #
105
- # 'employee_salary'.humanize # => "Employee salary"
106
- # 'author_id'.humanize # => "Author"
107
- def humanize(lower_case_and_underscored_word)
101
+ # Capitalizes the first word, turns underscores into spaces, and strips a
102
+ # trailing '_id' if present.
103
+ # Like +titleize+, this is meant for creating pretty output.
104
+ #
105
+ # The capitalization of the first word can be turned off by setting the
106
+ # optional parameter +capitalize+ to false.
107
+ # By default, this parameter is true.
108
+ #
109
+ # humanize('employee_salary') # => "Employee salary"
110
+ # humanize('author_id') # => "Author"
111
+ # humanize('author_id', capitalize: false) # => "author"
112
+ def humanize(lower_case_and_underscored_word, options = {})
108
113
  result = lower_case_and_underscored_word.to_s.dup
109
114
  inflections.humans.each { |(rule, replacement)| break if result.sub!(rule, replacement) }
110
115
  result.gsub!(/_id$/, "")
111
116
  result.tr!('_', ' ')
112
- result.gsub(/([a-z\d]*)/i) { |match|
117
+ result.gsub!(/([a-z\d]*)/i) { |match|
113
118
  "#{inflections.acronyms[match] || match.downcase}"
114
- }.gsub(/^\w/) { $&.upcase }
119
+ }
120
+ result.gsub!(/^\w/) { $&.upcase } if options.fetch(:capitalize, true)
121
+ result
115
122
  end
116
123
 
117
124
  # Capitalizes all the words and replaces some characters in the string to
@@ -185,7 +192,7 @@ module ActiveSupport
185
192
  #
186
193
  # See also +demodulize+.
187
194
  def deconstantize(path)
188
- path.to_s[0...(path.rindex('::') || 0)] # implementation based on the one in facets' Module#spacename
195
+ path.to_s[0, path.rindex('::') || 0] # implementation based on the one in facets' Module#spacename
189
196
  end
190
197
 
191
198
  # Creates a foreign key name from a class name.
@@ -219,7 +226,12 @@ module ActiveSupport
219
226
  # unknown.
220
227
  def constantize(camel_cased_word)
221
228
  names = camel_cased_word.split('::')
222
- names.shift if names.empty? || names.first.empty?
229
+
230
+ # Trigger a builtin NameError exception including the ill-formed constant in the message.
231
+ Object.const_get(camel_cased_word) if names.empty?
232
+
233
+ # Remove the first blank element in case of '::ClassName' notation.
234
+ names.shift if names.size > 1 && names.first.empty?
223
235
 
224
236
  names.inject(Object) do |constant, name|
225
237
  if constant == Object
@@ -314,9 +326,14 @@ module ActiveSupport
314
326
  private
315
327
 
316
328
  # Mount a regular expression that will match part by part of the constant.
317
- # For instance, Foo::Bar::Baz will generate Foo(::Bar(::Baz)?)?
329
+ #
330
+ # const_regexp("Foo::Bar::Baz") # => /Foo(::Bar(::Baz)?)?/
331
+ # const_regexp("::") # => /::/
318
332
  def const_regexp(camel_cased_word) #:nodoc:
319
333
  parts = camel_cased_word.split("::")
334
+
335
+ return Regexp.escape(camel_cased_word) if parts.blank?
336
+
320
337
  last = parts.pop
321
338
 
322
339
  parts.reverse.inject(last) do |acc, part|