activesupport 1.4.4 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activesupport might be problematic. Click here for more details.
- data/CHANGELOG +263 -7
- data/lib/active_support.rb +9 -4
- data/lib/active_support/basic_object.rb +5 -0
- data/lib/active_support/buffered_logger.rb +107 -0
- data/lib/active_support/clean_logger.rb +94 -5
- data/lib/active_support/core_ext.rb +4 -1
- data/lib/active_support/core_ext/array.rb +8 -2
- data/lib/active_support/core_ext/array/access.rb +28 -0
- data/lib/active_support/core_ext/array/conversions.rb +28 -15
- data/lib/active_support/core_ext/array/extract_options.rb +19 -0
- data/lib/active_support/core_ext/array/grouping.rb +20 -7
- data/lib/active_support/core_ext/array/random_access.rb +12 -0
- data/lib/active_support/core_ext/bigdecimal.rb +1 -2
- data/lib/active_support/core_ext/bigdecimal/{formatting.rb → conversions.rb} +1 -2
- data/lib/active_support/core_ext/blank.rb +2 -8
- data/lib/active_support/core_ext/cgi.rb +2 -2
- data/lib/active_support/core_ext/class.rb +4 -3
- data/lib/active_support/core_ext/class/attribute_accessors.rb +1 -1
- data/lib/active_support/core_ext/class/delegating_attributes.rb +40 -0
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +3 -3
- data/lib/active_support/core_ext/class/removal.rb +2 -2
- data/lib/active_support/core_ext/date.rb +5 -1
- data/lib/active_support/core_ext/date/behavior.rb +13 -0
- data/lib/active_support/core_ext/date/calculations.rb +188 -0
- data/lib/active_support/core_ext/date/conversions.rb +69 -13
- data/lib/active_support/core_ext/date_time.rb +10 -0
- data/lib/active_support/core_ext/date_time/calculations.rb +77 -0
- data/lib/active_support/core_ext/date_time/conversions.rb +54 -0
- data/lib/active_support/core_ext/duplicable.rb +37 -0
- data/lib/active_support/core_ext/enumerable.rb +1 -0
- data/lib/active_support/core_ext/exception.rb +2 -2
- data/lib/active_support/core_ext/file.rb +21 -0
- data/lib/active_support/core_ext/float.rb +5 -0
- data/lib/active_support/core_ext/float/rounding.rb +24 -0
- data/lib/active_support/core_ext/hash.rb +5 -5
- data/lib/active_support/core_ext/hash/conversions.rb +86 -34
- data/lib/active_support/core_ext/hash/diff.rb +8 -0
- data/lib/active_support/core_ext/hash/except.rb +24 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +15 -2
- data/lib/active_support/core_ext/hash/keys.rb +10 -3
- data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -2
- data/lib/active_support/core_ext/hash/slice.rb +28 -0
- data/lib/active_support/core_ext/integer.rb +2 -2
- data/lib/active_support/core_ext/kernel.rb +5 -4
- data/lib/active_support/core_ext/kernel/debugger.rb +13 -0
- data/lib/active_support/core_ext/module.rb +8 -7
- data/lib/active_support/core_ext/module/aliasing.rb +17 -5
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +31 -0
- data/lib/active_support/core_ext/module/attribute_accessors.rb +1 -1
- data/lib/active_support/core_ext/module/delegation.rb +21 -0
- data/lib/active_support/core_ext/name_error.rb +2 -2
- data/lib/active_support/core_ext/numeric.rb +2 -2
- data/lib/active_support/core_ext/numeric/time.rb +30 -11
- data/lib/active_support/core_ext/object.rb +3 -2
- data/lib/active_support/core_ext/object/extending.rb +40 -29
- data/lib/active_support/core_ext/object/instance_variables.rb +22 -0
- data/lib/active_support/core_ext/object/misc.rb +29 -4
- data/lib/active_support/core_ext/pathname.rb +1 -1
- data/lib/active_support/core_ext/range.rb +7 -1
- data/lib/active_support/core_ext/range/blockless_step.rb +22 -0
- data/lib/active_support/core_ext/range/conversions.rb +8 -6
- data/lib/active_support/core_ext/range/include_range.rb +22 -0
- data/lib/active_support/core_ext/range/overlaps.rb +12 -0
- data/lib/active_support/core_ext/string.rb +10 -7
- data/lib/active_support/core_ext/string/conversions.rb +5 -1
- data/lib/active_support/core_ext/string/unicode.rb +2 -2
- data/lib/active_support/core_ext/string/xchar.rb +11 -0
- data/lib/active_support/core_ext/symbol.rb +12 -10
- data/lib/active_support/core_ext/test.rb +1 -0
- data/lib/active_support/core_ext/test/unit/assertions.rb +62 -0
- data/lib/active_support/core_ext/time.rb +4 -2
- data/lib/active_support/core_ext/time/behavior.rb +13 -0
- data/lib/active_support/core_ext/time/calculations.rb +87 -54
- data/lib/active_support/core_ext/time/conversions.rb +71 -10
- data/lib/active_support/dependencies.rb +25 -24
- data/lib/active_support/deprecation.rb +4 -2
- data/lib/active_support/duration.rb +86 -0
- data/lib/active_support/inflections.rb +2 -1
- data/lib/active_support/inflector.rb +13 -6
- data/lib/active_support/json.rb +22 -39
- data/lib/active_support/json/decoding.rb +60 -0
- data/lib/active_support/json/encoders/date.rb +5 -0
- data/lib/active_support/json/encoders/date_time.rb +5 -0
- data/lib/active_support/json/encoders/enumerable.rb +12 -0
- data/lib/active_support/json/encoders/false_class.rb +5 -0
- data/lib/active_support/json/encoders/hash.rb +50 -0
- data/lib/active_support/json/encoders/nil_class.rb +5 -0
- data/lib/active_support/json/encoders/numeric.rb +5 -0
- data/lib/active_support/json/encoders/object.rb +6 -0
- data/lib/active_support/json/encoders/regexp.rb +5 -0
- data/lib/active_support/json/encoders/string.rb +30 -0
- data/lib/active_support/json/encoders/symbol.rb +5 -0
- data/lib/active_support/json/encoders/time.rb +5 -0
- data/lib/active_support/json/encoders/true_class.rb +5 -0
- data/lib/active_support/json/encoding.rb +38 -0
- data/lib/active_support/json/variable.rb +10 -0
- data/lib/active_support/multibyte.rb +7 -5
- data/lib/active_support/multibyte/chars.rb +6 -0
- data/lib/active_support/multibyte/handlers/utf8_handler.rb +115 -5
- data/lib/active_support/option_merger.rb +7 -7
- data/lib/active_support/ordered_options.rb +22 -17
- data/lib/active_support/test_case.rb +5 -0
- data/lib/active_support/testing.rb +1 -0
- data/lib/active_support/testing/default.rb +12 -0
- data/lib/active_support/values/time_zone.rb +3 -3
- data/lib/active_support/vendor.rb +14 -0
- data/lib/active_support/vendor/builder-2.1.2/blankslate.rb +113 -0
- data/lib/active_support/vendor/{builder.rb → builder-2.1.2/builder.rb} +0 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/blankslate.rb +20 -0
- data/lib/active_support/vendor/builder-2.1.2/builder/css.rb +250 -0
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xchar.rb +11 -8
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlbase.rb +38 -44
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlevents.rb +1 -1
- data/lib/active_support/vendor/{builder → builder-2.1.2/builder}/xmlmarkup.rb +40 -39
- data/lib/active_support/vendor/{xml_simple.rb → xml-simple-1.0.11/xmlsimple.rb} +3 -3
- data/lib/active_support/version.rb +3 -3
- data/lib/active_support/whiny_nil.rb +12 -12
- data/lib/activesupport.rb +1 -0
- metadata +69 -17
- data/lib/active_support/binding_of_caller.rb +0 -84
- data/lib/active_support/breakpoint.rb +0 -528
- data/lib/active_support/caching_tools.rb +0 -62
- data/lib/active_support/json/encoders.rb +0 -25
- data/lib/active_support/json/encoders/core.rb +0 -70
- data/lib/active_support/reloadable.rb +0 -60
- data/lib/active_support/vendor/builder/blankslate.rb +0 -63
@@ -0,0 +1,37 @@
|
|
1
|
+
class Object
|
2
|
+
# Can you safely .dup this object?
|
3
|
+
# False for nil, false, true, symbols, and numbers; true otherwise.
|
4
|
+
def duplicable?
|
5
|
+
true
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class NilClass #:nodoc:
|
10
|
+
def duplicable?
|
11
|
+
false
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class FalseClass #:nodoc:
|
16
|
+
def duplicable?
|
17
|
+
false
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class TrueClass #:nodoc:
|
22
|
+
def duplicable?
|
23
|
+
false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Symbol #:nodoc:
|
28
|
+
def duplicable?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Numeric #:nodoc:
|
34
|
+
def duplicable?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
end
|
@@ -8,8 +8,8 @@ class Exception # :nodoc:
|
|
8
8
|
|
9
9
|
def clean_backtrace
|
10
10
|
backtrace.collect do |line|
|
11
|
-
Pathname.clean_within(TraceSubstitutions.inject(line) do |
|
12
|
-
|
11
|
+
Pathname.clean_within(TraceSubstitutions.inject(line) do |result, (regexp, sub)|
|
12
|
+
result.gsub regexp, sub
|
13
13
|
end)
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
# Write to a file atomically. Useful for situations where you don't
|
4
|
+
# want other processes or threads to see half-written files.
|
5
|
+
#
|
6
|
+
# File.atomic_write("important.file") do |file|
|
7
|
+
# file.write("hello")
|
8
|
+
# end
|
9
|
+
#
|
10
|
+
# If your temp directory is not on the same filesystem as the file you're
|
11
|
+
# trying to write, you can provide a different temporary directory.
|
12
|
+
#
|
13
|
+
# File.atomic_write("/data/something.imporant", "/data/tmp") do |f|
|
14
|
+
# file.write("hello")
|
15
|
+
# end
|
16
|
+
def File.atomic_write(file_name, temp_dir = Dir.tmpdir)
|
17
|
+
temp_file = Tempfile.new(File.basename(file_name), temp_dir)
|
18
|
+
yield temp_file
|
19
|
+
temp_file.close
|
20
|
+
File.rename(temp_file.path, file_name)
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module ActiveSupport #:nodoc:
|
2
|
+
module CoreExtensions #:nodoc:
|
3
|
+
module Float #:nodoc:
|
4
|
+
module Rounding
|
5
|
+
def self.included(base) #:nodoc:
|
6
|
+
base.class_eval do
|
7
|
+
alias_method :round_without_precision, :round
|
8
|
+
alias_method :round, :round_with_precision
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Rounds the float with the specified precision.
|
13
|
+
#
|
14
|
+
# x = 1.337
|
15
|
+
# x.round # => 1
|
16
|
+
# x.round(1) # => 1.3
|
17
|
+
# x.round(2) # => 1.34
|
18
|
+
def round_with_precision(precision = nil)
|
19
|
+
precision.nil? ? round_without_precision : (self * (10 ** precision)).round / (10 ** precision).to_f
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,8 +1,6 @@
|
|
1
|
-
|
2
|
-
require
|
3
|
-
|
4
|
-
require File.dirname(__FILE__) + '/hash/conversions'
|
5
|
-
require File.dirname(__FILE__) + '/hash/diff'
|
1
|
+
%w(keys indifferent_access reverse_merge conversions diff slice except).each do |ext|
|
2
|
+
require "active_support/core_ext/hash/#{ext}"
|
3
|
+
end
|
6
4
|
|
7
5
|
class Hash #:nodoc:
|
8
6
|
include ActiveSupport::CoreExtensions::Hash::Keys
|
@@ -10,4 +8,6 @@ class Hash #:nodoc:
|
|
10
8
|
include ActiveSupport::CoreExtensions::Hash::ReverseMerge
|
11
9
|
include ActiveSupport::CoreExtensions::Hash::Conversions
|
12
10
|
include ActiveSupport::CoreExtensions::Hash::Diff
|
11
|
+
include ActiveSupport::CoreExtensions::Hash::Slice
|
12
|
+
include ActiveSupport::CoreExtensions::Hash::Except
|
13
13
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
require 'date'
|
2
|
-
require 'xml_simple'
|
3
2
|
require 'cgi'
|
4
|
-
|
3
|
+
require 'base64'
|
4
|
+
require 'builder'
|
5
|
+
require 'xmlsimple'
|
6
|
+
|
5
7
|
# Extensions needed for Hash#to_query
|
6
8
|
class Object
|
7
9
|
def to_param #:nodoc:
|
@@ -15,7 +17,7 @@ end
|
|
15
17
|
|
16
18
|
class Array
|
17
19
|
def to_query(key) #:nodoc:
|
18
|
-
collect { |value| value.to_query("#{key}[]") }
|
20
|
+
collect { |value| value.to_query("#{key}[]") } * '&'
|
19
21
|
end
|
20
22
|
end
|
21
23
|
|
@@ -45,22 +47,54 @@ module ActiveSupport #:nodoc:
|
|
45
47
|
module Hash #:nodoc:
|
46
48
|
module Conversions
|
47
49
|
XML_TYPE_NAMES = {
|
50
|
+
"Symbol" => "symbol",
|
48
51
|
"Fixnum" => "integer",
|
49
52
|
"Bignum" => "integer",
|
50
|
-
"BigDecimal" => "
|
53
|
+
"BigDecimal" => "decimal",
|
51
54
|
"Float" => "float",
|
52
55
|
"Date" => "date",
|
53
56
|
"DateTime" => "datetime",
|
54
57
|
"Time" => "datetime",
|
55
58
|
"TrueClass" => "boolean",
|
56
59
|
"FalseClass" => "boolean"
|
57
|
-
} unless defined?
|
60
|
+
} unless defined?(XML_TYPE_NAMES)
|
58
61
|
|
59
62
|
XML_FORMATTING = {
|
63
|
+
"symbol" => Proc.new { |symbol| symbol.to_s },
|
60
64
|
"date" => Proc.new { |date| date.to_s(:db) },
|
61
65
|
"datetime" => Proc.new { |time| time.xmlschema },
|
62
|
-
"binary" => Proc.new { |binary| Base64.encode64(binary) }
|
63
|
-
|
66
|
+
"binary" => Proc.new { |binary| Base64.encode64(binary) },
|
67
|
+
"yaml" => Proc.new { |yaml| yaml.to_yaml }
|
68
|
+
} unless defined?(XML_FORMATTING)
|
69
|
+
|
70
|
+
# TODO: use Time.xmlschema instead of Time.parse;
|
71
|
+
# use regexp instead of Date.parse
|
72
|
+
unless defined?(XML_PARSING)
|
73
|
+
XML_PARSING = {
|
74
|
+
"symbol" => Proc.new { |symbol| symbol.to_sym },
|
75
|
+
"date" => Proc.new { |date| ::Date.parse(date) },
|
76
|
+
"datetime" => Proc.new { |time| ::Time.parse(time).utc },
|
77
|
+
"integer" => Proc.new { |integer| integer.to_i },
|
78
|
+
"float" => Proc.new { |float| float.to_f },
|
79
|
+
"decimal" => Proc.new { |number| BigDecimal(number) },
|
80
|
+
"boolean" => Proc.new { |boolean| %w(1 true).include?(boolean.strip) },
|
81
|
+
"string" => Proc.new { |string| string.to_s },
|
82
|
+
"yaml" => Proc.new { |yaml| YAML::load(yaml) rescue yaml },
|
83
|
+
"base64Binary" => Proc.new { |bin| Base64.decode64(bin) },
|
84
|
+
# FIXME: Get rid of eval and institute a proper decorator here
|
85
|
+
"file" => Proc.new do |file, entity|
|
86
|
+
f = StringIO.new(Base64.decode64(file))
|
87
|
+
eval "def f.original_filename() '#{entity["name"]}' || 'untitled' end"
|
88
|
+
eval "def f.content_type() '#{entity["content_type"]}' || 'application/octet-stream' end"
|
89
|
+
f
|
90
|
+
end
|
91
|
+
}
|
92
|
+
|
93
|
+
XML_PARSING.update(
|
94
|
+
"double" => XML_PARSING["float"],
|
95
|
+
"dateTime" => XML_PARSING["datetime"]
|
96
|
+
)
|
97
|
+
end
|
64
98
|
|
65
99
|
def self.included(klass)
|
66
100
|
klass.extend(ClassMethods)
|
@@ -119,6 +153,8 @@ module ActiveSupport #:nodoc:
|
|
119
153
|
end
|
120
154
|
end
|
121
155
|
end
|
156
|
+
|
157
|
+
yield options[:builder] if block_given?
|
122
158
|
end
|
123
159
|
|
124
160
|
end
|
@@ -126,7 +162,7 @@ module ActiveSupport #:nodoc:
|
|
126
162
|
module ClassMethods
|
127
163
|
def from_xml(xml)
|
128
164
|
# TODO: Refactor this into something much cleaner that doesn't rely on XmlSimple
|
129
|
-
undasherize_keys(
|
165
|
+
typecast_xml_value(undasherize_keys(XmlSimple.xml_in_string(xml,
|
130
166
|
'forcearray' => false,
|
131
167
|
'forcecontent' => true,
|
132
168
|
'keeproot' => true,
|
@@ -134,52 +170,68 @@ module ActiveSupport #:nodoc:
|
|
134
170
|
))
|
135
171
|
end
|
136
172
|
|
137
|
-
def create_from_xml(xml)
|
138
|
-
ActiveSupport::Deprecation.warn("Hash.create_from_xml has been renamed to Hash.from_xml", caller)
|
139
|
-
from_xml(xml)
|
140
|
-
end
|
141
|
-
|
142
173
|
private
|
143
174
|
def typecast_xml_value(value)
|
144
175
|
case value.class.to_s
|
145
|
-
when
|
146
|
-
if value
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
when "
|
153
|
-
|
176
|
+
when 'Hash'
|
177
|
+
if value['type'] == 'array'
|
178
|
+
child_key, entries = value.detect { |k,v| k != 'type' } # child_key is throwaway
|
179
|
+
if entries.nil? || (c = value['__content__'] && c.blank?)
|
180
|
+
[]
|
181
|
+
else
|
182
|
+
case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
|
183
|
+
when "Array"
|
184
|
+
entries.collect { |v| typecast_xml_value(v) }
|
185
|
+
when "Hash"
|
186
|
+
[typecast_xml_value(entries)]
|
187
|
+
else
|
188
|
+
raise "can't typecast #{entries.inspect}"
|
189
|
+
end
|
190
|
+
end
|
191
|
+
elsif value.has_key?("__content__")
|
192
|
+
content = value["__content__"]
|
193
|
+
if parser = XML_PARSING[value["type"]]
|
194
|
+
if parser.arity == 2
|
195
|
+
XML_PARSING[value["type"]].call(content, value)
|
196
|
+
else
|
197
|
+
XML_PARSING[value["type"]].call(content)
|
198
|
+
end
|
199
|
+
else
|
200
|
+
content
|
154
201
|
end
|
202
|
+
elsif value['type'] == 'string' && value['nil'] != 'true'
|
203
|
+
""
|
204
|
+
# blank or nil parsed values are represented by nil
|
205
|
+
elsif value.blank? || value['nil'] == 'true'
|
206
|
+
nil
|
207
|
+
# If the type is the only element which makes it then
|
208
|
+
# this still makes the value nil
|
209
|
+
elsif value['type'] && value.size == 1
|
210
|
+
nil
|
155
211
|
else
|
156
|
-
|
212
|
+
xml_value = value.inject({}) do |h,(k,v)|
|
157
213
|
h[k] = typecast_xml_value(v)
|
158
214
|
h
|
159
215
|
end
|
216
|
+
|
217
|
+
# Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
|
218
|
+
# how multipart uploaded files from HTML appear
|
219
|
+
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
|
160
220
|
end
|
161
|
-
when
|
221
|
+
when 'Array'
|
162
222
|
value.map! { |i| typecast_xml_value(i) }
|
163
223
|
case value.length
|
164
224
|
when 0 then nil
|
165
225
|
when 1 then value.first
|
166
226
|
else value
|
167
227
|
end
|
168
|
-
when
|
228
|
+
when 'String'
|
169
229
|
value
|
170
230
|
else
|
171
|
-
raise "can't typecast #{value.inspect}"
|
231
|
+
raise "can't typecast #{value.class.name} - #{value.inspect}"
|
172
232
|
end
|
173
233
|
end
|
174
234
|
|
175
|
-
def translate_xml_entities(value)
|
176
|
-
value.gsub(/</, "<").
|
177
|
-
gsub(/>/, ">").
|
178
|
-
gsub(/"/, '"').
|
179
|
-
gsub(/'/, "'").
|
180
|
-
gsub(/&/, "&")
|
181
|
-
end
|
182
|
-
|
183
235
|
def undasherize_keys(params)
|
184
236
|
case params.class.to_s
|
185
237
|
when "Hash"
|
@@ -2,6 +2,14 @@ module ActiveSupport #:nodoc:
|
|
2
2
|
module CoreExtensions #:nodoc:
|
3
3
|
module Hash #:nodoc:
|
4
4
|
module Diff
|
5
|
+
# Returns a hash that represents the difference between two hashes.
|
6
|
+
#
|
7
|
+
# Examples:
|
8
|
+
#
|
9
|
+
# {1 => 2}.diff(1 => 2) # => {}
|
10
|
+
# {1 => 2}.diff(1 => 3) # => {1 => 2}
|
11
|
+
# {}.diff(1 => 2) # => {1 => 2}
|
12
|
+
# {1 => 2, 3 => 4}.diff(1 => 2) # => {3 => 4}
|
5
13
|
def diff(h2)
|
6
14
|
self.dup.delete_if { |k, v| h2[k] == v }.merge(h2.dup.delete_if { |k, v| self.has_key?(k) })
|
7
15
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ActiveSupport #:nodoc:
|
4
|
+
module CoreExtensions #:nodoc:
|
5
|
+
module Hash #:nodoc:
|
6
|
+
# Return a hash that includes everything but the given keys. This is useful for
|
7
|
+
# limiting a set of parameters to everything but a few known toggles:
|
8
|
+
#
|
9
|
+
# @person.update_attributes(params[:person].except(:admin))
|
10
|
+
module Except
|
11
|
+
# Returns a new hash without the given keys.
|
12
|
+
def except(*keys)
|
13
|
+
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
14
|
+
reject { |key,| rejected.include?(key) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Replaces the hash without only the given keys.
|
18
|
+
def except!(*keys)
|
19
|
+
replace(except(*keys))
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# This class has dubious semantics and we only have it so that
|
2
2
|
# people can write params[:key] instead of params['key']
|
3
3
|
|
4
4
|
class HashWithIndifferentAccess < Hash
|
@@ -64,12 +64,25 @@ class HashWithIndifferentAccess < Hash
|
|
64
64
|
def stringify_keys!; self end
|
65
65
|
def symbolize_keys!; self end
|
66
66
|
|
67
|
+
# Convert to a Hash with String keys.
|
68
|
+
def to_hash
|
69
|
+
Hash.new(default).merge(self)
|
70
|
+
end
|
71
|
+
|
67
72
|
protected
|
68
73
|
def convert_key(key)
|
69
74
|
key.kind_of?(Symbol) ? key.to_s : key
|
70
75
|
end
|
76
|
+
|
71
77
|
def convert_value(value)
|
72
|
-
|
78
|
+
case value
|
79
|
+
when Hash
|
80
|
+
value.with_indifferent_access
|
81
|
+
when Array
|
82
|
+
value.collect { |e| e.is_a?(Hash) ? e.with_indifferent_access : e }
|
83
|
+
else
|
84
|
+
value
|
85
|
+
end
|
73
86
|
end
|
74
87
|
end
|
75
88
|
|
@@ -24,7 +24,7 @@ module ActiveSupport #:nodoc:
|
|
24
24
|
# Return a new hash with all keys converted to symbols.
|
25
25
|
def symbolize_keys
|
26
26
|
inject({}) do |options, (key, value)|
|
27
|
-
options[key.to_sym] = value
|
27
|
+
options[key.to_sym || key] = value
|
28
28
|
options
|
29
29
|
end
|
30
30
|
end
|
@@ -32,8 +32,8 @@ module ActiveSupport #:nodoc:
|
|
32
32
|
# Destructively convert all keys to symbols.
|
33
33
|
def symbolize_keys!
|
34
34
|
keys.each do |key|
|
35
|
-
unless key.is_a?(Symbol)
|
36
|
-
self[
|
35
|
+
unless key.is_a?(Symbol) || (new_key = key.to_sym).nil?
|
36
|
+
self[new_key] = self[key]
|
37
37
|
delete(key)
|
38
38
|
end
|
39
39
|
end
|
@@ -43,6 +43,13 @@ module ActiveSupport #:nodoc:
|
|
43
43
|
alias_method :to_options, :symbolize_keys
|
44
44
|
alias_method :to_options!, :symbolize_keys!
|
45
45
|
|
46
|
+
# Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
|
47
|
+
# Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbol
|
48
|
+
# as keys, this will fail.
|
49
|
+
# examples:
|
50
|
+
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
|
51
|
+
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): years, name"
|
52
|
+
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
46
53
|
def assert_valid_keys(*valid_keys)
|
47
54
|
unknown_keys = keys - [valid_keys].flatten
|
48
55
|
raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module ActiveSupport #:nodoc:
|
4
|
+
module CoreExtensions #:nodoc:
|
5
|
+
module Hash #:nodoc:
|
6
|
+
# Slice a hash to include only the given keys. This is useful for
|
7
|
+
# limiting an options hash to valid keys before passing to a method:
|
8
|
+
#
|
9
|
+
# def search(criteria = {})
|
10
|
+
# assert_valid_keys(:mass, :velocity, :time)
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# search(options.slice(:mass, :velocity, :time))
|
14
|
+
module Slice
|
15
|
+
# Returns a new hash with only the given keys.
|
16
|
+
def slice(*keys)
|
17
|
+
allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
18
|
+
reject { |key,| !allowed.include?(key) }
|
19
|
+
end
|
20
|
+
|
21
|
+
# Replaces the hash with only the given keys.
|
22
|
+
def slice!(*keys)
|
23
|
+
replace(slice(*keys))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|