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.

Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +325 -136
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -2
  5. data/lib/active_support.rb +8 -21
  6. data/lib/active_support/backtrace_cleaner.rb +33 -25
  7. data/lib/active_support/basic_object.rb +7 -17
  8. data/lib/active_support/benchmarkable.rb +19 -15
  9. data/lib/active_support/buffered_logger.rb +9 -113
  10. data/lib/active_support/cache.rb +203 -171
  11. data/lib/active_support/cache/file_store.rb +12 -12
  12. data/lib/active_support/cache/mem_cache_store.rb +24 -30
  13. data/lib/active_support/cache/memory_store.rb +2 -0
  14. data/lib/active_support/callbacks.rb +195 -247
  15. data/lib/active_support/concern.rb +16 -23
  16. data/lib/active_support/concurrency/latch.rb +27 -0
  17. data/lib/active_support/configurable.rb +69 -12
  18. data/lib/active_support/core_ext.rb +1 -0
  19. data/lib/active_support/core_ext/array.rb +0 -1
  20. data/lib/active_support/core_ext/array/access.rb +17 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +113 -55
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +21 -22
  24. data/lib/active_support/core_ext/array/uniq_by.rb +12 -9
  25. data/lib/active_support/core_ext/array/wrap.rb +11 -14
  26. data/lib/active_support/core_ext/big_decimal/conversions.rb +7 -24
  27. data/lib/active_support/core_ext/class/attribute.rb +12 -8
  28. data/lib/active_support/core_ext/class/attribute_accessors.rb +14 -12
  29. data/lib/active_support/core_ext/class/delegating_attributes.rb +15 -19
  30. data/lib/active_support/core_ext/class/subclasses.rb +11 -5
  31. data/lib/active_support/core_ext/date.rb +6 -0
  32. data/lib/active_support/core_ext/date/calculations.rb +34 -188
  33. data/lib/active_support/core_ext/date/conversions.rb +16 -38
  34. data/lib/active_support/core_ext/date/infinite_comparable.rb +5 -0
  35. data/lib/active_support/core_ext/date/zones.rb +25 -2
  36. data/lib/active_support/core_ext/date_and_time/calculations.rb +232 -0
  37. data/lib/active_support/core_ext/date_time.rb +5 -0
  38. data/lib/active_support/core_ext/date_time/acts_like.rb +0 -1
  39. data/lib/active_support/core_ext/date_time/calculations.rb +73 -65
  40. data/lib/active_support/core_ext/date_time/conversions.rb +21 -33
  41. data/lib/active_support/core_ext/date_time/infinite_comparable.rb +5 -0
  42. data/lib/active_support/core_ext/date_time/zones.rb +11 -8
  43. data/lib/active_support/core_ext/enumerable.rb +26 -73
  44. data/lib/active_support/core_ext/file.rb +0 -1
  45. data/lib/active_support/core_ext/file/atomic.rb +27 -11
  46. data/lib/active_support/core_ext/hash.rb +0 -1
  47. data/lib/active_support/core_ext/hash/conversions.rb +145 -79
  48. data/lib/active_support/core_ext/hash/deep_merge.rb +14 -8
  49. data/lib/active_support/core_ext/hash/diff.rb +5 -4
  50. data/lib/active_support/core_ext/hash/except.rb +1 -9
  51. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -5
  52. data/lib/active_support/core_ext/hash/keys.rb +108 -24
  53. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  54. data/lib/active_support/core_ext/hash/slice.rb +12 -12
  55. data/lib/active_support/core_ext/infinite_comparable.rb +35 -0
  56. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  57. data/lib/active_support/core_ext/integer/time.rb +17 -12
  58. data/lib/active_support/core_ext/kernel/debugger.rb +2 -2
  59. data/lib/active_support/core_ext/kernel/reporting.rb +36 -22
  60. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  61. data/lib/active_support/core_ext/load_error.rb +7 -5
  62. data/lib/active_support/core_ext/logger.rb +7 -23
  63. data/lib/active_support/core_ext/marshal.rb +19 -0
  64. data/lib/active_support/core_ext/module.rb +1 -3
  65. data/lib/active_support/core_ext/module/aliasing.rb +8 -9
  66. data/lib/active_support/core_ext/module/anonymous.rb +2 -7
  67. data/lib/active_support/core_ext/module/attr_internal.rb +0 -1
  68. data/lib/active_support/core_ext/module/attribute_accessors.rb +12 -10
  69. data/lib/active_support/core_ext/module/delegation.rb +57 -40
  70. data/lib/active_support/core_ext/module/deprecation.rb +19 -3
  71. data/lib/active_support/core_ext/module/introspection.rb +17 -27
  72. data/lib/active_support/core_ext/module/qualified_const.rb +8 -20
  73. data/lib/active_support/core_ext/module/remove_method.rb +1 -5
  74. data/lib/active_support/core_ext/numeric.rb +2 -0
  75. data/lib/active_support/core_ext/numeric/conversions.rb +135 -0
  76. data/lib/active_support/core_ext/numeric/infinite_comparable.rb +9 -0
  77. data/lib/active_support/core_ext/numeric/time.rb +6 -6
  78. data/lib/active_support/core_ext/object.rb +1 -0
  79. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  80. data/lib/active_support/core_ext/object/blank.rb +7 -23
  81. data/lib/active_support/core_ext/object/deep_dup.rb +46 -0
  82. data/lib/active_support/core_ext/object/duplicable.rb +1 -30
  83. data/lib/active_support/core_ext/object/inclusion.rb +6 -6
  84. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  85. data/lib/active_support/core_ext/object/to_json.rb +8 -0
  86. data/lib/active_support/core_ext/object/to_param.rb +5 -2
  87. data/lib/active_support/core_ext/object/try.rb +46 -25
  88. data/lib/active_support/core_ext/object/with_options.rb +7 -8
  89. data/lib/active_support/core_ext/proc.rb +3 -0
  90. data/lib/active_support/core_ext/range.rb +0 -2
  91. data/lib/active_support/core_ext/range/conversions.rb +0 -2
  92. data/lib/active_support/core_ext/range/include_range.rb +1 -1
  93. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  94. data/lib/active_support/core_ext/string.rb +2 -2
  95. data/lib/active_support/core_ext/string/access.rb +95 -90
  96. data/lib/active_support/core_ext/string/conversions.rb +29 -38
  97. data/lib/active_support/core_ext/string/encoding.rb +6 -9
  98. data/lib/active_support/core_ext/string/filters.rb +24 -18
  99. data/lib/active_support/core_ext/string/indent.rb +43 -0
  100. data/lib/active_support/core_ext/string/inflections.rb +70 -60
  101. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  102. data/lib/active_support/core_ext/string/multibyte.rb +41 -64
  103. data/lib/active_support/core_ext/string/output_safety.rb +59 -51
  104. data/lib/active_support/core_ext/string/zones.rb +13 -0
  105. data/lib/active_support/core_ext/struct.rb +6 -0
  106. data/lib/active_support/core_ext/thread.rb +74 -0
  107. data/lib/active_support/core_ext/time.rb +6 -0
  108. data/lib/active_support/core_ext/time/calculations.rb +105 -193
  109. data/lib/active_support/core_ext/time/conversions.rb +27 -51
  110. data/lib/active_support/core_ext/time/infinite_comparable.rb +5 -0
  111. data/lib/active_support/core_ext/time/marshal.rb +0 -27
  112. data/lib/active_support/core_ext/time/zones.rb +27 -17
  113. data/lib/active_support/core_ext/uri.rb +13 -17
  114. data/lib/active_support/dependencies.rb +160 -141
  115. data/lib/active_support/dependencies/autoload.rb +47 -20
  116. data/lib/active_support/deprecation.rb +39 -14
  117. data/lib/active_support/deprecation/behaviors.rb +44 -30
  118. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  119. data/lib/active_support/deprecation/method_wrappers.rb +33 -18
  120. data/lib/active_support/deprecation/proxy_wrappers.rb +58 -13
  121. data/lib/active_support/deprecation/reporting.rb +40 -11
  122. data/lib/active_support/descendants_tracker.rb +34 -19
  123. data/lib/active_support/duration.rb +6 -8
  124. data/lib/active_support/file_update_checker.rb +63 -47
  125. data/lib/active_support/gzip.rb +11 -5
  126. data/lib/active_support/hash_with_indifferent_access.rb +112 -37
  127. data/lib/active_support/i18n.rb +4 -0
  128. data/lib/active_support/i18n_railtie.rb +5 -22
  129. data/lib/active_support/inflections.rb +14 -12
  130. data/lib/active_support/inflector/inflections.rb +108 -71
  131. data/lib/active_support/inflector/methods.rb +181 -160
  132. data/lib/active_support/inflector/transliterate.rb +16 -17
  133. data/lib/active_support/json/decoding.rb +18 -17
  134. data/lib/active_support/json/encoding.rb +93 -39
  135. data/lib/active_support/json/variable.rb +10 -1
  136. data/lib/active_support/key_generator.rb +75 -0
  137. data/lib/active_support/lazy_load_hooks.rb +21 -19
  138. data/lib/active_support/locale/en.yml +100 -3
  139. data/lib/active_support/log_subscriber.rb +56 -36
  140. data/lib/active_support/log_subscriber/test_helper.rb +18 -15
  141. data/lib/active_support/logger.rb +57 -0
  142. data/lib/active_support/logger_silence.rb +24 -0
  143. data/lib/active_support/message_encryptor.rb +32 -29
  144. data/lib/active_support/message_verifier.rb +8 -14
  145. data/lib/active_support/multibyte.rb +5 -28
  146. data/lib/active_support/multibyte/chars.rb +80 -333
  147. data/lib/active_support/multibyte/unicode.rb +74 -64
  148. data/lib/active_support/notifications.rb +57 -25
  149. data/lib/active_support/notifications/fanout.rb +105 -18
  150. data/lib/active_support/notifications/instrumenter.rb +32 -13
  151. data/lib/active_support/number_helper.rb +636 -0
  152. data/lib/active_support/ordered_hash.rb +8 -190
  153. data/lib/active_support/ordered_options.rb +21 -23
  154. data/lib/active_support/proxy_object.rb +13 -0
  155. data/lib/active_support/rails.rb +27 -0
  156. data/lib/active_support/railtie.rb +12 -32
  157. data/lib/active_support/rescuable.rb +9 -4
  158. data/lib/active_support/string_inquirer.rb +13 -8
  159. data/lib/active_support/tagged_logging.rb +51 -73
  160. data/lib/active_support/test_case.rb +46 -17
  161. data/lib/active_support/testing/assertions.rb +56 -26
  162. data/lib/active_support/testing/autorun.rb +5 -0
  163. data/lib/active_support/testing/constant_lookup.rb +52 -0
  164. data/lib/active_support/testing/declarative.rb +1 -1
  165. data/lib/active_support/testing/deprecation.rb +0 -19
  166. data/lib/active_support/testing/isolation.rb +25 -58
  167. data/lib/active_support/testing/pending.rb +5 -43
  168. data/lib/active_support/testing/setup_and_teardown.rb +6 -92
  169. data/lib/active_support/testing/tagged_logging.rb +25 -0
  170. data/lib/active_support/time.rb +6 -21
  171. data/lib/active_support/time_with_zone.rb +78 -43
  172. data/lib/active_support/values/time_zone.rb +77 -58
  173. data/lib/active_support/values/unicode_tables.dat +0 -0
  174. data/lib/active_support/version.rb +4 -4
  175. data/lib/active_support/xml_mini.rb +35 -17
  176. data/lib/active_support/xml_mini/jdom.rb +9 -17
  177. data/lib/active_support/xml_mini/libxml.rb +1 -2
  178. data/lib/active_support/xml_mini/libxmlsax.rb +1 -2
  179. data/lib/active_support/xml_mini/nokogiri.rb +1 -2
  180. data/lib/active_support/xml_mini/nokogirisax.rb +1 -2
  181. data/lib/active_support/xml_mini/rexml.rb +6 -8
  182. metadata +107 -77
  183. data/lib/active_support/base64.rb +0 -54
  184. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  185. data/lib/active_support/core_ext/date/freeze.rb +0 -33
  186. data/lib/active_support/core_ext/exception.rb +0 -3
  187. data/lib/active_support/core_ext/file/path.rb +0 -5
  188. data/lib/active_support/core_ext/float.rb +0 -1
  189. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  190. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -18
  191. data/lib/active_support/core_ext/io.rb +0 -15
  192. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  193. data/lib/active_support/core_ext/module/synchronization.rb +0 -45
  194. data/lib/active_support/core_ext/process.rb +0 -1
  195. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  196. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  197. data/lib/active_support/core_ext/range/cover.rb +0 -3
  198. data/lib/active_support/core_ext/rexml.rb +0 -46
  199. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  200. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  201. data/lib/active_support/memoizable.rb +0 -116
  202. data/lib/active_support/multibyte/exceptions.rb +0 -8
  203. data/lib/active_support/multibyte/utils.rb +0 -60
  204. data/lib/active_support/ruby/shim.rb +0 -22
  205. data/lib/active_support/security_utils.rb +0 -27
  206. data/lib/active_support/testing/mochaing.rb +0 -7
  207. data/lib/active_support/testing/performance.rb +0 -317
  208. data/lib/active_support/testing/performance/jruby.rb +0 -115
  209. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  210. data/lib/active_support/testing/performance/ruby.rb +0 -152
  211. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  212. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  213. data/lib/active_support/time/autoload.rb +0 -5
  214. 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] = "%B %Y"
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 unless (instance_methods(false) & [:to_s, 'to_s']).empty?
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
- # Converts self to a Ruby Date object; time portion is discarded.
62
- def to_date
63
- ::Date.new(year, month, day)
64
- end unless instance_methods(false).include?(:to_date)
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.
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
- offset = utc_or_local.to_sym == :local ? local_offset : 0
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 datetime to an appropriate format for use in XML.
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
- seconds_per_day = 86_400
101
- (self - ::DateTime.civil(1970)) * seconds_per_day
89
+ (jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
102
90
  end
103
91
  end
@@ -0,0 +1,5 @@
1
+ require 'active_support/core_ext/infinite_comparable'
2
+
3
+ class DateTime
4
+ include InfiniteComparable
5
+ 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> as the local zone
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 as an argument,
13
- # and the conversion will be based on that zone instead of <tt>Time.zone</tt>.
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') # => Fri, 31 Dec 1999 15:00:00 AKST -09:00
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
- return self unless zone
18
-
19
- ActiveSupport::TimeWithZone.new(utc? ? self : getutc, ::Time.find_zone!(zone))
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
- # Ruby 1.8.7 introduces group_by, but the result isn't ordered. Override it.
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
- # ["foo", "bar"].sum # => "foobar"
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(:+) || identity
24
+ inject { |sum, element| sum + element } || identity
63
25
  end
64
26
  end
65
27
 
66
- # Iterates over a collection, passing the current element *and* the
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
- return to_enum :index_by unless block_given?
96
- Hash[map { |elem| [yield(elem), elem] }]
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 equivalent to enum.to_a.size > 1.
100
- # Can be called with a block too, much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns true if more than one person is over 26.
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 collection does not include the object.
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
- return super if block_given? || !(first.instance_of?(Integer) && last.instance_of?(Integer))
124
- actual_last = exclude_end? ? (last - 1) : last
125
- (actual_last - first + 1) * (actual_last + first) / 2
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,2 +1 @@
1
1
  require 'active_support/core_ext/file/atomic'
2
- require 'active_support/core_ext/file/path'
@@ -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("important.file") do |file|
6
- # file.write("hello")
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("/data/something.important", "/data/tmp") do |file|
13
- # file.write("hello")
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
- begin
26
+ if File.exists?(file_name)
25
27
  # Get original file permissions
26
28
  old_stat = stat(file_name)
27
- rescue Errno::ENOENT
28
- # No old permissions, write a temp file to determine the defaults
29
- check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
30
- open(check_name, "w") { }
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
- # {"foo" => 1, "bar" => 2}.to_xml
13
+ # {'foo' => 1, 'bar' => 2}.to_xml
12
14
  # # =>
13
15
  # # <?xml version="1.0" encoding="UTF-8"?>
14
16
  # # <hash>
@@ -26,21 +28,21 @@ class Hash
26
28
  #
27
29
  # * If +value+ is a callable object it must expect one or two arguments. Depending
28
30
  # on the arity, the callable is invoked with the +options+ hash as first argument
29
- # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
31
+ # with +key+ as <tt>:root</tt>, and +key+ singularized as second argument. The
30
32
  # callable can add nodes by using <tt>options[:builder]</tt>.
31
33
  #
32
- # "foo".to_xml(lambda { |options, key| options[:builder].b(key) })
34
+ # 'foo'.to_xml(lambda { |options, key| options[:builder].b(key) })
33
35
  # # => "<b>foo</b>"
34
36
  #
35
37
  # * If +value+ responds to +to_xml+ the method is invoked with +key+ as <tt>:root</tt>.
36
- #
38
+ #
37
39
  # class Foo
38
40
  # def to_xml(options)
39
- # options[:builder].bar "fooing!"
41
+ # options[:builder].bar 'fooing!'
40
42
  # end
41
43
  # end
42
- #
43
- # {:foo => Foo.new}.to_xml(:skip_instruct => true)
44
+ #
45
+ # { foo: Foo.new }.to_xml(skip_instruct: true)
44
46
  # # => "<hash><bar>fooing!</bar></hash>"
45
47
  #
46
48
  # * Otherwise, a node with +key+ as tag is created with a string representation of
@@ -57,8 +59,8 @@ class Hash
57
59
  # "TrueClass" => "boolean",
58
60
  # "FalseClass" => "boolean",
59
61
  # "Date" => "date",
60
- # "DateTime" => "datetime",
61
- # "Time" => "datetime"
62
+ # "DateTime" => "dateTime",
63
+ # "Time" => "dateTime"
62
64
  # }
63
65
  #
64
66
  # By default the root node is "hash", but that's configurable via the <tt>:root</tt> option.
@@ -71,105 +73,169 @@ class Hash
71
73
 
72
74
  options = options.dup
73
75
  options[:indent] ||= 2
74
- options[:root] ||= "hash"
75
- options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
76
+ options[:root] ||= 'hash'
77
+ options[:builder] ||= Builder::XmlMarkup.new(indent: options[:indent])
76
78
 
77
79
  builder = options[:builder]
78
80
  builder.instruct! unless options.delete(:skip_instruct)
79
81
 
80
82
  root = ActiveSupport::XmlMini.rename_key(options[:root].to_s, options)
81
83
 
82
- builder.__send__(:method_missing, root) do
84
+ builder.tag!(root) do
83
85
  each { |key, value| ActiveSupport::XmlMini.to_tag(key, value, options) }
84
86
  yield builder if block_given?
85
87
  end
86
88
  end
87
89
 
88
- class DisallowedType < StandardError #:nodoc:
89
- def initialize(type)
90
- super "Disallowed type attribute: #{type.inspect}"
91
- end
92
- end
93
-
94
- DISALLOWED_XML_TYPES = %w(symbol yaml)
95
-
96
90
  class << self
91
+ # Returns a Hash containing a collection of pairs when the key is the node name and the value is
92
+ # its content
93
+ #
94
+ # xml = <<-XML
95
+ # <?xml version="1.0" encoding="UTF-8"?>
96
+ # <hash>
97
+ # <foo type="integer">1</foo>
98
+ # <bar type="integer">2</bar>
99
+ # </hash>
100
+ # XML
101
+ #
102
+ # hash = Hash.from_xml(xml)
103
+ # # => {"hash"=>{"foo"=>1, "bar"=>2}}
104
+ #
105
+ # DisallowedType is raise if the XML contains attributes with <tt>type="yaml"</tt> or
106
+ # <tt>type="symbol"</tt>. Use <tt>Hash.from_trusted_xml</tt> to parse this XML.
97
107
  def from_xml(xml, disallowed_types = nil)
98
- typecast_xml_value(unrename_keys(ActiveSupport::XmlMini.parse(xml)), disallowed_types)
108
+ ActiveSupport::XMLConverter.new(xml, disallowed_types).to_h
99
109
  end
100
110
 
111
+ # Builds a Hash from XML just like <tt>Hash.from_xml</tt>, but also allows Symbol and YAML.
101
112
  def from_trusted_xml(xml)
102
113
  from_xml xml, []
103
114
  end
115
+ end
116
+ end
104
117
 
105
- private
106
- def typecast_xml_value(value, disallowed_types = nil)
107
- disallowed_types ||= DISALLOWED_XML_TYPES
118
+ module ActiveSupport
119
+ class XMLConverter # :nodoc:
120
+ class DisallowedType < StandardError
121
+ def initialize(type)
122
+ super "Disallowed type attribute: #{type.inspect}"
123
+ end
124
+ end
108
125
 
109
- case value.class.to_s
110
- when 'Hash'
111
- if value.include?('type') && !value['type'].is_a?(Hash) && disallowed_types.include?(value['type'])
112
- raise DisallowedType, value['type']
113
- end
126
+ DISALLOWED_TYPES = %w(symbol yaml)
114
127
 
115
- if value['type'] == 'array'
116
- _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
117
- if entries.nil? || (c = value['__content__'] && c.blank?)
118
- []
119
- else
120
- case entries.class.to_s # something weird with classes not matching here. maybe singleton methods breaking is_a?
121
- when "Array"
122
- entries.collect { |v| typecast_xml_value(v, disallowed_types) }
123
- when "Hash"
124
- [typecast_xml_value(entries, disallowed_types)]
125
- else
126
- raise "can't typecast #{entries.inspect}"
127
- end
128
- end
129
- elsif value['type'] == 'file' ||
130
- (value["__content__"] && (value.keys.size == 1 || value["__content__"].present?))
131
- content = value["__content__"]
132
- if parser = ActiveSupport::XmlMini::PARSING[value["type"]]
133
- parser.arity == 1 ? parser.call(content) : parser.call(content, value)
134
- else
135
- content
136
- end
137
- elsif value['type'] == 'string' && value['nil'] != 'true'
138
- ""
139
- # blank or nil parsed values are represented by nil
140
- elsif value.blank? || value['nil'] == 'true'
141
- nil
142
- # If the type is the only element which makes it then
143
- # this still makes the value nil, except if type is
144
- # a XML node(where type['value'] is a Hash)
145
- elsif value['type'] && value.size == 1 && !value['type'].is_a?(::Hash)
146
- nil
147
- else
148
- xml_value = Hash[value.map { |k,v| [k, typecast_xml_value(v, disallowed_types)] }]
128
+ def initialize(xml, disallowed_types = nil)
129
+ @xml = normalize_keys(XmlMini.parse(xml))
130
+ @disallowed_types = disallowed_types || DISALLOWED_TYPES
131
+ end
149
132
 
150
- # Turn { :files => { :file => #<StringIO> } into { :files => #<StringIO> } so it is compatible with
151
- # how multipart uploaded files from HTML appear
152
- xml_value["file"].is_a?(StringIO) ? xml_value["file"] : xml_value
153
- end
154
- when 'Array'
155
- value.map! { |i| typecast_xml_value(i, disallowed_types) }
156
- value.length > 1 ? value : value.first
157
- when 'String'
133
+ def to_h
134
+ deep_to_h(@xml)
135
+ end
136
+
137
+ private
138
+ def normalize_keys(params)
139
+ case params
140
+ when Hash
141
+ Hash[params.map { |k,v| [k.to_s.tr('-', '_'), normalize_keys(v)] } ]
142
+ when Array
143
+ params.map { |v| normalize_keys(v) }
144
+ else
145
+ params
146
+ end
147
+ end
148
+
149
+ def deep_to_h(value)
150
+ case value
151
+ when Hash
152
+ process_hash(value)
153
+ when Array
154
+ process_array(value)
155
+ when String
158
156
  value
159
157
  else
160
158
  raise "can't typecast #{value.class.name} - #{value.inspect}"
161
159
  end
162
160
  end
163
161
 
164
- def unrename_keys(params)
165
- case params.class.to_s
166
- when "Hash"
167
- Hash[params.map { |k,v| [k.to_s.tr("-", "_"), unrename_keys(v)] } ]
168
- when "Array"
169
- params.map { |v| unrename_keys(v) }
162
+ def process_hash(value)
163
+ if value.include?('type') && !value['type'].is_a?(Hash) && @disallowed_types.include?(value['type'])
164
+ raise DisallowedType, value['type']
165
+ end
166
+
167
+ if become_array?(value)
168
+ _, entries = Array.wrap(value.detect { |k,v| not v.is_a?(String) })
169
+ if entries.nil? || value['__content__'].try(:empty?)
170
+ []
170
171
  else
171
- params
172
+ case entries
173
+ when Array
174
+ entries.collect { |v| deep_to_h(v) }
175
+ when Hash
176
+ [deep_to_h(entries)]
177
+ else
178
+ raise "can't typecast #{entries.inspect}"
179
+ end
180
+ end
181
+ elsif become_content?(value)
182
+ process_content(value)
183
+
184
+ elsif become_empty_string?(value)
185
+ ''
186
+ elsif become_hash?(value)
187
+ xml_value = Hash[value.map { |k,v| [k, deep_to_h(v)] }]
188
+
189
+ # Turn { files: { file: #<StringIO> } } into { files: #<StringIO> } so it is compatible with
190
+ # how multipart uploaded files from HTML appear
191
+ xml_value['file'].is_a?(StringIO) ? xml_value['file'] : xml_value
172
192
  end
173
193
  end
194
+
195
+ def become_content?(value)
196
+ value['type'] == 'file' || (value['__content__'] && (value.keys.size == 1 || value['__content__'].present?))
197
+ end
198
+
199
+ def become_array?(value)
200
+ value['type'] == 'array'
201
+ end
202
+
203
+ def become_empty_string?(value)
204
+ # {"string" => true}
205
+ # No tests fail when the second term is removed.
206
+ value['type'] == 'string' && value['nil'] != 'true'
207
+ end
208
+
209
+ def become_hash?(value)
210
+ !nothing?(value) && !garbage?(value)
211
+ end
212
+
213
+ def nothing?(value)
214
+ # blank or nil parsed values are represented by nil
215
+ value.blank? || value['nil'] == 'true'
216
+ end
217
+
218
+ def garbage?(value)
219
+ # If the type is the only element which makes it then
220
+ # this still makes the value nil, except if type is
221
+ # a XML node(where type['value'] is a Hash)
222
+ value['type'] && !value['type'].is_a?(::Hash) && value.size == 1
223
+ end
224
+
225
+ def process_content(value)
226
+ content = value['__content__']
227
+ if parser = ActiveSupport::XmlMini::PARSING[value['type']]
228
+ parser.arity == 1 ? parser.call(content) : parser.call(content, value)
229
+ else
230
+ content
231
+ end
232
+ end
233
+
234
+ def process_array(value)
235
+ value.map! { |i| deep_to_h(i) }
236
+ value.length > 1 ? value : value.first
237
+ end
238
+
174
239
  end
175
240
  end
241
+