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,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Module
4
+ # Declares an attribute reader backed by an internally-named instance variable.
5
+ def attr_internal_reader(*attrs)
6
+ attrs.each { |attr_name| attr_internal_define(attr_name, :reader) }
7
+ end
8
+
9
+ # Declares an attribute writer backed by an internally-named instance variable.
10
+ def attr_internal_writer(*attrs)
11
+ attrs.each { |attr_name| attr_internal_define(attr_name, :writer) }
12
+ end
13
+
14
+ # Declares an attribute reader and writer backed by an internally-named instance
15
+ # variable.
16
+ def attr_internal_accessor(*attrs)
17
+ attr_internal_reader(*attrs)
18
+ attr_internal_writer(*attrs)
19
+ end
20
+ alias_method :attr_internal, :attr_internal_accessor
21
+
22
+ class << self
23
+ attr_reader :attr_internal_naming_format
24
+
25
+ def attr_internal_naming_format=(format)
26
+ if format.start_with?("@")
27
+ raise ArgumentError, <<~MESSAGE.squish
28
+ Setting `attr_internal_naming_format` with a `@` prefix is not supported.
29
+
30
+ You can simply replace #{format.inspect} by #{format.delete_prefix("@").inspect}.
31
+ MESSAGE
32
+ end
33
+
34
+ @attr_internal_naming_format = format
35
+ end
36
+ end
37
+ self.attr_internal_naming_format = "_%s"
38
+
39
+ private
40
+ def attr_internal_define(attr_name, type)
41
+ internal_name = Module.attr_internal_naming_format % attr_name
42
+ # use native attr_* methods as they are faster on some Ruby implementations
43
+ public_send("attr_#{type}", internal_name)
44
+ attr_name, internal_name = "#{attr_name}=", "#{internal_name}=" if type == :writer
45
+ alias_method attr_name, internal_name
46
+ remove_method internal_name
47
+ end
48
+ end
@@ -0,0 +1,214 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Attribute Accessors
4
+ #
5
+ # Extends the module object with class/module and instance accessors for
6
+ # class/module attributes, just like the native attr* accessors for instance
7
+ # attributes.
8
+ class Module
9
+ # Defines a class attribute and creates a class and instance reader methods.
10
+ # The underlying class variable is set to +nil+, if it is not previously
11
+ # defined. All class and instance methods created will be public, even if
12
+ # this method is called with a private or protected access modifier.
13
+ #
14
+ # module HairColors
15
+ # mattr_reader :hair_colors
16
+ # end
17
+ #
18
+ # HairColors.hair_colors # => nil
19
+ # HairColors.class_variable_set("@@hair_colors", [:brown, :black])
20
+ # HairColors.hair_colors # => [:brown, :black]
21
+ #
22
+ # The attribute name must be a valid method name in Ruby.
23
+ #
24
+ # module Foo
25
+ # mattr_reader :"1_Badname"
26
+ # end
27
+ # # => NameError: invalid attribute name: 1_Badname
28
+ #
29
+ # To omit the instance reader method, pass
30
+ # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
31
+ #
32
+ # module HairColors
33
+ # mattr_reader :hair_colors, instance_reader: false
34
+ # end
35
+ #
36
+ # class Person
37
+ # include HairColors
38
+ # end
39
+ #
40
+ # Person.new.hair_colors # => NoMethodError
41
+ #
42
+ # You can set a default value for the attribute.
43
+ #
44
+ # module HairColors
45
+ # mattr_reader :hair_colors, default: [:brown, :black, :blonde, :red]
46
+ # mattr_reader(:hair_styles) { [:long, :short] }
47
+ # end
48
+ #
49
+ # class Person
50
+ # include HairColors
51
+ # end
52
+ #
53
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
54
+ # Person.new.hair_styles # => [:long, :short]
55
+ def mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil, location: nil)
56
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
57
+ location ||= caller_locations(1, 1).first
58
+
59
+ definition = []
60
+ syms.each do |sym|
61
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
62
+
63
+ definition << "def self.#{sym}; @@#{sym}; end"
64
+
65
+ if instance_reader && instance_accessor
66
+ definition << "def #{sym}; @@#{sym}; end"
67
+ end
68
+
69
+ sym_default_value = (block_given? && default.nil?) ? yield : default
70
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
71
+ end
72
+
73
+ module_eval(definition.join(";"), location.path, location.lineno)
74
+ end
75
+ alias :cattr_reader :mattr_reader
76
+
77
+ # Defines a class attribute and creates a class and instance writer methods to
78
+ # allow assignment to the attribute. All class and instance methods created
79
+ # will be public, even if this method is called with a private or protected
80
+ # access modifier.
81
+ #
82
+ # module HairColors
83
+ # mattr_writer :hair_colors
84
+ # end
85
+ #
86
+ # class Person
87
+ # include HairColors
88
+ # end
89
+ #
90
+ # HairColors.hair_colors = [:brown, :black]
91
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black]
92
+ # Person.new.hair_colors = [:blonde, :red]
93
+ # HairColors.class_variable_get("@@hair_colors") # => [:blonde, :red]
94
+ #
95
+ # To omit the instance writer method, pass
96
+ # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
97
+ #
98
+ # module HairColors
99
+ # mattr_writer :hair_colors, instance_writer: false
100
+ # end
101
+ #
102
+ # class Person
103
+ # include HairColors
104
+ # end
105
+ #
106
+ # Person.new.hair_colors = [:blonde, :red] # => NoMethodError
107
+ #
108
+ # You can set a default value for the attribute.
109
+ #
110
+ # module HairColors
111
+ # mattr_writer :hair_colors, default: [:brown, :black, :blonde, :red]
112
+ # mattr_writer(:hair_styles) { [:long, :short] }
113
+ # end
114
+ #
115
+ # class Person
116
+ # include HairColors
117
+ # end
118
+ #
119
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
120
+ # Person.class_variable_get("@@hair_styles") # => [:long, :short]
121
+ def mattr_writer(*syms, instance_writer: true, instance_accessor: true, default: nil, location: nil)
122
+ raise TypeError, "module attributes should be defined directly on class, not singleton" if singleton_class?
123
+ location ||= caller_locations(1, 1).first
124
+
125
+ definition = []
126
+ syms.each do |sym|
127
+ raise NameError.new("invalid attribute name: #{sym}") unless /\A[_A-Za-z]\w*\z/.match?(sym)
128
+ definition << "def self.#{sym}=(val); @@#{sym} = val; end"
129
+
130
+ if instance_writer && instance_accessor
131
+ definition << "def #{sym}=(val); @@#{sym} = val; end"
132
+ end
133
+
134
+ sym_default_value = (block_given? && default.nil?) ? yield : default
135
+ class_variable_set("@@#{sym}", sym_default_value) unless sym_default_value.nil? && class_variable_defined?("@@#{sym}")
136
+ end
137
+
138
+ module_eval(definition.join(";"), location.path, location.lineno)
139
+ end
140
+ alias :cattr_writer :mattr_writer
141
+
142
+ # Defines both class and instance accessors for class attributes.
143
+ # All class and instance methods created will be public, even if
144
+ # this method is called with a private or protected access modifier.
145
+ #
146
+ # module HairColors
147
+ # mattr_accessor :hair_colors
148
+ # end
149
+ #
150
+ # class Person
151
+ # include HairColors
152
+ # end
153
+ #
154
+ # HairColors.hair_colors = [:brown, :black, :blonde, :red]
155
+ # HairColors.hair_colors # => [:brown, :black, :blonde, :red]
156
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
157
+ #
158
+ # If a subclass changes the value then that would also change the value for
159
+ # parent class. Similarly if parent class changes the value then that would
160
+ # change the value of subclasses too.
161
+ #
162
+ # class Citizen < Person
163
+ # end
164
+ #
165
+ # Citizen.new.hair_colors << :blue
166
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red, :blue]
167
+ #
168
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
169
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
170
+ #
171
+ # module HairColors
172
+ # mattr_accessor :hair_colors, instance_writer: false, instance_reader: false
173
+ # end
174
+ #
175
+ # class Person
176
+ # include HairColors
177
+ # end
178
+ #
179
+ # Person.new.hair_colors = [:brown] # => NoMethodError
180
+ # Person.new.hair_colors # => NoMethodError
181
+ #
182
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
183
+ #
184
+ # module HairColors
185
+ # mattr_accessor :hair_colors, instance_accessor: false
186
+ # end
187
+ #
188
+ # class Person
189
+ # include HairColors
190
+ # end
191
+ #
192
+ # Person.new.hair_colors = [:brown] # => NoMethodError
193
+ # Person.new.hair_colors # => NoMethodError
194
+ #
195
+ # You can set a default value for the attribute.
196
+ #
197
+ # module HairColors
198
+ # mattr_accessor :hair_colors, default: [:brown, :black, :blonde, :red]
199
+ # mattr_accessor(:hair_styles) { [:long, :short] }
200
+ # end
201
+ #
202
+ # class Person
203
+ # include HairColors
204
+ # end
205
+ #
206
+ # Person.class_variable_get("@@hair_colors") # => [:brown, :black, :blonde, :red]
207
+ # Person.class_variable_get("@@hair_styles") # => [:long, :short]
208
+ def mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil, &blk)
209
+ location = caller_locations(1, 1).first
210
+ mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default, location: location, &blk)
211
+ mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor, default: default, location: location)
212
+ end
213
+ alias :cattr_accessor :mattr_accessor
214
+ end
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ # == Attribute Accessors per Thread
4
+ #
5
+ # Extends the module object with class/module and instance accessors for
6
+ # class/module attributes, just like the native attr* accessors for instance
7
+ # attributes, but does so on a per-thread basis.
8
+ #
9
+ # So the values are scoped within the Thread.current space under the class name
10
+ # of the module.
11
+ #
12
+ # Note that it can also be scoped per-fiber if +Rails.application.config.active_support.isolation_level+
13
+ # is set to +:fiber+.
14
+ class Module
15
+ # Defines a per-thread class attribute and creates class and instance reader methods.
16
+ # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
17
+ #
18
+ # module Current
19
+ # thread_mattr_reader :user
20
+ # end
21
+ #
22
+ # Current.user = "DHH"
23
+ # Current.user # => "DHH"
24
+ # Thread.new { Current.user }.value # => nil
25
+ #
26
+ # The attribute name must be a valid method name in Ruby.
27
+ #
28
+ # module Foo
29
+ # thread_mattr_reader :"1_Badname"
30
+ # end
31
+ # # => NameError: invalid attribute name: 1_Badname
32
+ #
33
+ # To omit the instance reader method, pass
34
+ # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
35
+ #
36
+ # class Current
37
+ # thread_mattr_reader :user, instance_reader: false
38
+ # end
39
+ #
40
+ # Current.new.user # => NoMethodError
41
+ def thread_mattr_reader(*syms, instance_reader: true, instance_accessor: true, default: nil) # :nodoc:
42
+ syms.each do |sym|
43
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
44
+
45
+ # The following generated method concatenates `object_id` because we want
46
+ # subclasses to maintain independent values.
47
+ if default.nil?
48
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
49
+ def self.#{sym}
50
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
51
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
52
+ end
53
+ EOS
54
+ else
55
+ default = default.dup.freeze unless default.frozen?
56
+ singleton_class.define_method("#{sym}_default_value") { default }
57
+
58
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
59
+ def self.#{sym}
60
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
61
+ value = ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}]
62
+
63
+ if value.nil? && !::ActiveSupport::IsolatedExecutionState.key?(@__thread_mattr_#{sym})
64
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = #{sym}_default_value
65
+ else
66
+ value
67
+ end
68
+ end
69
+ EOS
70
+ end
71
+
72
+ if instance_reader && instance_accessor
73
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
74
+ def #{sym}
75
+ self.class.#{sym}
76
+ end
77
+ EOS
78
+ end
79
+ end
80
+ end
81
+ alias :thread_cattr_reader :thread_mattr_reader
82
+
83
+ # Defines a per-thread class attribute and creates a class and instance writer methods to
84
+ # allow assignment to the attribute.
85
+ #
86
+ # module Current
87
+ # thread_mattr_writer :user
88
+ # end
89
+ #
90
+ # Current.user = "DHH"
91
+ # Thread.current[:attr_Current_user] # => "DHH"
92
+ #
93
+ # To omit the instance writer method, pass
94
+ # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
95
+ #
96
+ # class Current
97
+ # thread_mattr_writer :user, instance_writer: false
98
+ # end
99
+ #
100
+ # Current.new.user = "DHH" # => NoMethodError
101
+ def thread_mattr_writer(*syms, instance_writer: true, instance_accessor: true) # :nodoc:
102
+ syms.each do |sym|
103
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
104
+
105
+ # The following generated method concatenates `object_id` because we want
106
+ # subclasses to maintain independent values.
107
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
108
+ def self.#{sym}=(obj)
109
+ @__thread_mattr_#{sym} ||= "attr_#{sym}_\#{object_id}"
110
+ ::ActiveSupport::IsolatedExecutionState[@__thread_mattr_#{sym}] = obj
111
+ end
112
+ EOS
113
+
114
+ if instance_writer && instance_accessor
115
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
116
+ def #{sym}=(obj)
117
+ self.class.#{sym} = obj
118
+ end
119
+ EOS
120
+ end
121
+ end
122
+ end
123
+ alias :thread_cattr_writer :thread_mattr_writer
124
+
125
+ # Defines both class and instance accessors for class attributes.
126
+ #
127
+ # class Account
128
+ # thread_mattr_accessor :user
129
+ # end
130
+ #
131
+ # Account.user = "DHH"
132
+ # Account.user # => "DHH"
133
+ # Account.new.user # => "DHH"
134
+ #
135
+ # Unlike +mattr_accessor+, values are *not* shared with subclasses or parent classes.
136
+ # If a subclass changes the value, the parent class' value is not changed.
137
+ # If the parent class changes the value, the value of subclasses is not changed.
138
+ #
139
+ # class Customer < Account
140
+ # end
141
+ #
142
+ # Account.user # => "DHH"
143
+ # Customer.user # => nil
144
+ # Customer.user = "Rafael"
145
+ # Customer.user # => "Rafael"
146
+ # Account.user # => "DHH"
147
+ #
148
+ # To omit the instance writer method, pass <tt>instance_writer: false</tt>.
149
+ # To omit the instance reader method, pass <tt>instance_reader: false</tt>.
150
+ #
151
+ # class Current
152
+ # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
153
+ # end
154
+ #
155
+ # Current.new.user = "DHH" # => NoMethodError
156
+ # Current.new.user # => NoMethodError
157
+ #
158
+ # Or pass <tt>instance_accessor: false</tt>, to omit both instance methods.
159
+ #
160
+ # class Current
161
+ # thread_mattr_accessor :user, instance_accessor: false
162
+ # end
163
+ #
164
+ # Current.new.user = "DHH" # => NoMethodError
165
+ # Current.new.user # => NoMethodError
166
+ #
167
+ # A default value may be specified using the +:default+ option. Because
168
+ # multiple threads can access the default value, non-frozen default values
169
+ # will be <tt>dup</tt>ed and frozen.
170
+ def thread_mattr_accessor(*syms, instance_reader: true, instance_writer: true, instance_accessor: true, default: nil)
171
+ thread_mattr_reader(*syms, instance_reader: instance_reader, instance_accessor: instance_accessor, default: default)
172
+ thread_mattr_writer(*syms, instance_writer: instance_writer, instance_accessor: instance_accessor)
173
+ end
174
+ alias :thread_cattr_accessor :thread_mattr_accessor
175
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/concern"
4
+
5
+ class Module
6
+ # == Bite-sized separation of concerns
7
+ #
8
+ # We often find ourselves with a medium-sized chunk of behavior that we'd
9
+ # like to extract, but only mix in to a single class.
10
+ #
11
+ # Extracting a plain old Ruby object to encapsulate it and collaborate or
12
+ # delegate to the original object is often a good choice, but when there's
13
+ # no additional state to encapsulate or we're making DSL-style declarations
14
+ # about the parent class, introducing new collaborators can obfuscate rather
15
+ # than simplify.
16
+ #
17
+ # The typical route is to just dump everything in a monolithic class, perhaps
18
+ # with a comment, as a least-bad alternative. Using modules in separate files
19
+ # means tedious sifting to get a big-picture view.
20
+ #
21
+ # == Dissatisfying ways to separate small concerns
22
+ #
23
+ # === Using comments:
24
+ #
25
+ # class Todo < ApplicationRecord
26
+ # # Other todo implementation
27
+ # # ...
28
+ #
29
+ # ## Event tracking
30
+ # has_many :events
31
+ #
32
+ # before_create :track_creation
33
+ #
34
+ # private
35
+ # def track_creation
36
+ # # ...
37
+ # end
38
+ # end
39
+ #
40
+ # === With an inline module:
41
+ #
42
+ # Noisy syntax.
43
+ #
44
+ # class Todo < ApplicationRecord
45
+ # # Other todo implementation
46
+ # # ...
47
+ #
48
+ # module EventTracking
49
+ # extend ActiveSupport::Concern
50
+ #
51
+ # included do
52
+ # has_many :events
53
+ # before_create :track_creation
54
+ # end
55
+ #
56
+ # private
57
+ # def track_creation
58
+ # # ...
59
+ # end
60
+ # end
61
+ # include EventTracking
62
+ # end
63
+ #
64
+ # === Mix-in noise exiled to its own file:
65
+ #
66
+ # Once our chunk of behavior starts pushing the scroll-to-understand-it
67
+ # boundary, we give in and move it to a separate file. At this size, the
68
+ # increased overhead can be a reasonable tradeoff even if it reduces our
69
+ # at-a-glance perception of how things work.
70
+ #
71
+ # class Todo < ApplicationRecord
72
+ # # Other todo implementation
73
+ # # ...
74
+ #
75
+ # include TodoEventTracking
76
+ # end
77
+ #
78
+ # == Introducing Module#concerning
79
+ #
80
+ # By quieting the mix-in noise, we arrive at a natural, low-ceremony way to
81
+ # separate bite-sized concerns.
82
+ #
83
+ # class Todo < ApplicationRecord
84
+ # # Other todo implementation
85
+ # # ...
86
+ #
87
+ # concerning :EventTracking do
88
+ # included do
89
+ # has_many :events
90
+ # before_create :track_creation
91
+ # end
92
+ #
93
+ # private
94
+ # def track_creation
95
+ # # ...
96
+ # end
97
+ # end
98
+ # end
99
+ #
100
+ # Todo.ancestors
101
+ # # => [Todo, Todo::EventTracking, ApplicationRecord, Object]
102
+ #
103
+ # This small step has some wonderful ripple effects. We can
104
+ # * grok the behavior of our class in one glance,
105
+ # * clean up monolithic junk-drawer classes by separating their concerns, and
106
+ # * stop leaning on protected/private for crude "this is internal stuff" modularity.
107
+ #
108
+ # === Prepending concerning
109
+ #
110
+ # <tt>concerning</tt> supports a <tt>prepend: true</tt> argument which will <tt>prepend</tt> the
111
+ # concern instead of using <tt>include</tt> for it.
112
+ module Concerning
113
+ # Define a new concern and mix it in.
114
+ def concerning(topic, prepend: false, &block)
115
+ method = prepend ? :prepend : :include
116
+ __send__(method, concern(topic, &block))
117
+ end
118
+
119
+ # A low-cruft shortcut to define a concern.
120
+ #
121
+ # concern :EventTracking do
122
+ # ...
123
+ # end
124
+ #
125
+ # is equivalent to
126
+ #
127
+ # module EventTracking
128
+ # extend ActiveSupport::Concern
129
+ #
130
+ # ...
131
+ # end
132
+ def concern(topic, &module_definition)
133
+ const_set topic, Module.new {
134
+ extend ::ActiveSupport::Concern
135
+ module_eval(&module_definition)
136
+ }
137
+ end
138
+ end
139
+ include Concerning
140
+ end