activesupport 3.2.22.5 → 4.0.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +325 -136
- data/MIT-LICENSE +1 -1
- data/README.rdoc +4 -2
- data/lib/active_support.rb +8 -21
- 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.rb +203 -171
- data/lib/active_support/cache/file_store.rb +12 -12
- 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/callbacks.rb +195 -247
- 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.rb +1 -0
- data/lib/active_support/core_ext/array.rb +0 -1
- data/lib/active_support/core_ext/array/access.rb +17 -9
- data/lib/active_support/core_ext/array/conversions.rb +113 -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 +11 -14
- data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
- data/lib/active_support/core_ext/class/attribute.rb +12 -8
- 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.rb +6 -0
- data/lib/active_support/core_ext/date/calculations.rb +34 -188
- data/lib/active_support/core_ext/date/conversions.rb +16 -38
- data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
- data/lib/active_support/core_ext/date/zones.rb +25 -2
- data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
- data/lib/active_support/core_ext/date_time.rb +5 -0
- data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
- data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
- data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
- data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
- data/lib/active_support/core_ext/date_time/zones.rb +11 -8
- data/lib/active_support/core_ext/enumerable.rb +26 -73
- data/lib/active_support/core_ext/file.rb +0 -1
- data/lib/active_support/core_ext/file/atomic.rb +27 -11
- data/lib/active_support/core_ext/hash.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/infinite_comparable.rb +35 -0
- 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 +19 -0
- data/lib/active_support/core_ext/module.rb +1 -3
- 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 +57 -40
- 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/numeric.rb +2 -0
- data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
- data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
- data/lib/active_support/core_ext/numeric/time.rb +6 -6
- data/lib/active_support/core_ext/object.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 +6 -6
- 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/proc.rb +3 -0
- data/lib/active_support/core_ext/range.rb +0 -2
- data/lib/active_support/core_ext/range/conversions.rb +0 -2
- data/lib/active_support/core_ext/range/include_range.rb +1 -1
- data/lib/active_support/core_ext/range/overlaps.rb +1 -1
- data/lib/active_support/core_ext/string.rb +2 -2
- data/lib/active_support/core_ext/string/access.rb +95 -90
- data/lib/active_support/core_ext/string/conversions.rb +29 -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/struct.rb +6 -0
- data/lib/active_support/core_ext/thread.rb +74 -0
- data/lib/active_support/core_ext/time.rb +6 -0
- data/lib/active_support/core_ext/time/calculations.rb +105 -193
- data/lib/active_support/core_ext/time/conversions.rb +27 -51
- data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
- 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/uri.rb +13 -17
- data/lib/active_support/dependencies.rb +160 -141
- data/lib/active_support/dependencies/autoload.rb +47 -20
- data/lib/active_support/deprecation.rb +39 -14
- 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/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 +112 -37
- 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.rb +56 -36
- data/lib/active_support/log_subscriber/test_helper.rb +18 -15
- data/lib/active_support/logger.rb +57 -0
- data/lib/active_support/logger_silence.rb +24 -0
- data/lib/active_support/message_encryptor.rb +32 -29
- data/lib/active_support/message_verifier.rb +8 -14
- data/lib/active_support/multibyte.rb +5 -28
- data/lib/active_support/multibyte/chars.rb +80 -333
- data/lib/active_support/multibyte/unicode.rb +74 -64
- data/lib/active_support/notifications.rb +57 -25
- data/lib/active_support/notifications/fanout.rb +105 -18
- data/lib/active_support/notifications/instrumenter.rb +32 -13
- data/lib/active_support/number_helper.rb +636 -0
- data/lib/active_support/ordered_hash.rb +8 -190
- data/lib/active_support/ordered_options.rb +21 -23
- 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/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 +52 -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 +25 -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 +78 -43
- data/lib/active_support/values/time_zone.rb +77 -58
- data/lib/active_support/values/unicode_tables.dat +0 -0
- data/lib/active_support/version.rb +4 -4
- data/lib/active_support/xml_mini.rb +35 -17
- data/lib/active_support/xml_mini/jdom.rb +9 -17
- data/lib/active_support/xml_mini/libxml.rb +1 -2
- data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
- data/lib/active_support/xml_mini/nokogiri.rb +1 -2
- data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
- data/lib/active_support/xml_mini/rexml.rb +6 -8
- metadata +107 -77
- 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.rb +0 -1
- data/lib/active_support/core_ext/float/rounding.rb +0 -19
- 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.rb +0 -1
- data/lib/active_support/core_ext/process/daemon.rb +0 -23
- 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/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.rb +0 -317
- 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.rb +0 -152
- 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/time/autoload.rb +0 -5
- data/lib/active_support/whiny_nil.rb +0 -24
@@ -4,10 +4,6 @@ require 'active_support/core_ext/date_time/calculations'
|
|
4
4
|
require 'active_support/values/time_zone'
|
5
5
|
|
6
6
|
class DateTime
|
7
|
-
# Ruby 1.9 has DateTime#to_time which internally relies on Time. We define our own #to_time which allows
|
8
|
-
# DateTimes outside the range of what can be created with Time.
|
9
|
-
remove_method :to_time if instance_methods.include?(:to_time)
|
10
|
-
|
11
7
|
# Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
|
12
8
|
#
|
13
9
|
# This method is aliased to <tt>to_s</tt>.
|
@@ -30,7 +26,7 @@ class DateTime
|
|
30
26
|
# datetime argument as the value.
|
31
27
|
#
|
32
28
|
# # config/initializers/time_formats.rb
|
33
|
-
# Time::DATE_FORMATS[:month_and_year] =
|
29
|
+
# Time::DATE_FORMATS[:month_and_year] = '%B %Y'
|
34
30
|
# Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
|
35
31
|
def to_formatted_s(format = :default)
|
36
32
|
if formatter = ::Time::DATE_FORMATS[format]
|
@@ -39,10 +35,9 @@ class DateTime
|
|
39
35
|
to_default_s
|
40
36
|
end
|
41
37
|
end
|
42
|
-
alias_method :to_default_s, :to_s
|
38
|
+
alias_method :to_default_s, :to_s if instance_methods(false).include?(:to_s)
|
43
39
|
alias_method :to_s, :to_formatted_s
|
44
40
|
|
45
|
-
# Returns the +utc_offset+ as an +HH:MM formatted string. Examples:
|
46
41
|
#
|
47
42
|
# datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
|
48
43
|
# datetime.formatted_offset # => "-06:00"
|
@@ -58,46 +53,39 @@ class DateTime
|
|
58
53
|
alias_method :default_inspect, :inspect
|
59
54
|
alias_method :inspect, :readable_inspect
|
60
55
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
#
|
67
|
-
#
|
68
|
-
def to_time
|
69
|
-
self.offset == 0 ? ::Time.utc_time(year, month, day, hour, min, sec, sec_fraction * (RUBY_VERSION < '1.9' ? 86400000000 : 1000000)) : self
|
70
|
-
end
|
71
|
-
|
72
|
-
# To be able to keep Times, Dates and DateTimes interchangeable on conversions.
|
73
|
-
def to_datetime
|
74
|
-
self
|
75
|
-
end unless instance_methods(false).include?(:to_datetime)
|
76
|
-
|
56
|
+
# Returns DateTime with local offset for given year if format is local else
|
57
|
+
# offset is zero.
|
58
|
+
#
|
59
|
+
# DateTime.civil_from_format :local, 2012
|
60
|
+
# # => Sun, 01 Jan 2012 00:00:00 +0300
|
61
|
+
# DateTime.civil_from_format :local, 2012, 12, 17
|
62
|
+
# # => Mon, 17 Dec 2012 00:00:00 +0000
|
77
63
|
def self.civil_from_format(utc_or_local, year, month=1, day=1, hour=0, min=0, sec=0)
|
78
|
-
|
64
|
+
if utc_or_local.to_sym == :local
|
65
|
+
offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
|
66
|
+
else
|
67
|
+
offset = 0
|
68
|
+
end
|
79
69
|
civil(year, month, day, hour, min, sec, offset)
|
80
70
|
end
|
81
71
|
|
82
|
-
# Converts
|
83
|
-
def xmlschema
|
84
|
-
strftime("%Y-%m-%dT%H:%M:%S%Z")
|
85
|
-
end unless instance_methods(false).include?(:xmlschema)
|
86
|
-
|
87
|
-
# Converts self to a floating-point number of seconds since the Unix epoch.
|
72
|
+
# Converts +self+ to a floating-point number of seconds since the Unix epoch.
|
88
73
|
def to_f
|
89
74
|
seconds_since_unix_epoch.to_f
|
90
75
|
end
|
91
76
|
|
92
|
-
# Converts self to an integer number of seconds since the Unix epoch.
|
77
|
+
# Converts +self+ to an integer number of seconds since the Unix epoch.
|
93
78
|
def to_i
|
94
79
|
seconds_since_unix_epoch.to_i
|
95
80
|
end
|
96
81
|
|
97
82
|
private
|
98
83
|
|
84
|
+
def offset_in_seconds
|
85
|
+
(offset * 86400).to_i
|
86
|
+
end
|
87
|
+
|
99
88
|
def seconds_since_unix_epoch
|
100
|
-
|
101
|
-
(self - ::DateTime.civil(1970)) * seconds_per_day
|
89
|
+
(jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
|
102
90
|
end
|
103
91
|
end
|
@@ -6,16 +6,19 @@ class DateTime
|
|
6
6
|
# Time.zone = 'Hawaii' # => 'Hawaii'
|
7
7
|
# DateTime.new(2000).in_time_zone # => Fri, 31 Dec 1999 14:00:00 HST -10:00
|
8
8
|
#
|
9
|
-
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
|
10
|
-
# instead of the operating system's time zone.
|
9
|
+
# This method is similar to Time#localtime, except that it uses <tt>Time.zone</tt>
|
10
|
+
# as the local zone instead of the operating system's time zone.
|
11
11
|
#
|
12
|
-
# You can also pass in a TimeZone instance or string that identifies a TimeZone
|
13
|
-
# and the conversion will be based on that zone instead of
|
12
|
+
# You can also pass in a TimeZone instance or string that identifies a TimeZone
|
13
|
+
# as an argument, and the conversion will be based on that zone instead of
|
14
|
+
# <tt>Time.zone</tt>.
|
14
15
|
#
|
15
|
-
# DateTime.new(2000).in_time_zone('Alaska')
|
16
|
+
# DateTime.new(2000).in_time_zone('Alaska') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
|
16
17
|
def in_time_zone(zone = ::Time.zone)
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
if zone
|
19
|
+
ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
|
20
|
+
else
|
21
|
+
self
|
22
|
+
end
|
20
23
|
end
|
21
24
|
end
|
@@ -1,42 +1,5 @@
|
|
1
|
-
require 'active_support/ordered_hash'
|
2
|
-
|
3
1
|
module Enumerable
|
4
|
-
#
|
5
|
-
remove_method(:group_by) if [].respond_to?(:group_by) && RUBY_VERSION < '1.9'
|
6
|
-
|
7
|
-
# Collect an enumerable into sets, grouped by the result of a block. Useful,
|
8
|
-
# for example, for grouping records by date.
|
9
|
-
#
|
10
|
-
# Example:
|
11
|
-
#
|
12
|
-
# latest_transcripts.group_by(&:day).each do |day, transcripts|
|
13
|
-
# p "#{day} -> #{transcripts.map(&:class).join(', ')}"
|
14
|
-
# end
|
15
|
-
# "2006-03-01 -> Transcript"
|
16
|
-
# "2006-02-28 -> Transcript"
|
17
|
-
# "2006-02-27 -> Transcript, Transcript"
|
18
|
-
# "2006-02-26 -> Transcript, Transcript"
|
19
|
-
# "2006-02-25 -> Transcript"
|
20
|
-
# "2006-02-24 -> Transcript, Transcript"
|
21
|
-
# "2006-02-23 -> Transcript"
|
22
|
-
def group_by
|
23
|
-
return to_enum :group_by unless block_given?
|
24
|
-
assoc = ActiveSupport::OrderedHash.new
|
25
|
-
|
26
|
-
each do |element|
|
27
|
-
key = yield(element)
|
28
|
-
|
29
|
-
if assoc.has_key?(key)
|
30
|
-
assoc[key] << element
|
31
|
-
else
|
32
|
-
assoc[key] = [element]
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
assoc
|
37
|
-
end unless [].respond_to?(:group_by)
|
38
|
-
|
39
|
-
# Calculates a sum from the elements. Examples:
|
2
|
+
# Calculates a sum from the elements.
|
40
3
|
#
|
41
4
|
# payments.sum { |p| p.price * p.tax_rate }
|
42
5
|
# payments.sum(&:price)
|
@@ -48,56 +11,38 @@ module Enumerable
|
|
48
11
|
# It can also calculate the sum without the use of a block.
|
49
12
|
#
|
50
13
|
# [5, 15, 10].sum # => 30
|
51
|
-
# [
|
14
|
+
# ['foo', 'bar'].sum # => "foobar"
|
52
15
|
# [[1, 2], [3, 1, 5]].sum => [1, 2, 3, 1, 5]
|
53
16
|
#
|
54
17
|
# The default sum of an empty list is zero. You can override this default:
|
55
18
|
#
|
56
19
|
# [].sum(Payment.new(0)) { |i| i.amount } # => Payment.new(0)
|
57
|
-
#
|
58
20
|
def sum(identity = 0, &block)
|
59
21
|
if block_given?
|
60
22
|
map(&block).sum(identity)
|
61
23
|
else
|
62
|
-
inject
|
24
|
+
inject { |sum, element| sum + element } || identity
|
63
25
|
end
|
64
26
|
end
|
65
27
|
|
66
|
-
#
|
67
|
-
# +memo+ to the block. Handy for building up hashes or
|
68
|
-
# reducing collections down to one object. Examples:
|
69
|
-
#
|
70
|
-
# %w(foo bar).each_with_object({}) { |str, hsh| hsh[str] = str.upcase }
|
71
|
-
# # => {'foo' => 'FOO', 'bar' => 'BAR'}
|
72
|
-
#
|
73
|
-
# *Note* that you can't use immutable objects like numbers, true or false as
|
74
|
-
# the memo. You would think the following returns 120, but since the memo is
|
75
|
-
# never changed, it does not.
|
76
|
-
#
|
77
|
-
# (1..5).each_with_object(1) { |value, memo| memo *= value } # => 1
|
78
|
-
#
|
79
|
-
def each_with_object(memo)
|
80
|
-
return to_enum :each_with_object, memo unless block_given?
|
81
|
-
each do |element|
|
82
|
-
yield element, memo
|
83
|
-
end
|
84
|
-
memo
|
85
|
-
end unless [].respond_to?(:each_with_object)
|
86
|
-
|
87
|
-
# Convert an enumerable to a hash. Examples:
|
28
|
+
# Convert an enumerable to a hash.
|
88
29
|
#
|
89
30
|
# people.index_by(&:login)
|
90
31
|
# => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
|
91
32
|
# people.index_by { |person| "#{person.first_name} #{person.last_name}" }
|
92
33
|
# => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
|
93
|
-
#
|
94
34
|
def index_by
|
95
|
-
|
96
|
-
|
35
|
+
if block_given?
|
36
|
+
Hash[map { |elem| [yield(elem), elem] }]
|
37
|
+
else
|
38
|
+
to_enum :index_by
|
39
|
+
end
|
97
40
|
end
|
98
41
|
|
99
|
-
# Returns true if the enumerable has more than 1 element. Functionally
|
100
|
-
#
|
42
|
+
# Returns +true+ if the enumerable has more than 1 element. Functionally
|
43
|
+
# equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
|
44
|
+
# much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
|
45
|
+
# if more than one person is over 26.
|
101
46
|
def many?
|
102
47
|
cnt = 0
|
103
48
|
if block_given?
|
@@ -106,11 +51,12 @@ module Enumerable
|
|
106
51
|
cnt > 1
|
107
52
|
end
|
108
53
|
else
|
109
|
-
any?{ (cnt += 1) > 1 }
|
54
|
+
any? { (cnt += 1) > 1 }
|
110
55
|
end
|
111
56
|
end
|
112
57
|
|
113
|
-
# The negative of the <tt>Enumerable#include?</tt>. Returns true if the
|
58
|
+
# The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
|
59
|
+
# collection does not include the object.
|
114
60
|
def exclude?(object)
|
115
61
|
!include?(object)
|
116
62
|
end
|
@@ -120,8 +66,15 @@ class Range #:nodoc:
|
|
120
66
|
# Optimize range sum to use arithmetic progression if a block is not given and
|
121
67
|
# we have a range of numeric values.
|
122
68
|
def sum(identity = 0)
|
123
|
-
|
124
|
-
|
125
|
-
|
69
|
+
if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
|
70
|
+
super
|
71
|
+
else
|
72
|
+
actual_last = exclude_end? ? (last - 1) : last
|
73
|
+
if actual_last >= first
|
74
|
+
(actual_last - first + 1) * (actual_last + first) / 2
|
75
|
+
else
|
76
|
+
identity
|
77
|
+
end
|
78
|
+
end
|
126
79
|
end
|
127
80
|
end
|
@@ -1,16 +1,18 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
1
3
|
class File
|
2
4
|
# Write to a file atomically. Useful for situations where you don't
|
3
5
|
# want other processes or threads to see half-written files.
|
4
6
|
#
|
5
|
-
# File.atomic_write(
|
6
|
-
# file.write(
|
7
|
+
# File.atomic_write('important.file') do |file|
|
8
|
+
# file.write('hello')
|
7
9
|
# end
|
8
10
|
#
|
9
11
|
# If your temp directory is not on the same filesystem as the file you're
|
10
12
|
# trying to write, you can provide a different temporary directory.
|
11
13
|
#
|
12
|
-
# File.atomic_write(
|
13
|
-
# file.write(
|
14
|
+
# File.atomic_write('/data/something.important', '/data/tmp') do |file|
|
15
|
+
# file.write('hello')
|
14
16
|
# end
|
15
17
|
def self.atomic_write(file_name, temp_dir = Dir.tmpdir)
|
16
18
|
require 'tempfile' unless defined?(Tempfile)
|
@@ -21,15 +23,13 @@ class File
|
|
21
23
|
yield temp_file
|
22
24
|
temp_file.close
|
23
25
|
|
24
|
-
|
26
|
+
if File.exists?(file_name)
|
25
27
|
# Get original file permissions
|
26
28
|
old_stat = stat(file_name)
|
27
|
-
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
old_stat = stat(check_name)
|
32
|
-
unlink(check_name)
|
29
|
+
else
|
30
|
+
# If not possible, probe which are the default permissions in the
|
31
|
+
# destination directory.
|
32
|
+
old_stat = probe_stat_in(dirname(file_name))
|
33
33
|
end
|
34
34
|
|
35
35
|
# Overwrite original file with temp file
|
@@ -44,4 +44,20 @@ class File
|
|
44
44
|
# Changing file ownership failed, moving on.
|
45
45
|
end
|
46
46
|
end
|
47
|
+
|
48
|
+
# Private utility method.
|
49
|
+
def self.probe_stat_in(dir) #:nodoc:
|
50
|
+
basename = [
|
51
|
+
'.permissions_check',
|
52
|
+
Thread.current.object_id,
|
53
|
+
Process.pid,
|
54
|
+
rand(1000000)
|
55
|
+
].join('.')
|
56
|
+
|
57
|
+
file_name = join(dir, basename)
|
58
|
+
FileUtils.touch(file_name)
|
59
|
+
stat(file_name)
|
60
|
+
ensure
|
61
|
+
FileUtils.rm_f(file_name) if file_name
|
62
|
+
end
|
47
63
|
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'
|
@@ -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
|
+
|