activesupport 5.0.7.1

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 +1013 -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 +350 -0
@@ -0,0 +1,24 @@
1
+ require 'uri'
2
+ str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
3
+ parser = URI::Parser.new
4
+
5
+ unless str == parser.unescape(parser.escape(str))
6
+ URI::Parser.class_eval do
7
+ remove_method :unescape
8
+ def unescape(str, escaped = /%[a-fA-F\d]{2}/)
9
+ # TODO: Are we actually sure that ASCII == UTF-8?
10
+ # YK: My initial experiments say yes, but let's be sure please
11
+ enc = str.encoding
12
+ enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
13
+ str.gsub(escaped) { |match| [match[1, 2].hex].pack('C') }.force_encoding(enc)
14
+ end
15
+ end
16
+ end
17
+
18
+ module URI
19
+ class << self
20
+ def parser
21
+ @parser ||= URI::Parser.new
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,755 @@
1
+ require 'set'
2
+ require 'thread'
3
+ require 'concurrent/map'
4
+ require 'pathname'
5
+ require 'active_support/core_ext/module/aliasing'
6
+ require 'active_support/core_ext/module/attribute_accessors'
7
+ require 'active_support/core_ext/module/introspection'
8
+ require 'active_support/core_ext/module/anonymous'
9
+ require 'active_support/core_ext/module/qualified_const'
10
+ require 'active_support/core_ext/object/blank'
11
+ require 'active_support/core_ext/kernel/reporting'
12
+ require 'active_support/core_ext/load_error'
13
+ require 'active_support/core_ext/name_error'
14
+ require 'active_support/core_ext/string/starts_ends_with'
15
+ require "active_support/dependencies/interlock"
16
+ require 'active_support/inflector'
17
+
18
+ module ActiveSupport #:nodoc:
19
+ module Dependencies #:nodoc:
20
+ extend self
21
+
22
+ mattr_accessor :interlock
23
+ self.interlock = Interlock.new
24
+
25
+ # :doc:
26
+
27
+ # Execute the supplied block without interference from any
28
+ # concurrent loads.
29
+ def self.run_interlock
30
+ Dependencies.interlock.running { yield }
31
+ end
32
+
33
+ # Execute the supplied block while holding an exclusive lock,
34
+ # preventing any other thread from being inside a #run_interlock
35
+ # block at the same time.
36
+ def self.load_interlock
37
+ Dependencies.interlock.loading { yield }
38
+ end
39
+
40
+ # Execute the supplied block while holding an exclusive lock,
41
+ # preventing any other thread from being inside a #run_interlock
42
+ # block at the same time.
43
+ def self.unload_interlock
44
+ Dependencies.interlock.unloading { yield }
45
+ end
46
+
47
+ # :nodoc:
48
+
49
+ # Should we turn on Ruby warnings on the first load of dependent files?
50
+ mattr_accessor :warnings_on_first_load
51
+ self.warnings_on_first_load = false
52
+
53
+ # All files ever loaded.
54
+ mattr_accessor :history
55
+ self.history = Set.new
56
+
57
+ # All files currently loaded.
58
+ mattr_accessor :loaded
59
+ self.loaded = Set.new
60
+
61
+ # Stack of files being loaded.
62
+ mattr_accessor :loading
63
+ self.loading = []
64
+
65
+ # Should we load files or require them?
66
+ mattr_accessor :mechanism
67
+ self.mechanism = ENV['NO_RELOAD'] ? :require : :load
68
+
69
+ # The set of directories from which we may automatically load files. Files
70
+ # under these directories will be reloaded on each request in development mode,
71
+ # unless the directory also appears in autoload_once_paths.
72
+ mattr_accessor :autoload_paths
73
+ self.autoload_paths = []
74
+
75
+ # The set of directories from which automatically loaded constants are loaded
76
+ # only once. All directories in this set must also be present in +autoload_paths+.
77
+ mattr_accessor :autoload_once_paths
78
+ self.autoload_once_paths = []
79
+
80
+ # An array of qualified constant names that have been loaded. Adding a name
81
+ # to this array will cause it to be unloaded the next time Dependencies are
82
+ # cleared.
83
+ mattr_accessor :autoloaded_constants
84
+ self.autoloaded_constants = []
85
+
86
+ # An array of constant names that need to be unloaded on every request. Used
87
+ # to allow arbitrary constants to be marked for unloading.
88
+ mattr_accessor :explicitly_unloadable_constants
89
+ self.explicitly_unloadable_constants = []
90
+
91
+ # The WatchStack keeps a stack of the modules being watched as files are
92
+ # loaded. If a file in the process of being loaded (parent.rb) triggers the
93
+ # load of another file (child.rb) the stack will ensure that child.rb
94
+ # handles the new constants.
95
+ #
96
+ # If child.rb is being autoloaded, its constants will be added to
97
+ # autoloaded_constants. If it was being `require`d, they will be discarded.
98
+ #
99
+ # This is handled by walking back up the watch stack and adding the constants
100
+ # found by child.rb to the list of original constants in parent.rb.
101
+ class WatchStack
102
+ include Enumerable
103
+
104
+ # @watching is a stack of lists of constants being watched. For instance,
105
+ # if parent.rb is autoloaded, the stack will look like [[Object]]. If
106
+ # parent.rb then requires namespace/child.rb, the stack will look like
107
+ # [[Object], [Namespace]].
108
+
109
+ def initialize
110
+ @watching = []
111
+ @stack = Hash.new { |h,k| h[k] = [] }
112
+ end
113
+
114
+ def each(&block)
115
+ @stack.each(&block)
116
+ end
117
+
118
+ def watching?
119
+ !@watching.empty?
120
+ end
121
+
122
+ # Returns a list of new constants found since the last call to
123
+ # <tt>watch_namespaces</tt>.
124
+ def new_constants
125
+ constants = []
126
+
127
+ # Grab the list of namespaces that we're looking for new constants under
128
+ @watching.last.each do |namespace|
129
+ # Retrieve the constants that were present under the namespace when watch_namespaces
130
+ # was originally called
131
+ original_constants = @stack[namespace].last
132
+
133
+ mod = Inflector.constantize(namespace) if Dependencies.qualified_const_defined?(namespace)
134
+ next unless mod.is_a?(Module)
135
+
136
+ # Get a list of the constants that were added
137
+ new_constants = mod.constants(false) - original_constants
138
+
139
+ # @stack[namespace] returns an Array of the constants that are being evaluated
140
+ # for that namespace. For instance, if parent.rb requires child.rb, the first
141
+ # element of @stack[Object] will be an Array of the constants that were present
142
+ # before parent.rb was required. The second element will be an Array of the
143
+ # constants that were present before child.rb was required.
144
+ @stack[namespace].each do |namespace_constants|
145
+ namespace_constants.concat(new_constants)
146
+ end
147
+
148
+ # Normalize the list of new constants, and add them to the list we will return
149
+ new_constants.each do |suffix|
150
+ constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
151
+ end
152
+ end
153
+ constants
154
+ ensure
155
+ # A call to new_constants is always called after a call to watch_namespaces
156
+ pop_modules(@watching.pop)
157
+ end
158
+
159
+ # Add a set of modules to the watch stack, remembering the initial
160
+ # constants.
161
+ def watch_namespaces(namespaces)
162
+ @watching << namespaces.map do |namespace|
163
+ module_name = Dependencies.to_constant_name(namespace)
164
+ original_constants = Dependencies.qualified_const_defined?(module_name) ?
165
+ Inflector.constantize(module_name).constants(false) : []
166
+
167
+ @stack[module_name] << original_constants
168
+ module_name
169
+ end
170
+ end
171
+
172
+ private
173
+ def pop_modules(modules)
174
+ modules.each { |mod| @stack[mod].pop }
175
+ end
176
+ end
177
+
178
+ # An internal stack used to record which constants are loaded by any block.
179
+ mattr_accessor :constant_watch_stack
180
+ self.constant_watch_stack = WatchStack.new
181
+
182
+ # Module includes this module.
183
+ module ModuleConstMissing #:nodoc:
184
+ def self.append_features(base)
185
+ base.class_eval do
186
+ # Emulate #exclude via an ivar
187
+ return if defined?(@_const_missing) && @_const_missing
188
+ @_const_missing = instance_method(:const_missing)
189
+ remove_method(:const_missing)
190
+ end
191
+ super
192
+ end
193
+
194
+ def self.exclude_from(base)
195
+ base.class_eval do
196
+ define_method :const_missing, @_const_missing
197
+ @_const_missing = nil
198
+ end
199
+ end
200
+
201
+ def const_missing(const_name)
202
+ from_mod = anonymous? ? guess_for_anonymous(const_name) : self
203
+ Dependencies.load_missing_constant(from_mod, const_name)
204
+ end
205
+
206
+ # We assume that the name of the module reflects the nesting
207
+ # (unless it can be proven that is not the case) and the path to the file
208
+ # that defines the constant. Anonymous modules cannot follow these
209
+ # conventions and therefore we assume that the user wants to refer to a
210
+ # top-level constant.
211
+ def guess_for_anonymous(const_name)
212
+ if Object.const_defined?(const_name)
213
+ raise NameError.new "#{const_name} cannot be autoloaded from an anonymous class or module", const_name
214
+ else
215
+ Object
216
+ end
217
+ end
218
+
219
+ def unloadable(const_desc = self)
220
+ super(const_desc)
221
+ end
222
+ end
223
+
224
+ # Object includes this module.
225
+ module Loadable #:nodoc:
226
+ def self.exclude_from(base)
227
+ base.class_eval do
228
+ define_method(:load, Kernel.instance_method(:load))
229
+ private :load
230
+ end
231
+ end
232
+
233
+ def require_or_load(file_name)
234
+ Dependencies.require_or_load(file_name)
235
+ end
236
+
237
+ # Interprets a file using <tt>mechanism</tt> and marks its defined
238
+ # constants as autoloaded. <tt>file_name</tt> can be either a string or
239
+ # respond to <tt>to_path</tt>.
240
+ #
241
+ # Use this method in code that absolutely needs a certain constant to be
242
+ # defined at that point. A typical use case is to make constant name
243
+ # resolution deterministic for constants with the same relative name in
244
+ # different namespaces whose evaluation would depend on load order
245
+ # otherwise.
246
+ def require_dependency(file_name, message = "No such file to load -- %s")
247
+ file_name = file_name.to_path if file_name.respond_to?(:to_path)
248
+ unless file_name.is_a?(String)
249
+ raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
250
+ end
251
+
252
+ Dependencies.depend_on(file_name, message)
253
+ end
254
+
255
+ def load_dependency(file)
256
+ if Dependencies.load? && Dependencies.constant_watch_stack.watching?
257
+ Dependencies.new_constants_in(Object) { yield }
258
+ else
259
+ yield
260
+ end
261
+ rescue Exception => exception # errors from loading file
262
+ exception.blame_file! file if exception.respond_to? :blame_file!
263
+ raise
264
+ end
265
+
266
+ # Mark the given constant as unloadable. Unloadable constants are removed
267
+ # each time dependencies are cleared.
268
+ #
269
+ # Note that marking a constant for unloading need only be done once. Setup
270
+ # or init scripts may list each unloadable constant that may need unloading;
271
+ # each constant will be removed for every subsequent clear, as opposed to
272
+ # for the first clear.
273
+ #
274
+ # The provided constant descriptor may be a (non-anonymous) module or class,
275
+ # or a qualified constant name as a string or symbol.
276
+ #
277
+ # Returns +true+ if the constant was not previously marked for unloading,
278
+ # +false+ otherwise.
279
+ def unloadable(const_desc)
280
+ Dependencies.mark_for_unload const_desc
281
+ end
282
+
283
+ private
284
+
285
+ def load(file, wrap = false)
286
+ result = false
287
+ load_dependency(file) { result = super }
288
+ result
289
+ end
290
+
291
+ def require(file)
292
+ result = false
293
+ load_dependency(file) { result = super }
294
+ result
295
+ end
296
+ end
297
+
298
+ # Exception file-blaming.
299
+ module Blamable #:nodoc:
300
+ def blame_file!(file)
301
+ (@blamed_files ||= []).unshift file
302
+ end
303
+
304
+ def blamed_files
305
+ @blamed_files ||= []
306
+ end
307
+
308
+ def describe_blame
309
+ return nil if blamed_files.empty?
310
+ "This error occurred while loading the following files:\n #{blamed_files.join "\n "}"
311
+ end
312
+
313
+ def copy_blame!(exc)
314
+ @blamed_files = exc.blamed_files.clone
315
+ self
316
+ end
317
+ end
318
+
319
+ def hook!
320
+ Object.class_eval { include Loadable }
321
+ Module.class_eval { include ModuleConstMissing }
322
+ Exception.class_eval { include Blamable }
323
+ end
324
+
325
+ def unhook!
326
+ ModuleConstMissing.exclude_from(Module)
327
+ Loadable.exclude_from(Object)
328
+ end
329
+
330
+ def load?
331
+ mechanism == :load
332
+ end
333
+
334
+ def depend_on(file_name, message = "No such file to load -- %s.rb")
335
+ path = search_for_file(file_name)
336
+ require_or_load(path || file_name)
337
+ rescue LoadError => load_error
338
+ if file_name = load_error.message[/ -- (.*?)(\.rb)?$/, 1]
339
+ load_error.message.replace(message % file_name)
340
+ load_error.copy_blame!(load_error)
341
+ end
342
+ raise
343
+ end
344
+
345
+ def clear
346
+ Dependencies.unload_interlock do
347
+ loaded.clear
348
+ loading.clear
349
+ remove_unloadable_constants!
350
+ end
351
+ end
352
+
353
+ def require_or_load(file_name, const_path = nil)
354
+ file_name = $` if file_name =~ /\.rb\z/
355
+ expanded = File.expand_path(file_name)
356
+ return if loaded.include?(expanded)
357
+
358
+ Dependencies.load_interlock do
359
+ # Maybe it got loaded while we were waiting for our lock:
360
+ return if loaded.include?(expanded)
361
+
362
+ # Record that we've seen this file *before* loading it to avoid an
363
+ # infinite loop with mutual dependencies.
364
+ loaded << expanded
365
+ loading << expanded
366
+
367
+ begin
368
+ if load?
369
+ # Enable warnings if this file has not been loaded before and
370
+ # warnings_on_first_load is set.
371
+ load_args = ["#{file_name}.rb"]
372
+ load_args << const_path unless const_path.nil?
373
+
374
+ if !warnings_on_first_load or history.include?(expanded)
375
+ result = load_file(*load_args)
376
+ else
377
+ enable_warnings { result = load_file(*load_args) }
378
+ end
379
+ else
380
+ result = require file_name
381
+ end
382
+ rescue Exception
383
+ loaded.delete expanded
384
+ raise
385
+ ensure
386
+ loading.pop
387
+ end
388
+
389
+ # Record history *after* loading so first load gets warnings.
390
+ history << expanded
391
+ result
392
+ end
393
+ end
394
+
395
+ # Is the provided constant path defined?
396
+ def qualified_const_defined?(path)
397
+ Object.const_defined?(path, false)
398
+ end
399
+
400
+ # Given +path+, a filesystem path to a ruby file, return an array of
401
+ # constant paths which would cause Dependencies to attempt to load this
402
+ # file.
403
+ def loadable_constants_for_path(path, bases = autoload_paths)
404
+ path = $` if path =~ /\.rb\z/
405
+ expanded_path = File.expand_path(path)
406
+ paths = []
407
+
408
+ bases.each do |root|
409
+ expanded_root = File.expand_path(root)
410
+ next unless expanded_path.start_with?(expanded_root)
411
+
412
+ root_size = expanded_root.size
413
+ next if expanded_path[root_size] != ?/.freeze
414
+
415
+ nesting = expanded_path[(root_size + 1)..-1]
416
+ paths << nesting.camelize unless nesting.blank?
417
+ end
418
+
419
+ paths.uniq!
420
+ paths
421
+ end
422
+
423
+ # Search for a file in autoload_paths matching the provided suffix.
424
+ def search_for_file(path_suffix)
425
+ path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
426
+
427
+ autoload_paths.each do |root|
428
+ path = File.join(root, path_suffix)
429
+ return path if File.file? path
430
+ end
431
+ nil # Gee, I sure wish we had first_match ;-)
432
+ end
433
+
434
+ # Does the provided path_suffix correspond to an autoloadable module?
435
+ # Instead of returning a boolean, the autoload base for this module is
436
+ # returned.
437
+ def autoloadable_module?(path_suffix)
438
+ autoload_paths.each do |load_path|
439
+ return load_path if File.directory? File.join(load_path, path_suffix)
440
+ end
441
+ nil
442
+ end
443
+
444
+ def load_once_path?(path)
445
+ # to_s works around a ruby issue where String#starts_with?(Pathname)
446
+ # will raise a TypeError: no implicit conversion of Pathname into String
447
+ autoload_once_paths.any? { |base| path.starts_with? base.to_s }
448
+ end
449
+
450
+ # Attempt to autoload the provided module name by searching for a directory
451
+ # matching the expected path suffix. If found, the module is created and
452
+ # assigned to +into+'s constants with the name +const_name+. Provided that
453
+ # the directory was loaded from a reloadable base path, it is added to the
454
+ # set of constants that are to be unloaded.
455
+ def autoload_module!(into, const_name, qualified_name, path_suffix)
456
+ return nil unless base_path = autoloadable_module?(path_suffix)
457
+ mod = Module.new
458
+ into.const_set const_name, mod
459
+ autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
460
+ autoloaded_constants.uniq!
461
+ mod
462
+ end
463
+
464
+ # Load the file at the provided path. +const_paths+ is a set of qualified
465
+ # constant names. When loading the file, Dependencies will watch for the
466
+ # addition of these constants. Each that is defined will be marked as
467
+ # autoloaded, and will be removed when Dependencies.clear is next called.
468
+ #
469
+ # If the second parameter is left off, then Dependencies will construct a
470
+ # set of names that the file at +path+ may define. See
471
+ # +loadable_constants_for_path+ for more details.
472
+ def load_file(path, const_paths = loadable_constants_for_path(path))
473
+ const_paths = [const_paths].compact unless const_paths.is_a? Array
474
+ parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
475
+
476
+ result = nil
477
+ newly_defined_paths = new_constants_in(*parent_paths) do
478
+ result = Kernel.load path
479
+ end
480
+
481
+ autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
482
+ autoloaded_constants.uniq!
483
+ result
484
+ end
485
+
486
+ # Returns the constant path for the provided parent and constant name.
487
+ def qualified_name_for(mod, name)
488
+ mod_name = to_constant_name mod
489
+ mod_name == "Object" ? name.to_s : "#{mod_name}::#{name}"
490
+ end
491
+
492
+ # Load the constant named +const_name+ which is missing from +from_mod+. If
493
+ # it is not possible to load the constant into from_mod, try its parent
494
+ # module using +const_missing+.
495
+ def load_missing_constant(from_mod, const_name)
496
+ unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
497
+ raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
498
+ end
499
+
500
+ qualified_name = qualified_name_for from_mod, const_name
501
+ path_suffix = qualified_name.underscore
502
+
503
+ file_path = search_for_file(path_suffix)
504
+
505
+ if file_path
506
+ expanded = File.expand_path(file_path)
507
+ expanded.sub!(/\.rb\z/, ''.freeze)
508
+
509
+ if loading.include?(expanded)
510
+ raise "Circular dependency detected while autoloading constant #{qualified_name}"
511
+ else
512
+ require_or_load(expanded, qualified_name)
513
+ raise LoadError, "Unable to autoload constant #{qualified_name}, expected #{file_path} to define it" unless from_mod.const_defined?(const_name, false)
514
+ return from_mod.const_get(const_name)
515
+ end
516
+ elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix)
517
+ return mod
518
+ elsif (parent = from_mod.parent) && parent != from_mod &&
519
+ ! from_mod.parents.any? { |p| p.const_defined?(const_name, false) }
520
+ # If our parents do not have a constant named +const_name+ then we are free
521
+ # to attempt to load upwards. If they do have such a constant, then this
522
+ # const_missing must be due to from_mod::const_name, which should not
523
+ # return constants from from_mod's parents.
524
+ begin
525
+ # Since Ruby does not pass the nesting at the point the unknown
526
+ # constant triggered the callback we cannot fully emulate constant
527
+ # name lookup and need to make a trade-off: we are going to assume
528
+ # that the nesting in the body of Foo::Bar is [Foo::Bar, Foo] even
529
+ # though it might not be. Counterexamples are
530
+ #
531
+ # class Foo::Bar
532
+ # Module.nesting # => [Foo::Bar]
533
+ # end
534
+ #
535
+ # or
536
+ #
537
+ # module M::N
538
+ # module S::T
539
+ # Module.nesting # => [S::T, M::N]
540
+ # end
541
+ # end
542
+ #
543
+ # for example.
544
+ return parent.const_missing(const_name)
545
+ rescue NameError => e
546
+ raise unless e.missing_name? qualified_name_for(parent, const_name)
547
+ end
548
+ end
549
+
550
+ name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
551
+ name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
552
+ raise name_error
553
+ end
554
+
555
+ # Remove the constants that have been autoloaded, and those that have been
556
+ # marked for unloading. Before each constant is removed a callback is sent
557
+ # to its class/module if it implements +before_remove_const+.
558
+ #
559
+ # The callback implementation should be restricted to cleaning up caches, etc.
560
+ # as the environment will be in an inconsistent state, e.g. other constants
561
+ # may have already been unloaded and not accessible.
562
+ def remove_unloadable_constants!
563
+ autoloaded_constants.each { |const| remove_constant const }
564
+ autoloaded_constants.clear
565
+ Reference.clear!
566
+ explicitly_unloadable_constants.each { |const| remove_constant const }
567
+ end
568
+
569
+ class ClassCache
570
+ def initialize
571
+ @store = Concurrent::Map.new
572
+ end
573
+
574
+ def empty?
575
+ @store.empty?
576
+ end
577
+
578
+ def key?(key)
579
+ @store.key?(key)
580
+ end
581
+
582
+ def get(key)
583
+ key = key.name if key.respond_to?(:name)
584
+ @store[key] ||= Inflector.constantize(key)
585
+ end
586
+ alias :[] :get
587
+
588
+ def safe_get(key)
589
+ key = key.name if key.respond_to?(:name)
590
+ @store[key] ||= Inflector.safe_constantize(key)
591
+ end
592
+
593
+ def store(klass)
594
+ return self unless klass.respond_to?(:name)
595
+ raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
596
+ @store[klass.name] = klass
597
+ self
598
+ end
599
+
600
+ def clear!
601
+ @store.clear
602
+ end
603
+ end
604
+
605
+ Reference = ClassCache.new
606
+
607
+ # Store a reference to a class +klass+.
608
+ def reference(klass)
609
+ Reference.store klass
610
+ end
611
+
612
+ # Get the reference for class named +name+.
613
+ # Raises an exception if referenced class does not exist.
614
+ def constantize(name)
615
+ Reference.get(name)
616
+ end
617
+
618
+ # Get the reference for class named +name+ if one exists.
619
+ # Otherwise returns +nil+.
620
+ def safe_constantize(name)
621
+ Reference.safe_get(name)
622
+ end
623
+
624
+ # Determine if the given constant has been automatically loaded.
625
+ def autoloaded?(desc)
626
+ return false if desc.is_a?(Module) && desc.anonymous?
627
+ name = to_constant_name desc
628
+ return false unless qualified_const_defined?(name)
629
+ return autoloaded_constants.include?(name)
630
+ end
631
+
632
+ # Will the provided constant descriptor be unloaded?
633
+ def will_unload?(const_desc)
634
+ autoloaded?(const_desc) ||
635
+ explicitly_unloadable_constants.include?(to_constant_name(const_desc))
636
+ end
637
+
638
+ # Mark the provided constant name for unloading. This constant will be
639
+ # unloaded on each request, not just the next one.
640
+ def mark_for_unload(const_desc)
641
+ name = to_constant_name const_desc
642
+ if explicitly_unloadable_constants.include? name
643
+ false
644
+ else
645
+ explicitly_unloadable_constants << name
646
+ true
647
+ end
648
+ end
649
+
650
+ # Run the provided block and detect the new constants that were loaded during
651
+ # its execution. Constants may only be regarded as 'new' once -- so if the
652
+ # block calls +new_constants_in+ again, then the constants defined within the
653
+ # inner call will not be reported in this one.
654
+ #
655
+ # If the provided block does not run to completion, and instead raises an
656
+ # exception, any new constants are regarded as being only partially defined
657
+ # and will be removed immediately.
658
+ def new_constants_in(*descs)
659
+ constant_watch_stack.watch_namespaces(descs)
660
+ success = false
661
+
662
+ begin
663
+ yield # Now yield to the code that is to define new constants.
664
+ success = true
665
+ ensure
666
+ new_constants = constant_watch_stack.new_constants
667
+
668
+ return new_constants if success
669
+
670
+ # Remove partially loaded constants.
671
+ new_constants.each { |c| remove_constant(c) }
672
+ end
673
+ end
674
+
675
+ # Convert the provided const desc to a qualified constant name (as a string).
676
+ # A module, class, symbol, or string may be provided.
677
+ def to_constant_name(desc) #:nodoc:
678
+ case desc
679
+ when String then desc.sub(/^::/, '')
680
+ when Symbol then desc.to_s
681
+ when Module
682
+ desc.name ||
683
+ raise(ArgumentError, "Anonymous modules have no name to be referenced by")
684
+ else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
685
+ end
686
+ end
687
+
688
+ def remove_constant(const) #:nodoc:
689
+ # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
690
+ normalized = const.to_s.sub(/\A::/, '')
691
+ normalized.sub!(/\A(Object::)+/, '')
692
+
693
+ constants = normalized.split('::')
694
+ to_remove = constants.pop
695
+
696
+ # Remove the file path from the loaded list.
697
+ file_path = search_for_file(const.underscore)
698
+ if file_path
699
+ expanded = File.expand_path(file_path)
700
+ expanded.sub!(/\.rb\z/, '')
701
+ self.loaded.delete(expanded)
702
+ end
703
+
704
+ if constants.empty?
705
+ parent = Object
706
+ else
707
+ # This method is robust to non-reachable constants.
708
+ #
709
+ # Non-reachable constants may be passed if some of the parents were
710
+ # autoloaded and already removed. It is easier to do a sanity check
711
+ # here than require the caller to be clever. We check the parent
712
+ # rather than the very const argument because we do not want to
713
+ # trigger Kernel#autoloads, see the comment below.
714
+ parent_name = constants.join('::')
715
+ return unless qualified_const_defined?(parent_name)
716
+ parent = constantize(parent_name)
717
+ end
718
+
719
+ # In an autoloaded user.rb like this
720
+ #
721
+ # autoload :Foo, 'foo'
722
+ #
723
+ # class User < ActiveRecord::Base
724
+ # end
725
+ #
726
+ # we correctly register "Foo" as being autoloaded. But if the app does
727
+ # not use the "Foo" constant we need to be careful not to trigger
728
+ # loading "foo.rb" ourselves. While #const_defined? and #const_get? do
729
+ # require the file, #autoload? and #remove_const don't.
730
+ #
731
+ # We are going to remove the constant nonetheless ---which exists as
732
+ # far as Ruby is concerned--- because if the user removes the macro
733
+ # call from a class or module that were not autoloaded, as in the
734
+ # example above with Object, accessing to that constant must err.
735
+ unless parent.autoload?(to_remove)
736
+ begin
737
+ constantized = parent.const_get(to_remove, false)
738
+ rescue NameError
739
+ # The constant is no longer reachable, just skip it.
740
+ return
741
+ else
742
+ constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
743
+ end
744
+ end
745
+
746
+ begin
747
+ parent.instance_eval { remove_const to_remove }
748
+ rescue NameError
749
+ # The constant is no longer reachable, just skip it.
750
+ end
751
+ end
752
+ end
753
+ end
754
+
755
+ ActiveSupport::Dependencies.hook!