activesupport 5.0.7.2

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 (236) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1018 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/lib/active_support.rb +99 -0
  6. data/lib/active_support/all.rb +3 -0
  7. data/lib/active_support/array_inquirer.rb +44 -0
  8. data/lib/active_support/backtrace_cleaner.rb +103 -0
  9. data/lib/active_support/benchmarkable.rb +49 -0
  10. data/lib/active_support/builder.rb +6 -0
  11. data/lib/active_support/cache.rb +701 -0
  12. data/lib/active_support/cache/file_store.rb +204 -0
  13. data/lib/active_support/cache/mem_cache_store.rb +207 -0
  14. data/lib/active_support/cache/memory_store.rb +167 -0
  15. data/lib/active_support/cache/null_store.rb +41 -0
  16. data/lib/active_support/cache/strategy/local_cache.rb +172 -0
  17. data/lib/active_support/cache/strategy/local_cache_middleware.rb +44 -0
  18. data/lib/active_support/callbacks.rb +791 -0
  19. data/lib/active_support/concern.rb +142 -0
  20. data/lib/active_support/concurrency/latch.rb +26 -0
  21. data/lib/active_support/concurrency/share_lock.rb +226 -0
  22. data/lib/active_support/configurable.rb +148 -0
  23. data/lib/active_support/core_ext.rb +4 -0
  24. data/lib/active_support/core_ext/array.rb +7 -0
  25. data/lib/active_support/core_ext/array/access.rb +90 -0
  26. data/lib/active_support/core_ext/array/conversions.rb +211 -0
  27. data/lib/active_support/core_ext/array/extract_options.rb +29 -0
  28. data/lib/active_support/core_ext/array/grouping.rb +107 -0
  29. data/lib/active_support/core_ext/array/inquiry.rb +17 -0
  30. data/lib/active_support/core_ext/array/prepend_and_append.rb +7 -0
  31. data/lib/active_support/core_ext/array/wrap.rb +46 -0
  32. data/lib/active_support/core_ext/benchmark.rb +14 -0
  33. data/lib/active_support/core_ext/big_decimal.rb +1 -0
  34. data/lib/active_support/core_ext/big_decimal/conversions.rb +14 -0
  35. data/lib/active_support/core_ext/class.rb +2 -0
  36. data/lib/active_support/core_ext/class/attribute.rb +128 -0
  37. data/lib/active_support/core_ext/class/attribute_accessors.rb +4 -0
  38. data/lib/active_support/core_ext/class/subclasses.rb +41 -0
  39. data/lib/active_support/core_ext/date.rb +5 -0
  40. data/lib/active_support/core_ext/date/acts_like.rb +8 -0
  41. data/lib/active_support/core_ext/date/blank.rb +12 -0
  42. data/lib/active_support/core_ext/date/calculations.rb +143 -0
  43. data/lib/active_support/core_ext/date/conversions.rb +95 -0
  44. data/lib/active_support/core_ext/date/zones.rb +6 -0
  45. data/lib/active_support/core_ext/date_and_time/calculations.rb +335 -0
  46. data/lib/active_support/core_ext/date_and_time/compatibility.rb +14 -0
  47. data/lib/active_support/core_ext/date_and_time/zones.rb +40 -0
  48. data/lib/active_support/core_ext/date_time.rb +5 -0
  49. data/lib/active_support/core_ext/date_time/acts_like.rb +14 -0
  50. data/lib/active_support/core_ext/date_time/blank.rb +12 -0
  51. data/lib/active_support/core_ext/date_time/calculations.rb +199 -0
  52. data/lib/active_support/core_ext/date_time/compatibility.rb +16 -0
  53. data/lib/active_support/core_ext/date_time/conversions.rb +105 -0
  54. data/lib/active_support/core_ext/digest/uuid.rb +51 -0
  55. data/lib/active_support/core_ext/enumerable.rb +146 -0
  56. data/lib/active_support/core_ext/file.rb +1 -0
  57. data/lib/active_support/core_ext/file/atomic.rb +68 -0
  58. data/lib/active_support/core_ext/hash.rb +9 -0
  59. data/lib/active_support/core_ext/hash/compact.rb +24 -0
  60. data/lib/active_support/core_ext/hash/conversions.rb +262 -0
  61. data/lib/active_support/core_ext/hash/deep_merge.rb +38 -0
  62. data/lib/active_support/core_ext/hash/except.rb +22 -0
  63. data/lib/active_support/core_ext/hash/indifferent_access.rb +23 -0
  64. data/lib/active_support/core_ext/hash/keys.rb +170 -0
  65. data/lib/active_support/core_ext/hash/reverse_merge.rb +22 -0
  66. data/lib/active_support/core_ext/hash/slice.rb +48 -0
  67. data/lib/active_support/core_ext/hash/transform_values.rb +29 -0
  68. data/lib/active_support/core_ext/integer.rb +3 -0
  69. data/lib/active_support/core_ext/integer/inflections.rb +29 -0
  70. data/lib/active_support/core_ext/integer/multiple.rb +10 -0
  71. data/lib/active_support/core_ext/integer/time.rb +29 -0
  72. data/lib/active_support/core_ext/kernel.rb +4 -0
  73. data/lib/active_support/core_ext/kernel/agnostics.rb +11 -0
  74. data/lib/active_support/core_ext/kernel/concern.rb +12 -0
  75. data/lib/active_support/core_ext/kernel/debugger.rb +3 -0
  76. data/lib/active_support/core_ext/kernel/reporting.rb +43 -0
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +6 -0
  78. data/lib/active_support/core_ext/load_error.rb +31 -0
  79. data/lib/active_support/core_ext/marshal.rb +22 -0
  80. data/lib/active_support/core_ext/module.rb +12 -0
  81. data/lib/active_support/core_ext/module/aliasing.rb +74 -0
  82. data/lib/active_support/core_ext/module/anonymous.rb +28 -0
  83. data/lib/active_support/core_ext/module/attr_internal.rb +36 -0
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +212 -0
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +141 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +135 -0
  87. data/lib/active_support/core_ext/module/delegation.rb +216 -0
  88. data/lib/active_support/core_ext/module/deprecation.rb +23 -0
  89. data/lib/active_support/core_ext/module/introspection.rb +68 -0
  90. data/lib/active_support/core_ext/module/method_transplanting.rb +3 -0
  91. data/lib/active_support/core_ext/module/qualified_const.rb +70 -0
  92. data/lib/active_support/core_ext/module/reachable.rb +8 -0
  93. data/lib/active_support/core_ext/module/remove_method.rb +35 -0
  94. data/lib/active_support/core_ext/name_error.rb +31 -0
  95. data/lib/active_support/core_ext/numeric.rb +4 -0
  96. data/lib/active_support/core_ext/numeric/bytes.rb +64 -0
  97. data/lib/active_support/core_ext/numeric/conversions.rb +144 -0
  98. data/lib/active_support/core_ext/numeric/inquiry.rb +26 -0
  99. data/lib/active_support/core_ext/numeric/time.rb +74 -0
  100. data/lib/active_support/core_ext/object.rb +14 -0
  101. data/lib/active_support/core_ext/object/acts_like.rb +10 -0
  102. data/lib/active_support/core_ext/object/blank.rb +143 -0
  103. data/lib/active_support/core_ext/object/conversions.rb +4 -0
  104. data/lib/active_support/core_ext/object/deep_dup.rb +53 -0
  105. data/lib/active_support/core_ext/object/duplicable.rb +124 -0
  106. data/lib/active_support/core_ext/object/inclusion.rb +27 -0
  107. data/lib/active_support/core_ext/object/instance_variables.rb +28 -0
  108. data/lib/active_support/core_ext/object/json.rb +205 -0
  109. data/lib/active_support/core_ext/object/to_param.rb +1 -0
  110. data/lib/active_support/core_ext/object/to_query.rb +84 -0
  111. data/lib/active_support/core_ext/object/try.rb +146 -0
  112. data/lib/active_support/core_ext/object/with_options.rb +69 -0
  113. data/lib/active_support/core_ext/range.rb +4 -0
  114. data/lib/active_support/core_ext/range/conversions.rb +31 -0
  115. data/lib/active_support/core_ext/range/each.rb +21 -0
  116. data/lib/active_support/core_ext/range/include_range.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +8 -0
  118. data/lib/active_support/core_ext/regexp.rb +5 -0
  119. data/lib/active_support/core_ext/securerandom.rb +23 -0
  120. data/lib/active_support/core_ext/string.rb +13 -0
  121. data/lib/active_support/core_ext/string/access.rb +104 -0
  122. data/lib/active_support/core_ext/string/behavior.rb +6 -0
  123. data/lib/active_support/core_ext/string/conversions.rb +57 -0
  124. data/lib/active_support/core_ext/string/exclude.rb +11 -0
  125. data/lib/active_support/core_ext/string/filters.rb +102 -0
  126. data/lib/active_support/core_ext/string/indent.rb +43 -0
  127. data/lib/active_support/core_ext/string/inflections.rb +244 -0
  128. data/lib/active_support/core_ext/string/inquiry.rb +13 -0
  129. data/lib/active_support/core_ext/string/multibyte.rb +53 -0
  130. data/lib/active_support/core_ext/string/output_safety.rb +260 -0
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +4 -0
  132. data/lib/active_support/core_ext/string/strip.rb +23 -0
  133. data/lib/active_support/core_ext/string/zones.rb +14 -0
  134. data/lib/active_support/core_ext/struct.rb +3 -0
  135. data/lib/active_support/core_ext/time.rb +5 -0
  136. data/lib/active_support/core_ext/time/acts_like.rb +8 -0
  137. data/lib/active_support/core_ext/time/calculations.rb +290 -0
  138. data/lib/active_support/core_ext/time/compatibility.rb +14 -0
  139. data/lib/active_support/core_ext/time/conversions.rb +67 -0
  140. data/lib/active_support/core_ext/time/marshal.rb +3 -0
  141. data/lib/active_support/core_ext/time/zones.rb +111 -0
  142. data/lib/active_support/core_ext/uri.rb +24 -0
  143. data/lib/active_support/dependencies.rb +755 -0
  144. data/lib/active_support/dependencies/autoload.rb +77 -0
  145. data/lib/active_support/dependencies/interlock.rb +55 -0
  146. data/lib/active_support/deprecation.rb +43 -0
  147. data/lib/active_support/deprecation/behaviors.rb +90 -0
  148. data/lib/active_support/deprecation/instance_delegator.rb +37 -0
  149. data/lib/active_support/deprecation/method_wrappers.rb +70 -0
  150. data/lib/active_support/deprecation/proxy_wrappers.rb +149 -0
  151. data/lib/active_support/deprecation/reporting.rb +112 -0
  152. data/lib/active_support/descendants_tracker.rb +60 -0
  153. data/lib/active_support/duration.rb +235 -0
  154. data/lib/active_support/duration/iso8601_parser.rb +122 -0
  155. data/lib/active_support/duration/iso8601_serializer.rb +51 -0
  156. data/lib/active_support/evented_file_update_checker.rb +199 -0
  157. data/lib/active_support/execution_wrapper.rb +126 -0
  158. data/lib/active_support/executor.rb +6 -0
  159. data/lib/active_support/file_update_checker.rb +157 -0
  160. data/lib/active_support/gem_version.rb +15 -0
  161. data/lib/active_support/gzip.rb +36 -0
  162. data/lib/active_support/hash_with_indifferent_access.rb +329 -0
  163. data/lib/active_support/i18n.rb +13 -0
  164. data/lib/active_support/i18n_railtie.rb +115 -0
  165. data/lib/active_support/inflections.rb +70 -0
  166. data/lib/active_support/inflector.rb +7 -0
  167. data/lib/active_support/inflector/inflections.rb +242 -0
  168. data/lib/active_support/inflector/methods.rb +390 -0
  169. data/lib/active_support/inflector/transliterate.rb +112 -0
  170. data/lib/active_support/json.rb +2 -0
  171. data/lib/active_support/json/decoding.rb +74 -0
  172. data/lib/active_support/json/encoding.rb +127 -0
  173. data/lib/active_support/key_generator.rb +71 -0
  174. data/lib/active_support/lazy_load_hooks.rb +76 -0
  175. data/lib/active_support/locale/en.yml +135 -0
  176. data/lib/active_support/log_subscriber.rb +109 -0
  177. data/lib/active_support/log_subscriber/test_helper.rb +104 -0
  178. data/lib/active_support/logger.rb +106 -0
  179. data/lib/active_support/logger_silence.rb +28 -0
  180. data/lib/active_support/logger_thread_safe_level.rb +31 -0
  181. data/lib/active_support/message_encryptor.rb +114 -0
  182. data/lib/active_support/message_verifier.rb +134 -0
  183. data/lib/active_support/multibyte.rb +21 -0
  184. data/lib/active_support/multibyte/chars.rb +231 -0
  185. data/lib/active_support/multibyte/unicode.rb +413 -0
  186. data/lib/active_support/notifications.rb +212 -0
  187. data/lib/active_support/notifications/fanout.rb +157 -0
  188. data/lib/active_support/notifications/instrumenter.rb +91 -0
  189. data/lib/active_support/number_helper.rb +368 -0
  190. data/lib/active_support/number_helper/number_converter.rb +182 -0
  191. data/lib/active_support/number_helper/number_to_currency_converter.rb +44 -0
  192. data/lib/active_support/number_helper/number_to_delimited_converter.rb +28 -0
  193. data/lib/active_support/number_helper/number_to_human_converter.rb +68 -0
  194. data/lib/active_support/number_helper/number_to_human_size_converter.rb +62 -0
  195. data/lib/active_support/number_helper/number_to_percentage_converter.rb +12 -0
  196. data/lib/active_support/number_helper/number_to_phone_converter.rb +58 -0
  197. data/lib/active_support/number_helper/number_to_rounded_converter.rb +92 -0
  198. data/lib/active_support/option_merger.rb +25 -0
  199. data/lib/active_support/ordered_hash.rb +48 -0
  200. data/lib/active_support/ordered_options.rb +81 -0
  201. data/lib/active_support/per_thread_registry.rb +58 -0
  202. data/lib/active_support/proxy_object.rb +13 -0
  203. data/lib/active_support/rails.rb +27 -0
  204. data/lib/active_support/railtie.rb +51 -0
  205. data/lib/active_support/reloader.rb +129 -0
  206. data/lib/active_support/rescuable.rb +173 -0
  207. data/lib/active_support/security_utils.rb +27 -0
  208. data/lib/active_support/string_inquirer.rb +26 -0
  209. data/lib/active_support/subscriber.rb +120 -0
  210. data/lib/active_support/tagged_logging.rb +77 -0
  211. data/lib/active_support/test_case.rb +88 -0
  212. data/lib/active_support/testing/assertions.rb +99 -0
  213. data/lib/active_support/testing/autorun.rb +5 -0
  214. data/lib/active_support/testing/constant_lookup.rb +50 -0
  215. data/lib/active_support/testing/declarative.rb +26 -0
  216. data/lib/active_support/testing/deprecation.rb +36 -0
  217. data/lib/active_support/testing/file_fixtures.rb +34 -0
  218. data/lib/active_support/testing/isolation.rb +115 -0
  219. data/lib/active_support/testing/method_call_assertions.rb +41 -0
  220. data/lib/active_support/testing/setup_and_teardown.rb +50 -0
  221. data/lib/active_support/testing/stream.rb +42 -0
  222. data/lib/active_support/testing/tagged_logging.rb +25 -0
  223. data/lib/active_support/testing/time_helpers.rb +136 -0
  224. data/lib/active_support/time.rb +18 -0
  225. data/lib/active_support/time_with_zone.rb +511 -0
  226. data/lib/active_support/values/time_zone.rb +484 -0
  227. data/lib/active_support/values/unicode_tables.dat +0 -0
  228. data/lib/active_support/version.rb +8 -0
  229. data/lib/active_support/xml_mini.rb +209 -0
  230. data/lib/active_support/xml_mini/jdom.rb +181 -0
  231. data/lib/active_support/xml_mini/libxml.rb +77 -0
  232. data/lib/active_support/xml_mini/libxmlsax.rb +82 -0
  233. data/lib/active_support/xml_mini/nokogiri.rb +81 -0
  234. data/lib/active_support/xml_mini/nokogirisax.rb +85 -0
  235. data/lib/active_support/xml_mini/rexml.rb +128 -0
  236. metadata +349 -0
@@ -0,0 +1,142 @@
1
+ module ActiveSupport
2
+ # A typical module looks like this:
3
+ #
4
+ # module M
5
+ # def self.included(base)
6
+ # base.extend ClassMethods
7
+ # base.class_eval do
8
+ # scope :disabled, -> { where(disabled: true) }
9
+ # end
10
+ # end
11
+ #
12
+ # module ClassMethods
13
+ # ...
14
+ # end
15
+ # end
16
+ #
17
+ # By using <tt>ActiveSupport::Concern</tt> the above module could instead be
18
+ # written as:
19
+ #
20
+ # require 'active_support/concern'
21
+ #
22
+ # module M
23
+ # extend ActiveSupport::Concern
24
+ #
25
+ # included do
26
+ # scope :disabled, -> { where(disabled: true) }
27
+ # end
28
+ #
29
+ # class_methods do
30
+ # ...
31
+ # end
32
+ # end
33
+ #
34
+ # Moreover, it gracefully handles module dependencies. Given a +Foo+ module
35
+ # and a +Bar+ module which depends on the former, we would typically write the
36
+ # following:
37
+ #
38
+ # module Foo
39
+ # def self.included(base)
40
+ # base.class_eval do
41
+ # def self.method_injected_by_foo
42
+ # ...
43
+ # end
44
+ # end
45
+ # end
46
+ # end
47
+ #
48
+ # module Bar
49
+ # def self.included(base)
50
+ # base.method_injected_by_foo
51
+ # end
52
+ # end
53
+ #
54
+ # class Host
55
+ # include Foo # We need to include this dependency for Bar
56
+ # include Bar # Bar is the module that Host really needs
57
+ # end
58
+ #
59
+ # But why should +Host+ care about +Bar+'s dependencies, namely +Foo+? We
60
+ # could try to hide these from +Host+ directly including +Foo+ in +Bar+:
61
+ #
62
+ # module Bar
63
+ # include Foo
64
+ # def self.included(base)
65
+ # base.method_injected_by_foo
66
+ # end
67
+ # end
68
+ #
69
+ # class Host
70
+ # include Bar
71
+ # end
72
+ #
73
+ # Unfortunately this won't work, since when +Foo+ is included, its <tt>base</tt>
74
+ # is the +Bar+ module, not the +Host+ class. With <tt>ActiveSupport::Concern</tt>,
75
+ # module dependencies are properly resolved:
76
+ #
77
+ # require 'active_support/concern'
78
+ #
79
+ # module Foo
80
+ # extend ActiveSupport::Concern
81
+ # included do
82
+ # def self.method_injected_by_foo
83
+ # ...
84
+ # end
85
+ # end
86
+ # end
87
+ #
88
+ # module Bar
89
+ # extend ActiveSupport::Concern
90
+ # include Foo
91
+ #
92
+ # included do
93
+ # self.method_injected_by_foo
94
+ # end
95
+ # end
96
+ #
97
+ # class Host
98
+ # include Bar # It works, now Bar takes care of its dependencies
99
+ # end
100
+ module Concern
101
+ class MultipleIncludedBlocks < StandardError #:nodoc:
102
+ def initialize
103
+ super "Cannot define multiple 'included' blocks for a Concern"
104
+ end
105
+ end
106
+
107
+ def self.extended(base) #:nodoc:
108
+ base.instance_variable_set(:@_dependencies, [])
109
+ end
110
+
111
+ def append_features(base)
112
+ if base.instance_variable_defined?(:@_dependencies)
113
+ base.instance_variable_get(:@_dependencies) << self
114
+ return false
115
+ else
116
+ return false if base < self
117
+ @_dependencies.each { |dep| base.include(dep) }
118
+ super
119
+ base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods)
120
+ base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block)
121
+ end
122
+ end
123
+
124
+ def included(base = nil, &block)
125
+ if base.nil?
126
+ raise MultipleIncludedBlocks if instance_variable_defined?(:@_included_block)
127
+
128
+ @_included_block = block
129
+ else
130
+ super
131
+ end
132
+ end
133
+
134
+ def class_methods(&class_methods_module_definition)
135
+ mod = const_defined?(:ClassMethods, false) ?
136
+ const_get(:ClassMethods) :
137
+ const_set(:ClassMethods, Module.new)
138
+
139
+ mod.module_eval(&class_methods_module_definition)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,26 @@
1
+ require 'concurrent/atomic/count_down_latch'
2
+
3
+ module ActiveSupport
4
+ module Concurrency
5
+ class Latch
6
+
7
+ def initialize(count = 1)
8
+ if count == 1
9
+ ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::Event instead.")
10
+ else
11
+ ActiveSupport::Deprecation.warn("ActiveSupport::Concurrency::Latch is deprecated. Please use Concurrent::CountDownLatch instead.")
12
+ end
13
+
14
+ @inner = Concurrent::CountDownLatch.new(count)
15
+ end
16
+
17
+ def release
18
+ @inner.count_down
19
+ end
20
+
21
+ def await
22
+ @inner.wait(nil)
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,226 @@
1
+ require 'thread'
2
+ require 'monitor'
3
+
4
+ module ActiveSupport
5
+ module Concurrency
6
+ # A share/exclusive lock, otherwise known as a read/write lock.
7
+ #
8
+ # https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
9
+ class ShareLock
10
+ include MonitorMixin
11
+
12
+ # We track Thread objects, instead of just using counters, because
13
+ # we need exclusive locks to be reentrant, and we need to be able
14
+ # to upgrade share locks to exclusive.
15
+
16
+
17
+ def raw_state # :nodoc:
18
+ synchronize do
19
+ threads = @sleeping.keys | @sharing.keys | @waiting.keys
20
+ threads |= [@exclusive_thread] if @exclusive_thread
21
+
22
+ data = {}
23
+
24
+ threads.each do |thread|
25
+ purpose, compatible = @waiting[thread]
26
+
27
+ data[thread] = {
28
+ thread: thread,
29
+ sharing: @sharing[thread],
30
+ exclusive: @exclusive_thread == thread,
31
+ purpose: purpose,
32
+ compatible: compatible,
33
+ waiting: !!@waiting[thread],
34
+ sleeper: @sleeping[thread],
35
+ }
36
+ end
37
+
38
+ # NB: Yields while holding our *internal* synchronize lock,
39
+ # which is supposed to be used only for a few instructions at
40
+ # a time. This allows the caller to inspect additional state
41
+ # without things changing out from underneath, but would have
42
+ # disastrous effects upon normal operation. Fortunately, this
43
+ # method is only intended to be called when things have
44
+ # already gone wrong.
45
+ yield data
46
+ end
47
+ end
48
+
49
+ def initialize
50
+ super()
51
+
52
+ @cv = new_cond
53
+
54
+ @sharing = Hash.new(0)
55
+ @waiting = {}
56
+ @sleeping = {}
57
+ @exclusive_thread = nil
58
+ @exclusive_depth = 0
59
+ end
60
+
61
+ # Returns false if +no_wait+ is set and the lock is not
62
+ # immediately available. Otherwise, returns true after the lock
63
+ # has been acquired.
64
+ #
65
+ # +purpose+ and +compatible+ work together; while this thread is
66
+ # waiting for the exclusive lock, it will yield its share (if any)
67
+ # to any other attempt whose +purpose+ appears in this attempt's
68
+ # +compatible+ list. This allows a "loose" upgrade, which, being
69
+ # less strict, prevents some classes of deadlocks.
70
+ #
71
+ # For many resources, loose upgrades are sufficient: if a thread
72
+ # is awaiting a lock, it is not running any other code. With
73
+ # +purpose+ matching, it is possible to yield only to other
74
+ # threads whose activity will not interfere.
75
+ def start_exclusive(purpose: nil, compatible: [], no_wait: false)
76
+ synchronize do
77
+ unless @exclusive_thread == Thread.current
78
+ if busy_for_exclusive?(purpose)
79
+ return false if no_wait
80
+
81
+ yield_shares(purpose: purpose, compatible: compatible, block_share: true) do
82
+ wait_for(:start_exclusive) { busy_for_exclusive?(purpose) }
83
+ end
84
+ end
85
+ @exclusive_thread = Thread.current
86
+ end
87
+ @exclusive_depth += 1
88
+
89
+ true
90
+ end
91
+ end
92
+
93
+ # Relinquish the exclusive lock. Must only be called by the thread
94
+ # that called start_exclusive (and currently holds the lock).
95
+ def stop_exclusive(compatible: [])
96
+ synchronize do
97
+ raise "invalid unlock" if @exclusive_thread != Thread.current
98
+
99
+ @exclusive_depth -= 1
100
+ if @exclusive_depth == 0
101
+ @exclusive_thread = nil
102
+
103
+ if eligible_waiters?(compatible)
104
+ yield_shares(compatible: compatible, block_share: true) do
105
+ wait_for(:stop_exclusive) { @exclusive_thread || eligible_waiters?(compatible) }
106
+ end
107
+ end
108
+ @cv.broadcast
109
+ end
110
+ end
111
+ end
112
+
113
+ def start_sharing
114
+ synchronize do
115
+ if @sharing[Thread.current] > 0 || @exclusive_thread == Thread.current
116
+ # We already hold a lock; nothing to wait for
117
+ elsif @waiting[Thread.current]
118
+ # We're nested inside a +yield_shares+ call: we'll resume as
119
+ # soon as there isn't an exclusive lock in our way
120
+ wait_for(:start_sharing) { @exclusive_thread }
121
+ else
122
+ # This is an initial / outermost share call: any outstanding
123
+ # requests for an exclusive lock get to go first
124
+ wait_for(:start_sharing) { busy_for_sharing?(false) }
125
+ end
126
+ @sharing[Thread.current] += 1
127
+ end
128
+ end
129
+
130
+ def stop_sharing
131
+ synchronize do
132
+ if @sharing[Thread.current] > 1
133
+ @sharing[Thread.current] -= 1
134
+ else
135
+ @sharing.delete Thread.current
136
+ @cv.broadcast
137
+ end
138
+ end
139
+ end
140
+
141
+ # Execute the supplied block while holding the Exclusive lock. If
142
+ # +no_wait+ is set and the lock is not immediately available,
143
+ # returns +nil+ without yielding. Otherwise, returns the result of
144
+ # the block.
145
+ #
146
+ # See +start_exclusive+ for other options.
147
+ def exclusive(purpose: nil, compatible: [], after_compatible: [], no_wait: false)
148
+ if start_exclusive(purpose: purpose, compatible: compatible, no_wait: no_wait)
149
+ begin
150
+ yield
151
+ ensure
152
+ stop_exclusive(compatible: after_compatible)
153
+ end
154
+ end
155
+ end
156
+
157
+ # Execute the supplied block while holding the Share lock.
158
+ def sharing
159
+ start_sharing
160
+ begin
161
+ yield
162
+ ensure
163
+ stop_sharing
164
+ end
165
+ end
166
+
167
+ # Temporarily give up all held Share locks while executing the
168
+ # supplied block, allowing any +compatible+ exclusive lock request
169
+ # to proceed.
170
+ def yield_shares(purpose: nil, compatible: [], block_share: false)
171
+ loose_shares = previous_wait = nil
172
+ synchronize do
173
+ if loose_shares = @sharing.delete(Thread.current)
174
+ if previous_wait = @waiting[Thread.current]
175
+ purpose = nil unless purpose == previous_wait[0]
176
+ compatible &= previous_wait[1]
177
+ end
178
+ compatible |= [false] unless block_share
179
+ @waiting[Thread.current] = [purpose, compatible]
180
+ end
181
+
182
+ @cv.broadcast
183
+ end
184
+
185
+ begin
186
+ yield
187
+ ensure
188
+ synchronize do
189
+ wait_for(:yield_shares) { @exclusive_thread && @exclusive_thread != Thread.current }
190
+
191
+ if previous_wait
192
+ @waiting[Thread.current] = previous_wait
193
+ else
194
+ @waiting.delete Thread.current
195
+ end
196
+ @sharing[Thread.current] = loose_shares if loose_shares
197
+ end
198
+ end
199
+ end
200
+
201
+ private
202
+
203
+ # Must be called within synchronize
204
+ def busy_for_exclusive?(purpose)
205
+ busy_for_sharing?(purpose) ||
206
+ @sharing.size > (@sharing[Thread.current] > 0 ? 1 : 0)
207
+ end
208
+
209
+ def busy_for_sharing?(purpose)
210
+ (@exclusive_thread && @exclusive_thread != Thread.current) ||
211
+ @waiting.any? { |t, (_, c)| t != Thread.current && !c.include?(purpose) }
212
+ end
213
+
214
+ def eligible_waiters?(compatible)
215
+ @waiting.any? { |t, (p, _)| compatible.include?(p) && @waiting.all? { |t2, (_, c2)| t == t2 || c2.include?(p) } }
216
+ end
217
+
218
+ def wait_for(method)
219
+ @sleeping[Thread.current] = method
220
+ @cv.wait_while { yield }
221
+ ensure
222
+ @sleeping.delete Thread.current
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,148 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/ordered_options'
3
+ require 'active_support/core_ext/array/extract_options'
4
+
5
+ module ActiveSupport
6
+ # Configurable provides a <tt>config</tt> method to store and retrieve
7
+ # configuration options as an <tt>OrderedHash</tt>.
8
+ module Configurable
9
+ extend ActiveSupport::Concern
10
+
11
+ class Configuration < ActiveSupport::InheritableOptions
12
+ def compile_methods!
13
+ self.class.compile_methods!(keys)
14
+ end
15
+
16
+ # Compiles reader methods so we don't have to go through method_missing.
17
+ def self.compile_methods!(keys)
18
+ keys.reject { |m| method_defined?(m) }.each do |key|
19
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
20
+ def #{key}; _get(#{key.inspect}); end
21
+ RUBY
22
+ end
23
+ end
24
+ end
25
+
26
+ module ClassMethods
27
+ def config
28
+ @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
29
+ superclass.config.inheritable_copy
30
+ else
31
+ # create a new "anonymous" class that will host the compiled reader methods
32
+ Class.new(Configuration).new
33
+ end
34
+ end
35
+
36
+ def configure
37
+ yield config
38
+ end
39
+
40
+ # Allows you to add shortcut so that you don't have to refer to attribute
41
+ # through config. Also look at the example for config to contrast.
42
+ #
43
+ # Defines both class and instance config accessors.
44
+ #
45
+ # class User
46
+ # include ActiveSupport::Configurable
47
+ # config_accessor :allowed_access
48
+ # end
49
+ #
50
+ # User.allowed_access # => nil
51
+ # User.allowed_access = false
52
+ # User.allowed_access # => false
53
+ #
54
+ # user = User.new
55
+ # user.allowed_access # => false
56
+ # user.allowed_access = true
57
+ # user.allowed_access # => true
58
+ #
59
+ # User.allowed_access # => false
60
+ #
61
+ # The attribute name must be a valid method name in Ruby.
62
+ #
63
+ # class User
64
+ # include ActiveSupport::Configurable
65
+ # config_accessor :"1_Badname"
66
+ # end
67
+ # # => NameError: invalid config attribute name
68
+ #
69
+ # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
70
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
71
+ #
72
+ # class User
73
+ # include ActiveSupport::Configurable
74
+ # config_accessor :allowed_access, instance_reader: false, instance_writer: false
75
+ # end
76
+ #
77
+ # User.allowed_access = false
78
+ # User.allowed_access # => false
79
+ #
80
+ # User.new.allowed_access = true # => NoMethodError
81
+ # User.new.allowed_access # => NoMethodError
82
+ #
83
+ # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
84
+ #
85
+ # class User
86
+ # include ActiveSupport::Configurable
87
+ # config_accessor :allowed_access, instance_accessor: false
88
+ # end
89
+ #
90
+ # User.allowed_access = false
91
+ # User.allowed_access # => false
92
+ #
93
+ # User.new.allowed_access = true # => NoMethodError
94
+ # User.new.allowed_access # => NoMethodError
95
+ #
96
+ # Also you can pass a block to set up the attribute with a default value.
97
+ #
98
+ # class User
99
+ # include ActiveSupport::Configurable
100
+ # config_accessor :hair_colors do
101
+ # [:brown, :black, :blonde, :red]
102
+ # end
103
+ # end
104
+ #
105
+ # User.hair_colors # => [:brown, :black, :blonde, :red]
106
+ def config_accessor(*names)
107
+ options = names.extract_options!
108
+
109
+ names.each do |name|
110
+ raise NameError.new('invalid config attribute name') unless name =~ /\A[_A-Za-z]\w*\z/
111
+
112
+ reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
113
+ writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
114
+
115
+ singleton_class.class_eval reader, __FILE__, reader_line
116
+ singleton_class.class_eval writer, __FILE__, writer_line
117
+
118
+ unless options[:instance_accessor] == false
119
+ class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
120
+ class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
121
+ end
122
+ send("#{name}=", yield) if block_given?
123
+ end
124
+ end
125
+ private :config_accessor
126
+ end
127
+
128
+ # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
129
+ #
130
+ # require 'active_support/configurable'
131
+ #
132
+ # class User
133
+ # include ActiveSupport::Configurable
134
+ # end
135
+ #
136
+ # user = User.new
137
+ #
138
+ # user.config.allowed_access = true
139
+ # user.config.level = 1
140
+ #
141
+ # user.config.allowed_access # => true
142
+ # user.config.level # => 1
143
+ def config
144
+ @_config ||= self.class.config.inheritable_copy
145
+ end
146
+ end
147
+ end
148
+