activesupport 3.0.20 → 3.1.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.
- data/CHANGELOG +24 -28
- data/README.rdoc +2 -2
- data/lib/active_support/backtrace_cleaner.rb +17 -12
- data/lib/active_support/buffered_logger.rb +7 -9
- data/lib/active_support/cache/file_store.rb +4 -3
- data/lib/active_support/cache/mem_cache_store.rb +10 -2
- data/lib/active_support/cache/strategy/local_cache.rb +28 -23
- data/lib/active_support/cache.rb +11 -14
- data/lib/active_support/callbacks.rb +195 -175
- data/lib/active_support/concern.rb +105 -35
- data/lib/active_support/configurable.rb +50 -8
- data/lib/active_support/core_ext/array/access.rb +2 -2
- data/lib/active_support/core_ext/array/conversions.rb +2 -2
- data/lib/active_support/core_ext/array/random_access.rb +10 -7
- data/lib/active_support/core_ext/array/uniq_by.rb +2 -3
- data/lib/active_support/core_ext/array/wrap.rb +1 -1
- data/lib/active_support/core_ext/big_decimal/conversions.rb +0 -2
- data/lib/active_support/core_ext/class/attribute.rb +15 -1
- data/lib/active_support/core_ext/class/inheritable_attributes.rb +10 -86
- data/lib/active_support/core_ext/class/subclasses.rb +20 -34
- data/lib/active_support/core_ext/date/calculations.rb +17 -5
- data/lib/active_support/core_ext/date/conversions.rb +9 -2
- data/lib/active_support/core_ext/date_time/calculations.rb +26 -9
- data/lib/active_support/core_ext/date_time/conversions.rb +9 -9
- data/lib/active_support/core_ext/date_time/zones.rb +1 -1
- data/lib/active_support/core_ext/enumerable.rb +3 -6
- data/lib/active_support/core_ext/float/rounding.rb +1 -1
- data/lib/active_support/core_ext/hash/conversions.rb +22 -34
- data/lib/active_support/core_ext/hash/deep_dup.rb +11 -0
- data/lib/active_support/core_ext/hash/indifferent_access.rb +12 -0
- data/lib/active_support/core_ext/hash/keys.rb +6 -4
- data/lib/active_support/core_ext/hash/reverse_merge.rb +9 -14
- data/lib/active_support/core_ext/hash/slice.rb +3 -1
- data/lib/active_support/core_ext/hash.rb +1 -0
- data/lib/active_support/core_ext/integer/inflections.rb +7 -4
- data/lib/active_support/core_ext/kernel/reporting.rb +31 -0
- data/lib/active_support/core_ext/kernel/requires.rb +2 -0
- data/lib/active_support/core_ext/logger.rb +11 -38
- data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +12 -12
- data/lib/active_support/core_ext/module/attr_internal.rb +13 -6
- data/lib/active_support/core_ext/module/delegation.rb +1 -5
- data/lib/active_support/core_ext/module/remove_method.rb +7 -2
- data/lib/active_support/core_ext/numeric/time.rb +2 -0
- data/lib/active_support/core_ext/object/blank.rb +45 -12
- data/lib/active_support/core_ext/object/duplicable.rb +50 -9
- data/lib/active_support/core_ext/object/inclusion.rb +15 -0
- data/lib/active_support/core_ext/object/instance_variables.rb +1 -35
- data/lib/active_support/core_ext/object/to_param.rb +13 -8
- data/lib/active_support/core_ext/object/to_query.rb +2 -2
- data/lib/active_support/core_ext/object/try.rb +29 -10
- data/lib/active_support/core_ext/object/with_options.rb +25 -8
- data/lib/active_support/core_ext/object.rb +1 -1
- data/lib/active_support/core_ext/range/cover.rb +3 -0
- data/lib/active_support/core_ext/range.rb +1 -0
- data/lib/active_support/core_ext/string/behavior.rb +1 -2
- data/lib/active_support/core_ext/string/conversions.rb +1 -0
- data/lib/active_support/core_ext/string/exclude.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/inflections.rb +1 -1
- data/lib/active_support/core_ext/string/inquiry.rb +13 -0
- data/lib/active_support/core_ext/string/multibyte.rb +2 -2
- data/lib/active_support/core_ext/string/output_safety.rb +45 -62
- data/lib/active_support/core_ext/string.rb +1 -0
- data/lib/active_support/core_ext/time/calculations.rb +19 -16
- data/lib/active_support/core_ext/time/conversions.rb +2 -24
- data/lib/active_support/core_ext/time/zones.rb +32 -21
- data/lib/active_support/core_ext/uri.rb +8 -0
- data/lib/active_support/dependencies.rb +74 -28
- data/lib/active_support/deprecation/behaviors.rb +7 -0
- data/lib/active_support/deprecation/proxy_wrappers.rb +1 -1
- data/lib/active_support/deprecation/reporting.rb +4 -0
- data/lib/active_support/deprecation.rb +2 -2
- data/lib/active_support/descendants_tracker.rb +9 -7
- data/lib/active_support/duration.rb +5 -0
- data/lib/active_support/file_update_checker.rb +1 -1
- data/lib/active_support/file_watcher.rb +36 -0
- data/lib/active_support/hash_with_indifferent_access.rb +20 -8
- data/lib/active_support/i18n.rb +0 -1
- data/lib/active_support/i18n_railtie.rb +2 -2
- data/lib/active_support/inflections.rb +1 -0
- data/lib/active_support/inflector/inflections.rb +1 -1
- data/lib/active_support/inflector/methods.rb +5 -3
- data/lib/active_support/json/decoding.rb +37 -23
- data/lib/active_support/json/encoding.rb +12 -4
- data/lib/active_support/lazy_load_hooks.rb +7 -7
- data/lib/active_support/log_subscriber/test_helper.rb +5 -3
- data/lib/active_support/log_subscriber.rb +6 -6
- data/lib/active_support/message_encryptor.rb +1 -1
- data/lib/active_support/message_verifier.rb +1 -1
- data/lib/active_support/multibyte/chars.rb +5 -5
- data/lib/active_support/multibyte/unicode.rb +2 -1
- data/lib/active_support/notifications/fanout.rb +3 -3
- data/lib/active_support/notifications/instrumenter.rb +1 -1
- data/lib/active_support/notifications.rb +9 -10
- data/lib/active_support/ordered_hash.rb +23 -2
- data/lib/active_support/ordered_options.rb +37 -3
- data/lib/active_support/railtie.rb +1 -1
- data/lib/active_support/rescuable.rb +1 -0
- data/lib/active_support/secure_random.rb +3 -196
- data/lib/active_support/test_case.rb +2 -10
- data/lib/active_support/testing/assertions.rb +20 -7
- data/lib/active_support/testing/mochaing.rb +7 -0
- data/lib/active_support/testing/pending.rb +27 -23
- data/lib/active_support/testing/performance/jruby.rb +115 -0
- data/lib/active_support/testing/performance/rubinius.rb +113 -0
- data/lib/active_support/testing/performance/ruby/mri.rb +57 -0
- data/lib/active_support/testing/performance/ruby/yarv.rb +57 -0
- data/lib/active_support/testing/performance/ruby.rb +152 -0
- data/lib/active_support/testing/performance.rb +214 -322
- data/lib/active_support/testing/setup_and_teardown.rb +8 -11
- data/lib/active_support/time_with_zone.rb +6 -3
- data/lib/active_support/values/time_zone.rb +26 -8
- data/lib/active_support/version.rb +2 -2
- data/lib/active_support/whiny_nil.rb +1 -1
- data/lib/active_support/xml_mini/jdom.rb +1 -1
- data/lib/active_support/xml_mini/libxml.rb +1 -0
- data/lib/active_support/xml_mini/libxmlsax.rb +2 -1
- data/lib/active_support/xml_mini/nokogiri.rb +1 -0
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -0
- data/lib/active_support/xml_mini/rexml.rb +1 -0
- data/lib/active_support/xml_mini.rb +8 -3
- data/lib/active_support.rb +4 -2
- metadata +70 -32
- checksums.yaml +0 -7
- data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -19
- data/lib/active_support/core_ext/cgi.rb +0 -1
- data/lib/active_support/core_ext/object/returning.rb +0 -43
- data/lib/active_support/json/backends/jsongem.rb +0 -47
- data/lib/active_support/json/backends/okjson.rb +0 -644
- data/lib/active_support/json/backends/yajl.rb +0 -44
- data/lib/active_support/json/backends/yaml.rb +0 -19
- data/lib/active_support/testing/default.rb +0 -9
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
require 'rational' unless RUBY_VERSION >= '1.9.2'
|
|
2
|
-
require 'active_support/core_ext/object/acts_like'
|
|
3
|
-
require 'active_support/core_ext/time/zones'
|
|
4
2
|
|
|
5
3
|
class DateTime
|
|
6
4
|
class << self
|
|
7
5
|
# DateTimes aren't aware of DST rules, so use a consistent non-DST offset when creating a DateTime with an offset in the local zone
|
|
8
6
|
def local_offset
|
|
9
|
-
::Time.local(
|
|
7
|
+
::Time.local(2007).utc_offset.to_r / 86400
|
|
10
8
|
end
|
|
11
9
|
|
|
12
10
|
# Returns <tt>Time.zone.now.to_datetime</tt> when <tt>Time.zone</tt> or <tt>config.time_zone</tt> are set, otherwise returns <tt>Time.now.to_datetime</tt>.
|
|
@@ -83,6 +81,29 @@ class DateTime
|
|
|
83
81
|
change(:hour => 23, :min => 59, :sec => 59)
|
|
84
82
|
end
|
|
85
83
|
|
|
84
|
+
# 1.9.3 defines + and - on DateTime, < 1.9.3 do not.
|
|
85
|
+
if DateTime.public_instance_methods(false).include?(:+)
|
|
86
|
+
def plus_with_duration(other) #:nodoc:
|
|
87
|
+
if ActiveSupport::Duration === other
|
|
88
|
+
other.since(self)
|
|
89
|
+
else
|
|
90
|
+
plus_without_duration(other)
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
alias_method :plus_without_duration, :+
|
|
94
|
+
alias_method :+, :plus_with_duration
|
|
95
|
+
|
|
96
|
+
def minus_with_duration(other) #:nodoc:
|
|
97
|
+
if ActiveSupport::Duration === other
|
|
98
|
+
plus_with_duration(-other)
|
|
99
|
+
else
|
|
100
|
+
minus_without_duration(other)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
alias_method :minus_without_duration, :-
|
|
104
|
+
alias_method :-, :minus_with_duration
|
|
105
|
+
end
|
|
106
|
+
|
|
86
107
|
# Adjusts DateTime to UTC by adding its offset value; offset is set to 0
|
|
87
108
|
#
|
|
88
109
|
# Example:
|
|
@@ -105,11 +126,7 @@ class DateTime
|
|
|
105
126
|
end
|
|
106
127
|
|
|
107
128
|
# Layers additional behavior on DateTime#<=> so that Time and ActiveSupport::TimeWithZone instances can be compared with a DateTime
|
|
108
|
-
def
|
|
109
|
-
|
|
110
|
-
other = other.to_datetime unless other.acts_like?(:date)
|
|
111
|
-
compare_without_coercion(other)
|
|
129
|
+
def <=>(other)
|
|
130
|
+
super other.to_datetime
|
|
112
131
|
end
|
|
113
|
-
alias_method :compare_without_coercion, :<=>
|
|
114
|
-
alias_method :<=>, :compare_with_coercion
|
|
115
132
|
end
|
|
@@ -51,25 +51,25 @@ class DateTime
|
|
|
51
51
|
utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000"
|
|
54
|
+
# Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
|
|
55
55
|
def readable_inspect
|
|
56
56
|
to_s(:rfc822)
|
|
57
57
|
end
|
|
58
58
|
alias_method :default_inspect, :inspect
|
|
59
59
|
alias_method :inspect, :readable_inspect
|
|
60
60
|
|
|
61
|
-
# Converts self to a Ruby Date object; time portion is discarded
|
|
61
|
+
# Converts self to a Ruby Date object; time portion is discarded.
|
|
62
62
|
def to_date
|
|
63
63
|
::Date.new(year, month, day)
|
|
64
64
|
end unless instance_methods(false).include?(:to_date)
|
|
65
65
|
|
|
66
|
-
# Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class
|
|
67
|
-
# If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time
|
|
66
|
+
# Attempts to convert self to a Ruby Time object; returns self if out of range of Ruby Time class.
|
|
67
|
+
# If self has an offset other than 0, self will just be returned unaltered, since there's no clean way to map it to a Time.
|
|
68
68
|
def to_time
|
|
69
|
-
self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec) : self
|
|
69
|
+
self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
|
|
70
70
|
end
|
|
71
71
|
|
|
72
|
-
# To be able to keep Times, Dates and DateTimes interchangeable on conversions
|
|
72
|
+
# To be able to keep Times, Dates and DateTimes interchangeable on conversions.
|
|
73
73
|
def to_datetime
|
|
74
74
|
self
|
|
75
75
|
end unless instance_methods(false).include?(:to_datetime)
|
|
@@ -79,17 +79,17 @@ class DateTime
|
|
|
79
79
|
civil(year, month, day, hour, min, sec, offset)
|
|
80
80
|
end
|
|
81
81
|
|
|
82
|
-
# Converts datetime to an appropriate format for use in XML
|
|
82
|
+
# Converts datetime to an appropriate format for use in XML.
|
|
83
83
|
def xmlschema
|
|
84
84
|
strftime("%Y-%m-%dT%H:%M:%S%Z")
|
|
85
85
|
end unless instance_methods(false).include?(:xmlschema)
|
|
86
86
|
|
|
87
|
-
# Converts self to a floating-point number of seconds since the Unix epoch
|
|
87
|
+
# Converts self to a floating-point number of seconds since the Unix epoch.
|
|
88
88
|
def to_f
|
|
89
89
|
seconds_since_unix_epoch.to_f
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
# Converts self to an integer number of seconds since the Unix epoch
|
|
92
|
+
# Converts self to an integer number of seconds since the Unix epoch.
|
|
93
93
|
def to_i
|
|
94
94
|
seconds_since_unix_epoch.to_i
|
|
95
95
|
end
|
|
@@ -16,6 +16,6 @@ class DateTime
|
|
|
16
16
|
def in_time_zone(zone = ::Time.zone)
|
|
17
17
|
return self unless zone
|
|
18
18
|
|
|
19
|
-
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.
|
|
19
|
+
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
|
|
20
20
|
end
|
|
21
21
|
end
|
|
@@ -90,16 +90,13 @@ module Enumerable
|
|
|
90
90
|
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
|
91
91
|
#
|
|
92
92
|
def index_by
|
|
93
|
-
|
|
94
|
-
accum[yield(elem)] = elem
|
|
95
|
-
accum
|
|
96
|
-
end
|
|
93
|
+
Hash[map { |elem| [yield(elem), elem] }]
|
|
97
94
|
end
|
|
98
95
|
|
|
99
96
|
# Returns true if the collection has more than 1 element. Functionally equivalent to collection.size > 1.
|
|
100
|
-
#
|
|
97
|
+
# Can be called with a block too, much like any?, so people.many? { |p| p.age > 26 } returns true if more than 1 person is over 26.
|
|
101
98
|
def many?(&block)
|
|
102
|
-
size = block_given? ?
|
|
99
|
+
size = block_given? ? count(&block) : self.size
|
|
103
100
|
size > 1
|
|
104
101
|
end
|
|
105
102
|
|
|
@@ -26,10 +26,22 @@ class Hash
|
|
|
26
26
|
#
|
|
27
27
|
# * If +value+ is a callable object it must expect one or two arguments. Depending
|
|
28
28
|
# 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.
|
|
30
|
-
#
|
|
29
|
+
# with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
|
|
30
|
+
# callable can add nodes by using <tt>options[:builder]</tt>.
|
|
31
|
+
#
|
|
32
|
+
# "foo".to_xml(lambda { |options, key| options[:builder].b(key) })
|
|
33
|
+
# # => "<b>foo</b>"
|
|
31
34
|
#
|
|
32
35
|
# * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
|
|
36
|
+
#
|
|
37
|
+
# class Foo
|
|
38
|
+
# def to_xml(options)
|
|
39
|
+
# options[:builder].bar "fooing!"
|
|
40
|
+
# end
|
|
41
|
+
# end
|
|
42
|
+
#
|
|
43
|
+
# {:foo => Foo.new}.to_xml(:skip_instruct => true)
|
|
44
|
+
# # => "<hash><bar>fooing!</bar></hash>"
|
|
33
45
|
#
|
|
34
46
|
# * Otherwise, a node with +key+ as tag is created with a string representation of
|
|
35
47
|
# +value+ as text node. If +value+ is +nil+ an attribute "nil" set to "true" is added.
|
|
@@ -73,33 +85,15 @@ class Hash
|
|
|
73
85
|
end
|
|
74
86
|
end
|
|
75
87
|
|
|
76
|
-
class DisallowedType < StandardError #:nodoc:
|
|
77
|
-
def initialize(type)
|
|
78
|
-
super "Disallowed type attribute: #{type.inspect}"
|
|
79
|
-
end
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
DISALLOWED_XML_TYPES = %w(symbol yaml)
|
|
83
|
-
|
|
84
88
|
class << self
|
|
85
|
-
def from_xml(xml
|
|
86
|
-
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml))
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
def from_trusted_xml(xml)
|
|
90
|
-
from_xml xml, []
|
|
89
|
+
def from_xml(xml)
|
|
90
|
+
typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)))
|
|
91
91
|
end
|
|
92
92
|
|
|
93
93
|
private
|
|
94
|
-
def typecast_xml_value(value
|
|
95
|
-
disallowed_types ||= DISALLOWED_XML_TYPES
|
|
96
|
-
|
|
94
|
+
def typecast_xml_value(value)
|
|
97
95
|
case value.class.to_s
|
|
98
96
|
when 'Hash'
|
|
99
|
-
if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
|
|
100
|
-
raise DisallowedType, value['type']
|
|
101
|
-
end
|
|
102
|
-
|
|
103
97
|
if value['type'] == 'array'
|
|
104
98
|
_, entries = Array.wrap(value.detect { |k,v| k != 'type' })
|
|
105
99
|
if entries.nil? || (c = value['__content__'] && c.blank?)
|
|
@@ -107,9 +101,9 @@ class Hash
|
|
|
107
101
|
else
|
|
108
102
|
case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
|
|
109
103
|
when "Array"
|
|
110
|
-
entries.collect { |v| typecast_xml_value(v
|
|
104
|
+
entries.collect { |v| typecast_xml_value(v) }
|
|
111
105
|
when "Hash"
|
|
112
|
-
[typecast_xml_value(entries
|
|
106
|
+
[typecast_xml_value(entries)]
|
|
113
107
|
else
|
|
114
108
|
raise "can't typecast #{entries.inspect}"
|
|
115
109
|
end
|
|
@@ -133,17 +127,14 @@ class Hash
|
|
|
133
127
|
elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
|
|
134
128
|
nil
|
|
135
129
|
else
|
|
136
|
-
xml_value = value.
|
|
137
|
-
h[k] = typecast_xml_value(v, disallowed_types)
|
|
138
|
-
h
|
|
139
|
-
end
|
|
130
|
+
xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v)] }]
|
|
140
131
|
|
|
141
132
|
# Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
|
|
142
133
|
# how multipart uploaded files from HTML appear
|
|
143
134
|
xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
|
|
144
135
|
end
|
|
145
136
|
when 'Array'
|
|
146
|
-
value.map! { |i| typecast_xml_value(i
|
|
137
|
+
value.map! { |i| typecast_xml_value(i) }
|
|
147
138
|
value.length > 1 ? value : value.first
|
|
148
139
|
when 'String'
|
|
149
140
|
value
|
|
@@ -155,10 +146,7 @@ class Hash
|
|
|
155
146
|
def unrename_keys(params)
|
|
156
147
|
case params.class.to_s
|
|
157
148
|
when "Hash"
|
|
158
|
-
params.
|
|
159
|
-
h[k.to_s.tr("-", "_")] = unrename_keys(v)
|
|
160
|
-
h
|
|
161
|
-
end
|
|
149
|
+
Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
|
|
162
150
|
when "Array"
|
|
163
151
|
params.map { |v| unrename_keys(v) }
|
|
164
152
|
else
|
|
@@ -9,4 +9,16 @@ class Hash
|
|
|
9
9
|
def with_indifferent_access
|
|
10
10
|
ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(self)
|
|
11
11
|
end
|
|
12
|
+
|
|
13
|
+
# Called when object is nested under an object that receives
|
|
14
|
+
# #with_indifferent_access. This method will be called on the current object
|
|
15
|
+
# by the enclosing object and is aliased to #with_indifferent_access by
|
|
16
|
+
# default. Subclasses of Hash may overwrite this method to return +self+ if
|
|
17
|
+
# converting to an +ActiveSupport::HashWithIndifferentAccess+ would not be
|
|
18
|
+
# desirable.
|
|
19
|
+
#
|
|
20
|
+
# b = {:b => 1}
|
|
21
|
+
# {:a => b}.with_indifferent_access["a"] # calls b.nested_under_indifferent_access
|
|
22
|
+
#
|
|
23
|
+
alias nested_under_indifferent_access with_indifferent_access
|
|
12
24
|
end
|
|
@@ -35,11 +35,13 @@ class Hash
|
|
|
35
35
|
# as keys, this will fail.
|
|
36
36
|
#
|
|
37
37
|
# ==== Examples
|
|
38
|
-
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key
|
|
39
|
-
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key
|
|
38
|
+
# { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key: years"
|
|
39
|
+
# { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key: name"
|
|
40
40
|
# { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
|
|
41
41
|
def assert_valid_keys(*valid_keys)
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
valid_keys.flatten!
|
|
43
|
+
each_key do |k|
|
|
44
|
+
raise(ArgumentError, "Unknown key: #{k}") unless valid_keys.include?(k)
|
|
45
|
+
end
|
|
44
46
|
end
|
|
45
47
|
end
|
|
@@ -1,27 +1,22 @@
|
|
|
1
1
|
class Hash
|
|
2
|
-
#
|
|
3
|
-
# in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
|
|
2
|
+
# Merges the caller into +other_hash+. For example,
|
|
4
3
|
#
|
|
5
|
-
#
|
|
6
|
-
# options.reverse_merge! :size => 25, :velocity => 10
|
|
7
|
-
# end
|
|
4
|
+
# options = options.reverse_merge(:size => 25, :velocity => 10)
|
|
8
5
|
#
|
|
9
|
-
#
|
|
6
|
+
# is equivalent to
|
|
10
7
|
#
|
|
11
|
-
#
|
|
12
|
-
# { :size => 25, :velocity => 10 }.merge(options)
|
|
13
|
-
# end
|
|
8
|
+
# options = {:size => 25, :velocity => 10}.merge(options)
|
|
14
9
|
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
10
|
+
# This is particularly useful for initializing an options hash
|
|
11
|
+
# with default values.
|
|
17
12
|
def reverse_merge(other_hash)
|
|
18
13
|
other_hash.merge(self)
|
|
19
14
|
end
|
|
20
15
|
|
|
21
|
-
#
|
|
22
|
-
# Modifies the receiver in place.
|
|
16
|
+
# Destructive +reverse_merge+.
|
|
23
17
|
def reverse_merge!(other_hash)
|
|
24
|
-
|
|
18
|
+
# right wins if there is no left
|
|
19
|
+
merge!( other_hash ){|key,left,right| left }
|
|
25
20
|
end
|
|
26
21
|
|
|
27
22
|
alias_method :reverse_update, :reverse_merge!
|
|
@@ -21,7 +21,7 @@ class Hash
|
|
|
21
21
|
|
|
22
22
|
# Replaces the hash with only the given keys.
|
|
23
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}
|
|
24
|
+
# {:a => 1, :b => 2, :c => 3, :d => 4}.slice!(:a, :b) # => {:c => 3, :d => 4}
|
|
25
25
|
def slice!(*keys)
|
|
26
26
|
keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
|
|
27
27
|
omit = slice(*self.keys - keys)
|
|
@@ -30,6 +30,8 @@ class Hash
|
|
|
30
30
|
omit
|
|
31
31
|
end
|
|
32
32
|
|
|
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}
|
|
33
35
|
def extract!(*keys)
|
|
34
36
|
result = {}
|
|
35
37
|
keys.each {|key| result[key] = delete(key) }
|
|
@@ -1,5 +1,6 @@
|
|
|
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'
|
|
3
4
|
require 'active_support/core_ext/hash/diff'
|
|
4
5
|
require 'active_support/core_ext/hash/except'
|
|
5
6
|
require 'active_support/core_ext/hash/indifferent_access'
|
|
@@ -4,10 +4,13 @@ class Integer
|
|
|
4
4
|
# Ordinalize turns a number into an ordinal string used to denote the
|
|
5
5
|
# position in an ordered sequence such as 1st, 2nd, 3rd, 4th.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
7
|
+
# 1.ordinalize # => "1st"
|
|
8
|
+
# 2.ordinalize # => "2nd"
|
|
9
|
+
# 1002.ordinalize # => "1002nd"
|
|
10
|
+
# 1003.ordinalize # => "1003rd"
|
|
11
|
+
# -11.ordinalize # => "-11th"
|
|
12
|
+
# -1001.ordinalize # => "-1001st"
|
|
13
|
+
#
|
|
11
14
|
def ordinalize
|
|
12
15
|
ActiveSupport::Inflector.ordinalize(self)
|
|
13
16
|
end
|
|
@@ -59,4 +59,35 @@ module Kernel
|
|
|
59
59
|
raise unless exception_classes.any? { |cls| e.kind_of?(cls) }
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
|
+
|
|
63
|
+
# Captures the given stream and returns it:
|
|
64
|
+
#
|
|
65
|
+
# stream = capture(:stdout) { puts "Cool" }
|
|
66
|
+
# stream # => "Cool\n"
|
|
67
|
+
#
|
|
68
|
+
def capture(stream)
|
|
69
|
+
begin
|
|
70
|
+
stream = stream.to_s
|
|
71
|
+
eval "$#{stream} = StringIO.new"
|
|
72
|
+
yield
|
|
73
|
+
result = eval("$#{stream}").string
|
|
74
|
+
ensure
|
|
75
|
+
eval("$#{stream} = #{stream.upcase}")
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
result
|
|
79
|
+
end
|
|
80
|
+
alias :silence :capture
|
|
81
|
+
|
|
82
|
+
# Silences both STDOUT and STDERR, even for subprocesses.
|
|
83
|
+
#
|
|
84
|
+
# quietly { system 'bundle install' }
|
|
85
|
+
#
|
|
86
|
+
def quietly
|
|
87
|
+
silence_stream(STDOUT) do
|
|
88
|
+
silence_stream(STDERR) do
|
|
89
|
+
yield
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
62
93
|
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'active_support/core_ext/kernel/reporting'
|
|
2
|
+
require 'active_support/core_ext/module/deprecation'
|
|
2
3
|
|
|
3
4
|
module Kernel
|
|
4
5
|
# Require a library with fallback to RubyGems. Warnings during library
|
|
@@ -23,4 +24,5 @@ module Kernel
|
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
27
|
+
deprecate :require_library_or_gem
|
|
26
28
|
end
|
|
@@ -4,18 +4,17 @@ require 'active_support/core_ext/class/attribute_accessors'
|
|
|
4
4
|
class Logger #:nodoc:
|
|
5
5
|
def self.define_around_helper(level)
|
|
6
6
|
module_eval <<-end_eval, __FILE__, __LINE__ + 1
|
|
7
|
-
def around_#{level}(before_message, after_message
|
|
8
|
-
self.#{level}(before_message)
|
|
9
|
-
return_value =
|
|
10
|
-
self.#{level}(after_message)
|
|
11
|
-
|
|
12
|
-
end
|
|
7
|
+
def around_#{level}(before_message, after_message) # def around_debug(before_message, after_message, &block)
|
|
8
|
+
self.#{level}(before_message) # self.debug(before_message)
|
|
9
|
+
return_value = yield(self) # return_value = yield(self)
|
|
10
|
+
self.#{level}(after_message) # self.debug(after_message)
|
|
11
|
+
return_value # return_value
|
|
12
|
+
end # end
|
|
13
13
|
end_eval
|
|
14
14
|
end
|
|
15
15
|
[:debug, :info, :error, :fatal].each {|level| define_around_helper(level) }
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
|
|
19
18
|
require 'logger'
|
|
20
19
|
|
|
21
20
|
# Extensions to the built-in Ruby logger.
|
|
@@ -65,11 +64,11 @@ class Logger
|
|
|
65
64
|
formatter.datetime_format if formatter.respond_to?(:datetime_format)
|
|
66
65
|
end
|
|
67
66
|
|
|
68
|
-
alias :
|
|
69
|
-
#
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
alias :old_initialize :initialize
|
|
68
|
+
# Overwrite initialize to set a default formatter.
|
|
69
|
+
def initialize(*args)
|
|
70
|
+
old_initialize(*args)
|
|
71
|
+
self.formatter = SimpleFormatter.new
|
|
73
72
|
end
|
|
74
73
|
|
|
75
74
|
# Simple formatter which only displays the message.
|
|
@@ -79,30 +78,4 @@ class Logger
|
|
|
79
78
|
"#{String === msg ? msg : msg.inspect}\n"
|
|
80
79
|
end
|
|
81
80
|
end
|
|
82
|
-
|
|
83
|
-
private
|
|
84
|
-
alias old_format_message format_message
|
|
85
|
-
|
|
86
|
-
# Ruby 1.8.3 transposed the msg and progname arguments to format_message.
|
|
87
|
-
# We can't test RUBY_VERSION because some distributions don't keep Ruby
|
|
88
|
-
# and its standard library in sync, leading to installations of Ruby 1.8.2
|
|
89
|
-
# with Logger from 1.8.3 and vice versa.
|
|
90
|
-
if method_defined?(:formatter=)
|
|
91
|
-
def format_message(severity, timestamp, progname, msg)
|
|
92
|
-
formatter.call(severity, timestamp, progname, msg)
|
|
93
|
-
end
|
|
94
|
-
else
|
|
95
|
-
def format_message(severity, timestamp, msg, progname)
|
|
96
|
-
formatter.call(severity, timestamp, progname, msg)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
attr_writer :formatter
|
|
100
|
-
public :formatter=
|
|
101
|
-
|
|
102
|
-
alias old_format_datetime format_datetime
|
|
103
|
-
def format_datetime(datetime) datetime end
|
|
104
|
-
|
|
105
|
-
alias old_msg2str msg2str
|
|
106
|
-
def msg2str(msg) msg end
|
|
107
|
-
end
|
|
108
81
|
end
|
|
@@ -7,25 +7,25 @@ class Module
|
|
|
7
7
|
# attr_accessor_with_default :age, 25
|
|
8
8
|
# end
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
# => 25
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# => 26
|
|
10
|
+
# person = Person.new
|
|
11
|
+
# person.age # => 25
|
|
12
|
+
#
|
|
13
|
+
# person.age = 26
|
|
14
|
+
# person.age # => 26
|
|
15
15
|
#
|
|
16
16
|
# To give attribute <tt>:element_name</tt> a dynamic default value, evaluated
|
|
17
17
|
# in scope of self:
|
|
18
18
|
#
|
|
19
19
|
# attr_accessor_with_default(:element_name) { name.underscore }
|
|
20
20
|
#
|
|
21
|
-
def attr_accessor_with_default(sym, default =
|
|
22
|
-
|
|
23
|
-
define_method(sym, block_given? ?
|
|
21
|
+
def attr_accessor_with_default(sym, default = Proc.new)
|
|
22
|
+
ActiveSupport::Deprecation.warn "attr_accessor_with_default is deprecated. Use Ruby instead!"
|
|
23
|
+
define_method(sym, block_given? ? default : Proc.new { default })
|
|
24
24
|
module_eval(<<-EVAL, __FILE__, __LINE__ + 1)
|
|
25
|
-
def #{sym}=(value)
|
|
26
|
-
class << self;
|
|
27
|
-
@#{sym} = value
|
|
28
|
-
end
|
|
25
|
+
def #{sym}=(value) # def age=(value)
|
|
26
|
+
class << self; attr_accessor :#{sym} end # class << self; attr_accessor :age end
|
|
27
|
+
@#{sym} = value # @age = value
|
|
28
|
+
end # end
|
|
29
29
|
EVAL
|
|
30
30
|
end
|
|
31
31
|
end
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
class Module
|
|
2
2
|
# Declares an attribute reader backed by an internally-named instance variable.
|
|
3
3
|
def attr_internal_reader(*attrs)
|
|
4
|
-
attrs.each
|
|
5
|
-
module_eval "def #{attr}() #{attr_internal_ivar_name(attr)} end", __FILE__, __LINE__
|
|
6
|
-
end
|
|
4
|
+
attrs.each {|attr_name| attr_internal_define(attr_name, :reader)}
|
|
7
5
|
end
|
|
8
6
|
|
|
9
7
|
# Declares an attribute writer backed by an internally-named instance variable.
|
|
10
8
|
def attr_internal_writer(*attrs)
|
|
11
|
-
attrs.each
|
|
12
|
-
module_eval "def #{attr}=(v) #{attr_internal_ivar_name(attr)} = v end", __FILE__, __LINE__
|
|
13
|
-
end
|
|
9
|
+
attrs.each {|attr_name| attr_internal_define(attr_name, :writer)}
|
|
14
10
|
end
|
|
15
11
|
|
|
16
12
|
# Declares an attribute reader and writer backed by an internally-named instance
|
|
@@ -29,4 +25,15 @@ class Module
|
|
|
29
25
|
def attr_internal_ivar_name(attr)
|
|
30
26
|
Module.attr_internal_naming_format % attr
|
|
31
27
|
end
|
|
28
|
+
|
|
29
|
+
def attr_internal_define(attr_name, type)
|
|
30
|
+
internal_name = attr_internal_ivar_name(attr_name).sub(/\A@/, '')
|
|
31
|
+
class_eval do # class_eval is necessary on 1.9 or else the methods a made private
|
|
32
|
+
# use native attr_* methods as they are faster on some Ruby implementations
|
|
33
|
+
send("attr_#{type}", internal_name)
|
|
34
|
+
end
|
|
35
|
+
attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
|
|
36
|
+
alias_method attr_name, internal_name
|
|
37
|
+
remove_method internal_name
|
|
38
|
+
end
|
|
32
39
|
end
|
|
@@ -126,11 +126,7 @@ class Module
|
|
|
126
126
|
%(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
|
|
127
127
|
end
|
|
128
128
|
|
|
129
|
-
module_eval(<<-EOS, file, line -
|
|
130
|
-
if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
|
|
131
|
-
remove_possible_method("#{prefix}#{method}")
|
|
132
|
-
end
|
|
133
|
-
|
|
129
|
+
module_eval(<<-EOS, file, line - 1)
|
|
134
130
|
def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
|
|
135
131
|
#{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
|
|
136
132
|
rescue NoMethodError # rescue NoMethodError
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
class Module
|
|
2
2
|
def remove_possible_method(method)
|
|
3
|
-
|
|
3
|
+
if method_defined?(method) || private_method_defined?(method)
|
|
4
|
+
remove_method(method)
|
|
5
|
+
end
|
|
4
6
|
rescue NameError
|
|
7
|
+
# If the requested method is defined on a superclass or included module,
|
|
8
|
+
# method_defined? returns true but remove_method throws a NameError.
|
|
9
|
+
# Ignore this.
|
|
5
10
|
end
|
|
6
11
|
|
|
7
12
|
def redefine_method(method, &block)
|
|
8
13
|
remove_possible_method(method)
|
|
9
14
|
define_method(method, &block)
|
|
10
15
|
end
|
|
11
|
-
end
|
|
16
|
+
end
|