activesupport 4.2.11.3 → 5.2.8.1

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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +435 -403
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +7 -5
  8. data/lib/active_support/benchmarkable.rb +6 -4
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +41 -35
  11. data/lib/active_support/cache/mem_cache_store.rb +91 -91
  12. data/lib/active_support/cache/memory_store.rb +27 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +67 -34
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  17. data/lib/active_support/cache.rb +287 -196
  18. data/lib/active_support/callbacks.rb +640 -590
  19. data/lib/active_support/concern.rb +11 -5
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +35 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +29 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -6
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -6
  37. data/lib/active_support/core_ext/class.rb +4 -3
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +11 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +25 -23
  42. data/lib/active_support/core_ext/date/zones.rb +4 -2
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +170 -58
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +4 -3
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +36 -18
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +8 -6
  51. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  52. data/lib/active_support/core_ext/date_time.rb +7 -5
  53. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +101 -33
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +14 -9
  59. data/lib/active_support/core_ext/hash/conversions.rb +62 -41
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +11 -8
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  66. data/lib/active_support/core_ext/hash/transform_values.rb +14 -5
  67. data/lib/active_support/core_ext/hash.rb +11 -9
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -18
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  74. data/lib/active_support/core_ext/kernel/reporting.rb +4 -84
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -5
  77. data/lib/active_support/core_ext/load_error.rb +3 -22
  78. data/lib/active_support/core_ext/marshal.rb +8 -8
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  85. data/lib/active_support/core_ext/module/delegation.rb +98 -36
  86. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -11
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +78 -81
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +35 -23
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +27 -2
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  102. data/lib/active_support/core_ext/object/duplicable.rb +41 -14
  103. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +49 -19
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +10 -5
  108. data/lib/active_support/core_ext/object/try.rb +69 -21
  109. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  110. data/lib/active_support/core_ext/object.rb +14 -13
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +8 -6
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +7 -4
  123. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  124. data/lib/active_support/core_ext/string/filters.rb +6 -5
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +61 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +62 -38
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -2
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +85 -51
  136. data/lib/active_support/core_ext/time/compatibility.rb +4 -2
  137. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  138. data/lib/active_support/core_ext/time/zones.rb +41 -7
  139. data/lib/active_support/core_ext/time.rb +7 -6
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -1
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +152 -161
  146. data/lib/active_support/deprecation/behaviors.rb +44 -11
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +12 -9
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +314 -38
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +131 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +6 -4
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +123 -28
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +37 -13
  169. data/lib/active_support/inflections.rb +13 -11
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +163 -136
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +16 -13
  175. data/lib/active_support/json/encoding.rb +11 -58
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +25 -25
  178. data/lib/active_support/lazy_load_hooks.rb +50 -20
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +8 -7
  183. data/lib/active_support/logger_silence.rb +6 -4
  184. data/lib/active_support/logger_thread_safe_level.rb +7 -5
  185. data/lib/active_support/message_encryptor.rb +168 -53
  186. data/lib/active_support/message_verifier.rb +150 -17
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +36 -23
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +11 -9
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/notifications.rb +11 -7
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +94 -68
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +23 -5
  209. data/lib/active_support/per_thread_registry.rb +9 -4
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +15 -11
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +21 -16
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +19 -47
  220. data/lib/active_support/testing/assertions.rb +137 -20
  221. data/lib/active_support/testing/autorun.rb +4 -2
  222. data/lib/active_support/testing/constant_lookup.rb +2 -1
  223. data/lib/active_support/testing/declarative.rb +3 -1
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +34 -25
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +13 -8
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +3 -1
  231. data/lib/active_support/testing/time_helpers.rb +81 -15
  232. data/lib/active_support/time.rb +14 -12
  233. data/lib/active_support/time_with_zone.rb +169 -39
  234. data/lib/active_support/values/time_zone.rb +196 -61
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +3 -1
  237. data/lib/active_support/xml_mini/jdom.rb +116 -114
  238. data/lib/active_support/xml_mini/libxml.rb +16 -13
  239. data/lib/active_support/xml_mini/libxmlsax.rb +15 -14
  240. data/lib/active_support/xml_mini/nokogiri.rb +14 -12
  241. data/lib/active_support/xml_mini/nokogirisax.rb +14 -13
  242. data/lib/active_support/xml_mini/rexml.rb +11 -9
  243. data/lib/active_support/xml_mini.rb +37 -37
  244. data/lib/active_support.rb +12 -11
  245. metadata +57 -27
  246. data/lib/active_support/concurrency/latch.rb +0 -27
  247. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -16
  248. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  249. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  250. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  251. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -13
  252. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  253. data/lib/active_support/core_ext/object/itself.rb +0 -15
  254. data/lib/active_support/core_ext/struct.rb +0 -6
  255. data/lib/active_support/core_ext/thread.rb +0 -86
  256. data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+ require "active_support/core_ext/regexp"
5
+
6
+ module ActiveSupport
7
+ class Duration
8
+ # Parses a string formatted according to ISO 8601 Duration into the hash.
9
+ #
10
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
11
+ #
12
+ # This parser allows negative parts to be present in pattern.
13
+ class ISO8601Parser # :nodoc:
14
+ class ParsingError < ::ArgumentError; end
15
+
16
+ PERIOD_OR_COMMA = /\.|,/
17
+ PERIOD = ".".freeze
18
+ COMMA = ",".freeze
19
+
20
+ SIGN_MARKER = /\A\-|\+|/
21
+ DATE_MARKER = /P/
22
+ TIME_MARKER = /T/
23
+ DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
24
+ TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
25
+
26
+ DATE_TO_PART = { "Y" => :years, "M" => :months, "W" => :weeks, "D" => :days }
27
+ TIME_TO_PART = { "H" => :hours, "M" => :minutes, "S" => :seconds }
28
+
29
+ DATE_COMPONENTS = [:years, :months, :days]
30
+ TIME_COMPONENTS = [:hours, :minutes, :seconds]
31
+
32
+ attr_reader :parts, :scanner
33
+ attr_accessor :mode, :sign
34
+
35
+ def initialize(string)
36
+ @scanner = StringScanner.new(string)
37
+ @parts = {}
38
+ @mode = :start
39
+ @sign = 1
40
+ end
41
+
42
+ def parse!
43
+ while !finished?
44
+ case mode
45
+ when :start
46
+ if scan(SIGN_MARKER)
47
+ self.sign = (scanner.matched == "-") ? -1 : 1
48
+ self.mode = :sign
49
+ else
50
+ raise_parsing_error
51
+ end
52
+
53
+ when :sign
54
+ if scan(DATE_MARKER)
55
+ self.mode = :date
56
+ else
57
+ raise_parsing_error
58
+ end
59
+
60
+ when :date
61
+ if scan(TIME_MARKER)
62
+ self.mode = :time
63
+ elsif scan(DATE_COMPONENT)
64
+ parts[DATE_TO_PART[scanner[2]]] = number * sign
65
+ else
66
+ raise_parsing_error
67
+ end
68
+
69
+ when :time
70
+ if scan(TIME_COMPONENT)
71
+ parts[TIME_TO_PART[scanner[2]]] = number * sign
72
+ else
73
+ raise_parsing_error
74
+ end
75
+
76
+ end
77
+ end
78
+
79
+ validate!
80
+ parts
81
+ end
82
+
83
+ private
84
+
85
+ def finished?
86
+ scanner.eos?
87
+ end
88
+
89
+ # Parses number which can be a float with either comma or period.
90
+ def number
91
+ PERIOD_OR_COMMA.match?(scanner[1]) ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
92
+ end
93
+
94
+ def scan(pattern)
95
+ scanner.scan(pattern)
96
+ end
97
+
98
+ def raise_parsing_error(reason = nil)
99
+ raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
100
+ end
101
+
102
+ # Checks for various semantic errors as stated in ISO 8601 standard.
103
+ def validate!
104
+ raise_parsing_error("is empty duration") if parts.empty?
105
+
106
+ # Mixing any of Y, M, D with W is invalid.
107
+ if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
108
+ raise_parsing_error("mixing weeks with other date parts not allowed")
109
+ end
110
+
111
+ # Specifying an empty T part is invalid.
112
+ if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
113
+ raise_parsing_error("time part marker is present but time part is empty")
114
+ end
115
+
116
+ fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
117
+ unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
118
+ raise_parsing_error "(only last part can be fractional)"
119
+ end
120
+
121
+ true
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/object/blank"
4
+ require "active_support/core_ext/hash/transform_values"
5
+
6
+ module ActiveSupport
7
+ class Duration
8
+ # Serializes duration to string according to ISO 8601 Duration format.
9
+ class ISO8601Serializer # :nodoc:
10
+ def initialize(duration, precision: nil)
11
+ @duration = duration
12
+ @precision = precision
13
+ end
14
+
15
+ # Builds and returns output string.
16
+ def serialize
17
+ parts, sign = normalize
18
+ return "PT0S".freeze if parts.empty?
19
+
20
+ output = "P".dup
21
+ output << "#{parts[:years]}Y" if parts.key?(:years)
22
+ output << "#{parts[:months]}M" if parts.key?(:months)
23
+ output << "#{parts[:weeks]}W" if parts.key?(:weeks)
24
+ output << "#{parts[:days]}D" if parts.key?(:days)
25
+ time = "".dup
26
+ time << "#{parts[:hours]}H" if parts.key?(:hours)
27
+ time << "#{parts[:minutes]}M" if parts.key?(:minutes)
28
+ if parts.key?(:seconds)
29
+ time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
30
+ end
31
+ output << "T#{time}" unless time.empty?
32
+ "#{sign}#{output}"
33
+ end
34
+
35
+ private
36
+
37
+ # Return pair of duration's parts and whole duration sign.
38
+ # Parts are summarized (as they can become repetitive due to addition, etc).
39
+ # Zero parts are removed as not significant.
40
+ # If all parts are negative it will negate all of them and return minus as a sign.
41
+ def normalize
42
+ parts = @duration.parts.each_with_object(Hash.new(0)) do |(k, v), p|
43
+ p[k] += v unless v.zero?
44
+ end
45
+ # If all parts are negative - let's make a negative duration
46
+ sign = ""
47
+ if parts.values.all? { |v| v < 0 }
48
+ sign = "-"
49
+ parts.transform_values!(&:-@)
50
+ end
51
+ [parts, sign]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,10 @@
1
- require 'active_support/core_ext/array/conversions'
2
- require 'active_support/core_ext/object/acts_like'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/conversions"
4
+ require "active_support/core_ext/module/delegation"
5
+ require "active_support/core_ext/object/acts_like"
6
+ require "active_support/core_ext/string/filters"
7
+ require "active_support/deprecation"
3
8
 
4
9
  module ActiveSupport
5
10
  # Provides accurate date and time measurements using Date#advance and
@@ -7,19 +12,237 @@ module ActiveSupport
7
12
  #
8
13
  # 1.month.ago # equivalent to Time.now.advance(months: -1)
9
14
  class Duration
15
+ class Scalar < Numeric #:nodoc:
16
+ attr_reader :value
17
+ delegate :to_i, :to_f, :to_s, to: :value
18
+
19
+ def initialize(value)
20
+ @value = value
21
+ end
22
+
23
+ def coerce(other)
24
+ [Scalar.new(other), self]
25
+ end
26
+
27
+ def -@
28
+ Scalar.new(-value)
29
+ end
30
+
31
+ def <=>(other)
32
+ if Scalar === other || Duration === other
33
+ value <=> other.value
34
+ elsif Numeric === other
35
+ value <=> other
36
+ else
37
+ nil
38
+ end
39
+ end
40
+
41
+ def +(other)
42
+ if Duration === other
43
+ seconds = value + other.parts[:seconds]
44
+ new_parts = other.parts.merge(seconds: seconds)
45
+ new_value = value + other.value
46
+
47
+ Duration.new(new_value, new_parts)
48
+ else
49
+ calculate(:+, other)
50
+ end
51
+ end
52
+
53
+ def -(other)
54
+ if Duration === other
55
+ seconds = value - other.parts[:seconds]
56
+ new_parts = other.parts.map { |part, other_value| [part, -other_value] }.to_h
57
+ new_parts = new_parts.merge(seconds: seconds)
58
+ new_value = value - other.value
59
+
60
+ Duration.new(new_value, new_parts)
61
+ else
62
+ calculate(:-, other)
63
+ end
64
+ end
65
+
66
+ def *(other)
67
+ if Duration === other
68
+ new_parts = other.parts.map { |part, other_value| [part, value * other_value] }.to_h
69
+ new_value = value * other.value
70
+
71
+ Duration.new(new_value, new_parts)
72
+ else
73
+ calculate(:*, other)
74
+ end
75
+ end
76
+
77
+ def /(other)
78
+ if Duration === other
79
+ value / other.value
80
+ else
81
+ calculate(:/, other)
82
+ end
83
+ end
84
+
85
+ def %(other)
86
+ if Duration === other
87
+ Duration.build(value % other.value)
88
+ else
89
+ calculate(:%, other)
90
+ end
91
+ end
92
+
93
+ private
94
+ def calculate(op, other)
95
+ if Scalar === other
96
+ Scalar.new(value.public_send(op, other.value))
97
+ elsif Numeric === other
98
+ Scalar.new(value.public_send(op, other))
99
+ else
100
+ raise_type_error(other)
101
+ end
102
+ end
103
+
104
+ def raise_type_error(other)
105
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
106
+ end
107
+ end
108
+
109
+ SECONDS_PER_MINUTE = 60
110
+ SECONDS_PER_HOUR = 3600
111
+ SECONDS_PER_DAY = 86400
112
+ SECONDS_PER_WEEK = 604800
113
+ SECONDS_PER_MONTH = 2629746 # 1/12 of a gregorian year
114
+ SECONDS_PER_YEAR = 31556952 # length of a gregorian year (365.2425 days)
115
+
116
+ PARTS_IN_SECONDS = {
117
+ seconds: 1,
118
+ minutes: SECONDS_PER_MINUTE,
119
+ hours: SECONDS_PER_HOUR,
120
+ days: SECONDS_PER_DAY,
121
+ weeks: SECONDS_PER_WEEK,
122
+ months: SECONDS_PER_MONTH,
123
+ years: SECONDS_PER_YEAR
124
+ }.freeze
125
+
126
+ PARTS = [:years, :months, :weeks, :days, :hours, :minutes, :seconds].freeze
127
+
10
128
  attr_accessor :value, :parts
11
129
 
130
+ autoload :ISO8601Parser, "active_support/duration/iso8601_parser"
131
+ autoload :ISO8601Serializer, "active_support/duration/iso8601_serializer"
132
+
133
+ class << self
134
+ # Creates a new Duration from string formatted according to ISO 8601 Duration.
135
+ #
136
+ # See {ISO 8601}[https://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
137
+ # This method allows negative parts to be present in pattern.
138
+ # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
139
+ def parse(iso8601duration)
140
+ parts = ISO8601Parser.new(iso8601duration).parse!
141
+ new(calculate_total_seconds(parts), parts)
142
+ end
143
+
144
+ def ===(other) #:nodoc:
145
+ other.is_a?(Duration)
146
+ rescue ::NoMethodError
147
+ false
148
+ end
149
+
150
+ def seconds(value) #:nodoc:
151
+ new(value, [[:seconds, value]])
152
+ end
153
+
154
+ def minutes(value) #:nodoc:
155
+ new(value * SECONDS_PER_MINUTE, [[:minutes, value]])
156
+ end
157
+
158
+ def hours(value) #:nodoc:
159
+ new(value * SECONDS_PER_HOUR, [[:hours, value]])
160
+ end
161
+
162
+ def days(value) #:nodoc:
163
+ new(value * SECONDS_PER_DAY, [[:days, value]])
164
+ end
165
+
166
+ def weeks(value) #:nodoc:
167
+ new(value * SECONDS_PER_WEEK, [[:weeks, value]])
168
+ end
169
+
170
+ def months(value) #:nodoc:
171
+ new(value * SECONDS_PER_MONTH, [[:months, value]])
172
+ end
173
+
174
+ def years(value) #:nodoc:
175
+ new(value * SECONDS_PER_YEAR, [[:years, value]])
176
+ end
177
+
178
+ # Creates a new Duration from a seconds value that is converted
179
+ # to the individual parts:
180
+ #
181
+ # ActiveSupport::Duration.build(31556952).parts # => {:years=>1}
182
+ # ActiveSupport::Duration.build(2716146).parts # => {:months=>1, :days=>1}
183
+ #
184
+ def build(value)
185
+ parts = {}
186
+ remainder = value.round(9)
187
+
188
+ PARTS.each do |part|
189
+ unless part == :seconds
190
+ part_in_seconds = PARTS_IN_SECONDS[part]
191
+ parts[part] = remainder.div(part_in_seconds)
192
+ remainder %= part_in_seconds
193
+ end
194
+ end unless value == 0
195
+
196
+ parts[:seconds] = remainder
197
+
198
+ new(value, parts)
199
+ end
200
+
201
+ private
202
+
203
+ def calculate_total_seconds(parts)
204
+ parts.inject(0) do |total, (part, value)|
205
+ total + value * PARTS_IN_SECONDS[part]
206
+ end
207
+ end
208
+ end
209
+
12
210
  def initialize(value, parts) #:nodoc:
13
- @value, @parts = value, parts
211
+ @value, @parts = value, parts.to_h
212
+ @parts.default = 0
213
+ @parts.reject! { |k, v| v.zero? } unless value == 0
214
+ end
215
+
216
+ def coerce(other) #:nodoc:
217
+ if Scalar === other
218
+ [other, self]
219
+ else
220
+ [Scalar.new(other), self]
221
+ end
222
+ end
223
+
224
+ # Compares one Duration with another or a Numeric to this Duration.
225
+ # Numeric values are treated as seconds.
226
+ def <=>(other)
227
+ if Duration === other
228
+ value <=> other.value
229
+ elsif Numeric === other
230
+ value <=> other
231
+ end
14
232
  end
15
233
 
16
234
  # Adds another Duration or a Numeric to this Duration. Numeric values
17
235
  # are treated as seconds.
18
236
  def +(other)
19
237
  if Duration === other
20
- Duration.new(value + other.value, @parts + other.parts)
238
+ parts = @parts.dup
239
+ other.parts.each do |(key, value)|
240
+ parts[key] += value
241
+ end
242
+ Duration.new(value + other.value, parts)
21
243
  else
22
- Duration.new(value + other, @parts + [[:seconds, other]])
244
+ seconds = @parts[:seconds] + other
245
+ Duration.new(value + other, @parts.merge(seconds: seconds))
23
246
  end
24
247
  end
25
248
 
@@ -29,8 +252,44 @@ module ActiveSupport
29
252
  self + (-other)
30
253
  end
31
254
 
255
+ # Multiplies this Duration by a Numeric and returns a new Duration.
256
+ def *(other)
257
+ if Scalar === other || Duration === other
258
+ Duration.new(value * other.value, parts.map { |type, number| [type, number * other.value] })
259
+ elsif Numeric === other
260
+ Duration.new(value * other, parts.map { |type, number| [type, number * other] })
261
+ else
262
+ raise_type_error(other)
263
+ end
264
+ end
265
+
266
+ # Divides this Duration by a Numeric and returns a new Duration.
267
+ def /(other)
268
+ if Scalar === other
269
+ Duration.new(value / other.value, parts.map { |type, number| [type, number / other.value] })
270
+ elsif Duration === other
271
+ value / other.value
272
+ elsif Numeric === other
273
+ Duration.new(value / other, parts.map { |type, number| [type, number / other] })
274
+ else
275
+ raise_type_error(other)
276
+ end
277
+ end
278
+
279
+ # Returns the modulo of this Duration by another Duration or Numeric.
280
+ # Numeric values are treated as seconds.
281
+ def %(other)
282
+ if Duration === other || Scalar === other
283
+ Duration.build(value % other.value)
284
+ elsif Numeric === other
285
+ Duration.build(value % other)
286
+ else
287
+ raise_type_error(other)
288
+ end
289
+ end
290
+
32
291
  def -@ #:nodoc:
33
- Duration.new(-value, parts.map { |type,number| [type, -number] })
292
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
34
293
  end
35
294
 
36
295
  def is_a?(klass) #:nodoc:
@@ -52,6 +311,10 @@ module ActiveSupport
52
311
  end
53
312
  end
54
313
 
314
+ # Returns the amount of seconds a duration covers as a string.
315
+ # For more information check to_i method.
316
+ #
317
+ # 1.day.to_s # => "86400"
55
318
  def to_s
56
319
  @value.to_s
57
320
  end
@@ -63,14 +326,14 @@ module ActiveSupport
63
326
  # 1.day.to_i # => 86400
64
327
  #
65
328
  # Note that this conversion makes some assumptions about the
66
- # duration of some periods, e.g. months are always 30 days
67
- # and years are 365.25 days:
329
+ # duration of some periods, e.g. months are always 1/12 of year
330
+ # and years are 365.2425 days:
68
331
  #
69
- # # equivalent to 30.days.to_i
70
- # 1.month.to_i # => 2592000
332
+ # # equivalent to (1.year / 12).to_i
333
+ # 1.month.to_i # => 2629746
71
334
  #
72
- # # equivalent to 365.25.days.to_i
73
- # 1.year.to_i # => 31557600
335
+ # # equivalent to 365.2425.days.to_i
336
+ # 1.year.to_i # => 31556952
74
337
  #
75
338
  # In such cases, Ruby's core
76
339
  # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
@@ -90,18 +353,13 @@ module ActiveSupport
90
353
  @value.hash
91
354
  end
92
355
 
93
- def self.===(other) #:nodoc:
94
- other.is_a?(Duration)
95
- rescue ::NoMethodError
96
- false
97
- end
98
-
99
356
  # Calculates a new Time or Date that is as far in the future
100
357
  # as this Duration represents.
101
358
  def since(time = ::Time.current)
102
359
  sum(1, time)
103
360
  end
104
361
  alias :from_now :since
362
+ alias :after :since
105
363
 
106
364
  # Calculates a new Time or Date that is as far in the past
107
365
  # as this Duration represents.
@@ -109,12 +367,15 @@ module ActiveSupport
109
367
  sum(-1, time)
110
368
  end
111
369
  alias :until :ago
370
+ alias :before :ago
112
371
 
113
372
  def inspect #:nodoc:
373
+ return "0 seconds" if parts.empty?
374
+
114
375
  parts.
115
- reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
116
- sort_by {|unit, _ | [:years, :months, :days, :minutes, :seconds].index(unit)}.
117
- map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
376
+ reduce(::Hash.new(0)) { |h, (l, r)| h[l] += r; h }.
377
+ sort_by { |unit, _ | PARTS.index(unit) }.
378
+ map { |unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}" }.
118
379
  to_sentence(locale: ::I18n.default_locale)
119
380
  end
120
381
 
@@ -122,39 +383,54 @@ module ActiveSupport
122
383
  to_i
123
384
  end
124
385
 
125
- def respond_to_missing?(method, include_private=false) #:nodoc
126
- @value.respond_to?(method, include_private)
386
+ def init_with(coder) #:nodoc:
387
+ initialize(coder["value"], coder["parts"])
127
388
  end
128
389
 
129
- delegate :<=>, to: :value
390
+ def encode_with(coder) #:nodoc:
391
+ coder.map = { "value" => @value, "parts" => @parts }
392
+ end
130
393
 
131
- protected
394
+ # Build ISO 8601 Duration string for this duration.
395
+ # The +precision+ parameter can be used to limit seconds' precision of duration.
396
+ def iso8601(precision: nil)
397
+ ISO8601Serializer.new(self, precision: precision).serialize
398
+ end
399
+
400
+ private
132
401
 
133
- def sum(sign, time = ::Time.current) #:nodoc:
134
- parts.inject(time) do |t,(type,number)|
135
- if t.acts_like?(:time) || t.acts_like?(:date)
402
+ def sum(sign, time = ::Time.current)
403
+ unless time.acts_like?(:time) || time.acts_like?(:date)
404
+ raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
405
+ end
406
+
407
+ if parts.empty?
408
+ time.since(sign * value)
409
+ else
410
+ parts.inject(time) do |t, (type, number)|
136
411
  if type == :seconds
137
412
  t.since(sign * number)
413
+ elsif type == :minutes
414
+ t.since(sign * number * 60)
415
+ elsif type == :hours
416
+ t.since(sign * number * 3600)
138
417
  else
139
418
  t.advance(type => sign * number)
140
419
  end
141
- else
142
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
143
420
  end
144
421
  end
145
422
  end
146
423
 
147
- private
424
+ def respond_to_missing?(method, _)
425
+ value.respond_to?(method)
426
+ end
148
427
 
149
- # We define it as a workaround to Ruby 2.0.0-p353 bug.
150
- # For more information, check rails/rails#13055.
151
- # Remove it when we drop support for 2.0.0-p353.
152
- def ===(other) #:nodoc:
153
- value === other
428
+ def method_missing(method, *args, &block)
429
+ value.public_send(method, *args, &block)
154
430
  end
155
431
 
156
- def method_missing(method, *args, &block) #:nodoc:
157
- value.send(method, *args, &block)
432
+ def raise_type_error(other)
433
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
158
434
  end
159
435
  end
160
436
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "yaml"
4
+ require "active_support/encrypted_file"
5
+ require "active_support/ordered_options"
6
+ require "active_support/core_ext/object/inclusion"
7
+ require "active_support/core_ext/module/delegation"
8
+
9
+ module ActiveSupport
10
+ class EncryptedConfiguration < EncryptedFile
11
+ delegate :[], :fetch, to: :config
12
+ delegate_missing_to :options
13
+
14
+ def initialize(config_path:, key_path:, env_key:, raise_if_missing_key:)
15
+ super content_path: config_path, key_path: key_path,
16
+ env_key: env_key, raise_if_missing_key: raise_if_missing_key
17
+ end
18
+
19
+ # Allow a config to be started without a file present
20
+ def read
21
+ super
22
+ rescue ActiveSupport::EncryptedFile::MissingContentError
23
+ ""
24
+ end
25
+
26
+ def write(contents)
27
+ deserialize(contents)
28
+
29
+ super
30
+ end
31
+
32
+ def config
33
+ @config ||= deserialize(read).deep_symbolize_keys
34
+ end
35
+
36
+ private
37
+ def options
38
+ @options ||= ActiveSupport::InheritableOptions.new(config)
39
+ end
40
+
41
+ def serialize(config)
42
+ config.present? ? YAML.dump(config) : ""
43
+ end
44
+
45
+ def deserialize(config)
46
+ YAML.load(config).presence || {}
47
+ end
48
+ end
49
+ end