activesupport 3.2.22.5 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +364 -135
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -4
  5. data/lib/active_support/backtrace_cleaner.rb +33 -25
  6. data/lib/active_support/basic_object.rb +7 -17
  7. data/lib/active_support/benchmarkable.rb +19 -15
  8. data/lib/active_support/buffered_logger.rb +9 -113
  9. data/lib/active_support/cache/file_store.rb +14 -14
  10. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  11. data/lib/active_support/cache/memory_store.rb +2 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +48 -37
  13. data/lib/active_support/cache.rb +228 -183
  14. data/lib/active_support/callbacks.rb +201 -253
  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/array/access.rb +17 -9
  19. data/lib/active_support/core_ext/array/conversions.rb +115 -55
  20. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  21. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  22. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  23. data/lib/active_support/core_ext/array/wrap.rb +13 -16
  24. data/lib/active_support/core_ext/array.rb +0 -1
  25. data/lib/active_support/core_ext/benchmark.rb +7 -0
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +40 -32
  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/calculations.rb +44 -187
  32. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  33. data/lib/active_support/core_ext/date/zones.rb +25 -2
  34. data/lib/active_support/core_ext/date.rb +5 -0
  35. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  36. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  37. data/lib/active_support/core_ext/date_time/calculations.rb +77 -62
  38. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  39. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  40. data/lib/active_support/core_ext/date_time.rb +4 -0
  41. data/lib/active_support/core_ext/enumerable.rb +26 -73
  42. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  43. data/lib/active_support/core_ext/file.rb +0 -1
  44. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  45. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  46. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  47. data/lib/active_support/core_ext/hash/except.rb +1 -9
  48. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  49. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  50. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  51. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  52. data/lib/active_support/core_ext/hash.rb +0 -1
  53. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  54. data/lib/active_support/core_ext/integer/time.rb +17 -12
  55. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  56. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  57. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  58. data/lib/active_support/core_ext/load_error.rb +7 -5
  59. data/lib/active_support/core_ext/logger.rb +7 -23
  60. data/lib/active_support/core_ext/marshal.rb +21 -0
  61. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  62. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  63. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  64. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  65. data/lib/active_support/core_ext/module/delegation.rb +92 -49
  66. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  67. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  68. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  69. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  70. data/lib/active_support/core_ext/module.rb +1 -3
  71. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  72. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  73. data/lib/active_support/core_ext/numeric.rb +1 -0
  74. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  75. data/lib/active_support/core_ext/object/blank.rb +7 -23
  76. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  77. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  78. data/lib/active_support/core_ext/object/inclusion.rb +8 -7
  79. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  80. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  81. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  82. data/lib/active_support/core_ext/object/try.rb +46 -25
  83. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  84. data/lib/active_support/core_ext/object.rb +1 -0
  85. data/lib/active_support/core_ext/proc.rb +3 -0
  86. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  87. data/lib/active_support/core_ext/range/include_range.rb +3 -1
  88. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  89. data/lib/active_support/core_ext/range.rb +0 -2
  90. data/lib/active_support/core_ext/string/access.rb +95 -90
  91. data/lib/active_support/core_ext/string/conversions.rb +27 -38
  92. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  93. data/lib/active_support/core_ext/string/filters.rb +24 -18
  94. data/lib/active_support/core_ext/string/indent.rb +43 -0
  95. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  96. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  97. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  98. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  99. data/lib/active_support/core_ext/string/zones.rb +13 -0
  100. data/lib/active_support/core_ext/string.rb +2 -3
  101. data/lib/active_support/core_ext/struct.rb +6 -0
  102. data/lib/active_support/core_ext/thread.rb +74 -0
  103. data/lib/active_support/core_ext/time/calculations.rb +108 -184
  104. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  105. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  106. data/lib/active_support/core_ext/time/zones.rb +27 -17
  107. data/lib/active_support/core_ext/time.rb +5 -0
  108. data/lib/active_support/core_ext/uri.rb +13 -17
  109. data/lib/active_support/core_ext.rb +3 -2
  110. data/lib/active_support/dependencies/autoload.rb +47 -20
  111. data/lib/active_support/dependencies.rb +160 -141
  112. data/lib/active_support/deprecation/behaviors.rb +44 -30
  113. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  114. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  115. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  116. data/lib/active_support/deprecation/reporting.rb +40 -11
  117. data/lib/active_support/deprecation.rb +39 -14
  118. data/lib/active_support/descendants_tracker.rb +34 -19
  119. data/lib/active_support/duration.rb +6 -8
  120. data/lib/active_support/file_update_checker.rb +63 -47
  121. data/lib/active_support/gzip.rb +11 -5
  122. data/lib/active_support/hash_with_indifferent_access.rb +127 -42
  123. data/lib/active_support/i18n.rb +4 -0
  124. data/lib/active_support/i18n_railtie.rb +5 -22
  125. data/lib/active_support/inflections.rb +14 -12
  126. data/lib/active_support/inflector/inflections.rb +108 -71
  127. data/lib/active_support/inflector/methods.rb +181 -160
  128. data/lib/active_support/inflector/transliterate.rb +16 -17
  129. data/lib/active_support/json/decoding.rb +18 -17
  130. data/lib/active_support/json/encoding.rb +93 -39
  131. data/lib/active_support/json/variable.rb +10 -1
  132. data/lib/active_support/key_generator.rb +75 -0
  133. data/lib/active_support/lazy_load_hooks.rb +21 -19
  134. data/lib/active_support/locale/en.yml +100 -3
  135. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  136. data/lib/active_support/log_subscriber.rb +35 -52
  137. data/lib/active_support/logger.rb +57 -0
  138. data/lib/active_support/logger_silence.rb +24 -0
  139. data/lib/active_support/message_encryptor.rb +38 -35
  140. data/lib/active_support/message_verifier.rb +9 -15
  141. data/lib/active_support/multibyte/chars.rb +80 -333
  142. data/lib/active_support/multibyte/unicode.rb +74 -64
  143. data/lib/active_support/multibyte.rb +5 -28
  144. data/lib/active_support/notifications/fanout.rb +112 -18
  145. data/lib/active_support/notifications/instrumenter.rb +33 -14
  146. data/lib/active_support/notifications.rb +79 -26
  147. data/lib/active_support/number_helper.rb +637 -0
  148. data/lib/active_support/ordered_hash.rb +8 -190
  149. data/lib/active_support/ordered_options.rb +21 -23
  150. data/lib/active_support/per_thread_registry.rb +52 -0
  151. data/lib/active_support/proxy_object.rb +13 -0
  152. data/lib/active_support/rails.rb +27 -0
  153. data/lib/active_support/railtie.rb +12 -32
  154. data/lib/active_support/rescuable.rb +9 -4
  155. data/lib/active_support/string_inquirer.rb +13 -8
  156. data/lib/active_support/subscriber.rb +93 -0
  157. data/lib/active_support/tagged_logging.rb +51 -73
  158. data/lib/active_support/test_case.rb +46 -17
  159. data/lib/active_support/testing/assertions.rb +56 -26
  160. data/lib/active_support/testing/autorun.rb +5 -0
  161. data/lib/active_support/testing/constant_lookup.rb +54 -0
  162. data/lib/active_support/testing/declarative.rb +1 -1
  163. data/lib/active_support/testing/deprecation.rb +0 -19
  164. data/lib/active_support/testing/isolation.rb +29 -58
  165. data/lib/active_support/testing/pending.rb +5 -43
  166. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  167. data/lib/active_support/testing/tagged_logging.rb +25 -0
  168. data/lib/active_support/time.rb +6 -21
  169. data/lib/active_support/time_with_zone.rb +80 -43
  170. data/lib/active_support/values/time_zone.rb +81 -60
  171. data/lib/active_support/values/unicode_tables.dat +0 -0
  172. data/lib/active_support/version.rb +7 -6
  173. data/lib/active_support/xml_mini/jdom.rb +9 -11
  174. data/lib/active_support/xml_mini/libxml.rb +1 -2
  175. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  176. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  177. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  178. data/lib/active_support/xml_mini/rexml.rb +6 -8
  179. data/lib/active_support/xml_mini.rb +35 -17
  180. data/lib/active_support.rb +8 -21
  181. metadata +104 -72
  182. data/lib/active_support/base64.rb +0 -54
  183. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  184. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  185. data/lib/active_support/core_ext/exception.rb +0 -3
  186. data/lib/active_support/core_ext/file/path.rb +0 -5
  187. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  190. data/lib/active_support/core_ext/io.rb +0 -15
  191. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  192. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  193. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  196. data/lib/active_support/core_ext/range/cover.rb +0 -3
  197. data/lib/active_support/core_ext/rexml.rb +0 -46
  198. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  199. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  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/jruby.rb +0 -115
  208. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  209. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  210. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  211. data/lib/active_support/testing/performance/ruby.rb +0 -152
  212. data/lib/active_support/testing/performance.rb +0 -317
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. data/lib/active_support/whiny_nil.rb +0 -24
@@ -1,26 +1,21 @@
1
- require "active_support/core_ext/array/wrap"
2
- require "active_support/core_ext/array/extract_options"
3
-
4
1
  module ActiveSupport
5
- # \FileUpdateChecker specifies the API used by Rails to watch files
2
+ # FileUpdateChecker specifies the API used by Rails to watch files
6
3
  # and control reloading. The API depends on four methods:
7
4
  #
8
5
  # * +initialize+ which expects two parameters and one block as
9
- # described below;
6
+ # described below.
10
7
  #
11
8
  # * +updated?+ which returns a boolean if there were updates in
12
- # the filesystem or not;
9
+ # the filesystem or not.
13
10
  #
14
11
  # * +execute+ which executes the given block on initialization
15
- # and updates the counter to the latest timestamp;
12
+ # and updates the latest watched files and timestamp.
16
13
  #
17
- # * +execute_if_updated+ which just executes the block if it was updated;
14
+ # * +execute_if_updated+ which just executes the block if it was updated.
18
15
  #
19
16
  # After initialization, a call to +execute_if_updated+ must execute
20
17
  # the block only if there was really a change in the filesystem.
21
18
  #
22
- # == Examples
23
- #
24
19
  # This class is used by Rails to reload the I18n framework whenever
25
20
  # they are changed upon a new request.
26
21
  #
@@ -31,52 +26,55 @@ module ActiveSupport
31
26
  # ActionDispatch::Reloader.to_prepare do
32
27
  # i18n_reloader.execute_if_updated
33
28
  # end
34
- #
35
29
  class FileUpdateChecker
36
30
  # It accepts two parameters on initialization. The first is an array
37
31
  # of files and the second is an optional hash of directories. The hash must
38
32
  # have directories as keys and the value is an array of extensions to be
39
33
  # watched under that directory.
40
34
  #
41
- # This method must also receive a block that will be called once a path changes.
42
- #
43
- # == Implementation details
44
- #
45
- # This particular implementation checks for added and updated files,
46
- # but not removed files. Directories lookup are compiled to a glob for
47
- # performance. Therefore, while someone can add new files to the +files+
48
- # array after initialization (and parts of Rails do depend on this feature),
49
- # adding new directories after initialization is not allowed.
50
- #
51
- # Notice that other objects that implements FileUpdateChecker API may
52
- # not even allow new files to be added after initialization. If this
53
- # is the case, we recommend freezing the +files+ after initialization to
54
- # avoid changes that won't make effect.
35
+ # This method must also receive a block that will be called once a path
36
+ # changes. The array of files and list of directories cannot be changed
37
+ # after FileUpdateChecker has been initialized.
55
38
  def initialize(files, dirs={}, &block)
56
- @files = files
39
+ @files = files.freeze
57
40
  @glob = compile_glob(dirs)
58
41
  @block = block
42
+
43
+ @watched = nil
59
44
  @updated_at = nil
60
- @last_update_at = updated_at
45
+
46
+ @last_watched = watched
47
+ @last_update_at = updated_at(@last_watched)
61
48
  end
62
49
 
63
- # Check if any of the entries were updated. If so, the updated_at
64
- # value is cached until the block is executed via +execute+ or +execute_if_updated+
50
+ # Check if any of the entries were updated. If so, the watched and/or
51
+ # updated_at values are cached until the block is executed via +execute+
52
+ # or +execute_if_updated+.
65
53
  def updated?
66
- current_updated_at = updated_at
67
- if @last_update_at < current_updated_at
68
- @updated_at = updated_at
54
+ current_watched = watched
55
+ if @last_watched.size != current_watched.size
56
+ @watched = current_watched
69
57
  true
70
58
  else
71
- false
59
+ current_updated_at = updated_at(current_watched)
60
+ if @last_update_at < current_updated_at
61
+ @watched = current_watched
62
+ @updated_at = current_updated_at
63
+ true
64
+ else
65
+ false
66
+ end
72
67
  end
73
68
  end
74
69
 
75
- # Executes the given block and updates the counter to latest timestamp.
70
+ # Executes the given block and updates the latest watched files and
71
+ # timestamp.
76
72
  def execute
77
- @last_update_at = updated_at
73
+ @last_watched = watched
74
+ @last_update_at = updated_at(@last_watched)
78
75
  @block.call
79
76
  ensure
77
+ @watched = nil
80
78
  @updated_at = nil
81
79
  end
82
80
 
@@ -92,28 +90,46 @@ module ActiveSupport
92
90
 
93
91
  private
94
92
 
95
- def updated_at #:nodoc:
96
- @updated_at || begin
97
- all = []
98
- all.concat @files.select { |f| File.exists?(f) }
99
- all.concat Dir[@glob] if @glob
100
- all.map { |path| File.mtime(path) }.max || Time.at(0)
93
+ def watched
94
+ @watched || begin
95
+ all = @files.select { |f| File.exists?(f) }
96
+ all.concat(Dir[@glob]) if @glob
97
+ all
101
98
  end
102
99
  end
103
100
 
104
- def compile_glob(hash) #:nodoc:
101
+ def updated_at(paths)
102
+ @updated_at || max_mtime(paths) || Time.at(0)
103
+ end
104
+
105
+ # This method returns the maximum mtime of the files in +paths+, or +nil+
106
+ # if the array is empty.
107
+ #
108
+ # Files with a mtime in the future are ignored. Such abnormal situation
109
+ # can happen for example if the user changes the clock by hand. It is
110
+ # healthy to consider this edge case because with mtimes in the future
111
+ # reloading is not triggered.
112
+ def max_mtime(paths)
113
+ time_now = Time.now
114
+ paths.map {|path| File.mtime(path)}.reject {|mtime| time_now < mtime}.max
115
+ end
116
+
117
+ def compile_glob(hash)
105
118
  hash.freeze # Freeze so changes aren't accidently pushed
106
119
  return if hash.empty?
107
120
 
108
- globs = []
109
- hash.each do |key, value|
110
- globs << "#{key}/**/*#{compile_ext(value)}"
121
+ globs = hash.map do |key, value|
122
+ "#{escape(key)}/**/*#{compile_ext(value)}"
111
123
  end
112
124
  "{#{globs.join(",")}}"
113
125
  end
114
126
 
115
- def compile_ext(array) #:nodoc:
116
- array = Array.wrap(array)
127
+ def escape(key)
128
+ key.gsub(',','\,')
129
+ end
130
+
131
+ def compile_ext(array)
132
+ array = Array(array)
117
133
  return if array.empty?
118
134
  ".{#{array.join(",")}}"
119
135
  end
@@ -1,14 +1,20 @@
1
1
  require 'zlib'
2
2
  require 'stringio'
3
- require 'active_support/core_ext/string/encoding'
4
3
 
5
4
  module ActiveSupport
6
- # A convenient wrapper for the zlib standard library that allows compression/decompression of strings with gzip.
5
+ # A convenient wrapper for the zlib standard library that allows
6
+ # compression/decompression of strings with gzip.
7
+ #
8
+ # gzip = ActiveSupport::Gzip.compress('compress me!')
9
+ # # => "\x1F\x8B\b\x00o\x8D\xCDO\x00\x03K\xCE\xCF-(J-.V\xC8MU\x04\x00R>n\x83\f\x00\x00\x00"
10
+ #
11
+ # ActiveSupport::Gzip.decompress(gzip)
12
+ # # => "compress me!"
7
13
  module Gzip
8
14
  class Stream < StringIO
9
15
  def initialize(*)
10
16
  super
11
- set_encoding "BINARY" if "".encoding_aware?
17
+ set_encoding "BINARY"
12
18
  end
13
19
  def close; rewind; end
14
20
  end
@@ -19,9 +25,9 @@ module ActiveSupport
19
25
  end
20
26
 
21
27
  # Compresses a string using gzip.
22
- def self.compress(source)
28
+ def self.compress(source, level=Zlib::DEFAULT_COMPRESSION, strategy=Zlib::DEFAULT_STRATEGY)
23
29
  output = Stream.new
24
- gz = Zlib::GzipWriter.new(output)
30
+ gz = Zlib::GzipWriter.new(output, level, strategy)
25
31
  gz.write(source)
26
32
  gz.close
27
33
  output.string
@@ -1,13 +1,47 @@
1
1
  require 'active_support/core_ext/hash/keys'
2
2
 
3
- # This class has dubious semantics and we only have it so that
4
- # people can write <tt>params[:key]</tt> instead of <tt>params['key']</tt>
5
- # and they get the same value for both keys.
6
-
7
3
  module ActiveSupport
4
+ # Implements a hash where keys <tt>:foo</tt> and <tt>"foo"</tt> are considered
5
+ # to be the same.
6
+ #
7
+ # rgb = ActiveSupport::HashWithIndifferentAccess.new
8
+ #
9
+ # rgb[:black] = '#000000'
10
+ # rgb[:black] # => '#000000'
11
+ # rgb['black'] # => '#000000'
12
+ #
13
+ # rgb['white'] = '#FFFFFF'
14
+ # rgb[:white] # => '#FFFFFF'
15
+ # rgb['white'] # => '#FFFFFF'
16
+ #
17
+ # Internally symbols are mapped to strings when used as keys in the entire
18
+ # writing interface (calling <tt>[]=</tt>, <tt>merge</tt>, etc). This
19
+ # mapping belongs to the public interface. For example, given:
20
+ #
21
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
22
+ #
23
+ # You are guaranteed that the key is returned as a string:
24
+ #
25
+ # hash.keys # => ["a"]
26
+ #
27
+ # Technically other types of keys are accepted:
28
+ #
29
+ # hash = ActiveSupport::HashWithIndifferentAccess.new(a: 1)
30
+ # hash[0] = 0
31
+ # hash # => {"a"=>1, 0=>0}
32
+ #
33
+ # but this class is intended for use cases where strings or symbols are the
34
+ # expected keys and it is convenient to understand both as the same. For
35
+ # example the +params+ hash in Ruby on Rails.
36
+ #
37
+ # Note that core extensions define <tt>Hash#with_indifferent_access</tt>:
38
+ #
39
+ # rgb = { black: '#000000', white: '#FFFFFF' }.with_indifferent_access
40
+ #
41
+ # which may be handy.
8
42
  class HashWithIndifferentAccess < Hash
9
-
10
- # Always returns true, so that <tt>Array#extract_options!</tt> finds members of this class.
43
+ # Returns +true+ so that <tt>Array#extract_options!</tt> finds members of
44
+ # this class.
11
45
  def extractable_options?
12
46
  true
13
47
  end
@@ -43,35 +77,60 @@ module ActiveSupport
43
77
  end
44
78
  end
45
79
 
80
+ def self.[](*args)
81
+ new.merge!(Hash[*args])
82
+ end
83
+
46
84
  alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
47
85
  alias_method :regular_update, :update unless method_defined?(:regular_update)
48
86
 
49
87
  # Assigns a new value to the hash:
50
88
  #
51
- # hash = HashWithIndifferentAccess.new
52
- # hash[:key] = "value"
89
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
90
+ # hash[:key] = 'value'
53
91
  #
92
+ # This value can be later fetched using either +:key+ or +'key'+.
54
93
  def []=(key, value)
55
- regular_writer(convert_key(key), convert_value(value))
94
+ regular_writer(convert_key(key), convert_value(value, for: :assignment))
56
95
  end
57
96
 
58
97
  alias_method :store, :[]=
59
98
 
60
- # Updates the instantized hash with values from the second:
99
+ # Updates the receiver in-place, merging in the hash passed as argument:
61
100
  #
62
- # hash_1 = HashWithIndifferentAccess.new
63
- # hash_1[:key] = "value"
101
+ # hash_1 = ActiveSupport::HashWithIndifferentAccess.new
102
+ # hash_1[:key] = 'value'
64
103
  #
65
- # hash_2 = HashWithIndifferentAccess.new
66
- # hash_2[:key] = "New Value!"
104
+ # hash_2 = ActiveSupport::HashWithIndifferentAccess.new
105
+ # hash_2[:key] = 'New Value!'
67
106
  #
68
107
  # hash_1.update(hash_2) # => {"key"=>"New Value!"}
69
108
  #
109
+ # The argument can be either an
110
+ # <tt>ActiveSupport::HashWithIndifferentAccess</tt> or a regular +Hash+.
111
+ # In either case the merge respects the semantics of indifferent access.
112
+ #
113
+ # If the argument is a regular hash with keys +:key+ and +"key"+ only one
114
+ # of the values end up in the receiver, but which one is unspecified.
115
+ #
116
+ # When given a block, the value for duplicated keys will be determined
117
+ # by the result of invoking the block with the duplicated key, the value
118
+ # in the receiver, and the value in +other_hash+. The rules for duplicated
119
+ # keys follow the semantics of indifferent access:
120
+ #
121
+ # hash_1[:key] = 10
122
+ # hash_2['key'] = 12
123
+ # hash_1.update(hash_2) { |key, old, new| old + new } # => {"key"=>22}
70
124
  def update(other_hash)
71
125
  if other_hash.is_a? HashWithIndifferentAccess
72
126
  super(other_hash)
73
127
  else
74
- other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
128
+ other_hash.each_pair do |key, value|
129
+ if block_given? && key?(key)
130
+ value = yield(convert_key(key), self[key], value)
131
+ end
132
+ regular_writer(convert_key(key), convert_value(value))
133
+ end
75
134
  self
76
135
  end
77
136
  end
@@ -80,11 +139,10 @@ module ActiveSupport
80
139
 
81
140
  # Checks the hash for a key matching the argument passed in:
82
141
  #
83
- # hash = HashWithIndifferentAccess.new
84
- # hash["key"] = "value"
85
- # hash.key? :key # => true
86
- # hash.key? "key" # => true
87
- #
142
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
143
+ # hash['key'] = 'value'
144
+ # hash.key?(:key) # => true
145
+ # hash.key?('key') # => true
88
146
  def key?(key)
89
147
  super(convert_key(key))
90
148
  end
@@ -96,25 +154,23 @@ module ActiveSupport
96
154
  # Same as <tt>Hash#fetch</tt> where the key passed as argument can be
97
155
  # either a string or a symbol:
98
156
  #
99
- # counters = HashWithIndifferentAccess.new
157
+ # counters = ActiveSupport::HashWithIndifferentAccess.new
100
158
  # counters[:foo] = 1
101
159
  #
102
- # counters.fetch("foo") # => 1
160
+ # counters.fetch('foo') # => 1
103
161
  # counters.fetch(:bar, 0) # => 0
104
162
  # counters.fetch(:bar) {|key| 0} # => 0
105
163
  # counters.fetch(:zoo) # => KeyError: key not found: "zoo"
106
- #
107
164
  def fetch(key, *extras)
108
165
  super(convert_key(key), *extras)
109
166
  end
110
167
 
111
168
  # Returns an array of the values at the specified indices:
112
169
  #
113
- # hash = HashWithIndifferentAccess.new
114
- # hash[:a] = "x"
115
- # hash[:b] = "y"
116
- # hash.values_at("a", "b") # => ["x", "y"]
117
- #
170
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
171
+ # hash[:a] = 'x'
172
+ # hash[:b] = 'y'
173
+ # hash.values_at('a', 'b') # => ["x", "y"]
118
174
  def values_at(*indices)
119
175
  indices.collect {|key| self[convert_key(key)]}
120
176
  end
@@ -126,36 +182,58 @@ module ActiveSupport
126
182
  end
127
183
  end
128
184
 
129
- # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash.
130
- # Does not overwrite the existing hash.
131
- def merge(hash)
132
- self.dup.update(hash)
185
+ # This method has the same semantics of +update+, except it does not
186
+ # modify the receiver but rather returns a new hash with indifferent
187
+ # access with the result of the merge.
188
+ def merge(hash, &block)
189
+ self.dup.update(hash, &block)
133
190
  end
134
191
 
135
- # Performs the opposite of merge, with the keys and values from the first hash taking precedence over the second.
136
- # This overloaded definition prevents returning a regular hash, if reverse_merge is called on a <tt>HashWithDifferentAccess</tt>.
192
+ # Like +merge+ but the other way around: Merges the receiver into the
193
+ # argument and returns a new hash with indifferent access as result:
194
+ #
195
+ # hash = ActiveSupport::HashWithIndifferentAccess.new
196
+ # hash['a'] = nil
197
+ # hash.reverse_merge(a: 0, b: 1) # => {"a"=>nil, "b"=>1}
137
198
  def reverse_merge(other_hash)
138
- super self.class.new_from_hash_copying_default(other_hash)
199
+ super(self.class.new_from_hash_copying_default(other_hash))
139
200
  end
140
201
 
202
+ # Same semantics as +reverse_merge+ but modifies the receiver in-place.
141
203
  def reverse_merge!(other_hash)
142
204
  replace(reverse_merge( other_hash ))
143
205
  end
144
206
 
145
- # Removes a specified key from the hash.
207
+ # Replaces the contents of this hash with other_hash.
208
+ #
209
+ # h = { "a" => 100, "b" => 200 }
210
+ # h.replace({ "c" => 300, "d" => 400 }) #=> {"c"=>300, "d"=>400}
211
+ def replace(other_hash)
212
+ super(self.class.new_from_hash_copying_default(other_hash))
213
+ end
214
+
215
+ # Removes the specified key from the hash.
146
216
  def delete(key)
147
217
  super(convert_key(key))
148
218
  end
149
219
 
150
220
  def stringify_keys!; self end
221
+ def deep_stringify_keys!; self end
151
222
  def stringify_keys; dup end
223
+ def deep_stringify_keys; dup end
152
224
  undef :symbolize_keys!
153
- def symbolize_keys; to_hash.symbolize_keys end
225
+ undef :deep_symbolize_keys!
226
+ def symbolize_keys; to_hash.symbolize_keys! end
227
+ def deep_symbolize_keys; to_hash.deep_symbolize_keys end
154
228
  def to_options!; self end
155
229
 
156
- # Convert to a Hash with String keys.
230
+ # Convert to a regular hash with string keys.
157
231
  def to_hash
158
- Hash.new(default).merge!(self)
232
+ _new_hash= {}
233
+ each do |key, value|
234
+ _new_hash[convert_key(key)] = convert_value(value, for: :to_hash)
235
+ end
236
+ Hash.new(default).merge!(_new_hash)
159
237
  end
160
238
 
161
239
  protected
@@ -163,11 +241,18 @@ module ActiveSupport
163
241
  key.kind_of?(Symbol) ? key.to_s : key
164
242
  end
165
243
 
166
- def convert_value(value)
244
+ def convert_value(value, options = {})
167
245
  if value.is_a? Hash
168
- value.nested_under_indifferent_access
246
+ if options[:for] == :to_hash
247
+ value.to_hash
248
+ else
249
+ value.nested_under_indifferent_access
250
+ end
169
251
  elsif value.is_a?(Array)
170
- value.dup.replace(value.map { |e| convert_value(e) })
252
+ unless options[:for] == :assignment
253
+ value = value.dup
254
+ end
255
+ value.map! { |e| convert_value(e, options) }
171
256
  else
172
257
  value
173
258
  end
@@ -1,4 +1,7 @@
1
1
  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'
2
5
  require 'i18n'
3
6
  require 'active_support/lazy_load_hooks'
4
7
  rescue LoadError => e
@@ -6,4 +9,5 @@ rescue LoadError => e
6
9
  raise e
7
10
  end
8
11
 
12
+ ActiveSupport.run_load_hooks(:i18n)
9
13
  I18n.load_path << "#{File.dirname(__FILE__)}/locale/en.yml"
@@ -9,25 +9,6 @@ module I18n
9
9
  config.i18n.load_path = []
10
10
  config.i18n.fallbacks = ActiveSupport::OrderedOptions.new
11
11
 
12
- def self.reloader
13
- @reloader ||= ActiveSupport::FileUpdateChecker.new(reloader_paths){ I18n.reload! }
14
- end
15
-
16
- def self.reloader_paths
17
- @reloader_paths ||= []
18
- end
19
-
20
- # Add <tt>I18n::Railtie.reloader</tt> to ActionDispatch callbacks. Since, at this
21
- # point, no path was added to the reloader, I18n.reload! is not triggered
22
- # on to_prepare callbacks. This will only happen on the config.after_initialize
23
- # callback below.
24
- initializer "i18n.callbacks" do |app|
25
- app.reloaders << I18n::Railtie.reloader
26
- ActionDispatch::Reloader.to_prepare do
27
- I18n::Railtie.reloader.execute_if_updated
28
- end
29
- end
30
-
31
12
  # Set the i18n configuration after initialization since a lot of
32
13
  # configuration is still usually done in application initializers.
33
14
  config.after_initialize do |app|
@@ -35,7 +16,7 @@ module I18n
35
16
  end
36
17
 
37
18
  # Trigger i18n config before any eager loading has happened
38
- # so it's ready if any classes require it when eager loaded
19
+ # so it's ready if any classes require it when eager loaded.
39
20
  config.before_eager_load do |app|
40
21
  I18n::Railtie.initialize_i18n(app)
41
22
  end
@@ -44,7 +25,7 @@ module I18n
44
25
 
45
26
  @i18n_inited = false
46
27
 
47
- # Setup i18n configuration
28
+ # Setup i18n configuration.
48
29
  def self.initialize_i18n(app)
49
30
  return if @i18n_inited
50
31
 
@@ -63,7 +44,9 @@ module I18n
63
44
 
64
45
  init_fallbacks(fallbacks) if fallbacks && validate_fallbacks(fallbacks)
65
46
 
66
- reloader_paths.concat I18n.load_path
47
+ reloader = ActiveSupport::FileUpdateChecker.new(I18n.load_path.dup){ I18n.reload! }
48
+ app.reloaders << reloader
49
+ ActionDispatch::Reloader.to_prepare { reloader.execute_if_updated }
67
50
  reloader.execute
68
51
 
69
52
  @i18n_inited = true
@@ -1,10 +1,10 @@
1
1
  require 'active_support/inflector/inflections'
2
2
 
3
3
  module ActiveSupport
4
- Inflector.inflections do |inflect|
4
+ Inflector.inflections(:en) do |inflect|
5
5
  inflect.plural(/$/, 's')
6
6
  inflect.plural(/s$/i, 's')
7
- inflect.plural(/(ax|test)is$/i, '\1es')
7
+ inflect.plural(/^(ax|test)is$/i, '\1es')
8
8
  inflect.plural(/(octop|vir)us$/i, '\1i')
9
9
  inflect.plural(/(octop|vir)i$/i, '\1i')
10
10
  inflect.plural(/(alias|status)$/i, '\1es')
@@ -18,17 +18,18 @@ module ActiveSupport
18
18
  inflect.plural(/([^aeiouy]|qu)y$/i, '\1ies')
19
19
  inflect.plural(/(x|ch|ss|sh)$/i, '\1es')
20
20
  inflect.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
21
- inflect.plural(/(m|l)ouse$/i, '\1ice')
22
- inflect.plural(/(m|l)ice$/i, '\1ice')
21
+ inflect.plural(/^(m|l)ouse$/i, '\1ice')
22
+ inflect.plural(/^(m|l)ice$/i, '\1ice')
23
23
  inflect.plural(/^(ox)$/i, '\1en')
24
24
  inflect.plural(/^(oxen)$/i, '\1')
25
25
  inflect.plural(/(quiz)$/i, '\1zes')
26
26
 
27
27
  inflect.singular(/s$/i, '')
28
+ inflect.singular(/(ss)$/i, '\1')
28
29
  inflect.singular(/(n)ews$/i, '\1ews')
29
30
  inflect.singular(/([ti])a$/i, '\1um')
30
- inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
31
- inflect.singular(/(^analy)ses$/i, '\1sis')
31
+ inflect.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
32
+ inflect.singular(/(^analy)(sis|ses)$/i, '\1sis')
32
33
  inflect.singular(/([^f])ves$/i, '\1fe')
33
34
  inflect.singular(/(hive)s$/i, '\1')
34
35
  inflect.singular(/(tive)s$/i, '\1')
@@ -37,13 +38,14 @@ module ActiveSupport
37
38
  inflect.singular(/(s)eries$/i, '\1eries')
38
39
  inflect.singular(/(m)ovies$/i, '\1ovie')
39
40
  inflect.singular(/(x|ch|ss|sh)es$/i, '\1')
40
- inflect.singular(/(m|l)ice$/i, '\1ouse')
41
- inflect.singular(/(bus)es$/i, '\1')
41
+ inflect.singular(/^(m|l)ice$/i, '\1ouse')
42
+ inflect.singular(/(bus)(es)?$/i, '\1')
42
43
  inflect.singular(/(o)es$/i, '\1')
43
44
  inflect.singular(/(shoe)s$/i, '\1')
44
- inflect.singular(/(cris|ax|test)es$/i, '\1is')
45
- inflect.singular(/(octop|vir)i$/i, '\1us')
46
- inflect.singular(/(alias|status)es$/i, '\1')
45
+ inflect.singular(/(cris|test)(is|es)$/i, '\1is')
46
+ inflect.singular(/^(a)x[ie]s$/i, '\1xis')
47
+ inflect.singular(/(octop|vir)(us|i)$/i, '\1us')
48
+ inflect.singular(/(alias|status)(es)?$/i, '\1')
47
49
  inflect.singular(/^(ox)en/i, '\1')
48
50
  inflect.singular(/(vert|ind)ices$/i, '\1ex')
49
51
  inflect.singular(/(matr)ices$/i, '\1ix')
@@ -58,6 +60,6 @@ module ActiveSupport
58
60
  inflect.irregular('cow', 'kine')
59
61
  inflect.irregular('zombie', 'zombies')
60
62
 
61
- inflect.uncountable(%w(equipment information rice money species series fish sheep jeans))
63
+ inflect.uncountable(%w(equipment information rice money species series fish sheep jeans police))
62
64
  end
63
65
  end