activesupport 4.0.13 → 5.2.5

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activesupport might be problematic. Click here for more details.

Files changed (264) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +412 -444
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +8 -4
  5. data/lib/active_support/all.rb +5 -3
  6. data/lib/active_support/array_inquirer.rb +48 -0
  7. data/lib/active_support/backtrace_cleaner.rb +14 -12
  8. data/lib/active_support/benchmarkable.rb +6 -14
  9. data/lib/active_support/builder.rb +3 -1
  10. data/lib/active_support/cache/file_store.rb +67 -51
  11. data/lib/active_support/cache/mem_cache_store.rb +95 -97
  12. data/lib/active_support/cache/memory_store.rb +28 -30
  13. data/lib/active_support/cache/null_store.rb +7 -8
  14. data/lib/active_support/cache/redis_cache_store.rb +466 -0
  15. data/lib/active_support/cache/strategy/local_cache.rb +70 -56
  16. data/lib/active_support/cache/strategy/local_cache_middleware.rb +45 -0
  17. data/lib/active_support/cache.rb +331 -206
  18. data/lib/active_support/callbacks.rb +697 -426
  19. data/lib/active_support/concern.rb +32 -10
  20. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  21. data/lib/active_support/concurrency/share_lock.rb +227 -0
  22. data/lib/active_support/configurable.rb +8 -5
  23. data/lib/active_support/core_ext/array/access.rb +39 -1
  24. data/lib/active_support/core_ext/array/conversions.rb +24 -35
  25. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  26. data/lib/active_support/core_ext/array/grouping.rb +23 -13
  27. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  28. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -5
  29. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  30. data/lib/active_support/core_ext/array.rb +9 -7
  31. data/lib/active_support/core_ext/benchmark.rb +3 -1
  32. data/lib/active_support/core_ext/big_decimal/conversions.rb +9 -26
  33. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  34. data/lib/active_support/core_ext/class/attribute.rb +41 -23
  35. data/lib/active_support/core_ext/class/attribute_accessors.rb +5 -169
  36. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  37. data/lib/active_support/core_ext/class.rb +4 -4
  38. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  39. data/lib/active_support/core_ext/date/blank.rb +14 -0
  40. data/lib/active_support/core_ext/date/calculations.rb +21 -9
  41. data/lib/active_support/core_ext/date/conversions.rb +32 -22
  42. data/lib/active_support/core_ext/date/zones.rb +5 -34
  43. data/lib/active_support/core_ext/date.rb +6 -4
  44. data/lib/active_support/core_ext/date_and_time/calculations.rb +199 -57
  45. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  46. data/lib/active_support/core_ext/date_and_time/zones.rb +41 -0
  47. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  48. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  49. data/lib/active_support/core_ext/date_time/calculations.rb +78 -37
  50. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  51. data/lib/active_support/core_ext/date_time/conversions.rb +19 -13
  52. data/lib/active_support/core_ext/date_time.rb +7 -4
  53. data/lib/active_support/core_ext/digest/uuid.rb +53 -0
  54. data/lib/active_support/core_ext/digest.rb +3 -0
  55. data/lib/active_support/core_ext/enumerable.rb +113 -29
  56. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/hash/compact.rb +29 -0
  59. data/lib/active_support/core_ext/hash/conversions.rb +71 -49
  60. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  61. data/lib/active_support/core_ext/hash/except.rb +12 -3
  62. data/lib/active_support/core_ext/hash/indifferent_access.rb +5 -3
  63. data/lib/active_support/core_ext/hash/keys.rb +50 -38
  64. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  65. data/lib/active_support/core_ext/hash/slice.rb +12 -6
  66. data/lib/active_support/core_ext/hash/transform_values.rb +32 -0
  67. data/lib/active_support/core_ext/hash.rb +11 -8
  68. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  69. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  70. data/lib/active_support/core_ext/integer/time.rb +11 -33
  71. data/lib/active_support/core_ext/integer.rb +5 -3
  72. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  73. data/lib/active_support/core_ext/kernel/concern.rb +14 -0
  74. data/lib/active_support/core_ext/kernel/reporting.rb +5 -74
  75. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  76. data/lib/active_support/core_ext/kernel.rb +6 -4
  77. data/lib/active_support/core_ext/load_error.rb +5 -21
  78. data/lib/active_support/core_ext/marshal.rb +13 -10
  79. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  80. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  81. data/lib/active_support/core_ext/module/attr_internal.rb +8 -8
  82. data/lib/active_support/core_ext/module/attribute_accessors.rb +170 -21
  83. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  84. data/lib/active_support/core_ext/module/concerning.rb +134 -0
  85. data/lib/active_support/core_ext/module/delegation.rb +135 -45
  86. data/lib/active_support/core_ext/module/deprecation.rb +3 -3
  87. data/lib/active_support/core_ext/module/introspection.rb +9 -25
  88. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  89. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  90. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  91. data/lib/active_support/core_ext/module.rb +14 -10
  92. data/lib/active_support/core_ext/name_error.rb +22 -2
  93. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  94. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  95. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  96. data/lib/active_support/core_ext/numeric/time.rb +37 -50
  97. data/lib/active_support/core_ext/numeric.rb +6 -3
  98. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  99. data/lib/active_support/core_ext/object/blank.rb +70 -19
  100. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  101. data/lib/active_support/core_ext/object/deep_dup.rb +19 -10
  102. data/lib/active_support/core_ext/object/duplicable.rb +100 -34
  103. data/lib/active_support/core_ext/object/inclusion.rb +18 -15
  104. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  105. data/lib/active_support/core_ext/object/json.rb +227 -0
  106. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  107. data/lib/active_support/core_ext/object/to_query.rb +21 -8
  108. data/lib/active_support/core_ext/object/try.rb +94 -24
  109. data/lib/active_support/core_ext/object/with_options.rb +45 -5
  110. data/lib/active_support/core_ext/object.rb +14 -12
  111. data/lib/active_support/core_ext/range/compare_range.rb +61 -0
  112. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  113. data/lib/active_support/core_ext/range/each.rb +19 -17
  114. data/lib/active_support/core_ext/range/include_range.rb +2 -22
  115. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  116. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  117. data/lib/active_support/core_ext/range.rb +7 -4
  118. data/lib/active_support/core_ext/regexp.rb +6 -0
  119. data/lib/active_support/core_ext/securerandom.rb +25 -0
  120. data/lib/active_support/core_ext/string/access.rb +41 -39
  121. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  122. data/lib/active_support/core_ext/string/conversions.rb +17 -13
  123. data/lib/active_support/core_ext/string/exclude.rb +5 -3
  124. data/lib/active_support/core_ext/string/filters.rb +55 -6
  125. data/lib/active_support/core_ext/string/indent.rb +6 -4
  126. data/lib/active_support/core_ext/string/inflections.rb +66 -24
  127. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  128. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  129. data/lib/active_support/core_ext/string/output_safety.rb +114 -54
  130. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  131. data/lib/active_support/core_ext/string/strip.rb +4 -5
  132. data/lib/active_support/core_ext/string/zones.rb +4 -1
  133. data/lib/active_support/core_ext/string.rb +15 -13
  134. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  135. data/lib/active_support/core_ext/time/calculations.rb +123 -110
  136. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  137. data/lib/active_support/core_ext/time/conversions.rb +23 -14
  138. data/lib/active_support/core_ext/time/zones.rb +42 -26
  139. data/lib/active_support/core_ext/time.rb +7 -5
  140. data/lib/active_support/core_ext/uri.rb +6 -8
  141. data/lib/active_support/core_ext.rb +3 -2
  142. data/lib/active_support/current_attributes.rb +195 -0
  143. data/lib/active_support/dependencies/autoload.rb +3 -1
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/dependencies.rb +196 -166
  146. data/lib/active_support/deprecation/behaviors.rb +48 -15
  147. data/lib/active_support/deprecation/constant_accessor.rb +52 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +17 -2
  149. data/lib/active_support/deprecation/method_wrappers.rb +66 -20
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +56 -28
  151. data/lib/active_support/deprecation/reporting.rb +32 -12
  152. data/lib/active_support/deprecation.rb +14 -11
  153. data/lib/active_support/descendants_tracker.rb +2 -0
  154. data/lib/active_support/digest.rb +20 -0
  155. data/lib/active_support/duration/iso8601_parser.rb +125 -0
  156. data/lib/active_support/duration/iso8601_serializer.rb +55 -0
  157. data/lib/active_support/duration.rb +354 -34
  158. data/lib/active_support/encrypted_configuration.rb +49 -0
  159. data/lib/active_support/encrypted_file.rb +99 -0
  160. data/lib/active_support/evented_file_update_checker.rb +205 -0
  161. data/lib/active_support/execution_wrapper.rb +128 -0
  162. data/lib/active_support/executor.rb +8 -0
  163. data/lib/active_support/file_update_checker.rb +63 -37
  164. data/lib/active_support/gem_version.rb +17 -0
  165. data/lib/active_support/gzip.rb +7 -5
  166. data/lib/active_support/hash_with_indifferent_access.rb +158 -35
  167. data/lib/active_support/i18n.rb +8 -6
  168. data/lib/active_support/i18n_railtie.rb +38 -20
  169. data/lib/active_support/inflections.rb +19 -12
  170. data/lib/active_support/inflector/inflections.rb +79 -30
  171. data/lib/active_support/inflector/methods.rb +197 -129
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/inflector.rb +7 -5
  174. data/lib/active_support/json/decoding.rb +21 -25
  175. data/lib/active_support/json/encoding.rb +84 -292
  176. data/lib/active_support/json.rb +4 -2
  177. data/lib/active_support/key_generator.rb +26 -28
  178. data/lib/active_support/lazy_load_hooks.rb +51 -21
  179. data/lib/active_support/locale/en.yml +2 -0
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/log_subscriber.rb +13 -10
  182. data/lib/active_support/logger.rb +54 -3
  183. data/lib/active_support/logger_silence.rb +12 -7
  184. data/lib/active_support/logger_thread_safe_level.rb +34 -0
  185. data/lib/active_support/message_encryptor.rb +173 -50
  186. data/lib/active_support/message_verifier.rb +159 -22
  187. data/lib/active_support/messages/metadata.rb +71 -0
  188. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  189. data/lib/active_support/messages/rotator.rb +56 -0
  190. data/lib/active_support/multibyte/chars.rb +38 -26
  191. data/lib/active_support/multibyte/unicode.rb +138 -146
  192. data/lib/active_support/multibyte.rb +4 -2
  193. data/lib/active_support/notifications/fanout.rb +23 -16
  194. data/lib/active_support/notifications/instrumenter.rb +29 -8
  195. data/lib/active_support/notifications.rb +22 -13
  196. data/lib/active_support/number_helper/number_converter.rb +184 -0
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +46 -0
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +29 -0
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +59 -0
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +14 -0
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +54 -0
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/number_helper.rb +125 -391
  206. data/lib/active_support/option_merger.rb +3 -1
  207. data/lib/active_support/ordered_hash.rb +6 -4
  208. data/lib/active_support/ordered_options.rb +31 -5
  209. data/lib/active_support/per_thread_registry.rb +19 -11
  210. data/lib/active_support/proxy_object.rb +2 -0
  211. data/lib/active_support/rails.rb +16 -8
  212. data/lib/active_support/railtie.rb +43 -9
  213. data/lib/active_support/reloader.rb +131 -0
  214. data/lib/active_support/rescuable.rb +108 -53
  215. data/lib/active_support/security_utils.rb +31 -0
  216. data/lib/active_support/string_inquirer.rb +11 -3
  217. data/lib/active_support/subscriber.rb +54 -17
  218. data/lib/active_support/tagged_logging.rb +14 -11
  219. data/lib/active_support/test_case.rb +42 -37
  220. data/lib/active_support/testing/assertions.rb +126 -39
  221. data/lib/active_support/testing/autorun.rb +5 -3
  222. data/lib/active_support/testing/constant_lookup.rb +3 -6
  223. data/lib/active_support/testing/declarative.rb +10 -22
  224. data/lib/active_support/testing/deprecation.rb +14 -10
  225. data/lib/active_support/testing/file_fixtures.rb +36 -0
  226. data/lib/active_support/testing/isolation.rb +55 -86
  227. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  228. data/lib/active_support/testing/setup_and_teardown.rb +30 -10
  229. data/lib/active_support/testing/stream.rb +44 -0
  230. data/lib/active_support/testing/tagged_logging.rb +5 -3
  231. data/lib/active_support/testing/time_helpers.rb +200 -0
  232. data/lib/active_support/time.rb +13 -13
  233. data/lib/active_support/time_with_zone.rb +223 -73
  234. data/lib/active_support/values/time_zone.rb +261 -126
  235. data/lib/active_support/values/unicode_tables.dat +0 -0
  236. data/lib/active_support/version.rb +6 -7
  237. data/lib/active_support/xml_mini/jdom.rb +116 -113
  238. data/lib/active_support/xml_mini/libxml.rb +17 -16
  239. data/lib/active_support/xml_mini/libxmlsax.rb +16 -18
  240. data/lib/active_support/xml_mini/nokogiri.rb +15 -15
  241. data/lib/active_support/xml_mini/nokogirisax.rb +15 -16
  242. data/lib/active_support/xml_mini/rexml.rb +17 -16
  243. data/lib/active_support/xml_mini.rb +69 -51
  244. data/lib/active_support.rb +29 -3
  245. metadata +84 -54
  246. data/lib/active_support/basic_object.rb +0 -11
  247. data/lib/active_support/buffered_logger.rb +0 -21
  248. data/lib/active_support/concurrency/latch.rb +0 -27
  249. data/lib/active_support/core_ext/array/uniq_by.rb +0 -19
  250. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -40
  251. data/lib/active_support/core_ext/date_time/zones.rb +0 -24
  252. data/lib/active_support/core_ext/hash/diff.rb +0 -14
  253. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  254. data/lib/active_support/core_ext/logger.rb +0 -67
  255. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  256. data/lib/active_support/core_ext/object/to_json.rb +0 -27
  257. data/lib/active_support/core_ext/proc.rb +0 -17
  258. data/lib/active_support/core_ext/string/encoding.rb +0 -8
  259. data/lib/active_support/core_ext/struct.rb +0 -6
  260. data/lib/active_support/core_ext/thread.rb +0 -79
  261. data/lib/active_support/core_ext/time/marshal.rb +0 -30
  262. data/lib/active_support/file_watcher.rb +0 -36
  263. data/lib/active_support/json/variable.rb +0 -18
  264. data/lib/active_support/testing/pending.rb +0 -14
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/array/extract_options"
4
+ require "active_support/core_ext/regexp"
5
+
6
+ # Extends the module object with class/module and instance accessors for
7
+ # class/module attributes, just like the native attr* accessors for instance
8
+ # attributes, but does so on a per-thread basis.
9
+ #
10
+ # So the values are scoped within the Thread.current space under the class name
11
+ # of the module.
12
+ class Module
13
+ # Defines a per-thread class attribute and creates class and instance reader methods.
14
+ # The underlying per-thread class variable is set to +nil+, if it is not previously defined.
15
+ #
16
+ # module Current
17
+ # thread_mattr_reader :user
18
+ # end
19
+ #
20
+ # Current.user # => nil
21
+ # Thread.current[:attr_Current_user] = "DHH"
22
+ # Current.user # => "DHH"
23
+ #
24
+ # The attribute name must be a valid method name in Ruby.
25
+ #
26
+ # module Foo
27
+ # thread_mattr_reader :"1_Badname"
28
+ # end
29
+ # # => NameError: invalid attribute name: 1_Badname
30
+ #
31
+ # If you want to opt out of the creation of the instance reader method, pass
32
+ # <tt>instance_reader: false</tt> or <tt>instance_accessor: false</tt>.
33
+ #
34
+ # class Current
35
+ # thread_mattr_reader :user, instance_reader: false
36
+ # end
37
+ #
38
+ # Current.new.user # => NoMethodError
39
+ def thread_mattr_reader(*syms) # :nodoc:
40
+ options = syms.extract_options!
41
+
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 `name` because we want it
46
+ # to work with inheritance via polymorphism.
47
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
48
+ def self.#{sym}
49
+ Thread.current["attr_" + name + "_#{sym}"]
50
+ end
51
+ EOS
52
+
53
+ unless options[:instance_reader] == false || options[:instance_accessor] == false
54
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
+ def #{sym}
56
+ self.class.#{sym}
57
+ end
58
+ EOS
59
+ end
60
+ end
61
+ end
62
+ alias :thread_cattr_reader :thread_mattr_reader
63
+
64
+ # Defines a per-thread class attribute and creates a class and instance writer methods to
65
+ # allow assignment to the attribute.
66
+ #
67
+ # module Current
68
+ # thread_mattr_writer :user
69
+ # end
70
+ #
71
+ # Current.user = "DHH"
72
+ # Thread.current[:attr_Current_user] # => "DHH"
73
+ #
74
+ # If you want to opt out of the creation of the instance writer method, pass
75
+ # <tt>instance_writer: false</tt> or <tt>instance_accessor: false</tt>.
76
+ #
77
+ # class Current
78
+ # thread_mattr_writer :user, instance_writer: false
79
+ # end
80
+ #
81
+ # Current.new.user = "DHH" # => NoMethodError
82
+ def thread_mattr_writer(*syms) # :nodoc:
83
+ options = syms.extract_options!
84
+ syms.each do |sym|
85
+ raise NameError.new("invalid attribute name: #{sym}") unless /^[_A-Za-z]\w*$/.match?(sym)
86
+
87
+ # The following generated method concatenates `name` because we want it
88
+ # to work with inheritance via polymorphism.
89
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
90
+ def self.#{sym}=(obj)
91
+ Thread.current["attr_" + name + "_#{sym}"] = obj
92
+ end
93
+ EOS
94
+
95
+ unless options[:instance_writer] == false || options[:instance_accessor] == false
96
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
97
+ def #{sym}=(obj)
98
+ self.class.#{sym} = obj
99
+ end
100
+ EOS
101
+ end
102
+ end
103
+ end
104
+ alias :thread_cattr_writer :thread_mattr_writer
105
+
106
+ # Defines both class and instance accessors for class attributes.
107
+ #
108
+ # class Account
109
+ # thread_mattr_accessor :user
110
+ # end
111
+ #
112
+ # Account.user = "DHH"
113
+ # Account.user # => "DHH"
114
+ # Account.new.user # => "DHH"
115
+ #
116
+ # If a subclass changes the value, the parent class' value is not changed.
117
+ # Similarly, if the parent class changes the value, the value of subclasses
118
+ # is not changed.
119
+ #
120
+ # class Customer < Account
121
+ # end
122
+ #
123
+ # Customer.user = "Rafael"
124
+ # Customer.user # => "Rafael"
125
+ # Account.user # => "DHH"
126
+ #
127
+ # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
128
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
129
+ #
130
+ # class Current
131
+ # thread_mattr_accessor :user, instance_writer: false, instance_reader: false
132
+ # end
133
+ #
134
+ # Current.new.user = "DHH" # => NoMethodError
135
+ # Current.new.user # => NoMethodError
136
+ #
137
+ # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
138
+ #
139
+ # class Current
140
+ # mattr_accessor :user, instance_accessor: false
141
+ # end
142
+ #
143
+ # Current.new.user = "DHH" # => NoMethodError
144
+ # Current.new.user # => NoMethodError
145
+ def thread_mattr_accessor(*syms)
146
+ thread_mattr_reader(*syms)
147
+ thread_mattr_writer(*syms)
148
+ end
149
+ alias :thread_cattr_accessor :thread_mattr_accessor
150
+ end
@@ -0,0 +1,134 @@
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
+ module Concerning
108
+ # Define a new concern and mix it in.
109
+ def concerning(topic, &block)
110
+ include concern(topic, &block)
111
+ end
112
+
113
+ # A low-cruft shortcut to define a concern.
114
+ #
115
+ # concern :EventTracking do
116
+ # ...
117
+ # end
118
+ #
119
+ # is equivalent to
120
+ #
121
+ # module EventTracking
122
+ # extend ActiveSupport::Concern
123
+ #
124
+ # ...
125
+ # end
126
+ def concern(topic, &module_definition)
127
+ const_set topic, Module.new {
128
+ extend ::ActiveSupport::Concern
129
+ module_eval(&module_definition)
130
+ }
131
+ end
132
+ end
133
+ include Concerning
134
+ end
@@ -1,7 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "active_support/core_ext/regexp"
5
+
1
6
  class Module
7
+ # Error generated by +delegate+ when a method is called on +nil+ and +allow_nil+
8
+ # option is not used.
9
+ class DelegationError < NoMethodError; end
10
+
11
+ RUBY_RESERVED_KEYWORDS = %w(alias and BEGIN begin break case class def defined? do
12
+ else elsif END end ensure false for if in module next nil not or redo rescue retry
13
+ return self super then true undef unless until when while yield)
14
+ DELEGATION_RESERVED_KEYWORDS = %w(_ arg args block)
15
+ DELEGATION_RESERVED_METHOD_NAMES = Set.new(
16
+ RUBY_RESERVED_KEYWORDS + DELEGATION_RESERVED_KEYWORDS
17
+ ).freeze
18
+
2
19
  # Provides a +delegate+ class method to easily expose contained objects'
3
20
  # public methods as your own.
4
21
  #
22
+ # ==== Options
23
+ # * <tt>:to</tt> - Specifies the target object
24
+ # * <tt>:prefix</tt> - Prefixes the new method with the target name or a custom prefix
25
+ # * <tt>:allow_nil</tt> - if set to true, prevents a +Module::DelegationError+
26
+ # from being raised
27
+ #
5
28
  # The macro receives one or more method names (specified as symbols or
6
29
  # strings) and the name of the target object via the <tt>:to</tt> option
7
30
  # (also a symbol or string).
@@ -92,18 +115,16 @@ class Module
92
115
  # invoice.customer_address # => 'Vimmersvej 13'
93
116
  #
94
117
  # If the target is +nil+ and does not respond to the delegated method a
95
- # +NoMethodError+ is raised, as with any other value. Sometimes, however, it
96
- # makes sense to be robust to that situation and that is the purpose of the
97
- # <tt>:allow_nil</tt> option: If the target is not +nil+, or it is and
98
- # responds to the method, everything works as usual. But if it is +nil+ and
99
- # does not respond to the delegated method, +nil+ is returned.
118
+ # +Module::DelegationError+ is raised. If you wish to instead return +nil+,
119
+ # use the <tt>:allow_nil</tt> option.
100
120
  #
101
121
  # class User < ActiveRecord::Base
102
122
  # has_one :profile
103
123
  # delegate :age, to: :profile
104
124
  # end
105
125
  #
106
- # User.new.age # raises NoMethodError: undefined method `age'
126
+ # User.new.age
127
+ # # => Module::DelegationError: User#age delegated to profile.age, but profile is nil
107
128
  #
108
129
  # But if not having a profile yet is fine and should not be an error
109
130
  # condition:
@@ -129,69 +150,138 @@ class Module
129
150
  #
130
151
  # Foo.new("Bar").name # raises NoMethodError: undefined method `name'
131
152
  #
132
- def delegate(*methods)
133
- options = methods.pop
134
- unless options.is_a?(Hash) && to = options[:to]
135
- raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
153
+ # The target method must be public, otherwise it will raise +NoMethodError+.
154
+ def delegate(*methods, to: nil, prefix: nil, allow_nil: nil)
155
+ unless to
156
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter)."
136
157
  end
137
158
 
138
- prefix, allow_nil = options.values_at(:prefix, :allow_nil)
139
-
140
- if prefix == true && to =~ /^[^a-z_]/
141
- raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
159
+ if prefix == true && /^[^a-z_]/.match?(to)
160
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
142
161
  end
143
162
 
144
163
  method_prefix = \
145
164
  if prefix
146
165
  "#{prefix == true ? to : prefix}_"
147
166
  else
148
- ''
167
+ ""
149
168
  end
150
169
 
151
- file, line = caller.first.split(':', 2)
152
- line = line.to_i
170
+ location = caller_locations(1, 1).first
171
+ file, line = location.path, location.lineno
153
172
 
154
173
  to = to.to_s
155
- to = 'self.class' if to == 'class'
174
+ to = "self.#{to}" if DELEGATION_RESERVED_METHOD_NAMES.include?(to)
156
175
 
157
- methods.each do |method|
176
+ methods.map do |method|
158
177
  # Attribute writer methods only accept one argument. Makes sure []=
159
178
  # methods still accept two arguments.
160
- definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'
179
+ definition = /[^\]]=$/.match?(method) ? "arg" : "*args, &block"
161
180
 
162
- # The following generated methods call the target exactly once, storing
181
+ # The following generated method calls the target exactly once, storing
163
182
  # the returned value in a dummy variable.
164
183
  #
165
184
  # Reason is twofold: On one hand doing less calls is in general better.
166
185
  # On the other hand it could be that the target has side-effects,
167
- # whereas conceptualy, from the user point of view, the delegator should
186
+ # whereas conceptually, from the user point of view, the delegator should
168
187
  # be doing one call.
169
188
  if allow_nil
170
- module_eval(<<-EOS, file, line - 3)
171
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
172
- _ = #{to} # _ = client
173
- if !_.nil? || nil.respond_to?(:#{method}) # if !_.nil? || nil.respond_to?(:name)
174
- _.#{method}(#{definition}) # _.name(*args, &block)
175
- end # end
176
- end # end
177
- EOS
189
+ method_def = [
190
+ "def #{method_prefix}#{method}(#{definition})",
191
+ "_ = #{to}",
192
+ "if !_.nil? || nil.respond_to?(:#{method})",
193
+ " _.#{method}(#{definition})",
194
+ "end",
195
+ "end"
196
+ ].join ";"
178
197
  else
179
- exception = %(raise "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
180
-
181
- module_eval(<<-EOS, file, line - 2)
182
- def #{method_prefix}#{method}(#{definition}) # def customer_name(*args, &block)
183
- _ = #{to} # _ = client
184
- _.#{method}(#{definition}) # _.name(*args, &block)
185
- rescue NoMethodError => e # rescue NoMethodError => e
186
- location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, '#{method_prefix}#{method}'] # location = "%s:%d:in `%s'" % [__FILE__, __LINE__ - 2, 'customer_name']
187
- if _.nil? && e.backtrace.first == location # if _.nil? && e.backtrace.first == location
188
- #{exception} # # add helpful message to the exception
189
- else # else
190
- raise # raise
191
- end # end
192
- end # end
193
- EOS
198
+ exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
199
+
200
+ method_def = [
201
+ "def #{method_prefix}#{method}(#{definition})",
202
+ " _ = #{to}",
203
+ " _.#{method}(#{definition})",
204
+ "rescue NoMethodError => e",
205
+ " if _.nil? && e.name == :#{method}",
206
+ " #{exception}",
207
+ " else",
208
+ " raise",
209
+ " end",
210
+ "end"
211
+ ].join ";"
194
212
  end
213
+
214
+ module_eval(method_def, file, line)
195
215
  end
196
216
  end
217
+
218
+ # When building decorators, a common pattern may emerge:
219
+ #
220
+ # class Partition
221
+ # def initialize(event)
222
+ # @event = event
223
+ # end
224
+ #
225
+ # def person
226
+ # @event.detail.person || @event.creator
227
+ # end
228
+ #
229
+ # private
230
+ # def respond_to_missing?(name, include_private = false)
231
+ # @event.respond_to?(name, include_private)
232
+ # end
233
+ #
234
+ # def method_missing(method, *args, &block)
235
+ # @event.send(method, *args, &block)
236
+ # end
237
+ # end
238
+ #
239
+ # With <tt>Module#delegate_missing_to</tt>, the above is condensed to:
240
+ #
241
+ # class Partition
242
+ # delegate_missing_to :@event
243
+ #
244
+ # def initialize(event)
245
+ # @event = event
246
+ # end
247
+ #
248
+ # def person
249
+ # @event.detail.person || @event.creator
250
+ # end
251
+ # end
252
+ #
253
+ # The target can be anything callable within the object, e.g. instance
254
+ # variables, methods, constants, etc.
255
+ #
256
+ # The delegated method must be public on the target, otherwise it will
257
+ # raise +NoMethodError+.
258
+ def delegate_missing_to(target)
259
+ target = target.to_s
260
+ target = "self.#{target}" if DELEGATION_RESERVED_METHOD_NAMES.include?(target)
261
+
262
+ module_eval <<-RUBY, __FILE__, __LINE__ + 1
263
+ def respond_to_missing?(name, include_private = false)
264
+ # It may look like an oversight, but we deliberately do not pass
265
+ # +include_private+, because they do not get delegated.
266
+
267
+ #{target}.respond_to?(name) || super
268
+ end
269
+
270
+ def method_missing(method, *args, &block)
271
+ if #{target}.respond_to?(method)
272
+ #{target}.public_send(method, *args, &block)
273
+ else
274
+ begin
275
+ super
276
+ rescue NoMethodError
277
+ if #{target}.nil?
278
+ raise DelegationError, "\#{method} delegated to #{target}, but #{target} is nil"
279
+ else
280
+ raise
281
+ end
282
+ end
283
+ end
284
+ end
285
+ RUBY
286
+ end
197
287
  end
@@ -1,4 +1,4 @@
1
- require 'active_support/deprecation/method_wrappers'
1
+ # frozen_string_literal: true
2
2
 
3
3
  class Module
4
4
  # deprecate :foo
@@ -15,8 +15,8 @@ class Module
15
15
  #
16
16
  # class MyLib::Deprecator
17
17
  # def deprecation_warning(deprecated_method_name, message, caller_backtrace = nil)
18
- # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
19
- # Kernel.warn message
18
+ # message = "#{deprecated_method_name} is deprecated and will be removed from MyLibrary | #{message}"
19
+ # Kernel.warn message
20
20
  # end
21
21
  # end
22
22
  def deprecate(*method_names)
@@ -1,14 +1,18 @@
1
- require 'active_support/inflector'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/inflector"
2
4
 
3
5
  class Module
4
6
  # Returns the name of the module containing this one.
5
7
  #
6
8
  # M::N.parent_name # => "M"
7
9
  def parent_name
8
- if defined? @parent_name
10
+ if defined?(@parent_name)
9
11
  @parent_name
10
12
  else
11
- @parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
13
+ parent_name = name =~ /::[^:]+\Z/ ? $`.freeze : nil
14
+ @parent_name = parent_name unless frozen?
15
+ parent_name
12
16
  end
13
17
  end
14
18
 
@@ -46,33 +50,13 @@ class Module
46
50
  def parents
47
51
  parents = []
48
52
  if parent_name
49
- parts = parent_name.split('::')
53
+ parts = parent_name.split("::")
50
54
  until parts.empty?
51
- parents << ActiveSupport::Inflector.constantize(parts * '::')
55
+ parents << ActiveSupport::Inflector.constantize(parts * "::")
52
56
  parts.pop
53
57
  end
54
58
  end
55
59
  parents << Object unless parents.include? Object
56
60
  parents
57
61
  end
58
-
59
- def local_constants #:nodoc:
60
- constants(false)
61
- end
62
-
63
- # *DEPRECATED*: Use +local_constants+ instead.
64
- #
65
- # Returns the names of the constants defined locally as strings.
66
- #
67
- # module M
68
- # X = 1
69
- # end
70
- # M.local_constant_names # => ["X"]
71
- #
72
- # This method is useful for forward compatibility, since Ruby 1.8 returns
73
- # constant names as strings, whereas 1.9 returns them as symbols.
74
- def local_constant_names
75
- ActiveSupport::Deprecation.warn 'Module#local_constant_names is deprecated, use Module#local_constants instead'
76
- local_constants.map { |c| c.to_s }
77
- end
78
62
  end
@@ -1,8 +1,11 @@
1
- require 'active_support/core_ext/module/anonymous'
2
- require 'active_support/core_ext/string/inflections'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/anonymous"
4
+ require "active_support/core_ext/string/inflections"
3
5
 
4
6
  class Module
5
7
  def reachable? #:nodoc:
6
8
  !anonymous? && name.safe_constantize.equal?(self)
7
9
  end
10
+ deprecate :reachable?
8
11
  end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Module
4
+ if RUBY_VERSION >= "2.3"
5
+ # Marks the named method as intended to be redefined, if it exists.
6
+ # Suppresses the Ruby method redefinition warning. Prefer
7
+ # #redefine_method where possible.
8
+ def silence_redefinition_of_method(method)
9
+ if method_defined?(method) || private_method_defined?(method)
10
+ # This suppresses the "method redefined" warning; the self-alias
11
+ # looks odd, but means we don't need to generate a unique name
12
+ alias_method method, method
13
+ end
14
+ end
15
+ else
16
+ def silence_redefinition_of_method(method)
17
+ if method_defined?(method) || private_method_defined?(method)
18
+ alias_method :__rails_redefine, method
19
+ remove_method :__rails_redefine
20
+ end
21
+ end
22
+ end
23
+
24
+ # Replaces the existing method definition, if there is one, with the passed
25
+ # block as its body.
26
+ def redefine_method(method, &block)
27
+ visibility = method_visibility(method)
28
+ silence_redefinition_of_method(method)
29
+ define_method(method, &block)
30
+ send(visibility, method)
31
+ end
32
+
33
+ # Replaces the existing singleton method definition, if there is one, with
34
+ # the passed block as its body.
35
+ def redefine_singleton_method(method, &block)
36
+ singleton_class.redefine_method(method, &block)
37
+ end
38
+
39
+ def method_visibility(method) # :nodoc:
40
+ case
41
+ when private_method_defined?(method)
42
+ :private
43
+ when protected_method_defined?(method)
44
+ :protected
45
+ else
46
+ :public
47
+ end
48
+ end
49
+ end
@@ -1,12 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/module/redefine_method"
4
+
1
5
  class Module
6
+ # Removes the named method, if it exists.
2
7
  def remove_possible_method(method)
3
8
  if method_defined?(method) || private_method_defined?(method)
4
9
  undef_method(method)
5
10
  end
6
11
  end
7
12
 
8
- def redefine_method(method, &block)
9
- remove_possible_method(method)
10
- define_method(method, &block)
13
+ # Removes the named singleton method, if it exists.
14
+ def remove_possible_singleton_method(method)
15
+ singleton_class.remove_possible_method(method)
11
16
  end
12
17
  end