activesupport 4.0.13 → 5.2.5

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 (264) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +412 -444
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +8 -4
  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 +14 -12
  8. data/lib/active_support/benchmarkable.rb +6 -14
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +67 -51
  11. data/lib/active_support/cache/mem_cache_store.rb +95 -97
  12. data/lib/active_support/cache/memory_store.rb +28 -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 +70 -56
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  17. data/lib/active_support/cache.rb +331 -206
  18. data/lib/active_support/callbacks.rb +697 -426
  19. data/lib/active_support/concern.rb +32 -10
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -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 +39 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +24 -35
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +23 -13
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -5
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -7
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -23
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  37. data/lib/active_support/core_ext/class.rb +4 -4
  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 +21 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +32 -22
  42. data/lib/active_support/core_ext/date/zones.rb +5 -34
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +199 -57
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  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 +78 -37
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +19 -13
  52. data/lib/active_support/core_ext/date_time.rb +7 -4
  53. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +113 -29
  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 +29 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +12 -3
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +50 -38
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +12 -6
  66. data/lib/active_support/core_ext/hash/transform_values.rb +32 -0
  67. data/lib/active_support/core_ext/hash.rb +11 -8
  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 -33
  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 +14 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +5 -74
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -4
  77. data/lib/active_support/core_ext/load_error.rb +5 -21
  78. data/lib/active_support/core_ext/marshal.rb +13 -10
  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 -8
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +170 -21
  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 +134 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +135 -45
  86. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -25
  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 -10
  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 +79 -74
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  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 +70 -19
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  102. data/lib/active_support/core_ext/object/duplicable.rb +100 -34
  103. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +227 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  108. data/lib/active_support/core_ext/object/try.rb +94 -24
  109. data/lib/active_support/core_ext/object/with_options.rb +45 -5
  110. data/lib/active_support/core_ext/object.rb +14 -12
  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 +41 -39
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +17 -13
  123. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  124. data/lib/active_support/core_ext/string/filters.rb +55 -6
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +66 -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 +114 -54
  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 -1
  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 +123 -110
  136. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  137. data/lib/active_support/core_ext/time/conversions.rb +23 -14
  138. data/lib/active_support/core_ext/time/zones.rb +42 -26
  139. data/lib/active_support/core_ext/time.rb +7 -5
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -2
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +3 -1
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +196 -166
  146. data/lib/active_support/deprecation/behaviors.rb +48 -15
  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 +14 -11
  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 +354 -34
  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 +128 -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 +17 -0
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +158 -35
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +38 -20
  169. data/lib/active_support/inflections.rb +19 -12
  170. data/lib/active_support/inflector/inflections.rb +79 -30
  171. data/lib/active_support/inflector/methods.rb +197 -129
  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 +21 -25
  175. data/lib/active_support/json/encoding.rb +84 -292
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +26 -28
  178. data/lib/active_support/lazy_load_hooks.rb +51 -21
  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 +54 -3
  183. data/lib/active_support/logger_silence.rb +12 -7
  184. data/lib/active_support/logger_thread_safe_level.rb +34 -0
  185. data/lib/active_support/message_encryptor.rb +173 -50
  186. data/lib/active_support/message_verifier.rb +159 -22
  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 +38 -26
  191. data/lib/active_support/multibyte/unicode.rb +138 -146
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +23 -16
  194. data/lib/active_support/notifications/instrumenter.rb +29 -8
  195. data/lib/active_support/notifications.rb +22 -13
  196. data/lib/active_support/number_helper/number_converter.rb +184 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +29 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +59 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +14 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +54 -0
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +125 -391
  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 +31 -5
  209. data/lib/active_support/per_thread_registry.rb +19 -11
  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 +31 -0
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +54 -17
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +42 -37
  220. data/lib/active_support/testing/assertions.rb +126 -39
  221. data/lib/active_support/testing/autorun.rb +5 -3
  222. data/lib/active_support/testing/constant_lookup.rb +3 -6
  223. data/lib/active_support/testing/declarative.rb +10 -22
  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 +55 -86
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +5 -3
  231. data/lib/active_support/testing/time_helpers.rb +200 -0
  232. data/lib/active_support/time.rb +13 -13
  233. data/lib/active_support/time_with_zone.rb +223 -73
  234. data/lib/active_support/values/time_zone.rb +261 -126
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +6 -7
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. data/lib/active_support/xml_mini.rb +69 -51
  244. data/lib/active_support.rb +29 -3
  245. metadata +84 -54
  246. data/lib/active_support/basic_object.rb +0 -11
  247. data/lib/active_support/buffered_logger.rb +0 -21
  248. data/lib/active_support/concurrency/latch.rb +0 -27
  249. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  250. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  251. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  252. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  253. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  254. data/lib/active_support/core_ext/logger.rb +0 -67
  255. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  256. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  257. data/lib/active_support/core_ext/proc.rb +0 -17
  258. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  259. data/lib/active_support/core_ext/struct.rb +0 -6
  260. data/lib/active_support/core_ext/thread.rb +0 -79
  261. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  262. data/lib/active_support/file_watcher.rb +0 -36
  263. data/lib/active_support/json/variable.rb +0 -18
  264. data/lib/active_support/testing/pending.rb +0 -14
@@ -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,26 +1,248 @@
1
- require 'active_support/proxy_object'
2
- require 'active_support/core_ext/array/conversions'
3
- 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"
4
8
 
5
9
  module ActiveSupport
6
10
  # Provides accurate date and time measurements using Date#advance and
7
11
  # Time#advance, respectively. It mainly supports the methods on Numeric.
8
12
  #
9
13
  # 1.month.ago # equivalent to Time.now.advance(months: -1)
10
- class Duration < ProxyObject
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
+
11
128
  attr_accessor :value, :parts
12
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
+
13
210
  def initialize(value, parts) #:nodoc:
14
- @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
15
232
  end
16
233
 
17
234
  # Adds another Duration or a Numeric to this Duration. Numeric values
18
235
  # are treated as seconds.
19
236
  def +(other)
20
237
  if Duration === other
21
- 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)
22
243
  else
23
- Duration.new(value + other, @parts + [[:seconds, other]])
244
+ seconds = @parts[:seconds] + other
245
+ Duration.new(value + other, @parts.merge(seconds: seconds))
24
246
  end
25
247
  end
26
248
 
@@ -30,8 +252,44 @@ module ActiveSupport
30
252
  self + (-other)
31
253
  end
32
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
+
33
291
  def -@ #:nodoc:
34
- Duration.new(-value, parts.map { |type,number| [type, -number] })
292
+ Duration.new(-value, parts.map { |type, number| [type, -number] })
35
293
  end
36
294
 
37
295
  def is_a?(klass) #:nodoc:
@@ -39,6 +297,10 @@ module ActiveSupport
39
297
  end
40
298
  alias :kind_of? :is_a?
41
299
 
300
+ def instance_of?(klass) # :nodoc:
301
+ Duration == klass || value.instance_of?(klass)
302
+ end
303
+
42
304
  # Returns +true+ if +other+ is also a Duration instance with the
43
305
  # same +value+, or if <tt>other == value</tt>.
44
306
  def ==(other)
@@ -49,10 +311,46 @@ module ActiveSupport
49
311
  end
50
312
  end
51
313
 
52
- def self.===(other) #:nodoc:
53
- other.is_a?(Duration)
54
- rescue ::NoMethodError
55
- false
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"
318
+ def to_s
319
+ @value.to_s
320
+ end
321
+
322
+ # Returns the number of seconds that this Duration represents.
323
+ #
324
+ # 1.minute.to_i # => 60
325
+ # 1.hour.to_i # => 3600
326
+ # 1.day.to_i # => 86400
327
+ #
328
+ # Note that this conversion makes some assumptions about the
329
+ # duration of some periods, e.g. months are always 1/12 of year
330
+ # and years are 365.2425 days:
331
+ #
332
+ # # equivalent to (1.year / 12).to_i
333
+ # 1.month.to_i # => 2629746
334
+ #
335
+ # # equivalent to 365.2425.days.to_i
336
+ # 1.year.to_i # => 31556952
337
+ #
338
+ # In such cases, Ruby's core
339
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
340
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
341
+ # date and time arithmetic.
342
+ def to_i
343
+ @value.to_i
344
+ end
345
+
346
+ # Returns +true+ if +other+ is also a Duration instance, which has the
347
+ # same parts as this one.
348
+ def eql?(other)
349
+ Duration === other && other.value.eql?(value)
350
+ end
351
+
352
+ def hash
353
+ @value.hash
56
354
  end
57
355
 
58
356
  # Calculates a new Time or Date that is as far in the future
@@ -61,6 +359,7 @@ module ActiveSupport
61
359
  sum(1, time)
62
360
  end
63
361
  alias :from_now :since
362
+ alias :after :since
64
363
 
65
364
  # Calculates a new Time or Date that is as far in the past
66
365
  # as this Duration represents.
@@ -68,49 +367,70 @@ module ActiveSupport
68
367
  sum(-1, time)
69
368
  end
70
369
  alias :until :ago
370
+ alias :before :ago
71
371
 
72
372
  def inspect #:nodoc:
73
- consolidated = parts.inject(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }
74
- parts = [:years, :months, :days, :minutes, :seconds].map do |length|
75
- n = consolidated[length]
76
- "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
77
- end.compact
78
- parts = ["0 seconds"] if parts.empty?
79
- parts.to_sentence(:locale => :en)
373
+ return "0 seconds" if parts.empty?
374
+
375
+ parts.
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}" }.
379
+ to_sentence(locale: ::I18n.default_locale)
80
380
  end
81
381
 
82
382
  def as_json(options = nil) #:nodoc:
83
383
  to_i
84
384
  end
85
385
 
86
- protected
386
+ def init_with(coder) #:nodoc:
387
+ initialize(coder["value"], coder["parts"])
388
+ end
389
+
390
+ def encode_with(coder) #:nodoc:
391
+ coder.map = { "value" => @value, "parts" => @parts }
392
+ end
393
+
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
401
+
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
87
406
 
88
- def sum(sign, time = ::Time.current) #:nodoc:
89
- parts.inject(time) do |t,(type,number)|
90
- if t.acts_like?(:time) || t.acts_like?(:date)
407
+ if parts.empty?
408
+ time.since(sign * value)
409
+ else
410
+ parts.inject(time) do |t, (type, number)|
91
411
  if type == :seconds
92
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)
93
417
  else
94
418
  t.advance(type => sign * number)
95
419
  end
96
- else
97
- raise ::ArgumentError, "expected a time or date, got #{time.inspect}"
98
420
  end
99
421
  end
100
422
  end
101
423
 
102
- private
424
+ def respond_to_missing?(method, _)
425
+ value.respond_to?(method)
426
+ end
103
427
 
104
- # We define it as a workaround to Ruby 2.0.0-p353 bug.
105
- # For more information, check rails/rails#13055.
106
- # It should be dropped once a new Ruby patch-level
107
- # release after 2.0.0-p353 happens.
108
- def ===(other) #:nodoc:
109
- value === other
428
+ def method_missing(method, *args, &block)
429
+ value.public_send(method, *args, &block)
110
430
  end
111
431
 
112
- def method_missing(method, *args, &block) #:nodoc:
113
- value.send(method, *args, &block)
432
+ def raise_type_error(other)
433
+ raise TypeError, "no implicit conversion of #{other.class} into #{self.class}"
114
434
  end
115
435
  end
116
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