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,14 +1,16 @@
1
1
  require 'active_support/xml_mini'
2
2
  require 'active_support/time'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/object/to_param'
5
+ require 'active_support/core_ext/object/to_query'
3
6
  require 'active_support/core_ext/array/wrap'
4
7
  require 'active_support/core_ext/hash/reverse_merge'
5
- require 'active_support/core_ext/object/blank'
6
8
  require 'active_support/core_ext/string/inflections'
7
9
 
8
10
  class Hash
9
11
  # Returns a string containing an XML representation of its receiver:
10
12
  #
11
- # {"foo" => 1, "bar" => 2}.to_xml
13
+ # {'foo' => 1, 'bar' => 2}.to_xml
12
14
  # # =>
13
15
  # # <?xml version="1.0" encoding="UTF-8"?>
14
16
  # # <hash>
@@ -26,21 +28,21 @@ class Hash
26
28
  #
27
29
  # * If +value+ is a callable object it must expect one or two arguments. Depending
28
30
  # on the arity, the callable is invoked with the +options+ hash as first argument
29
- # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
31
+ # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
30
32
  # callable can add nodes by using <tt>options[:builder]</tt>.
31
33
  #
32
- # "foo".to_xml(lambda { |options, key| options[:builder].b(key) })
34
+ # 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
33
35
  # # => "<b>foo</b>"
34
36
  #
35
37
  # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
36
- #
38
+ #
37
39
  # class Foo
38
40
  # def to_xml(options)
39
- # options[:builder].bar "fooing!"
41
+ # options[:builder].bar 'fooing!'
40
42
  # end
41
43
  # end
42
- #
43
- # {:foo => Foo.new}.to_xml(:skip_instruct => true)
44
+ #
45
+ # { foo: Foo.new }.to_xml(skip_instruct: true)
44
46
  # # => "<hash><bar>fooing!</bar></hash>"
45
47
  #
46
48
  # * Otherwise, a node with +key+ as tag is created with a string representation of
@@ -57,8 +59,8 @@ class Hash
57
59
  # "TrueClass" => "boolean",
58
60
  # "FalseClass" => "boolean",
59
61
  # "Date" => "date",
60
- # "DateTime" => "datetime",
61
- # "Time" => "datetime"
62
+ # "DateTime" => "dateTime",
63
+ # "Time" => "dateTime"
62
64
  # }
63
65
  #
64
66
  # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
@@ -71,105 +73,169 @@ class Hash
71
73
 
72
74
  options = options.dup
73
75
  options[:indent] ||= 2
74
- options[:root] ||= "hash"
75
- options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
76
+ options[:root] ||= 'hash'
77
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
76
78
 
77
79
  builder = options[:builder]
78
80
  builder.instruct! unless options.delete(:skip_instruct)
79
81
 
80
82
  root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
81
83
 
82
- builder.__send__(:method_missing, root) do
84
+ builder.tag!(root) do
83
85
  each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
84
86
  yield builder if block_given?
85
87
  end
86
88
  end
87
89
 
88
- class DisallowedType < StandardError #:nodoc:
89
- def initialize(type)
90
- super "Disallowed type attribute: #{type.inspect}"
91
- end
92
- end
93
-
94
- DISALLOWED_XML_TYPES = %w(symbol yaml)
95
-
96
90
  class << self
91
+ # Returns a Hash containing a collection of pairs when the key is the node name and the value is
92
+ # its content
93
+ #
94
+ # xml = <<-XML
95
+ # <?xml version="1.0" encoding="UTF-8"?>
96
+ # <hash>
97
+ # <foo type="integer">1</foo>
98
+ # <bar type="integer">2</bar>
99
+ # </hash>
100
+ # XML
101
+ #
102
+ # hash = Hash.from_xml(xml)
103
+ # # => {"hash"=>{"foo"=>1, "bar"=>2}}
104
+ #
105
+ # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or
106
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
97
107
  def from_xml(xml, disallowed_types = nil)
98
- typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)), disallowed_types)
108
+ ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
99
109
  end
100
110
 
111
+ # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
101
112
  def from_trusted_xml(xml)
102
113
  from_xml xml, []
103
114
  end
115
+ end
116
+ end
104
117
 
105
- private
106
- def typecast_xml_value(value, disallowed_types = nil)
107
- disallowed_types ||= DISALLOWED_XML_TYPES
118
+ module ActiveSupport
119
+ class XMLConverter # :nodoc:
120
+ class DisallowedType < StandardError
121
+ def initialize(type)
122
+ super "Disallowed type attribute: #{type.inspect}"
123
+ end
124
+ end
108
125
 
109
- case value.class.to_s
110
- when 'Hash'
111
- if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
112
- raise DisallowedType, value['type']
113
- end
126
+ DISALLOWED_TYPES = %w(symbol yaml)
114
127
 
115
- if value['type'] == 'array'
116
- _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
117
- if entries.nil? || (c = value['__content__'] && c.blank?)
118
- []
119
- else
120
- case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
121
- when "Array"
122
- entries.collect { |v| typecast_xml_value(v, disallowed_types) }
123
- when "Hash"
124
- [typecast_xml_value(entries, disallowed_types)]
125
- else
126
- raise "can't typecast #{entries.inspect}"
127
- end
128
- end
129
- elsif value['type'] == 'file' ||
130
- (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
131
- content = value["__content__"]
132
- if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
133
- parser.arity == 1 ? parser.call(content) : parser.call(content, value)
134
- else
135
- content
136
- end
137
- elsif value['type'] == 'string' && value['nil'] != 'true'
138
- ""
139
- # blank or nil parsed values are represented by nil
140
- elsif value.blank? || value['nil'] == 'true'
141
- nil
142
- # If the type is the only element which makes it then
143
- # this still makes the value nil, except if type is
144
- # a XML node(where type['value'] is a Hash)
145
- elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
146
- nil
147
- else
148
- xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v, disallowed_types)] }]
128
+ def initialize(xml, disallowed_types = nil)
129
+ @xml = normalize_keys(XmlMini.parse(xml))
130
+ @disallowed_types = disallowed_types || DISALLOWED_TYPES
131
+ end
149
132
 
150
- # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
151
- # how multipart uploaded files from HTML appear
152
- xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
153
- end
154
- when 'Array'
155
- value.map! { |i| typecast_xml_value(i, disallowed_types) }
156
- value.length > 1 ? value : value.first
157
- when 'String'
133
+ def to_h
134
+ deep_to_h(@xml)
135
+ end
136
+
137
+ private
138
+ def normalize_keys(params)
139
+ case params
140
+ when Hash
141
+ Hash[params.map { |k,v| [k.to_s.tr('-', '_'), normalize_keys(v)] } ]
142
+ when Array
143
+ params.map { |v| normalize_keys(v) }
144
+ else
145
+ params
146
+ end
147
+ end
148
+
149
+ def deep_to_h(value)
150
+ case value
151
+ when Hash
152
+ process_hash(value)
153
+ when Array
154
+ process_array(value)
155
+ when String
158
156
  value
159
157
  else
160
158
  raise "can't typecast #{value.class.name} - #{value.inspect}"
161
159
  end
162
160
  end
163
161
 
164
- def unrename_keys(params)
165
- case params.class.to_s
166
- when "Hash"
167
- Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
168
- when "Array"
169
- params.map { |v| unrename_keys(v) }
162
+ def process_hash(value)
163
+ if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type'])
164
+ raise DisallowedType, value['type']
165
+ end
166
+
167
+ if become_array?(value)
168
+ _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
169
+ if entries.nil? || value['__content__'].try(:empty?)
170
+ []
170
171
  else
171
- params
172
+ case entries
173
+ when Array
174
+ entries.collect { |v| deep_to_h(v) }
175
+ when Hash
176
+ [deep_to_h(entries)]
177
+ else
178
+ raise "can't typecast #{entries.inspect}"
179
+ end
180
+ end
181
+ elsif become_content?(value)
182
+ process_content(value)
183
+
184
+ elsif become_empty_string?(value)
185
+ ''
186
+ elsif become_hash?(value)
187
+ xml_value = Hash[value.map { |k,v| [k, deep_to_h(v)] }]
188
+
189
+ # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
190
+ # how multipart uploaded files from HTML appear
191
+ xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
172
192
  end
173
193
  end
194
+
195
+ def become_content?(value)
196
+ value['type'] == 'file' || (value['__content__'] && (value.keys.size == 1 || value['__content__'].present?))
197
+ end
198
+
199
+ def become_array?(value)
200
+ value['type'] == 'array'
201
+ end
202
+
203
+ def become_empty_string?(value)
204
+ # {"string" => true}
205
+ # No tests fail when the second term is removed.
206
+ value['type'] == 'string' && value['nil'] != 'true'
207
+ end
208
+
209
+ def become_hash?(value)
210
+ !nothing?(value) && !garbage?(value)
211
+ end
212
+
213
+ def nothing?(value)
214
+ # blank or nil parsed values are represented by nil
215
+ value.blank? || value['nil'] == 'true'
216
+ end
217
+
218
+ def garbage?(value)
219
+ # If the type is the only element which makes it then
220
+ # this still makes the value nil, except if type is
221
+ # a XML node(where type['value'] is a Hash)
222
+ value['type'] && !value['type'].is_a?(::Hash) && value.size == 1
223
+ end
224
+
225
+ def process_content(value)
226
+ content = value['__content__']
227
+ if parser = ActiveSupport::XmlMini::PARSING[value['type']]
228
+ parser.arity == 1 ? parser.call(content) : parser.call(content, value)
229
+ else
230
+ content
231
+ end
232
+ end
233
+
234
+ def process_array(value)
235
+ value.map! { |i| deep_to_h(i) }
236
+ value.length > 1 ? value : value.first
237
+ end
238
+
174
239
  end
175
240
  end
241
+
@@ -1,20 +1,26 @@
1
1
  class Hash
2
2
  # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
3
  #
4
- # h1 = {:x => {:y => [4,5,6]}, :z => [7,8,9]}
5
- # h2 = {:x => {:y => [7,8,9]}, :z => "xyz"}
4
+ # h1 = { x: { y: [4,5,6] }, z: [7,8,9] }
5
+ # h2 = { x: { y: [7,8,9] }, z: 'xyz' }
6
6
  #
7
- # h1.deep_merge(h2) #=> { :x => {:y => [7, 8, 9]}, :z => "xyz" }
8
- # h2.deep_merge(h1) #=> { :x => {:y => [4, 5, 6]}, :z => [7, 8, 9] }
9
- def deep_merge(other_hash)
10
- dup.deep_merge!(other_hash)
7
+ # h1.deep_merge(h2) #=> {x: {y: [7, 8, 9]}, z: "xyz"}
8
+ # h2.deep_merge(h1) #=> {x: {y: [4, 5, 6]}, z: [7, 8, 9]}
9
+ # h1.deep_merge(h2) { |key, old, new| Array.wrap(old) + Array.wrap(new) }
10
+ # #=> {:x=>{:y=>[4, 5, 6, 7, 8, 9]}, :z=>[7, 8, 9, "xyz"]}
11
+ def deep_merge(other_hash, &block)
12
+ dup.deep_merge!(other_hash, &block)
11
13
  end
12
14
 
13
15
  # Same as +deep_merge+, but modifies +self+.
14
- def deep_merge!(other_hash)
16
+ def deep_merge!(other_hash, &block)
15
17
  other_hash.each_pair do |k,v|
16
18
  tv = self[k]
17
- self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
19
+ if tv.is_a?(Hash) && v.is_a?(Hash)
20
+ self[k] = tv.deep_merge(v, &block)
21
+ else
22
+ self[k] = block && tv ? block.call(k, tv, v) : v
23
+ end
18
24
  end
19
25
  self
20
26
  end
@@ -1,13 +1,14 @@
1
1
  class Hash
2
2
  # Returns a hash that represents the difference between two hashes.
3
3
  #
4
- # Examples:
5
- #
6
4
  # {1 => 2}.diff(1 => 2) # => {}
7
5
  # {1 => 2}.diff(1 => 3) # => {1 => 2}
8
6
  # {}.diff(1 => 2) # => {1 => 2}
9
7
  # {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
10
- def diff(h2)
11
- dup.delete_if { |k, v| h2[k] == v }.merge!(h2.dup.delete_if { |k, v| has_key?(k) })
8
+ def diff(other)
9
+ ActiveSupport::Deprecation.warn "Hash#diff is no longer used inside of Rails, and is being deprecated with no replacement. If you're using it to compare hashes for the purpose of testing, please use MiniTest's assert_equal instead."
10
+ dup.
11
+ delete_if { |k, v| other[k] == v }.
12
+ merge!(other.dup.delete_if { |k, v| has_key?(k) })
12
13
  end
13
14
  end
@@ -2,15 +2,7 @@ class Hash
2
2
  # Return a hash that includes everything but the given keys. This is useful for
3
3
  # limiting a set of parameters to everything but a few known toggles:
4
4
  #
5
- # @person.update_attributes(params[:person].except(:admin))
6
- #
7
- # If the receiver responds to +convert_key+, the method is called on each of the
8
- # arguments. This allows +except+ to play nice with hashes with indifferent access
9
- # for instance:
10
- #
11
- # {:a => 1}.with_indifferent_access.except(:a) # => {}
12
- # {:a => 1}.with_indifferent_access.except("a") # => {}
13
- #
5
+ # @person.update(params[:person].except(:admin))
14
6
  def except(*keys)
15
7
  dup.except!(*keys)
16
8
  end
@@ -1,10 +1,10 @@
1
1
  require 'active_support/hash_with_indifferent_access'
2
2
 
3
3
  class Hash
4
+
4
5
  # Returns an <tt>ActiveSupport::HashWithIndifferentAccess</tt> out of its receiver:
5
6
  #
6
- # {:a => 1}.with_indifferent_access["a"] # => 1
7
- #
7
+ # { a: 1 }.with_indifferent_access['a'] # => 1
8
8
  def with_indifferent_access
9
9
  ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
10
10
  end
@@ -16,8 +16,7 @@ class Hash
16
16
  # converting to an <tt>ActiveSupport::HashWithIndifferentAccess</tt> would not be
17
17
  # desirable.
18
18
  #
19
- # b = {:b => 1}
20
- # {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
21
- #
19
+ # b = { b: 1 }
20
+ # { a: b }.with_indifferent_access['a'] # calls b.nested_under_indifferent_access
22
21
  alias nested_under_indifferent_access with_indifferent_access
23
22
  end
@@ -1,54 +1,138 @@
1
1
  class Hash
2
+ # Return a new hash with all keys converted using the block operation.
3
+ #
4
+ # hash = { name: 'Rob', age: '28' }
5
+ #
6
+ # hash.transform_keys{ |key| key.to_s.upcase }
7
+ # # => { "NAME" => "Rob", "AGE" => "28" }
8
+ def transform_keys
9
+ result = {}
10
+ each_key do |key|
11
+ result[yield(key)] = self[key]
12
+ end
13
+ result
14
+ end
15
+
16
+ # Destructively convert all keys using the block operations.
17
+ # Same as transform_keys but modifies +self+.
18
+ def transform_keys!
19
+ keys.each do |key|
20
+ self[yield(key)] = delete(key)
21
+ end
22
+ self
23
+ end
24
+
2
25
  # Return a new hash with all keys converted to strings.
3
26
  #
4
- # { :name => 'Rob', :years => '28' }.stringify_keys
5
- # #=> { "name" => "Rob", "years" => "28" }
27
+ # hash = { name: 'Rob', age: '28' }
28
+ #
29
+ # hash.stringify_keys
30
+ # #=> { "name" => "Rob", "age" => "28" }
6
31
  def stringify_keys
7
- dup.stringify_keys!
32
+ transform_keys{ |key| key.to_s }
8
33
  end
9
34
 
10
35
  # Destructively convert all keys to strings. Same as
11
36
  # +stringify_keys+, but modifies +self+.
12
37
  def stringify_keys!
13
- keys.each do |key|
14
- self[key.to_s] = delete(key)
15
- end
16
- self
38
+ transform_keys!{ |key| key.to_s }
17
39
  end
18
40
 
19
41
  # Return a new hash with all keys converted to symbols, as long as
20
42
  # they respond to +to_sym+.
21
43
  #
22
- # { 'name' => 'Rob', 'years' => '28' }.symbolize_keys
23
- # #=> { :name => "Rob", :years => "28" }
44
+ # hash = { 'name' => 'Rob', 'age' => '28' }
45
+ #
46
+ # hash.symbolize_keys
47
+ # #=> { name: "Rob", age: "28" }
24
48
  def symbolize_keys
25
- dup.symbolize_keys!
49
+ transform_keys{ |key| key.to_sym rescue key }
26
50
  end
51
+ alias_method :to_options, :symbolize_keys
27
52
 
28
53
  # Destructively convert all keys to symbols, as long as they respond
29
54
  # to +to_sym+. Same as +symbolize_keys+, but modifies +self+.
30
55
  def symbolize_keys!
31
- keys.each do |key|
32
- self[(key.to_sym rescue key) || key] = delete(key)
33
- end
34
- self
56
+ transform_keys!{ |key| key.to_sym rescue key }
35
57
  end
36
-
37
- alias_method :to_options, :symbolize_keys
38
58
  alias_method :to_options!, :symbolize_keys!
39
59
 
40
- # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
41
- # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
42
- # as keys, this will fail.
60
+ # Validate all keys in a hash match <tt>*valid_keys</tt>, raising ArgumentError
61
+ # on a mismatch. Note that keys are NOT treated indifferently, meaning if you
62
+ # use strings for keys but assert symbols as keys, this will fail.
43
63
  #
44
- # ==== Examples
45
- # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
46
- # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
47
- # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
64
+ # { name: 'Rob', years: '28' }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
65
+ # { name: 'Rob', age: '28' }.assert_valid_keys('name', 'age') # => raises "ArgumentError: Unknown key: name"
66
+ # { name: 'Rob', age: '28' }.assert_valid_keys(:name, :age) # => passes, raises nothing
48
67
  def assert_valid_keys(*valid_keys)
49
68
  valid_keys.flatten!
50
69
  each_key do |k|
51
- raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
70
+ raise ArgumentError.new("Unknown key: #{k}") unless valid_keys.include?(k)
71
+ end
72
+ end
73
+
74
+ # Return a new hash with all keys converted by the block operation.
75
+ # This includes the keys from the root hash and from all
76
+ # nested hashes.
77
+ #
78
+ # hash = { person: { name: 'Rob', age: '28' } }
79
+ #
80
+ # hash.deep_transform_keys{ |key| key.to_s.upcase }
81
+ # # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
82
+ def deep_transform_keys(&block)
83
+ result = {}
84
+ each do |key, value|
85
+ result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
86
+ end
87
+ result
88
+ end
89
+
90
+ # Destructively convert all keys by using the block operation.
91
+ # This includes the keys from the root hash and from all
92
+ # nested hashes.
93
+ def deep_transform_keys!(&block)
94
+ keys.each do |key|
95
+ value = delete(key)
96
+ self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
52
97
  end
98
+ self
99
+ end
100
+
101
+ # Return a new hash with all keys converted to strings.
102
+ # This includes the keys from the root hash and from all
103
+ # nested hashes.
104
+ #
105
+ # hash = { person: { name: 'Rob', age: '28' } }
106
+ #
107
+ # hash.deep_stringify_keys
108
+ # # => { "person" => { "name" => "Rob", "age" => "28" } }
109
+ def deep_stringify_keys
110
+ deep_transform_keys{ |key| key.to_s }
111
+ end
112
+
113
+ # Destructively convert all keys to strings.
114
+ # This includes the keys from the root hash and from all
115
+ # nested hashes.
116
+ def deep_stringify_keys!
117
+ deep_transform_keys!{ |key| key.to_s }
118
+ end
119
+
120
+ # Return a new hash with all keys converted to symbols, as long as
121
+ # they respond to +to_sym+. This includes the keys from the root hash
122
+ # and from all nested hashes.
123
+ #
124
+ # hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
125
+ #
126
+ # hash.deep_symbolize_keys
127
+ # # => { person: { name: "Rob", age: "28" } }
128
+ def deep_symbolize_keys
129
+ deep_transform_keys{ |key| key.to_sym rescue key }
130
+ end
131
+
132
+ # Destructively convert all keys to symbols, as long as they respond
133
+ # to +to_sym+. This includes the keys from the root hash and from all
134
+ # nested hashes.
135
+ def deep_symbolize_keys!
136
+ deep_transform_keys!{ |key| key.to_sym rescue key }
53
137
  end
54
138
  end
@@ -1,11 +1,11 @@
1
1
  class Hash
2
2
  # Merges the caller into +other_hash+. For example,
3
3
  #
4
- # options = options.reverse_merge(:size => 25, :velocity => 10)
4
+ # options = options.reverse_merge(size: 25, velocity: 10)
5
5
  #
6
6
  # is equivalent to
7
7
  #
8
- # options = {:size => 25, :velocity => 10}.merge(options)
8
+ # options = { size: 25, velocity: 10 }.merge(options)
9
9
  #
10
10
  # This is particularly useful for initializing an options hash
11
11
  # with default values.
@@ -18,6 +18,5 @@ class Hash
18
18
  # right wins if there is no left
19
19
  merge!( other_hash ){|key,left,right| left }
20
20
  end
21
-
22
21
  alias_method :reverse_update, :reverse_merge!
23
22
  end
@@ -3,7 +3,7 @@ class Hash
3
3
  # limiting an options hash to valid keys before passing to a method:
4
4
  #
5
5
  # def search(criteria = {})
6
- # assert_valid_keys(:mass, :velocity, :time)
6
+ # criteria.assert_valid_keys(:mass, :velocity, :time)
7
7
  # end
8
8
  #
9
9
  # search(options.slice(:mass, :velocity, :time))
@@ -13,17 +13,17 @@ class Hash
13
13
  # valid_keys = [:mass, :velocity, :time]
14
14
  # search(options.slice(*valid_keys))
15
15
  def slice(*keys)
16
- keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
17
- hash = self.class.new
18
- keys.each { |k| hash[k] = self[k] if has_key?(k) }
19
- hash
16
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
17
+ keys.each_with_object(self.class.new) { |k, hash| hash[k] = self[k] if has_key?(k) }
20
18
  end
21
19
 
22
20
  # Replaces the hash with only the given keys.
23
- # Returns a hash contained the removed key/value pairs
24
- # {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4}
21
+ # Returns a hash containing the removed key/value pairs.
22
+ #
23
+ # { a: 1, b: 2, c: 3, d: 4 }.slice!(:a, :b)
24
+ # # => {:c=>3, :d=>4}
25
25
  def slice!(*keys)
26
- keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
26
+ keys.map! { |key| convert_key(key) } if respond_to?(:convert_key, true)
27
27
  omit = slice(*self.keys - keys)
28
28
  hash = slice(*keys)
29
29
  replace(hash)
@@ -31,10 +31,10 @@ class Hash
31
31
  end
32
32
 
33
33
  # Removes and returns the key/value pairs matching the given keys.
34
- # {:a => 1, :b => 2, :c => 3, :d => 4}.extract!(:a, :b) # => {:a => 1, :b => 2}
34
+ #
35
+ # { a: 1, b: 2, c: 3, d: 4 }.extract!(:a, :b) # => {:a=>1, :b=>2}
36
+ # { a: 1, b: 2 }.extract!(:a, :x) # => {:a=>1}
35
37
  def extract!(*keys)
36
- result = {}
37
- keys.each {|key| result[key] = delete(key) }
38
- result
38
+ keys.each_with_object(self.class.new) { |key, result| result[key] = delete(key) if has_key?(key) }
39
39
  end
40
40
  end
@@ -1,6 +1,5 @@
1
1
  require 'active_support/core_ext/hash/conversions'
2
2
  require 'active_support/core_ext/hash/deep_merge'
3
- require 'active_support/core_ext/hash/deep_dup'
4
3
  require 'active_support/core_ext/hash/diff'
5
4
  require 'active_support/core_ext/hash/except'
6
5
  require 'active_support/core_ext/hash/indifferent_access'
@@ -10,8 +10,20 @@ class Integer
10
10
  # 1003.ordinalize # => "1003rd"
11
11
  # -11.ordinalize # => "-11th"
12
12
  # -1001.ordinalize # => "-1001st"
13
- #
14
13
  def ordinalize
15
14
  ActiveSupport::Inflector.ordinalize(self)
16
15
  end
16
+
17
+ # Ordinal returns the suffix used to denote the position
18
+ # in an ordered sequence such as 1st, 2nd, 3rd, 4th.
19
+ #
20
+ # 1.ordinal # => "st"
21
+ # 2.ordinal # => "nd"
22
+ # 1002.ordinal # => "nd"
23
+ # 1003.ordinal # => "rd"
24
+ # -11.ordinal # => "th"
25
+ # -1001.ordinal # => "st"
26
+ def ordinal
27
+ ActiveSupport::Inflector.ordinal(self)
28
+ end
17
29
  end