activesupport 3.1.0 → 5.0.0

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 (276) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +798 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +13 -7
  5. data/lib/active_support/array_inquirer.rb +44 -0
  6. data/lib/active_support/backtrace_cleaner.rb +38 -34
  7. data/lib/active_support/benchmarkable.rb +17 -28
  8. data/lib/active_support/cache/file_store.rb +85 -70
  9. data/lib/active_support/cache/mem_cache_store.rb +75 -66
  10. data/lib/active_support/cache/memory_store.rb +31 -23
  11. data/lib/active_support/cache/null_store.rb +41 -0
  12. data/lib/active_support/cache/strategy/local_cache.rb +73 -70
  13. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  14. data/lib/active_support/cache.rb +360 -294
  15. data/lib/active_support/callbacks.rb +563 -393
  16. data/lib/active_support/concern.rb +42 -34
  17. data/lib/active_support/concurrency/latch.rb +19 -0
  18. data/lib/active_support/concurrency/share_lock.rb +186 -0
  19. data/lib/active_support/configurable.rb +70 -12
  20. data/lib/active_support/core_ext/array/access.rb +53 -9
  21. data/lib/active_support/core_ext/array/conversions.rb +109 -62
  22. data/lib/active_support/core_ext/array/extract_options.rb +2 -2
  23. data/lib/active_support/core_ext/array/grouping.rb +39 -32
  24. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  25. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  26. data/lib/active_support/core_ext/array/wrap.rb +16 -18
  27. data/lib/active_support/core_ext/array.rb +2 -2
  28. data/lib/active_support/core_ext/benchmark.rb +7 -0
  29. data/lib/active_support/core_ext/big_decimal/conversions.rb +8 -36
  30. data/lib/active_support/core_ext/class/attribute.rb +47 -34
  31. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -79
  32. data/lib/active_support/core_ext/class/subclasses.rb +12 -7
  33. data/lib/active_support/core_ext/class.rb +0 -3
  34. data/lib/active_support/core_ext/date/blank.rb +12 -0
  35. data/lib/active_support/core_ext/date/calculations.rb +57 -167
  36. data/lib/active_support/core_ext/date/conversions.rb +31 -42
  37. data/lib/active_support/core_ext/date/zones.rb +2 -10
  38. data/lib/active_support/core_ext/date.rb +5 -0
  39. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  40. data/lib/active_support/core_ext/date_and_time/compatibility.rb +18 -0
  41. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  42. data/lib/active_support/core_ext/date_time/acts_like.rb +1 -0
  43. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  44. data/lib/active_support/core_ext/date_time/calculations.rb +132 -65
  45. data/lib/active_support/core_ext/date_time/compatibility.rb +5 -0
  46. data/lib/active_support/core_ext/date_time/conversions.rb +36 -34
  47. data/lib/active_support/core_ext/date_time.rb +5 -0
  48. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  49. data/lib/active_support/core_ext/enumerable.rb +81 -74
  50. data/lib/active_support/core_ext/file/atomic.rb +53 -26
  51. data/lib/active_support/core_ext/file.rb +0 -1
  52. data/lib/active_support/core_ext/hash/compact.rb +20 -0
  53. data/lib/active_support/core_ext/hash/conversions.rb +175 -70
  54. data/lib/active_support/core_ext/hash/deep_merge.rb +30 -8
  55. data/lib/active_support/core_ext/hash/except.rb +11 -12
  56. data/lib/active_support/core_ext/hash/indifferent_access.rb +7 -8
  57. data/lib/active_support/core_ext/hash/keys.rb +147 -24
  58. data/lib/active_support/core_ext/hash/reverse_merge.rb +2 -3
  59. data/lib/active_support/core_ext/hash/slice.rb +22 -14
  60. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  61. data/lib/active_support/core_ext/hash.rb +2 -2
  62. data/lib/active_support/core_ext/integer/inflections.rb +13 -1
  63. data/lib/active_support/core_ext/integer/multiple.rb +4 -0
  64. data/lib/active_support/core_ext/integer/time.rb +12 -22
  65. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -2
  66. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  67. data/lib/active_support/core_ext/kernel/debugger.rb +2 -15
  68. data/lib/active_support/core_ext/kernel/reporting.rb +12 -62
  69. data/lib/active_support/core_ext/kernel/singleton_class.rb +0 -7
  70. data/lib/active_support/core_ext/kernel.rb +2 -3
  71. data/lib/active_support/core_ext/load_error.rb +14 -7
  72. data/lib/active_support/core_ext/marshal.rb +22 -0
  73. data/lib/active_support/core_ext/module/aliasing.rb +16 -12
  74. data/lib/active_support/core_ext/module/anonymous.rb +12 -8
  75. data/lib/active_support/core_ext/module/attr_internal.rb +2 -5
  76. data/lib/active_support/core_ext/module/attribute_accessors.rb +165 -13
  77. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  78. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  79. data/lib/active_support/core_ext/module/delegation.rb +141 -68
  80. data/lib/active_support/core_ext/module/deprecation.rb +17 -3
  81. data/lib/active_support/core_ext/module/introspection.rb +9 -31
  82. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  83. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  84. data/lib/active_support/core_ext/module/reachable.rb +1 -3
  85. data/lib/active_support/core_ext/module/remove_method.rb +24 -5
  86. data/lib/active_support/core_ext/module.rb +3 -3
  87. data/lib/active_support/core_ext/name_error.rb +15 -2
  88. data/lib/active_support/core_ext/numeric/bytes.rb +20 -0
  89. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  90. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  91. data/lib/active_support/core_ext/numeric/time.rb +31 -36
  92. data/lib/active_support/core_ext/numeric.rb +2 -0
  93. data/lib/active_support/core_ext/object/acts_like.rb +4 -4
  94. data/lib/active_support/core_ext/object/blank.rb +52 -18
  95. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  96. data/lib/active_support/core_ext/object/duplicable.rb +12 -20
  97. data/lib/active_support/core_ext/object/inclusion.rb +13 -1
  98. data/lib/active_support/core_ext/object/instance_variables.rb +7 -12
  99. data/lib/active_support/core_ext/object/json.rb +205 -0
  100. data/lib/active_support/core_ext/object/to_param.rb +1 -55
  101. data/lib/active_support/core_ext/object/to_query.rb +66 -9
  102. data/lib/active_support/core_ext/object/try.rb +124 -33
  103. data/lib/active_support/core_ext/object/with_options.rb +37 -11
  104. data/lib/active_support/core_ext/object.rb +2 -1
  105. data/lib/active_support/core_ext/range/conversions.rb +17 -7
  106. data/lib/active_support/core_ext/range/each.rb +21 -0
  107. data/lib/active_support/core_ext/range/include_range.rb +20 -18
  108. data/lib/active_support/core_ext/range/overlaps.rb +1 -1
  109. data/lib/active_support/core_ext/range.rb +1 -2
  110. data/lib/active_support/core_ext/securerandom.rb +23 -0
  111. data/lib/active_support/core_ext/string/access.rb +95 -90
  112. data/lib/active_support/core_ext/string/behavior.rb +1 -1
  113. data/lib/active_support/core_ext/string/conversions.rb +41 -38
  114. data/lib/active_support/core_ext/string/exclude.rb +6 -1
  115. data/lib/active_support/core_ext/string/filters.rb +70 -17
  116. data/lib/active_support/core_ext/string/indent.rb +43 -0
  117. data/lib/active_support/core_ext/string/inflections.rb +139 -59
  118. data/lib/active_support/core_ext/string/inquiry.rb +2 -2
  119. data/lib/active_support/core_ext/string/multibyte.rb +46 -65
  120. data/lib/active_support/core_ext/string/output_safety.rb +153 -56
  121. data/lib/active_support/core_ext/string/strip.rb +3 -6
  122. data/lib/active_support/core_ext/string/zones.rb +14 -0
  123. data/lib/active_support/core_ext/string.rb +2 -3
  124. data/lib/active_support/core_ext/struct.rb +3 -0
  125. data/lib/active_support/core_ext/time/calculations.rb +173 -173
  126. data/lib/active_support/core_ext/time/compatibility.rb +5 -0
  127. data/lib/active_support/core_ext/time/conversions.rb +33 -29
  128. data/lib/active_support/core_ext/time/marshal.rb +2 -56
  129. data/lib/active_support/core_ext/time/zones.rb +57 -32
  130. data/lib/active_support/core_ext/time.rb +5 -0
  131. data/lib/active_support/core_ext/uri.rb +13 -19
  132. data/lib/active_support/core_ext.rb +3 -2
  133. data/lib/active_support/dependencies/autoload.rb +47 -20
  134. data/lib/active_support/dependencies/interlock.rb +51 -0
  135. data/lib/active_support/dependencies.rb +315 -265
  136. data/lib/active_support/deprecation/behaviors.rb +71 -30
  137. data/lib/active_support/deprecation/instance_delegator.rb +24 -0
  138. data/lib/active_support/deprecation/method_wrappers.rb +59 -18
  139. data/lib/active_support/deprecation/proxy_wrappers.rb +82 -14
  140. data/lib/active_support/deprecation/reporting.rb +61 -14
  141. data/lib/active_support/deprecation.rb +38 -13
  142. data/lib/active_support/descendants_tracker.rb +34 -19
  143. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  144. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  145. data/lib/active_support/duration.rb +85 -14
  146. data/lib/active_support/evented_file_update_checker.rb +194 -0
  147. data/lib/active_support/execution_wrapper.rb +117 -0
  148. data/lib/active_support/executor.rb +6 -0
  149. data/lib/active_support/file_update_checker.rb +138 -17
  150. data/lib/active_support/gem_version.rb +15 -0
  151. data/lib/active_support/gzip.rb +11 -5
  152. data/lib/active_support/hash_with_indifferent_access.rb +199 -49
  153. data/lib/active_support/i18n.rb +6 -2
  154. data/lib/active_support/i18n_railtie.rb +40 -21
  155. data/lib/active_support/inflections.rb +22 -13
  156. data/lib/active_support/inflector/inflections.rb +175 -144
  157. data/lib/active_support/inflector/methods.rb +328 -91
  158. data/lib/active_support/inflector/transliterate.rb +51 -37
  159. data/lib/active_support/json/decoding.rb +31 -22
  160. data/lib/active_support/json/encoding.rb +88 -248
  161. data/lib/active_support/key_generator.rb +71 -0
  162. data/lib/active_support/lazy_load_hooks.rb +27 -25
  163. data/lib/active_support/locale/en.yml +102 -3
  164. data/lib/active_support/log_subscriber/test_helper.rb +24 -21
  165. data/lib/active_support/log_subscriber.rb +36 -49
  166. data/lib/active_support/logger.rb +106 -0
  167. data/lib/active_support/logger_silence.rb +28 -0
  168. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  169. data/lib/active_support/message_encryptor.rb +72 -36
  170. data/lib/active_support/message_verifier.rb +96 -24
  171. data/lib/active_support/multibyte/chars.rb +88 -333
  172. data/lib/active_support/multibyte/unicode.rb +156 -136
  173. data/lib/active_support/multibyte.rb +5 -28
  174. data/lib/active_support/notifications/fanout.rb +115 -19
  175. data/lib/active_support/notifications/instrumenter.rb +52 -15
  176. data/lib/active_support/notifications.rb +168 -33
  177. data/lib/active_support/number_helper/number_converter.rb +182 -0
  178. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  179. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  180. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  181. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  182. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  183. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  184. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  185. data/lib/active_support/number_helper.rb +368 -0
  186. data/lib/active_support/option_merger.rb +1 -1
  187. data/lib/active_support/ordered_hash.rb +18 -183
  188. data/lib/active_support/ordered_options.rb +44 -24
  189. data/lib/active_support/per_thread_registry.rb +58 -0
  190. data/lib/active_support/proxy_object.rb +13 -0
  191. data/lib/active_support/rails.rb +27 -0
  192. data/lib/active_support/railtie.rb +25 -34
  193. data/lib/active_support/reloader.rb +129 -0
  194. data/lib/active_support/rescuable.rb +98 -48
  195. data/lib/active_support/security_utils.rb +27 -0
  196. data/lib/active_support/string_inquirer.rb +14 -9
  197. data/lib/active_support/subscriber.rb +120 -0
  198. data/lib/active_support/tagged_logging.rb +78 -0
  199. data/lib/active_support/test_case.rb +69 -17
  200. data/lib/active_support/testing/assertions.rb +43 -41
  201. data/lib/active_support/testing/autorun.rb +12 -0
  202. data/lib/active_support/testing/constant_lookup.rb +50 -0
  203. data/lib/active_support/testing/declarative.rb +7 -21
  204. data/lib/active_support/testing/deprecation.rb +14 -33
  205. data/lib/active_support/testing/file_fixtures.rb +34 -0
  206. data/lib/active_support/testing/isolation.rb +53 -95
  207. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  208. data/lib/active_support/testing/setup_and_teardown.rb +21 -82
  209. data/lib/active_support/testing/stream.rb +42 -0
  210. data/lib/active_support/testing/tagged_logging.rb +25 -0
  211. data/lib/active_support/testing/time_helpers.rb +134 -0
  212. data/lib/active_support/time.rb +6 -23
  213. data/lib/active_support/time_with_zone.rb +239 -92
  214. data/lib/active_support/values/time_zone.rb +236 -160
  215. data/lib/active_support/values/unicode_tables.dat +0 -0
  216. data/lib/active_support/version.rb +5 -7
  217. data/lib/active_support/xml_mini/jdom.rb +19 -13
  218. data/lib/active_support/xml_mini/libxml.rb +3 -4
  219. data/lib/active_support/xml_mini/libxmlsax.rb +2 -3
  220. data/lib/active_support/xml_mini/nokogiri.rb +3 -4
  221. data/lib/active_support/xml_mini/nokogirisax.rb +2 -3
  222. data/lib/active_support/xml_mini/rexml.rb +8 -10
  223. data/lib/active_support/xml_mini.rb +66 -34
  224. data/lib/active_support.rb +40 -23
  225. metadata +185 -134
  226. data/CHANGELOG +0 -1534
  227. data/lib/active_support/base64.rb +0 -42
  228. data/lib/active_support/basic_object.rb +0 -21
  229. data/lib/active_support/buffered_logger.rb +0 -137
  230. data/lib/active_support/cache/compressed_mem_cache_store.rb +0 -13
  231. data/lib/active_support/cache/synchronized_memory_store.rb +0 -11
  232. data/lib/active_support/core_ext/array/random_access.rb +0 -30
  233. data/lib/active_support/core_ext/array/uniq_by.rb +0 -16
  234. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -44
  235. data/lib/active_support/core_ext/class/inheritable_attributes.rb +0 -178
  236. data/lib/active_support/core_ext/date/freeze.rb +0 -31
  237. data/lib/active_support/core_ext/date_time/zones.rb +0 -21
  238. data/lib/active_support/core_ext/exception.rb +0 -3
  239. data/lib/active_support/core_ext/file/path.rb +0 -5
  240. data/lib/active_support/core_ext/float/rounding.rb +0 -19
  241. data/lib/active_support/core_ext/float.rb +0 -1
  242. data/lib/active_support/core_ext/hash/deep_dup.rb +0 -11
  243. data/lib/active_support/core_ext/hash/diff.rb +0 -13
  244. data/lib/active_support/core_ext/kernel/requires.rb +0 -28
  245. data/lib/active_support/core_ext/logger.rb +0 -81
  246. data/lib/active_support/core_ext/module/attr_accessor_with_default.rb +0 -31
  247. data/lib/active_support/core_ext/module/method_names.rb +0 -14
  248. data/lib/active_support/core_ext/module/synchronization.rb +0 -43
  249. data/lib/active_support/core_ext/object/to_json.rb +0 -19
  250. data/lib/active_support/core_ext/proc.rb +0 -14
  251. data/lib/active_support/core_ext/process/daemon.rb +0 -23
  252. data/lib/active_support/core_ext/process.rb +0 -1
  253. data/lib/active_support/core_ext/range/blockless_step.rb +0 -29
  254. data/lib/active_support/core_ext/range/cover.rb +0 -3
  255. data/lib/active_support/core_ext/rexml.rb +0 -46
  256. data/lib/active_support/core_ext/string/encoding.rb +0 -11
  257. data/lib/active_support/core_ext/string/interpolation.rb +0 -2
  258. data/lib/active_support/core_ext/string/xchar.rb +0 -18
  259. data/lib/active_support/core_ext/time/publicize_conversion_methods.rb +0 -10
  260. data/lib/active_support/file_watcher.rb +0 -36
  261. data/lib/active_support/json/variable.rb +0 -9
  262. data/lib/active_support/memoizable.rb +0 -105
  263. data/lib/active_support/multibyte/exceptions.rb +0 -8
  264. data/lib/active_support/multibyte/utils.rb +0 -60
  265. data/lib/active_support/ruby/shim.rb +0 -22
  266. data/lib/active_support/secure_random.rb +0 -6
  267. data/lib/active_support/testing/mochaing.rb +0 -7
  268. data/lib/active_support/testing/pending.rb +0 -52
  269. data/lib/active_support/testing/performance/jruby.rb +0 -115
  270. data/lib/active_support/testing/performance/rubinius.rb +0 -113
  271. data/lib/active_support/testing/performance/ruby/mri.rb +0 -57
  272. data/lib/active_support/testing/performance/ruby/yarv.rb +0 -57
  273. data/lib/active_support/testing/performance/ruby.rb +0 -152
  274. data/lib/active_support/testing/performance.rb +0 -317
  275. data/lib/active_support/time/autoload.rb +0 -5
  276. data/lib/active_support/whiny_nil.rb +0 -60
@@ -0,0 +1,122 @@
1
+ require 'strscan'
2
+
3
+ module ActiveSupport
4
+ class Duration
5
+ # Parses a string formatted according to ISO 8601 Duration into the hash.
6
+ #
7
+ # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
8
+ #
9
+ # This parser allows negative parts to be present in pattern.
10
+ class ISO8601Parser # :nodoc:
11
+ class ParsingError < ::ArgumentError; end
12
+
13
+ PERIOD_OR_COMMA = /\.|,/
14
+ PERIOD = '.'.freeze
15
+ COMMA = ','.freeze
16
+
17
+ SIGN_MARKER = /\A\-|\+|/
18
+ DATE_MARKER = /P/
19
+ TIME_MARKER = /T/
20
+ DATE_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(Y|M|D|W)/
21
+ TIME_COMPONENT = /(\-?\d+(?:[.,]\d+)?)(H|M|S)/
22
+
23
+ DATE_TO_PART = { 'Y' => :years, 'M' => :months, 'W' => :weeks, 'D' => :days }
24
+ TIME_TO_PART = { 'H' => :hours, 'M' => :minutes, 'S' => :seconds }
25
+
26
+ DATE_COMPONENTS = [:years, :months, :days]
27
+ TIME_COMPONENTS = [:hours, :minutes, :seconds]
28
+
29
+ attr_reader :parts, :scanner
30
+ attr_accessor :mode, :sign
31
+
32
+ def initialize(string)
33
+ @scanner = StringScanner.new(string)
34
+ @parts = {}
35
+ @mode = :start
36
+ @sign = 1
37
+ end
38
+
39
+ def parse!
40
+ while !finished?
41
+ case mode
42
+ when :start
43
+ if scan(SIGN_MARKER)
44
+ self.sign = (scanner.matched == '-') ? -1 : 1
45
+ self.mode = :sign
46
+ else
47
+ raise_parsing_error
48
+ end
49
+
50
+ when :sign
51
+ if scan(DATE_MARKER)
52
+ self.mode = :date
53
+ else
54
+ raise_parsing_error
55
+ end
56
+
57
+ when :date
58
+ if scan(TIME_MARKER)
59
+ self.mode = :time
60
+ elsif scan(DATE_COMPONENT)
61
+ parts[DATE_TO_PART[scanner[2]]] = number * sign
62
+ else
63
+ raise_parsing_error
64
+ end
65
+
66
+ when :time
67
+ if scan(TIME_COMPONENT)
68
+ parts[TIME_TO_PART[scanner[2]]] = number * sign
69
+ else
70
+ raise_parsing_error
71
+ end
72
+
73
+ end
74
+ end
75
+
76
+ validate!
77
+ parts
78
+ end
79
+
80
+ private
81
+
82
+ def finished?
83
+ scanner.eos?
84
+ end
85
+
86
+ # Parses number which can be a float with either comma or period.
87
+ def number
88
+ scanner[1] =~ PERIOD_OR_COMMA ? scanner[1].tr(COMMA, PERIOD).to_f : scanner[1].to_i
89
+ end
90
+
91
+ def scan(pattern)
92
+ scanner.scan(pattern)
93
+ end
94
+
95
+ def raise_parsing_error(reason = nil)
96
+ raise ParsingError, "Invalid ISO 8601 duration: #{scanner.string.inspect} #{reason}".strip
97
+ end
98
+
99
+ # Checks for various semantic errors as stated in ISO 8601 standard.
100
+ def validate!
101
+ raise_parsing_error('is empty duration') if parts.empty?
102
+
103
+ # Mixing any of Y, M, D with W is invalid.
104
+ if parts.key?(:weeks) && (parts.keys & DATE_COMPONENTS).any?
105
+ raise_parsing_error('mixing weeks with other date parts not allowed')
106
+ end
107
+
108
+ # Specifying an empty T part is invalid.
109
+ if mode == :time && (parts.keys & TIME_COMPONENTS).empty?
110
+ raise_parsing_error('time part marker is present but time part is empty')
111
+ end
112
+
113
+ fractions = parts.values.reject(&:zero?).select { |a| (a % 1) != 0 }
114
+ unless fractions.empty? || (fractions.size == 1 && fractions.last == @parts.values.reject(&:zero?).last)
115
+ raise_parsing_error '(only last part can be fractional)'
116
+ end
117
+
118
+ return true
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,51 @@
1
+ require 'active_support/core_ext/object/blank'
2
+ require 'active_support/core_ext/hash/transform_values'
3
+
4
+ module ActiveSupport
5
+ class Duration
6
+ # Serializes duration to string according to ISO 8601 Duration format.
7
+ class ISO8601Serializer
8
+ def initialize(duration, precision: nil)
9
+ @duration = duration
10
+ @precision = precision
11
+ end
12
+
13
+ # Builds and returns output string.
14
+ def serialize
15
+ output = 'P'
16
+ parts, sign = normalize
17
+ output << "#{parts[:years]}Y" if parts.key?(:years)
18
+ output << "#{parts[:months]}M" if parts.key?(:months)
19
+ output << "#{parts[:weeks]}W" if parts.key?(:weeks)
20
+ output << "#{parts[:days]}D" if parts.key?(:days)
21
+ time = ''
22
+ time << "#{parts[:hours]}H" if parts.key?(:hours)
23
+ time << "#{parts[:minutes]}M" if parts.key?(:minutes)
24
+ if parts.key?(:seconds)
25
+ time << "#{sprintf(@precision ? "%0.0#{@precision}f" : '%g', parts[:seconds])}S"
26
+ end
27
+ output << "T#{time}" if time.present?
28
+ "#{sign}#{output}"
29
+ end
30
+
31
+ private
32
+
33
+ # Return pair of duration's parts and whole duration sign.
34
+ # Parts are summarized (as they can become repetitive due to addition, etc).
35
+ # Zero parts are removed as not significant.
36
+ # If all parts are negative it will negate all of them and return minus as a sign.
37
+ def normalize
38
+ parts = @duration.parts.each_with_object(Hash.new(0)) do |(k,v),p|
39
+ p[k] += v unless v.zero?
40
+ end
41
+ # If all parts are negative - let's make a negative duration
42
+ sign = ''
43
+ if parts.values.all? { |v| v < 0 }
44
+ sign = '-'
45
+ parts.transform_values!(&:-@)
46
+ end
47
+ [parts, sign]
48
+ end
49
+ end
50
+ end
51
+ end
@@ -1,16 +1,16 @@
1
- require 'active_support/basic_object'
2
1
  require 'active_support/core_ext/array/conversions'
3
2
  require 'active_support/core_ext/object/acts_like'
4
3
 
5
4
  module ActiveSupport
6
5
  # Provides accurate date and time measurements using Date#advance and
7
6
  # Time#advance, respectively. It mainly supports the methods on Numeric.
8
- # Example:
9
7
  #
10
- # 1.month.ago # equivalent to Time.now.advance(:months => -1)
11
- class Duration < BasicObject
8
+ # 1.month.ago # equivalent to Time.now.advance(months: -1)
9
+ class Duration
12
10
  attr_accessor :value, :parts
13
- delegate :duplicable?, :to => :value # required when using ActiveSupport's BasicObject on 1.8
11
+
12
+ autoload :ISO8601Parser, 'active_support/duration/iso8601_parser'
13
+ autoload :ISO8601Serializer, 'active_support/duration/iso8601_serializer'
14
14
 
15
15
  def initialize(value, parts) #:nodoc:
16
16
  @value, @parts = value, parts
@@ -41,8 +41,12 @@ module ActiveSupport
41
41
  end
42
42
  alias :kind_of? :is_a?
43
43
 
44
- # Returns true if <tt>other</tt> is also a Duration instance with the
45
- # same <tt>value</tt>, or if <tt>other == value</tt>.
44
+ def instance_of?(klass) # :nodoc:
45
+ Duration == klass || value.instance_of?(klass)
46
+ end
47
+
48
+ # Returns +true+ if +other+ is also a Duration instance with the
49
+ # same +value+, or if <tt>other == value</tt>.
46
50
  def ==(other)
47
51
  if Duration === other
48
52
  other.value == value
@@ -51,6 +55,48 @@ module ActiveSupport
51
55
  end
52
56
  end
53
57
 
58
+ # Returns the amount of seconds a duration covers as a string.
59
+ # For more information check to_i method.
60
+ #
61
+ # 1.day.to_s # => "86400"
62
+ def to_s
63
+ @value.to_s
64
+ end
65
+
66
+ # Returns the number of seconds that this Duration represents.
67
+ #
68
+ # 1.minute.to_i # => 60
69
+ # 1.hour.to_i # => 3600
70
+ # 1.day.to_i # => 86400
71
+ #
72
+ # Note that this conversion makes some assumptions about the
73
+ # duration of some periods, e.g. months are always 30 days
74
+ # and years are 365.25 days:
75
+ #
76
+ # # equivalent to 30.days.to_i
77
+ # 1.month.to_i # => 2592000
78
+ #
79
+ # # equivalent to 365.25.days.to_i
80
+ # 1.year.to_i # => 31557600
81
+ #
82
+ # In such cases, Ruby's core
83
+ # Date[http://ruby-doc.org/stdlib/libdoc/date/rdoc/Date.html] and
84
+ # Time[http://ruby-doc.org/stdlib/libdoc/time/rdoc/Time.html] should be used for precision
85
+ # date and time arithmetic.
86
+ def to_i
87
+ @value.to_i
88
+ end
89
+
90
+ # Returns +true+ if +other+ is also a Duration instance, which has the
91
+ # same parts as this one.
92
+ def eql?(other)
93
+ Duration === other && other.value.eql?(value)
94
+ end
95
+
96
+ def hash
97
+ @value.hash
98
+ end
99
+
54
100
  def self.===(other) #:nodoc:
55
101
  other.is_a?(Duration)
56
102
  rescue ::NoMethodError
@@ -72,19 +118,40 @@ module ActiveSupport
72
118
  alias :until :ago
73
119
 
74
120
  def inspect #:nodoc:
75
- consolidated = parts.inject(::Hash.new(0)) { |h,part| h[part.first] += part.last; h }
76
- parts = [:years, :months, :days, :minutes, :seconds].map do |length|
77
- n = consolidated[length]
78
- "#{n} #{n == 1 ? length.to_s.singularize : length.to_s}" if n.nonzero?
79
- end.compact
80
- parts = ["0 seconds"] if parts.empty?
81
- parts.to_sentence(:locale => :en)
121
+ parts.
122
+ reduce(::Hash.new(0)) { |h,(l,r)| h[l] += r; h }.
123
+ sort_by {|unit, _ | [:years, :months, :weeks, :days, :hours, :minutes, :seconds].index(unit)}.
124
+ map {|unit, val| "#{val} #{val == 1 ? unit.to_s.chop : unit.to_s}"}.
125
+ to_sentence(locale: ::I18n.default_locale)
82
126
  end
83
127
 
84
128
  def as_json(options = nil) #:nodoc:
85
129
  to_i
86
130
  end
87
131
 
132
+ def respond_to_missing?(method, include_private=false) #:nodoc:
133
+ @value.respond_to?(method, include_private)
134
+ end
135
+
136
+ # Creates a new Duration from string formatted according to ISO 8601 Duration.
137
+ #
138
+ # See {ISO 8601}[http://en.wikipedia.org/wiki/ISO_8601#Durations] for more information.
139
+ # This method allows negative parts to be present in pattern.
140
+ # If invalid string is provided, it will raise +ActiveSupport::Duration::ISO8601Parser::ParsingError+.
141
+ def self.parse(iso8601duration)
142
+ parts = ISO8601Parser.new(iso8601duration).parse!
143
+ time = ::Time.current
144
+ new(time.advance(parts) - time, parts)
145
+ end
146
+
147
+ # Build ISO 8601 Duration string for this duration.
148
+ # The +precision+ parameter can be used to limit seconds' precision of duration.
149
+ def iso8601(precision: nil)
150
+ ISO8601Serializer.new(self, precision: precision).serialize
151
+ end
152
+
153
+ delegate :<=>, to: :value
154
+
88
155
  protected
89
156
 
90
157
  def sum(sign, time = ::Time.current) #:nodoc:
@@ -92,6 +159,10 @@ module ActiveSupport
92
159
  if t.acts_like?(:time) || t.acts_like?(:date)
93
160
  if type == :seconds
94
161
  t.since(sign * number)
162
+ elsif type == :minutes
163
+ t.since(sign * number * 60)
164
+ elsif type == :hours
165
+ t.since(sign * number * 3600)
95
166
  else
96
167
  t.advance(type => sign * number)
97
168
  end
@@ -0,0 +1,194 @@
1
+ require 'set'
2
+ require 'pathname'
3
+ require 'concurrent/atomic/atomic_boolean'
4
+
5
+ module ActiveSupport
6
+ # Allows you to "listen" to changes in a file system.
7
+ # The evented file updater does not hit disk when checking for updates
8
+ # instead it uses platform specific file system events to trigger a change
9
+ # in state.
10
+ #
11
+ # The file checker takes an array of files to watch or a hash specifying directories
12
+ # and file extensions to watch. It also takes a block that is called when
13
+ # EventedFileUpdateChecker#execute is run or when EventedFileUpdateChecker#execute_if_updated
14
+ # is run and there have been changes to the file system.
15
+ #
16
+ # Note: Forking will cause the first call to `updated?` to return `true`.
17
+ #
18
+ # Example:
19
+ #
20
+ # checker = EventedFileUpdateChecker.new(["/tmp/foo"], -> { puts "changed" })
21
+ # checker.updated?
22
+ # # => false
23
+ # checker.execute_if_updated
24
+ # # => nil
25
+ #
26
+ # FileUtils.touch("/tmp/foo")
27
+ #
28
+ # checker.updated?
29
+ # # => true
30
+ # checker.execute_if_updated
31
+ # # => "changed"
32
+ #
33
+ class EventedFileUpdateChecker #:nodoc: all
34
+ def initialize(files, dirs = {}, &block)
35
+ @ph = PathHelper.new
36
+ @files = files.map { |f| @ph.xpath(f) }.to_set
37
+
38
+ @dirs = {}
39
+ dirs.each do |dir, exts|
40
+ @dirs[@ph.xpath(dir)] = Array(exts).map { |ext| @ph.normalize_extension(ext) }
41
+ end
42
+
43
+ @block = block
44
+ @updated = Concurrent::AtomicBoolean.new(false)
45
+ @lcsp = @ph.longest_common_subpath(@dirs.keys)
46
+ @pid = Process.pid
47
+ @boot_mutex = Mutex.new
48
+
49
+ if (@dtw = directories_to_watch).any?
50
+ # Loading listen triggers warnings. These are originated by a legit
51
+ # usage of attr_* macros for private attributes, but adds a lot of noise
52
+ # to our test suite. Thus, we lazy load it and disable warnings locally.
53
+ silence_warnings do
54
+ begin
55
+ require 'listen'
56
+ rescue LoadError => e
57
+ raise LoadError, "Could not load the 'listen' gem. Add `gem 'listen'` to the development group of your Gemfile", e.backtrace
58
+ end
59
+ end
60
+ end
61
+ boot!
62
+ end
63
+
64
+ def updated?
65
+ @boot_mutex.synchronize do
66
+ if @pid != Process.pid
67
+ boot!
68
+ @pid = Process.pid
69
+ @updated.make_true
70
+ end
71
+ end
72
+ @updated.true?
73
+ end
74
+
75
+ def execute
76
+ @updated.make_false
77
+ @block.call
78
+ end
79
+
80
+ def execute_if_updated
81
+ if updated?
82
+ yield if block_given?
83
+ execute
84
+ true
85
+ end
86
+ end
87
+
88
+ private
89
+ def boot!
90
+ Listen.to(*@dtw, &method(:changed)).start
91
+ end
92
+
93
+ def changed(modified, added, removed)
94
+ unless updated?
95
+ @updated.make_true if (modified + added + removed).any? { |f| watching?(f) }
96
+ end
97
+ end
98
+
99
+ def watching?(file)
100
+ file = @ph.xpath(file)
101
+
102
+ if @files.member?(file)
103
+ true
104
+ elsif file.directory?
105
+ false
106
+ else
107
+ ext = @ph.normalize_extension(file.extname)
108
+
109
+ file.dirname.ascend do |dir|
110
+ if @dirs.fetch(dir, []).include?(ext)
111
+ break true
112
+ elsif dir == @lcsp || dir.root?
113
+ break false
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ def directories_to_watch
120
+ dtw = (@files + @dirs.keys).map { |f| @ph.existing_parent(f) }
121
+ dtw.compact!
122
+ dtw.uniq!
123
+
124
+ @ph.filter_out_descendants(dtw)
125
+ end
126
+
127
+ class PathHelper
128
+ def xpath(path)
129
+ Pathname.new(path).expand_path
130
+ end
131
+
132
+ def normalize_extension(ext)
133
+ ext.to_s.sub(/\A\./, '')
134
+ end
135
+
136
+ # Given a collection of Pathname objects returns the longest subpath
137
+ # common to all of them, or +nil+ if there is none.
138
+ def longest_common_subpath(paths)
139
+ return if paths.empty?
140
+
141
+ lcsp = Pathname.new(paths[0])
142
+
143
+ paths[1..-1].each do |path|
144
+ until ascendant_of?(lcsp, path)
145
+ if lcsp.root?
146
+ # If we get here a root directory is not an ascendant of path.
147
+ # This may happen if there are paths in different drives on
148
+ # Windows.
149
+ return
150
+ else
151
+ lcsp = lcsp.parent
152
+ end
153
+ end
154
+ end
155
+
156
+ lcsp
157
+ end
158
+
159
+ # Returns the deepest existing ascendant, which could be the argument itself.
160
+ def existing_parent(dir)
161
+ dir.ascend do |ascendant|
162
+ break ascendant if ascendant.directory?
163
+ end
164
+ end
165
+
166
+ # Filters out directories which are descendants of others in the collection (stable).
167
+ def filter_out_descendants(dirs)
168
+ return dirs if dirs.length < 2
169
+
170
+ dirs_sorted_by_nparts = dirs.sort_by { |dir| dir.each_filename.to_a.length }
171
+ descendants = []
172
+
173
+ until dirs_sorted_by_nparts.empty?
174
+ dir = dirs_sorted_by_nparts.shift
175
+
176
+ dirs_sorted_by_nparts.reject! do |possible_descendant|
177
+ ascendant_of?(dir, possible_descendant) && descendants << possible_descendant
178
+ end
179
+ end
180
+
181
+ # Array#- preserves order.
182
+ dirs - descendants
183
+ end
184
+
185
+ private
186
+
187
+ def ascendant_of?(base, other)
188
+ base != other && other.ascend do |ascendant|
189
+ break true if base == ascendant
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,117 @@
1
+ require 'active_support/callbacks'
2
+
3
+ module ActiveSupport
4
+ class ExecutionWrapper
5
+ include ActiveSupport::Callbacks
6
+
7
+ Null = Object.new # :nodoc:
8
+ def Null.complete! # :nodoc:
9
+ end
10
+
11
+ define_callbacks :run
12
+ define_callbacks :complete
13
+
14
+ def self.to_run(*args, &block)
15
+ set_callback(:run, *args, &block)
16
+ end
17
+
18
+ def self.to_complete(*args, &block)
19
+ set_callback(:complete, *args, &block)
20
+ end
21
+
22
+ # Register an object to be invoked during both the +run+ and
23
+ # +complete+ steps.
24
+ #
25
+ # +hook.complete+ will be passed the value returned from +hook.run+,
26
+ # and will only be invoked if +run+ has previously been called.
27
+ # (Mostly, this means it won't be invoked if an exception occurs in
28
+ # a preceding +to_run+ block; all ordinary +to_complete+ blocks are
29
+ # invoked in that situation.)
30
+ def self.register_hook(hook, outer: false)
31
+ if outer
32
+ run_args = [prepend: true]
33
+ complete_args = [:after]
34
+ else
35
+ run_args = complete_args = []
36
+ end
37
+
38
+ to_run(*run_args) do
39
+ hook_state[hook] = hook.run
40
+ end
41
+ to_complete(*complete_args) do
42
+ if hook_state.key?(hook)
43
+ hook.complete hook_state[hook]
44
+ end
45
+ end
46
+ end
47
+
48
+ # Run this execution.
49
+ #
50
+ # Returns an instance, whose +complete!+ method *must* be invoked
51
+ # after the work has been performed.
52
+ #
53
+ # Where possible, prefer +wrap+.
54
+ def self.run!
55
+ if active?
56
+ Null
57
+ else
58
+ new.tap do |instance|
59
+ success = nil
60
+ begin
61
+ instance.run!
62
+ success = true
63
+ ensure
64
+ instance.complete! unless success
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ # Perform the work in the supplied block as an execution.
71
+ def self.wrap
72
+ return yield if active?
73
+
74
+ instance = run!
75
+ begin
76
+ yield
77
+ ensure
78
+ instance.complete!
79
+ end
80
+ end
81
+
82
+ class << self # :nodoc:
83
+ attr_accessor :active
84
+ end
85
+
86
+ def self.inherited(other) # :nodoc:
87
+ super
88
+ other.active = Concurrent::Hash.new
89
+ end
90
+
91
+ self.active = Concurrent::Hash.new
92
+
93
+ def self.active? # :nodoc:
94
+ @active[Thread.current]
95
+ end
96
+
97
+ def run! # :nodoc:
98
+ self.class.active[Thread.current] = true
99
+ run_callbacks(:run)
100
+ end
101
+
102
+ # Complete this in-flight execution. This method *must* be called
103
+ # exactly once on the result of any call to +run!+.
104
+ #
105
+ # Where possible, prefer +wrap+.
106
+ def complete!
107
+ run_callbacks(:complete)
108
+ ensure
109
+ self.class.active.delete Thread.current
110
+ end
111
+
112
+ private
113
+ def hook_state
114
+ @_hook_state ||= {}
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,6 @@
1
+ require 'active_support/execution_wrapper'
2
+
3
+ module ActiveSupport
4
+ class Executor < ExecutionWrapper
5
+ end
6
+ end