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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +364 -135
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -4
- data/lib/active_support/backtrace_cleaner.rb +33 -25
- data/lib/active_support/basic_object.rb +7 -17
- data/lib/active_support/benchmarkable.rb +19 -15
- data/lib/active_support/buffered_logger.rb +9 -113
- data/lib/active_support/cache/file_store.rb +14 -14
- data/lib/active_support/cache/mem_cache_store.rb +24 -30
- data/lib/active_support/cache/memory_store.rb +2 -0
- data/lib/active_support/cache/strategy/local_cache.rb +48 -37
- data/lib/active_support/cache.rb +228 -183
- data/lib/active_support/callbacks.rb +201 -253
- data/lib/active_support/concern.rb +16 -23
- data/lib/active_support/concurrency/latch.rb +27 -0
- data/lib/active_support/configurable.rb +69 -12
- data/lib/active_support/core_ext/array/access.rb +17 -9
- data/lib/active_support/core_ext/array/conversions.rb +115 -55
- data/lib/active_support/core_ext/array/extract_options.rb +2 -2
- data/lib/active_support/core_ext/array/grouping.rb +21 -22
- data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
- data/lib/active_support/core_ext/array/wrap.rb +13 -16
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/benchmark.rb +7 -0
- data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -24
- data/lib/active_support/core_ext/class/attribute.rb +40 -32
- data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
- data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
- data/lib/active_support/core_ext/class/subclasses.rb +11 -5
- data/lib/active_support/core_ext/date/calculations.rb +44 -187
- data/lib/active_support/core_ext/date/conversions.rb +16 -38
- data/lib/active_support/core_ext/date/zones.rb +25 -2
- data/lib/active_support/core_ext/date.rb +5 -0
- data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +77 -62
- data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
- data/lib/active_support/core_ext/date_time/zones.rb +11 -8
- data/lib/active_support/core_ext/date_time.rb +4 -0
- data/lib/active_support/core_ext/enumerable.rb +26 -73
- data/lib/active_support/core_ext/file/atomic.rb +27 -11
- data/lib/active_support/core_ext/file.rb +0 -1
- data/lib/active_support/core_ext/hash/conversions.rb +145 -79
- data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
- data/lib/active_support/core_ext/hash/diff.rb +5 -4
- data/lib/active_support/core_ext/hash/except.rb +1 -9
- data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
- data/lib/active_support/core_ext/hash/keys.rb +108 -24
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
- data/lib/active_support/core_ext/hash/slice.rb +12 -12
- data/lib/active_support/core_ext/hash.rb +0 -1
- data/lib/active_support/core_ext/integer/inflections.rb +13 -1
- data/lib/active_support/core_ext/integer/time.rb +17 -12
- data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
- data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
- data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
- data/lib/active_support/core_ext/load_error.rb +7 -5
- data/lib/active_support/core_ext/logger.rb +7 -23
- data/lib/active_support/core_ext/marshal.rb +21 -0
- data/lib/active_support/core_ext/module/aliasing.rb +8 -9
- data/lib/active_support/core_ext/module/anonymous.rb +2 -7
- data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
- data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
- data/lib/active_support/core_ext/module/delegation.rb +92 -49
- data/lib/active_support/core_ext/module/deprecation.rb +19 -3
- data/lib/active_support/core_ext/module/introspection.rb +17 -27
- data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
- data/lib/active_support/core_ext/module/remove_method.rb +1 -5
- data/lib/active_support/core_ext/module.rb +1 -3
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
- data/lib/active_support/core_ext/numeric/time.rb +6 -6
- data/lib/active_support/core_ext/numeric.rb +1 -0
- data/lib/active_support/core_ext/object/acts_like.rb +4 -4
- data/lib/active_support/core_ext/object/blank.rb +7 -23
- data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
- data/lib/active_support/core_ext/object/duplicable.rb +1 -30
- data/lib/active_support/core_ext/object/inclusion.rb +8 -7
- data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
- data/lib/active_support/core_ext/object/to_json.rb +8 -0
- data/lib/active_support/core_ext/object/to_param.rb +5 -2
- data/lib/active_support/core_ext/object/try.rb +46 -25
- data/lib/active_support/core_ext/object/with_options.rb +7 -8
- data/lib/active_support/core_ext/object.rb +1 -0
- data/lib/active_support/core_ext/proc.rb +3 -0
- data/lib/active_support/core_ext/range/conversions.rb +0 -2
- data/lib/active_support/core_ext/range/include_range.rb +3 -1
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/range.rb +0 -2
- data/lib/active_support/core_ext/string/access.rb +95 -90
- data/lib/active_support/core_ext/string/conversions.rb +27 -38
- data/lib/active_support/core_ext/string/encoding.rb +6 -9
- data/lib/active_support/core_ext/string/filters.rb +24 -18
- data/lib/active_support/core_ext/string/indent.rb +43 -0
- data/lib/active_support/core_ext/string/inflections.rb +70 -60
- data/lib/active_support/core_ext/string/inquiry.rb +2 -2
- data/lib/active_support/core_ext/string/multibyte.rb +41 -64
- data/lib/active_support/core_ext/string/output_safety.rb +59 -51
- data/lib/active_support/core_ext/string/zones.rb +13 -0
- data/lib/active_support/core_ext/string.rb +2 -3
- data/lib/active_support/core_ext/struct.rb +6 -0
- data/lib/active_support/core_ext/thread.rb +74 -0
- data/lib/active_support/core_ext/time/calculations.rb +108 -184
- data/lib/active_support/core_ext/time/conversions.rb +27 -51
- data/lib/active_support/core_ext/time/marshal.rb +0 -27
- data/lib/active_support/core_ext/time/zones.rb +27 -17
- data/lib/active_support/core_ext/time.rb +5 -0
- data/lib/active_support/core_ext/uri.rb +13 -17
- data/lib/active_support/core_ext.rb +3 -2
- data/lib/active_support/dependencies/autoload.rb +47 -20
- data/lib/active_support/dependencies.rb +160 -141
- data/lib/active_support/deprecation/behaviors.rb +44 -30
- data/lib/active_support/deprecation/instance_delegator.rb +24 -0
- data/lib/active_support/deprecation/method_wrappers.rb +33 -18
- data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
- data/lib/active_support/deprecation/reporting.rb +40 -11
- data/lib/active_support/deprecation.rb +39 -14
- data/lib/active_support/descendants_tracker.rb +34 -19
- data/lib/active_support/duration.rb +6 -8
- data/lib/active_support/file_update_checker.rb +63 -47
- data/lib/active_support/gzip.rb +11 -5
- data/lib/active_support/hash_with_indifferent_access.rb +127 -42
- data/lib/active_support/i18n.rb +4 -0
- data/lib/active_support/i18n_railtie.rb +5 -22
- data/lib/active_support/inflections.rb +14 -12
- data/lib/active_support/inflector/inflections.rb +108 -71
- data/lib/active_support/inflector/methods.rb +181 -160
- data/lib/active_support/inflector/transliterate.rb +16 -17
- data/lib/active_support/json/decoding.rb +18 -17
- data/lib/active_support/json/encoding.rb +93 -39
- data/lib/active_support/json/variable.rb +10 -1
- data/lib/active_support/key_generator.rb +75 -0
- data/lib/active_support/lazy_load_hooks.rb +21 -19
- data/lib/active_support/locale/en.yml +100 -3
- data/lib/active_support/log_subscriber/test_helper.rb +18 -15
- data/lib/active_support/log_subscriber.rb +35 -52
- data/lib/active_support/logger.rb +57 -0
- data/lib/active_support/logger_silence.rb +24 -0
- data/lib/active_support/message_encryptor.rb +38 -35
- data/lib/active_support/message_verifier.rb +9 -15
- data/lib/active_support/multibyte/chars.rb +80 -333
- data/lib/active_support/multibyte/unicode.rb +74 -64
- data/lib/active_support/multibyte.rb +5 -28
- data/lib/active_support/notifications/fanout.rb +112 -18
- data/lib/active_support/notifications/instrumenter.rb +33 -14
- data/lib/active_support/notifications.rb +79 -26
- data/lib/active_support/number_helper.rb +637 -0
- data/lib/active_support/ordered_hash.rb +8 -190
- data/lib/active_support/ordered_options.rb +21 -23
- data/lib/active_support/per_thread_registry.rb +52 -0
- data/lib/active_support/proxy_object.rb +13 -0
- data/lib/active_support/rails.rb +27 -0
- data/lib/active_support/railtie.rb +12 -32
- data/lib/active_support/rescuable.rb +9 -4
- data/lib/active_support/string_inquirer.rb +13 -8
- data/lib/active_support/subscriber.rb +93 -0
- data/lib/active_support/tagged_logging.rb +51 -73
- data/lib/active_support/test_case.rb +46 -17
- data/lib/active_support/testing/assertions.rb +56 -26
- data/lib/active_support/testing/autorun.rb +5 -0
- data/lib/active_support/testing/constant_lookup.rb +54 -0
- data/lib/active_support/testing/declarative.rb +1 -1
- data/lib/active_support/testing/deprecation.rb +0 -19
- data/lib/active_support/testing/isolation.rb +29 -58
- data/lib/active_support/testing/pending.rb +5 -43
- data/lib/active_support/testing/setup_and_teardown.rb +6 -92
- data/lib/active_support/testing/tagged_logging.rb +25 -0
- data/lib/active_support/time.rb +6 -21
- data/lib/active_support/time_with_zone.rb +80 -43
- data/lib/active_support/values/time_zone.rb +81 -60
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +7 -6
- data/lib/active_support/xml_mini/jdom.rb +9 -11
- data/lib/active_support/xml_mini/libxml.rb +1 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
- data/lib/active_support/xml_mini/nokogiri.rb +1 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
- data/lib/active_support/xml_mini/rexml.rb +6 -8
- data/lib/active_support/xml_mini.rb +35 -17
- data/lib/active_support.rb +8 -21
- metadata +104 -72
- data/lib/active_support/base64.rb +0 -54
- data/lib/active_support/core_ext/array/random_access.rb +0 -30
- data/lib/active_support/core_ext/date/freeze.rb +0 -33
- data/lib/active_support/core_ext/exception.rb +0 -3
- data/lib/active_support/core_ext/file/path.rb +0 -5
- data/lib/active_support/core_ext/float/rounding.rb +0 -19
- data/lib/active_support/core_ext/float.rb +0 -1
- data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
- data/lib/active_support/core_ext/io.rb +0 -15
- data/lib/active_support/core_ext/module/method_names.rb +0 -14
- data/lib/active_support/core_ext/module/synchronization.rb +0 -45
- data/lib/active_support/core_ext/process/daemon.rb +0 -23
- data/lib/active_support/core_ext/process.rb +0 -1
- data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
- data/lib/active_support/core_ext/range/cover.rb +0 -3
- data/lib/active_support/core_ext/rexml.rb +0 -46
- data/lib/active_support/core_ext/string/interpolation.rb +0 -2
- data/lib/active_support/core_ext/string/xchar.rb +0 -18
- data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
- data/lib/active_support/memoizable.rb +0 -116
- data/lib/active_support/multibyte/exceptions.rb +0 -8
- data/lib/active_support/multibyte/utils.rb +0 -60
- data/lib/active_support/ruby/shim.rb +0 -22
- data/lib/active_support/security_utils.rb +0 -27
- data/lib/active_support/testing/mochaing.rb +0 -7
- data/lib/active_support/testing/performance/jruby.rb +0 -115
- data/lib/active_support/testing/performance/rubinius.rb +0 -113
- data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
- data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
- data/lib/active_support/testing/performance/ruby.rb +0 -152
- data/lib/active_support/testing/performance.rb +0 -317
- data/lib/active_support/time/autoload.rb +0 -5
- 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
|
-
# {
|
|
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
|
-
#
|
|
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
|
|
41
|
+
# options[:builder].bar 'fooing!'
|
|
40
42
|
# end
|
|
41
43
|
# end
|
|
42
|
-
#
|
|
43
|
-
# {:
|
|
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" => "
|
|
61
|
-
# "Time" => "
|
|
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] ||=
|
|
75
|
-
options[:builder] ||= Builder::XmlMarkup.new(:
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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
|
-
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
when
|
|
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
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
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 = {:
|
|
5
|
-
# h2 = {:
|
|
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) #=> {
|
|
8
|
-
# h2.deep_merge(h1) #=> {
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
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(
|
|
11
|
-
|
|
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.
|
|
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
|
-
# {:
|
|
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 = {:
|
|
20
|
-
# {:
|
|
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
|
-
# { :
|
|
5
|
-
#
|
|
27
|
+
# hash = { name: 'Rob', age: '28' }
|
|
28
|
+
#
|
|
29
|
+
# hash.stringify_keys
|
|
30
|
+
# #=> { "name" => "Rob", "age" => "28" }
|
|
6
31
|
def stringify_keys
|
|
7
|
-
|
|
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
|
-
|
|
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', '
|
|
23
|
-
#
|
|
44
|
+
# hash = { 'name' => 'Rob', 'age' => '28' }
|
|
45
|
+
#
|
|
46
|
+
# hash.symbolize_keys
|
|
47
|
+
# #=> { name: "Rob", age: "28" }
|
|
24
48
|
def symbolize_keys
|
|
25
|
-
|
|
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
|
-
|
|
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
|
|
41
|
-
# Note that keys are NOT treated indifferently, meaning if you
|
|
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
|
-
#
|
|
45
|
-
# { :
|
|
46
|
-
# { :
|
|
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(
|
|
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(:
|
|
4
|
+
# options = options.reverse_merge(size: 25, velocity: 10)
|
|
5
5
|
#
|
|
6
6
|
# is equivalent to
|
|
7
7
|
#
|
|
8
|
-
# 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
|
|
17
|
-
hash = self
|
|
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
|
|
24
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|