activesupport 1.2.4 → 8.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (309) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +505 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +40 -0
  5. data/lib/active_support/actionable_error.rb +50 -0
  6. data/lib/active_support/all.rb +5 -0
  7. data/lib/active_support/array_inquirer.rb +50 -0
  8. data/lib/active_support/backtrace_cleaner.rb +234 -0
  9. data/lib/active_support/benchmark.rb +21 -0
  10. data/lib/active_support/benchmarkable.rb +53 -0
  11. data/lib/active_support/broadcast_logger.rb +238 -0
  12. data/lib/active_support/builder.rb +8 -0
  13. data/lib/active_support/cache/coder.rb +153 -0
  14. data/lib/active_support/cache/entry.rb +134 -0
  15. data/lib/active_support/cache/file_store.rb +244 -0
  16. data/lib/active_support/cache/mem_cache_store.rb +288 -0
  17. data/lib/active_support/cache/memory_store.rb +264 -0
  18. data/lib/active_support/cache/null_store.rb +62 -0
  19. data/lib/active_support/cache/redis_cache_store.rb +498 -0
  20. data/lib/active_support/cache/serializer_with_fallback.rb +152 -0
  21. data/lib/active_support/cache/strategy/local_cache.rb +246 -0
  22. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  23. data/lib/active_support/cache.rb +1170 -0
  24. data/lib/active_support/callbacks.rb +960 -0
  25. data/lib/active_support/class_attribute.rb +33 -0
  26. data/lib/active_support/code_generator.rb +79 -0
  27. data/lib/active_support/concern.rb +217 -0
  28. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +18 -0
  29. data/lib/active_support/concurrency/null_lock.rb +13 -0
  30. data/lib/active_support/concurrency/share_lock.rb +225 -0
  31. data/lib/active_support/concurrency/thread_monitor.rb +55 -0
  32. data/lib/active_support/configurable.rb +193 -0
  33. data/lib/active_support/configuration_file.rb +60 -0
  34. data/lib/active_support/continuous_integration.rb +145 -0
  35. data/lib/active_support/core_ext/array/access.rb +100 -0
  36. data/lib/active_support/core_ext/array/conversions.rb +209 -26
  37. data/lib/active_support/core_ext/array/extract.rb +21 -0
  38. data/lib/active_support/core_ext/array/extract_options.rb +31 -0
  39. data/lib/active_support/core_ext/array/grouping.rb +109 -0
  40. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  41. data/lib/active_support/core_ext/array/wrap.rb +48 -0
  42. data/lib/active_support/core_ext/array.rb +8 -4
  43. data/lib/active_support/core_ext/benchmark.rb +6 -0
  44. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  45. data/lib/active_support/core_ext/big_decimal.rb +3 -0
  46. data/lib/active_support/core_ext/class/attribute.rb +137 -0
  47. data/lib/active_support/core_ext/class/attribute_accessors.rb +6 -0
  48. data/lib/active_support/core_ext/class/subclasses.rb +24 -0
  49. data/lib/active_support/core_ext/class.rb +4 -0
  50. data/lib/active_support/core_ext/date/acts_like.rb +10 -0
  51. data/lib/active_support/core_ext/date/blank.rb +18 -0
  52. data/lib/active_support/core_ext/date/calculations.rb +161 -0
  53. data/lib/active_support/core_ext/date/conversions.rb +95 -28
  54. data/lib/active_support/core_ext/date/zones.rb +8 -0
  55. data/lib/active_support/core_ext/date.rb +6 -5
  56. data/lib/active_support/core_ext/date_and_time/calculations.rb +374 -0
  57. data/lib/active_support/core_ext/date_and_time/compatibility.rb +23 -0
  58. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  59. data/lib/active_support/core_ext/date_time/acts_like.rb +16 -0
  60. data/lib/active_support/core_ext/date_time/blank.rb +18 -0
  61. data/lib/active_support/core_ext/date_time/calculations.rb +215 -0
  62. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  63. data/lib/active_support/core_ext/date_time/conversions.rb +108 -0
  64. data/lib/active_support/core_ext/date_time.rb +7 -0
  65. data/lib/active_support/core_ext/digest/uuid.rb +76 -0
  66. data/lib/active_support/core_ext/digest.rb +3 -0
  67. data/lib/active_support/core_ext/enumerable.rb +277 -7
  68. data/lib/active_support/core_ext/erb/util.rb +201 -0
  69. data/lib/active_support/core_ext/file/atomic.rb +72 -0
  70. data/lib/active_support/core_ext/file.rb +3 -0
  71. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  72. data/lib/active_support/core_ext/hash/deep_merge.rb +43 -0
  73. data/lib/active_support/core_ext/hash/deep_transform_values.rb +46 -0
  74. data/lib/active_support/core_ext/hash/except.rb +12 -0
  75. data/lib/active_support/core_ext/hash/indifferent_access.rb +19 -55
  76. data/lib/active_support/core_ext/hash/keys.rb +134 -44
  77. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -22
  78. data/lib/active_support/core_ext/hash/slice.rb +27 -0
  79. data/lib/active_support/core_ext/hash.rb +9 -8
  80. data/lib/active_support/core_ext/integer/inflections.rb +29 -13
  81. data/lib/active_support/core_ext/integer/multiple.rb +12 -0
  82. data/lib/active_support/core_ext/integer/time.rb +22 -0
  83. data/lib/active_support/core_ext/integer.rb +4 -6
  84. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  85. data/lib/active_support/core_ext/kernel/reporting.rb +45 -0
  86. data/lib/active_support/core_ext/kernel/singleton_class.rb +8 -0
  87. data/lib/active_support/core_ext/kernel.rb +4 -78
  88. data/lib/active_support/core_ext/load_error.rb +6 -35
  89. data/lib/active_support/core_ext/module/aliasing.rb +31 -0
  90. data/lib/active_support/core_ext/module/anonymous.rb +30 -0
  91. data/lib/active_support/core_ext/module/attr_internal.rb +48 -0
  92. data/lib/active_support/core_ext/module/attribute_accessors.rb +214 -0
  93. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +175 -0
  94. data/lib/active_support/core_ext/module/concerning.rb +140 -0
  95. data/lib/active_support/core_ext/module/delegation.rb +225 -0
  96. data/lib/active_support/core_ext/module/deprecation.rb +25 -0
  97. data/lib/active_support/core_ext/module/introspection.rb +65 -0
  98. data/lib/active_support/core_ext/module/redefine_method.rb +40 -0
  99. data/lib/active_support/core_ext/module/remove_method.rb +17 -0
  100. data/lib/active_support/core_ext/module.rb +13 -0
  101. data/lib/active_support/core_ext/name_error.rb +59 -0
  102. data/lib/active_support/core_ext/numeric/bytes.rb +73 -42
  103. data/lib/active_support/core_ext/numeric/conversions.rb +145 -0
  104. data/lib/active_support/core_ext/numeric/time.rb +64 -57
  105. data/lib/active_support/core_ext/numeric.rb +4 -6
  106. data/lib/active_support/core_ext/object/acts_like.rb +45 -0
  107. data/lib/active_support/core_ext/object/blank.rb +199 -0
  108. data/lib/active_support/core_ext/object/conversions.rb +6 -0
  109. data/lib/active_support/core_ext/object/deep_dup.rb +71 -0
  110. data/lib/active_support/core_ext/object/duplicable.rb +69 -0
  111. data/lib/active_support/core_ext/object/inclusion.rb +37 -0
  112. data/lib/active_support/core_ext/object/instance_variables.rb +32 -0
  113. data/lib/active_support/core_ext/object/json.rb +267 -0
  114. data/lib/active_support/core_ext/object/to_param.rb +3 -0
  115. data/lib/active_support/core_ext/object/to_query.rb +93 -0
  116. data/lib/active_support/core_ext/object/try.rb +158 -0
  117. data/lib/active_support/core_ext/object/with.rb +46 -0
  118. data/lib/active_support/core_ext/object/with_options.rb +101 -0
  119. data/lib/active_support/core_ext/object.rb +17 -0
  120. data/lib/active_support/core_ext/pathname/blank.rb +20 -0
  121. data/lib/active_support/core_ext/pathname/existence.rb +23 -0
  122. data/lib/active_support/core_ext/pathname.rb +4 -0
  123. data/lib/active_support/core_ext/range/compare_range.rb +57 -0
  124. data/lib/active_support/core_ext/range/conversions.rb +58 -17
  125. data/lib/active_support/core_ext/range/overlap.rb +40 -0
  126. data/lib/active_support/core_ext/range/sole.rb +17 -0
  127. data/lib/active_support/core_ext/range.rb +5 -4
  128. data/lib/active_support/core_ext/regexp.rb +14 -0
  129. data/lib/active_support/core_ext/securerandom.rb +57 -0
  130. data/lib/active_support/core_ext/string/access.rb +93 -56
  131. data/lib/active_support/core_ext/string/behavior.rb +8 -0
  132. data/lib/active_support/core_ext/string/conversions.rb +57 -16
  133. data/lib/active_support/core_ext/string/exclude.rb +13 -0
  134. data/lib/active_support/core_ext/string/filters.rb +151 -0
  135. data/lib/active_support/core_ext/string/indent.rb +45 -0
  136. data/lib/active_support/core_ext/string/inflections.rb +297 -54
  137. data/lib/active_support/core_ext/string/inquiry.rb +16 -0
  138. data/lib/active_support/core_ext/string/multibyte.rb +67 -0
  139. data/lib/active_support/core_ext/string/output_safety.rb +235 -0
  140. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -18
  141. data/lib/active_support/core_ext/string/strip.rb +27 -0
  142. data/lib/active_support/core_ext/string/zones.rb +16 -0
  143. data/lib/active_support/core_ext/string.rb +14 -10
  144. data/lib/active_support/core_ext/symbol/starts_ends_with.rb +6 -0
  145. data/lib/active_support/core_ext/symbol.rb +3 -0
  146. data/lib/active_support/core_ext/thread/backtrace/location.rb +7 -0
  147. data/lib/active_support/core_ext/time/acts_like.rb +10 -0
  148. data/lib/active_support/core_ext/time/calculations.rb +358 -153
  149. data/lib/active_support/core_ext/time/compatibility.rb +15 -0
  150. data/lib/active_support/core_ext/time/conversions.rb +69 -30
  151. data/lib/active_support/core_ext/time/zones.rb +97 -0
  152. data/lib/active_support/core_ext/time.rb +6 -6
  153. data/lib/active_support/core_ext.rb +5 -1
  154. data/lib/active_support/current_attributes/test_helper.rb +13 -0
  155. data/lib/active_support/current_attributes.rb +243 -0
  156. data/lib/active_support/deep_mergeable.rb +53 -0
  157. data/lib/active_support/delegation.rb +183 -0
  158. data/lib/active_support/dependencies/autoload.rb +72 -0
  159. data/lib/active_support/dependencies/interlock.rb +55 -0
  160. data/lib/active_support/dependencies/require_dependency.rb +28 -0
  161. data/lib/active_support/dependencies.rb +84 -222
  162. data/lib/active_support/deprecation/behaviors.rb +148 -0
  163. data/lib/active_support/deprecation/constant_accessor.rb +74 -0
  164. data/lib/active_support/deprecation/deprecators.rb +104 -0
  165. data/lib/active_support/deprecation/disallowed.rb +54 -0
  166. data/lib/active_support/deprecation/method_wrappers.rb +68 -0
  167. data/lib/active_support/deprecation/proxy_wrappers.rb +189 -0
  168. data/lib/active_support/deprecation/reporting.rb +162 -0
  169. data/lib/active_support/deprecation.rb +81 -0
  170. data/lib/active_support/deprecator.rb +7 -0
  171. data/lib/active_support/descendants_tracker.rb +112 -0
  172. data/lib/active_support/digest.rb +22 -0
  173. data/lib/active_support/duration/iso8601_parser.rb +123 -0
  174. data/lib/active_support/duration/iso8601_serializer.rb +64 -0
  175. data/lib/active_support/duration.rb +524 -0
  176. data/lib/active_support/editor.rb +70 -0
  177. data/lib/active_support/encrypted_configuration.rb +126 -0
  178. data/lib/active_support/encrypted_file.rb +133 -0
  179. data/lib/active_support/environment_inquirer.rb +40 -0
  180. data/lib/active_support/error_reporter/test_helper.rb +15 -0
  181. data/lib/active_support/error_reporter.rb +318 -0
  182. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  183. data/lib/active_support/event_reporter.rb +592 -0
  184. data/lib/active_support/evented_file_update_checker.rb +185 -0
  185. data/lib/active_support/execution_context/test_helper.rb +13 -0
  186. data/lib/active_support/execution_context.rb +110 -0
  187. data/lib/active_support/execution_wrapper.rb +150 -0
  188. data/lib/active_support/executor/test_helper.rb +7 -0
  189. data/lib/active_support/executor.rb +8 -0
  190. data/lib/active_support/file_update_checker.rb +166 -0
  191. data/lib/active_support/fork_tracker.rb +43 -0
  192. data/lib/active_support/gem_version.rb +17 -0
  193. data/lib/active_support/gzip.rb +41 -0
  194. data/lib/active_support/hash_with_indifferent_access.rb +464 -0
  195. data/lib/active_support/html_safe_translation.rb +56 -0
  196. data/lib/active_support/i18n.rb +17 -0
  197. data/lib/active_support/i18n_railtie.rb +140 -0
  198. data/lib/active_support/inflections.rb +68 -49
  199. data/lib/active_support/inflector/inflections.rb +290 -0
  200. data/lib/active_support/inflector/methods.rb +387 -0
  201. data/lib/active_support/inflector/transliterate.rb +147 -0
  202. data/lib/active_support/inflector.rb +7 -164
  203. data/lib/active_support/isolated_execution_state.rb +76 -0
  204. data/lib/active_support/json/decoding.rb +78 -0
  205. data/lib/active_support/json/encoding.rb +256 -0
  206. data/lib/active_support/json.rb +4 -0
  207. data/lib/active_support/key_generator.rb +66 -0
  208. data/lib/active_support/lazy_load_hooks.rb +107 -0
  209. data/lib/active_support/locale/en.rb +33 -0
  210. data/lib/active_support/locale/en.yml +141 -0
  211. data/lib/active_support/log_subscriber/test_helper.rb +106 -0
  212. data/lib/active_support/log_subscriber.rb +188 -0
  213. data/lib/active_support/logger.rb +55 -0
  214. data/lib/active_support/logger_silence.rb +21 -0
  215. data/lib/active_support/logger_thread_safe_level.rb +50 -0
  216. data/lib/active_support/message_encryptor.rb +374 -0
  217. data/lib/active_support/message_encryptors.rb +193 -0
  218. data/lib/active_support/message_pack/cache_serializer.rb +23 -0
  219. data/lib/active_support/message_pack/extensions.rb +310 -0
  220. data/lib/active_support/message_pack/serializer.rb +63 -0
  221. data/lib/active_support/message_pack.rb +50 -0
  222. data/lib/active_support/message_verifier.rb +377 -0
  223. data/lib/active_support/message_verifiers.rb +189 -0
  224. data/lib/active_support/messages/codec.rb +65 -0
  225. data/lib/active_support/messages/metadata.rb +146 -0
  226. data/lib/active_support/messages/rotation_configuration.rb +23 -0
  227. data/lib/active_support/messages/rotation_coordinator.rb +102 -0
  228. data/lib/active_support/messages/rotator.rb +69 -0
  229. data/lib/active_support/messages/serializer_with_fallback.rb +158 -0
  230. data/lib/active_support/multibyte/chars.rb +188 -0
  231. data/lib/active_support/multibyte/unicode.rb +42 -0
  232. data/lib/active_support/multibyte.rb +27 -0
  233. data/lib/active_support/notifications/fanout.rb +467 -0
  234. data/lib/active_support/notifications/instrumenter.rb +240 -0
  235. data/lib/active_support/notifications.rb +281 -0
  236. data/lib/active_support/number_helper/number_converter.rb +190 -0
  237. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  238. data/lib/active_support/number_helper/number_to_delimited_converter.rb +30 -0
  239. data/lib/active_support/number_helper/number_to_human_converter.rb +69 -0
  240. data/lib/active_support/number_helper/number_to_human_size_converter.rb +60 -0
  241. data/lib/active_support/number_helper/number_to_percentage_converter.rb +16 -0
  242. data/lib/active_support/number_helper/number_to_phone_converter.rb +60 -0
  243. data/lib/active_support/number_helper/number_to_rounded_converter.rb +59 -0
  244. data/lib/active_support/number_helper/rounding_helper.rb +46 -0
  245. data/lib/active_support/number_helper.rb +479 -0
  246. data/lib/active_support/option_merger.rb +38 -0
  247. data/lib/active_support/ordered_hash.rb +50 -0
  248. data/lib/active_support/ordered_options.rb +141 -25
  249. data/lib/active_support/parameter_filter.rb +157 -0
  250. data/lib/active_support/rails.rb +26 -0
  251. data/lib/active_support/railtie.rb +180 -0
  252. data/lib/active_support/reloader.rb +138 -0
  253. data/lib/active_support/rescuable.rb +176 -0
  254. data/lib/active_support/secure_compare_rotator.rb +58 -0
  255. data/lib/active_support/security_utils.rb +38 -0
  256. data/lib/active_support/string_inquirer.rb +35 -0
  257. data/lib/active_support/structured_event_subscriber.rb +99 -0
  258. data/lib/active_support/subscriber.rb +141 -0
  259. data/lib/active_support/syntax_error_proxy.rb +67 -0
  260. data/lib/active_support/tagged_logging.rb +157 -0
  261. data/lib/active_support/test_case.rb +365 -0
  262. data/lib/active_support/testing/assertions.rb +369 -0
  263. data/lib/active_support/testing/autorun.rb +10 -0
  264. data/lib/active_support/testing/constant_lookup.rb +51 -0
  265. data/lib/active_support/testing/constant_stubbing.rb +54 -0
  266. data/lib/active_support/testing/declarative.rb +28 -0
  267. data/lib/active_support/testing/deprecation.rb +82 -0
  268. data/lib/active_support/testing/error_reporter_assertions.rb +124 -0
  269. data/lib/active_support/testing/event_reporter_assertions.rb +227 -0
  270. data/lib/active_support/testing/file_fixtures.rb +38 -0
  271. data/lib/active_support/testing/isolation.rb +121 -0
  272. data/lib/active_support/testing/method_call_assertions.rb +69 -0
  273. data/lib/active_support/testing/notification_assertions.rb +92 -0
  274. data/lib/active_support/testing/parallelization/server.rb +98 -0
  275. data/lib/active_support/testing/parallelization/worker.rb +107 -0
  276. data/lib/active_support/testing/parallelization.rb +79 -0
  277. data/lib/active_support/testing/parallelize_executor.rb +81 -0
  278. data/lib/active_support/testing/setup_and_teardown.rb +57 -0
  279. data/lib/active_support/testing/stream.rb +41 -0
  280. data/lib/active_support/testing/tagged_logging.rb +27 -0
  281. data/lib/active_support/testing/tests_without_assertions.rb +19 -0
  282. data/lib/active_support/testing/time_helpers.rb +273 -0
  283. data/lib/active_support/time.rb +20 -0
  284. data/lib/active_support/time_with_zone.rb +613 -0
  285. data/lib/active_support/values/time_zone.rb +599 -158
  286. data/lib/active_support/version.rb +7 -6
  287. data/lib/active_support/xml_mini/jdom.rb +175 -0
  288. data/lib/active_support/xml_mini/libxml.rb +80 -0
  289. data/lib/active_support/xml_mini/libxmlsax.rb +83 -0
  290. data/lib/active_support/xml_mini/nokogiri.rb +83 -0
  291. data/lib/active_support/xml_mini/nokogirisax.rb +86 -0
  292. data/lib/active_support/xml_mini/rexml.rb +137 -0
  293. data/lib/active_support/xml_mini.rb +212 -0
  294. data/lib/active_support.rb +122 -10
  295. metadata +524 -93
  296. data/CHANGELOG +0 -283
  297. data/lib/active_support/binding_of_caller.rb +0 -84
  298. data/lib/active_support/breakpoint.rb +0 -523
  299. data/lib/active_support/class_attribute_accessors.rb +0 -57
  300. data/lib/active_support/class_inheritable_attributes.rb +0 -117
  301. data/lib/active_support/clean_logger.rb +0 -36
  302. data/lib/active_support/core_ext/blank.rb +0 -38
  303. data/lib/active_support/core_ext/cgi/escape_skipping_slashes.rb +0 -14
  304. data/lib/active_support/core_ext/cgi.rb +0 -5
  305. data/lib/active_support/core_ext/exception.rb +0 -29
  306. data/lib/active_support/core_ext/integer/even_odd.rb +0 -24
  307. data/lib/active_support/core_ext/object_and_class.rb +0 -44
  308. data/lib/active_support/module_attribute_accessors.rb +0 -57
  309. data/lib/active_support/whiny_nil.rb +0 -38
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "date"
4
+ require "active_support/inflector/methods"
5
+ require "active_support/core_ext/time/conversions"
6
+ require "active_support/core_ext/date_time/calculations"
7
+ require "active_support/values/time_zone"
8
+
9
+ class DateTime
10
+ # Convert to a formatted string. See Time::DATE_FORMATS for predefined formats.
11
+ #
12
+ # This method is aliased to <tt>to_formatted_s</tt>.
13
+ #
14
+ # ==== Examples
15
+ #
16
+ # datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
17
+ #
18
+ # datetime.to_fs(:db) # => "2007-12-04 00:00:00"
19
+ # datetime.to_formatted_s(:db) # => "2007-12-04 00:00:00"
20
+ # datetime.to_fs(:number) # => "20071204000000"
21
+ # datetime.to_fs(:short) # => "04 Dec 00:00"
22
+ # datetime.to_fs(:long) # => "December 04, 2007 00:00"
23
+ # datetime.to_fs(:long_ordinal) # => "December 4th, 2007 00:00"
24
+ # datetime.to_fs(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
25
+ # datetime.to_fs(:iso8601) # => "2007-12-04T00:00:00+00:00"
26
+ #
27
+ # ==== Adding your own datetime formats to +to_fs+
28
+ #
29
+ # DateTime formats are shared with Time. You can add your own to the
30
+ # Time::DATE_FORMATS hash. Use the format name as the hash key and
31
+ # either a strftime string or Proc instance that takes a time or
32
+ # datetime argument as the value.
33
+ #
34
+ # # config/initializers/time_formats.rb
35
+ # Time::DATE_FORMATS[:month_and_year] = '%B %Y'
36
+ # Time::DATE_FORMATS[:short_ordinal] = lambda { |time| time.strftime("%B #{time.day.ordinalize}") }
37
+ def to_fs(format = :default)
38
+ if formatter = ::Time::DATE_FORMATS[format]
39
+ formatter.respond_to?(:call) ? formatter.call(self).to_s : strftime(formatter)
40
+ else
41
+ to_s
42
+ end
43
+ end
44
+ alias_method :to_formatted_s, :to_fs
45
+
46
+
47
+ # Returns a formatted string of the offset from UTC, or an alternative
48
+ # string if the time zone is already UTC.
49
+ #
50
+ # datetime = DateTime.civil(2000, 1, 1, 0, 0, 0, Rational(-6, 24))
51
+ # datetime.formatted_offset # => "-06:00"
52
+ # datetime.formatted_offset(false) # => "-0600"
53
+ def formatted_offset(colon = true, alternate_utc_string = nil)
54
+ utc? && alternate_utc_string || ActiveSupport::TimeZone.seconds_to_utc_offset(utc_offset, colon)
55
+ end
56
+
57
+ # Overrides the default inspect method with a human readable one, e.g., "Mon, 21 Feb 2005 14:30:00 +0000".
58
+ def readable_inspect
59
+ to_fs(:rfc822)
60
+ end
61
+ alias_method :default_inspect, :inspect
62
+ alias_method :inspect, :readable_inspect
63
+
64
+ # Returns DateTime with local offset for given year if format is local else
65
+ # offset is zero.
66
+ #
67
+ # DateTime.civil_from_format :local, 2012
68
+ # # => Sun, 01 Jan 2012 00:00:00 +0300
69
+ # DateTime.civil_from_format :local, 2012, 12, 17
70
+ # # => Mon, 17 Dec 2012 00:00:00 +0000
71
+ def self.civil_from_format(utc_or_local, year, month = 1, day = 1, hour = 0, min = 0, sec = 0)
72
+ if utc_or_local.to_sym == :local
73
+ offset = ::Time.local(year, month, day).utc_offset.to_r / 86400
74
+ else
75
+ offset = 0
76
+ end
77
+ civil(year, month, day, hour, min, sec, offset)
78
+ end
79
+
80
+ # Converts +self+ to a floating-point number of seconds, including fractional microseconds, since the Unix epoch.
81
+ def to_f
82
+ seconds_since_unix_epoch.to_f + sec_fraction
83
+ end
84
+
85
+ # Converts +self+ to an integer number of seconds since the Unix epoch.
86
+ def to_i
87
+ seconds_since_unix_epoch.to_i
88
+ end
89
+
90
+ # Returns the fraction of a second as microseconds
91
+ def usec
92
+ (sec_fraction * 1_000_000).to_i
93
+ end
94
+
95
+ # Returns the fraction of a second as nanoseconds
96
+ def nsec
97
+ (sec_fraction * 1_000_000_000).to_i
98
+ end
99
+
100
+ private
101
+ def offset_in_seconds
102
+ (offset * 86400).to_i
103
+ end
104
+
105
+ def seconds_since_unix_epoch
106
+ (jd - 2440588) * 86400 - offset_in_seconds + seconds_since_midnight
107
+ end
108
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "date_time/acts_like"
4
+ require_relative "date_time/blank"
5
+ require_relative "date_time/calculations"
6
+ require_relative "date_time/compatibility"
7
+ require_relative "date_time/conversions"
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+ require "openssl"
5
+
6
+ module Digest
7
+ module UUID
8
+ DNS_NAMESPACE = "k\xA7\xB8\x10\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
9
+ URL_NAMESPACE = "k\xA7\xB8\x11\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
10
+ OID_NAMESPACE = "k\xA7\xB8\x12\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
11
+ X500_NAMESPACE = "k\xA7\xB8\x14\x9D\xAD\x11\xD1\x80\xB4\x00\xC0O\xD40\xC8" # :nodoc:
12
+
13
+ # Generates a v5 non-random UUID (Universally Unique IDentifier).
14
+ #
15
+ # Using OpenSSL::Digest::MD5 generates version 3 UUIDs; OpenSSL::Digest::SHA1 generates version 5 UUIDs.
16
+ # uuid_from_hash always generates the same UUID for a given name and namespace combination.
17
+ #
18
+ # See RFC 4122 for details of UUID at: https://www.ietf.org/rfc/rfc4122.txt
19
+ def self.uuid_from_hash(hash_class, namespace, name)
20
+ if hash_class == Digest::MD5 || hash_class == OpenSSL::Digest::MD5
21
+ version = 3
22
+ elsif hash_class == Digest::SHA1 || hash_class == OpenSSL::Digest::SHA1
23
+ version = 5
24
+ else
25
+ raise ArgumentError, "Expected OpenSSL::Digest::SHA1 or OpenSSL::Digest::MD5, got #{hash_class.name}."
26
+ end
27
+
28
+ uuid_namespace = pack_uuid_namespace(namespace)
29
+
30
+ hash = hash_class.new
31
+ hash.update(uuid_namespace)
32
+ hash.update(name)
33
+
34
+ ary = hash.digest.unpack("NnnnnN")
35
+ ary[2] = (ary[2] & 0x0FFF) | (version << 12)
36
+ ary[3] = (ary[3] & 0x3FFF) | 0x8000
37
+
38
+ "%08x-%04x-%04x-%04x-%04x%08x" % ary
39
+ end
40
+
41
+ # Convenience method for uuid_from_hash using OpenSSL::Digest::MD5.
42
+ def self.uuid_v3(uuid_namespace, name)
43
+ uuid_from_hash(OpenSSL::Digest::MD5, uuid_namespace, name)
44
+ end
45
+
46
+ # Convenience method for uuid_from_hash using OpenSSL::Digest::SHA1.
47
+ def self.uuid_v5(uuid_namespace, name)
48
+ uuid_from_hash(OpenSSL::Digest::SHA1, uuid_namespace, name)
49
+ end
50
+
51
+ # Convenience method for SecureRandom.uuid.
52
+ def self.uuid_v4
53
+ SecureRandom.uuid
54
+ end
55
+
56
+ # Returns the nil UUID. This is a special form of UUID that is specified to
57
+ # have all 128 bits set to zero.
58
+ def self.nil_uuid
59
+ "00000000-0000-0000-0000-000000000000"
60
+ end
61
+
62
+ def self.pack_uuid_namespace(namespace)
63
+ if [DNS_NAMESPACE, OID_NAMESPACE, URL_NAMESPACE, X500_NAMESPACE].include?(namespace)
64
+ namespace
65
+ else
66
+ match_data = namespace.match(/\A(\h{8})-(\h{4})-(\h{4})-(\h{4})-(\h{4})(\h{8})\z/)
67
+
68
+ raise ArgumentError, "Only UUIDs are valid namespace identifiers" unless match_data.present?
69
+
70
+ match_data.captures.map { |s| s.to_i(16) }.pack("NnnnnN")
71
+ end
72
+ end
73
+
74
+ private_class_method :pack_uuid_namespace
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "digest/uuid"
@@ -1,9 +1,279 @@
1
- module Enumerable #:nodoc:
2
- def first_match
3
- match = nil
4
- each do |items|
5
- break if match = yield(items)
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ module EnumerableCoreExt # :nodoc:
5
+ module Constants
6
+ private
7
+ def const_missing(name)
8
+ if name == :SoleItemExpectedError
9
+ ::ActiveSupport::EnumerableCoreExt::SoleItemExpectedError
10
+ else
11
+ super
12
+ end
13
+ end
6
14
  end
7
- match
8
15
  end
9
- end
16
+ end
17
+
18
+ module Enumerable
19
+ # Error generated by +sole+ when called on an enumerable that doesn't have
20
+ # exactly one item.
21
+ class SoleItemExpectedError < StandardError; end
22
+
23
+ # HACK: For performance reasons, Enumerable shouldn't have any constants of its own.
24
+ # So we move SoleItemExpectedError into ActiveSupport::EnumerableCoreExt.
25
+ ActiveSupport::EnumerableCoreExt::SoleItemExpectedError = remove_const(:SoleItemExpectedError)
26
+ singleton_class.prepend(ActiveSupport::EnumerableCoreExt::Constants)
27
+
28
+ # Calculates the minimum from the extracted elements.
29
+ #
30
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
31
+ # payments.minimum(:price) # => 5
32
+ def minimum(key)
33
+ map(&key).min
34
+ end
35
+
36
+ # Calculates the maximum from the extracted elements.
37
+ #
38
+ # payments = [Payment.new(5), Payment.new(15), Payment.new(10)]
39
+ # payments.maximum(:price) # => 15
40
+ def maximum(key)
41
+ map(&key).max
42
+ end
43
+
44
+ # Convert an enumerable to a hash, using the block result as the key and the
45
+ # element as the value.
46
+ #
47
+ # people.index_by(&:login)
48
+ # # => { "nextangle" => <Person ...>, "chade-" => <Person ...>, ...}
49
+ #
50
+ # people.index_by { |person| "#{person.first_name} #{person.last_name}" }
51
+ # # => { "Chade- Fowlersburg-e" => <Person ...>, "David Heinemeier Hansson" => <Person ...>, ...}
52
+ def index_by
53
+ if block_given?
54
+ result = {}
55
+ each { |elem| result[yield(elem)] = elem }
56
+ result
57
+ else
58
+ to_enum(:index_by) { size if respond_to?(:size) }
59
+ end
60
+ end
61
+
62
+ # Convert an enumerable to a hash, using the element as the key and the block
63
+ # result as the value.
64
+ #
65
+ # post = Post.new(title: "hey there", body: "what's up?")
66
+ #
67
+ # %i( title body ).index_with { |attr_name| post.public_send(attr_name) }
68
+ # # => { title: "hey there", body: "what's up?" }
69
+ #
70
+ # If an argument is passed instead of a block, it will be used as the value
71
+ # for all elements:
72
+ #
73
+ # %i( created_at updated_at ).index_with(Time.now)
74
+ # # => { created_at: 2020-03-09 22:31:47, updated_at: 2020-03-09 22:31:47 }
75
+ def index_with(default = (no_default = true))
76
+ if block_given?
77
+ result = {}
78
+ each { |elem| result[elem] = yield(elem) }
79
+ result
80
+ elsif no_default
81
+ to_enum(:index_with) { size if respond_to?(:size) }
82
+ else
83
+ result = {}
84
+ each { |elem| result[elem] = default }
85
+ result
86
+ end
87
+ end
88
+
89
+ # Returns +true+ if the enumerable has more than 1 element. Functionally
90
+ # equivalent to <tt>enum.to_a.size > 1</tt>. Can be called with a block too,
91
+ # much like any?, so <tt>people.many? { |p| p.age > 26 }</tt> returns +true+
92
+ # if more than one person is over 26.
93
+ def many?
94
+ cnt = 0
95
+ if block_given?
96
+ any? do |*args|
97
+ cnt += 1 if yield(*args)
98
+ cnt > 1
99
+ end
100
+ else
101
+ any? { (cnt += 1) > 1 }
102
+ end
103
+ end
104
+
105
+ # Returns a new array that includes the passed elements.
106
+ #
107
+ # [ 1, 2, 3 ].including(4, 5)
108
+ # # => [ 1, 2, 3, 4, 5 ]
109
+ #
110
+ # ["David", "Rafael"].including %w[ Aaron Todd ]
111
+ # # => ["David", "Rafael", "Aaron", "Todd"]
112
+ def including(*elements)
113
+ to_a.including(*elements)
114
+ end
115
+
116
+ # The negative of the <tt>Enumerable#include?</tt>. Returns +true+ if the
117
+ # collection does not include the object.
118
+ def exclude?(object)
119
+ !include?(object)
120
+ end
121
+
122
+ # Returns a copy of the enumerable excluding the specified elements.
123
+ #
124
+ # ["David", "Rafael", "Aaron", "Todd"].excluding "Aaron", "Todd"
125
+ # # => ["David", "Rafael"]
126
+ #
127
+ # ["David", "Rafael", "Aaron", "Todd"].excluding %w[ Aaron Todd ]
128
+ # # => ["David", "Rafael"]
129
+ #
130
+ # {foo: 1, bar: 2, baz: 3}.excluding :bar
131
+ # # => {foo: 1, baz: 3}
132
+ def excluding(*elements)
133
+ elements.flatten!(1)
134
+ reject { |element| elements.include?(element) }
135
+ end
136
+ alias :without :excluding
137
+
138
+ # Extract the given key from each element in the enumerable.
139
+ #
140
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pluck(:name)
141
+ # # => ["David", "Rafael", "Aaron"]
142
+ #
143
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pluck(:id, :name)
144
+ # # => [[1, "David"], [2, "Rafael"]]
145
+ def pluck(*keys)
146
+ if keys.many?
147
+ map { |element| keys.map { |key| element[key] } }
148
+ else
149
+ key = keys.first
150
+ map { |element| element[key] }
151
+ end
152
+ end
153
+
154
+ # Extract the given key from the first element in the enumerable.
155
+ #
156
+ # [{ name: "David" }, { name: "Rafael" }, { name: "Aaron" }].pick(:name)
157
+ # # => "David"
158
+ #
159
+ # [{ id: 1, name: "David" }, { id: 2, name: "Rafael" }].pick(:id, :name)
160
+ # # => [1, "David"]
161
+ def pick(*keys)
162
+ return if none?
163
+
164
+ if keys.many?
165
+ keys.map { |key| first[key] }
166
+ else
167
+ first[keys.first]
168
+ end
169
+ end
170
+
171
+ # Returns a new +Array+ without the blank items.
172
+ # Uses Object#blank? for determining if an item is blank.
173
+ #
174
+ # [1, "", nil, 2, " ", [], {}, false, true].compact_blank
175
+ # # => [1, 2, true]
176
+ #
177
+ # Set.new([nil, "", 1, false]).compact_blank
178
+ # # => [1]
179
+ #
180
+ # When called on a +Hash+, returns a new +Hash+ without the blank values.
181
+ #
182
+ # { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
183
+ # # => { b: 1, f: true }
184
+ def compact_blank
185
+ reject(&:blank?)
186
+ end
187
+
188
+ # Returns a new +Array+ where the order has been set to that provided in the +series+, based on the +key+ of the
189
+ # objects in the original enumerable.
190
+ #
191
+ # [ Person.find(5), Person.find(3), Person.find(1) ].in_order_of(:id, [ 1, 5, 3 ])
192
+ # # => [ Person.find(1), Person.find(5), Person.find(3) ]
193
+ #
194
+ # If the +series+ include keys that have no corresponding element in the Enumerable, these are ignored.
195
+ # If the Enumerable has additional elements that aren't named in the +series+, these are not included in the result, unless
196
+ # the +filter+ option is set to +false+.
197
+ def in_order_of(key, series, filter: true)
198
+ if filter
199
+ group_by(&key).values_at(*series).flatten(1).compact
200
+ else
201
+ sort_by { |v| series.index(v.public_send(key)) || series.size }.compact
202
+ end
203
+ end
204
+
205
+ # Returns the sole item in the enumerable. If there are no items, or more
206
+ # than one item, raises Enumerable::SoleItemExpectedError.
207
+ #
208
+ # ["x"].sole # => "x"
209
+ # Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
210
+ # { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
211
+ def sole
212
+ result = nil
213
+ found = false
214
+
215
+ each do |*element|
216
+ if found
217
+ raise SoleItemExpectedError, "multiple items found"
218
+ end
219
+
220
+ result = element.size == 1 ? element[0] : element
221
+ found = true
222
+ end
223
+
224
+ if found
225
+ result
226
+ else
227
+ raise SoleItemExpectedError, "no item found"
228
+ end
229
+ end
230
+ end
231
+
232
+ class Hash
233
+ # Hash#reject has its own definition, so this needs one too.
234
+ def compact_blank # :nodoc:
235
+ reject { |_k, v| v.blank? }
236
+ end
237
+
238
+ # Removes all blank values from the +Hash+ in place and returns self.
239
+ # Uses Object#blank? for determining if a value is blank.
240
+ #
241
+ # h = { a: "", b: 1, c: nil, d: [], e: false, f: true }
242
+ # h.compact_blank!
243
+ # # => { b: 1, f: true }
244
+ def compact_blank!
245
+ # use delete_if rather than reject! because it always returns self even if nothing changed
246
+ delete_if { |_k, v| v.blank? }
247
+ end
248
+ end
249
+
250
+ class Range # :nodoc:
251
+ # Optimize range sum to use arithmetic progression if a block is not given and
252
+ # we have a range of numeric values.
253
+ def sum(initial_value = 0)
254
+ if block_given? || !(first.is_a?(Integer) && last.is_a?(Integer))
255
+ super
256
+ else
257
+ actual_last = exclude_end? ? (last - 1) : last
258
+ if actual_last >= first
259
+ sum = initial_value || 0
260
+ sum + (actual_last - first + 1) * (actual_last + first) / 2
261
+ else
262
+ initial_value || 0
263
+ end
264
+ end
265
+ end
266
+ end
267
+
268
+ class Array # :nodoc:
269
+ # Removes all blank elements from the +Array+ in place and returns self.
270
+ # Uses Object#blank? for determining if an item is blank.
271
+ #
272
+ # a = [1, "", nil, 2, " ", [], {}, false, true]
273
+ # a.compact_blank!
274
+ # # => [1, 2, true]
275
+ def compact_blank!
276
+ # use delete_if rather than reject! because it always returns self even if nothing changed
277
+ delete_if(&:blank?)
278
+ end
279
+ end
@@ -0,0 +1,201 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+
5
+ module ActiveSupport
6
+ module CoreExt
7
+ module ERBUtil
8
+ # HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
9
+ # This method is not for public consumption! Seriously!
10
+ def html_escape(s) # :nodoc:
11
+ s = s.to_s
12
+ if s.html_safe?
13
+ s
14
+ else
15
+ super(s)
16
+ end
17
+ end
18
+ alias :unwrapped_html_escape :html_escape # :nodoc:
19
+
20
+ # A utility method for escaping HTML tag characters.
21
+ # This method is also aliased as <tt>h</tt>.
22
+ #
23
+ # puts html_escape('is a > 0 & a < 10?')
24
+ # # => is a &gt; 0 &amp; a &lt; 10?
25
+ def html_escape(s) # rubocop:disable Lint/DuplicateMethods
26
+ unwrapped_html_escape(s).html_safe
27
+ end
28
+ alias h html_escape
29
+ end
30
+
31
+ module ERBUtilPrivate
32
+ include ERBUtil
33
+ private :unwrapped_html_escape, :html_escape, :h
34
+ end
35
+ end
36
+ end
37
+
38
+ class ERB
39
+ module Util
40
+ HTML_ESCAPE = { "&" => "&amp;", ">" => "&gt;", "<" => "&lt;", '"' => "&quot;", "'" => "&#39;" }
41
+ HTML_ESCAPE_ONCE_REGEXP = /["><']|&(?!([a-zA-Z]+|(#\d+)|(#[xX][\dA-Fa-f]+));)/
42
+
43
+ # Following XML requirements: https://www.w3.org/TR/REC-xml/#NT-Name
44
+ TAG_NAME_START_CODEPOINTS = "@:A-Z_a-z\u{C0}-\u{D6}\u{D8}-\u{F6}\u{F8}-\u{2FF}\u{370}-\u{37D}\u{37F}-\u{1FFF}" \
45
+ "\u{200C}-\u{200D}\u{2070}-\u{218F}\u{2C00}-\u{2FEF}\u{3001}-\u{D7FF}\u{F900}-\u{FDCF}" \
46
+ "\u{FDF0}-\u{FFFD}\u{10000}-\u{EFFFF}"
47
+ INVALID_TAG_NAME_START_REGEXP = /[^#{TAG_NAME_START_CODEPOINTS}]/
48
+ TAG_NAME_FOLLOWING_CODEPOINTS = "#{TAG_NAME_START_CODEPOINTS}\\-.0-9\u{B7}\u{0300}-\u{036F}\u{203F}-\u{2040}"
49
+ INVALID_TAG_NAME_FOLLOWING_REGEXP = /[^#{TAG_NAME_FOLLOWING_CODEPOINTS}]/
50
+ SAFE_XML_TAG_NAME_REGEXP = /\A[#{TAG_NAME_START_CODEPOINTS}][#{TAG_NAME_FOLLOWING_CODEPOINTS}]*\z/
51
+ TAG_NAME_REPLACEMENT_CHAR = "_"
52
+
53
+ prepend ActiveSupport::CoreExt::ERBUtilPrivate
54
+ singleton_class.prepend ActiveSupport::CoreExt::ERBUtil
55
+
56
+ # A utility method for escaping HTML without affecting existing escaped entities.
57
+ #
58
+ # html_escape_once('1 < 2 &amp; 3')
59
+ # # => "1 &lt; 2 &amp; 3"
60
+ #
61
+ # html_escape_once('&lt;&lt; Accept & Checkout')
62
+ # # => "&lt;&lt; Accept &amp; Checkout"
63
+ def html_escape_once(s)
64
+ s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
65
+ end
66
+
67
+ module_function :html_escape_once
68
+
69
+ # A utility method for escaping HTML entities in JSON strings. Specifically, the
70
+ # &, > and < characters are replaced with their equivalent unicode escaped form -
71
+ # \u0026, \u003e, and \u003c. The Unicode sequences \u2028 and \u2029 are also
72
+ # escaped as they are treated as newline characters in some JavaScript engines.
73
+ # These sequences have identical meaning as the original characters inside the
74
+ # context of a JSON string, so assuming the input is a valid and well-formed
75
+ # JSON value, the output will have equivalent meaning when parsed:
76
+ #
77
+ # json = JSON.generate({ name: "</script><script>alert('PWNED!!!')</script>"})
78
+ # # => "{\"name\":\"</script><script>alert('PWNED!!!')</script>\"}"
79
+ #
80
+ # json_escape(json)
81
+ # # => "{\"name\":\"\\u003C/script\\u003E\\u003Cscript\\u003Ealert('PWNED!!!')\\u003C/script\\u003E\"}"
82
+ #
83
+ # JSON.parse(json) == JSON.parse(json_escape(json))
84
+ # # => true
85
+ #
86
+ # The intended use case for this method is to escape JSON strings before including
87
+ # them inside a script tag to avoid XSS vulnerability:
88
+ #
89
+ # <script>
90
+ # var currentUser = <%= raw json_escape(current_user.to_json) %>;
91
+ # </script>
92
+ #
93
+ # It is necessary to +raw+ the result of +json_escape+, so that quotation marks
94
+ # don't get converted to <tt>&quot;</tt> entities. +json_escape+ doesn't
95
+ # automatically flag the result as HTML safe, since the raw value is unsafe to
96
+ # use inside HTML attributes.
97
+ #
98
+ # If your JSON is being used downstream for insertion into the DOM, be aware of
99
+ # whether or not it is being inserted via <tt>html()</tt>. Most jQuery plugins do this.
100
+ # If that is the case, be sure to +html_escape+ or +sanitize+ any user-generated
101
+ # content returned by your JSON.
102
+ #
103
+ # If you need to output JSON elsewhere in your HTML, you can just do something
104
+ # like this, as any unsafe characters (including quotation marks) will be
105
+ # automatically escaped for you:
106
+ #
107
+ # <div data-user-info="<%= current_user.to_json %>">...</div>
108
+ #
109
+ # WARNING: this helper only works with valid JSON. Using this on non-JSON values
110
+ # will open up serious XSS vulnerabilities. For example, if you replace the
111
+ # +current_user.to_json+ in the example above with user input instead, the browser
112
+ # will happily <tt>eval()</tt> that string as JavaScript.
113
+ #
114
+ # The escaping performed in this method is identical to those performed in the
115
+ # Active Support JSON encoder when +ActiveSupport.escape_html_entities_in_json+ is
116
+ # set to true. Because this transformation is idempotent, this helper can be
117
+ # applied even if +ActiveSupport.escape_html_entities_in_json+ is already true.
118
+ #
119
+ # Therefore, when you are unsure if +ActiveSupport.escape_html_entities_in_json+
120
+ # is enabled, or if you are unsure where your JSON string originated from, it
121
+ # is recommended that you always apply this helper (other libraries, such as the
122
+ # JSON gem, do not provide this kind of protection by default; also some gems
123
+ # might override +to_json+ to bypass Active Support's encoder).
124
+ def json_escape(s)
125
+ result = s.to_s.dup
126
+ result.gsub!(">", '\u003e')
127
+ result.gsub!("<", '\u003c')
128
+ result.gsub!("&", '\u0026')
129
+ result.gsub!("\u2028", '\u2028')
130
+ result.gsub!("\u2029", '\u2029')
131
+ s.html_safe? ? result.html_safe : result
132
+ end
133
+
134
+ module_function :json_escape
135
+
136
+ # A utility method for escaping XML names of tags and names of attributes.
137
+ #
138
+ # xml_name_escape('1 < 2 & 3')
139
+ # # => "1___2___3"
140
+ #
141
+ # It follows the requirements of the specification: https://www.w3.org/TR/REC-xml/#NT-Name
142
+ def xml_name_escape(name)
143
+ name = name.to_s
144
+ return "" if name.blank?
145
+ return name if name.match?(SAFE_XML_TAG_NAME_REGEXP)
146
+
147
+ starting_char = name[0]
148
+ starting_char.gsub!(INVALID_TAG_NAME_START_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
149
+
150
+ return starting_char if name.size == 1
151
+
152
+ following_chars = name[1..-1]
153
+ following_chars.gsub!(INVALID_TAG_NAME_FOLLOWING_REGEXP, TAG_NAME_REPLACEMENT_CHAR)
154
+
155
+ starting_char << following_chars
156
+ end
157
+ module_function :xml_name_escape
158
+
159
+ # Tokenizes a line of ERB. This is really just for error reporting and
160
+ # nobody should use it.
161
+ def self.tokenize(source) # :nodoc:
162
+ require "strscan"
163
+ source = StringScanner.new(source.chomp)
164
+ tokens = []
165
+
166
+ start_re = /<%(?:={1,2}|-|\#|%)?/m
167
+ finish_re = /(?:[-=])?%>/m
168
+
169
+ while !source.eos?
170
+ pos = source.pos
171
+ source.scan_until(/(?:#{start_re}|#{finish_re})/)
172
+ return [[:PLAIN, source.string]] unless source.matched?
173
+ len = source.pos - source.matched.bytesize - pos
174
+
175
+ case source.matched
176
+ when start_re
177
+ tokens << [:TEXT, source.string.byteslice(pos, len)] if len > 0
178
+ tokens << [:OPEN, source.matched]
179
+ if source.scan(/(.*?)(?=#{finish_re}|\z)/m)
180
+ tokens << [:CODE, source.matched] unless source.matched.empty?
181
+ tokens << [:CLOSE, source.scan(finish_re)] unless source.eos?
182
+ else
183
+ raise NotImplementedError
184
+ end
185
+ when finish_re
186
+ tokens << [:CODE, source.string.byteslice(pos, len)] if len > 0
187
+ tokens << [:CLOSE, source.matched]
188
+ else
189
+ raise NotImplementedError, source.matched
190
+ end
191
+
192
+ unless source.eos? || source.exist?(start_re) || source.exist?(finish_re)
193
+ tokens << [:TEXT, source.rest]
194
+ source.terminate
195
+ end
196
+ end
197
+
198
+ tokens
199
+ end
200
+ end
201
+ end