activesupport 4.2.0 → 5.2.0

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 (254) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +366 -232
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +4 -5
  5. data/lib/active_support.rb +17 -7
  6. data/lib/active_support/all.rb +5 -3
  7. data/lib/active_support/array_inquirer.rb +48 -0
  8. data/lib/active_support/backtrace_cleaner.rb +7 -5
  9. data/lib/active_support/benchmarkable.rb +6 -4
  10. data/lib/active_support/builder.rb +3 -1
  11. data/lib/active_support/cache.rb +271 -177
  12. data/lib/active_support/cache/file_store.rb +41 -35
  13. data/lib/active_support/cache/mem_cache_store.rb +97 -88
  14. data/lib/active_support/cache/memory_store.rb +27 -30
  15. data/lib/active_support/cache/null_store.rb +7 -8
  16. data/lib/active_support/cache/redis_cache_store.rb +454 -0
  17. data/lib/active_support/cache/strategy/local_cache.rb +67 -34
  18. data/lib/active_support/cache/strategy/local_cache_middleware.rb +10 -9
  19. data/lib/active_support/callbacks.rb +654 -560
  20. data/lib/active_support/concern.rb +5 -3
  21. data/lib/active_support/concurrency/load_interlock_aware_monitor.rb +17 -0
  22. data/lib/active_support/concurrency/share_lock.rb +227 -0
  23. data/lib/active_support/configurable.rb +8 -5
  24. data/lib/active_support/core_ext.rb +3 -1
  25. data/lib/active_support/core_ext/array.rb +9 -6
  26. data/lib/active_support/core_ext/array/access.rb +29 -1
  27. data/lib/active_support/core_ext/array/conversions.rb +22 -18
  28. data/lib/active_support/core_ext/array/extract_options.rb +2 -0
  29. data/lib/active_support/core_ext/array/grouping.rb +11 -18
  30. data/lib/active_support/core_ext/array/inquiry.rb +19 -0
  31. data/lib/active_support/core_ext/array/prepend_and_append.rb +5 -3
  32. data/lib/active_support/core_ext/array/wrap.rb +7 -4
  33. data/lib/active_support/core_ext/benchmark.rb +3 -1
  34. data/lib/active_support/core_ext/big_decimal.rb +3 -1
  35. data/lib/active_support/core_ext/big_decimal/conversions.rb +10 -12
  36. data/lib/active_support/core_ext/class.rb +4 -3
  37. data/lib/active_support/core_ext/class/attribute.rb +41 -22
  38. data/lib/active_support/core_ext/class/attribute_accessors.rb +3 -1
  39. data/lib/active_support/core_ext/class/subclasses.rb +20 -8
  40. data/lib/active_support/core_ext/date.rb +6 -4
  41. data/lib/active_support/core_ext/date/acts_like.rb +3 -1
  42. data/lib/active_support/core_ext/date/blank.rb +14 -0
  43. data/lib/active_support/core_ext/date/calculations.rb +11 -9
  44. data/lib/active_support/core_ext/date/conversions.rb +31 -23
  45. data/lib/active_support/core_ext/date/zones.rb +4 -2
  46. data/lib/active_support/core_ext/date_and_time/calculations.rb +179 -56
  47. data/lib/active_support/core_ext/date_and_time/compatibility.rb +16 -0
  48. data/lib/active_support/core_ext/date_and_time/zones.rb +12 -12
  49. data/lib/active_support/core_ext/date_time.rb +7 -4
  50. data/lib/active_support/core_ext/date_time/acts_like.rb +4 -2
  51. data/lib/active_support/core_ext/date_time/blank.rb +14 -0
  52. data/lib/active_support/core_ext/date_time/calculations.rb +58 -20
  53. data/lib/active_support/core_ext/date_time/compatibility.rb +18 -0
  54. data/lib/active_support/core_ext/date_time/conversions.rb +16 -12
  55. data/lib/active_support/core_ext/digest/uuid.rb +7 -5
  56. data/lib/active_support/core_ext/enumerable.rb +107 -28
  57. data/lib/active_support/core_ext/file.rb +3 -1
  58. data/lib/active_support/core_ext/file/atomic.rb +38 -31
  59. data/lib/active_support/core_ext/hash.rb +11 -9
  60. data/lib/active_support/core_ext/hash/compact.rb +24 -15
  61. data/lib/active_support/core_ext/hash/conversions.rb +63 -43
  62. data/lib/active_support/core_ext/hash/deep_merge.rb +9 -13
  63. data/lib/active_support/core_ext/hash/except.rb +11 -8
  64. data/lib/active_support/core_ext/hash/indifferent_access.rb +4 -3
  65. data/lib/active_support/core_ext/hash/keys.rb +33 -27
  66. data/lib/active_support/core_ext/hash/reverse_merge.rb +5 -2
  67. data/lib/active_support/core_ext/hash/slice.rb +8 -8
  68. data/lib/active_support/core_ext/hash/transform_values.rb +16 -7
  69. data/lib/active_support/core_ext/integer.rb +5 -3
  70. data/lib/active_support/core_ext/integer/inflections.rb +3 -1
  71. data/lib/active_support/core_ext/integer/multiple.rb +2 -0
  72. data/lib/active_support/core_ext/integer/time.rb +11 -33
  73. data/lib/active_support/core_ext/kernel.rb +6 -5
  74. data/lib/active_support/core_ext/kernel/agnostics.rb +2 -0
  75. data/lib/active_support/core_ext/kernel/concern.rb +5 -1
  76. data/lib/active_support/core_ext/kernel/reporting.rb +4 -83
  77. data/lib/active_support/core_ext/kernel/singleton_class.rb +2 -0
  78. data/lib/active_support/core_ext/load_error.rb +3 -22
  79. data/lib/active_support/core_ext/marshal.rb +13 -10
  80. data/lib/active_support/core_ext/module.rb +14 -11
  81. data/lib/active_support/core_ext/module/aliasing.rb +6 -44
  82. data/lib/active_support/core_ext/module/anonymous.rb +12 -1
  83. data/lib/active_support/core_ext/module/attr_internal.rb +8 -9
  84. data/lib/active_support/core_ext/module/attribute_accessors.rb +43 -40
  85. data/lib/active_support/core_ext/module/attribute_accessors_per_thread.rb +150 -0
  86. data/lib/active_support/core_ext/module/concerning.rb +11 -12
  87. data/lib/active_support/core_ext/module/delegation.rb +121 -39
  88. data/lib/active_support/core_ext/module/deprecation.rb +4 -2
  89. data/lib/active_support/core_ext/module/introspection.rb +9 -9
  90. data/lib/active_support/core_ext/module/reachable.rb +5 -2
  91. data/lib/active_support/core_ext/module/redefine_method.rb +49 -0
  92. data/lib/active_support/core_ext/module/remove_method.rb +8 -3
  93. data/lib/active_support/core_ext/name_error.rb +22 -2
  94. data/lib/active_support/core_ext/numeric.rb +6 -3
  95. data/lib/active_support/core_ext/numeric/bytes.rb +22 -0
  96. data/lib/active_support/core_ext/numeric/conversions.rb +79 -74
  97. data/lib/active_support/core_ext/numeric/inquiry.rb +28 -0
  98. data/lib/active_support/core_ext/numeric/time.rb +35 -38
  99. data/lib/active_support/core_ext/object.rb +14 -13
  100. data/lib/active_support/core_ext/object/acts_like.rb +12 -1
  101. data/lib/active_support/core_ext/object/blank.rb +29 -4
  102. data/lib/active_support/core_ext/object/conversions.rb +6 -4
  103. data/lib/active_support/core_ext/object/deep_dup.rb +13 -4
  104. data/lib/active_support/core_ext/object/duplicable.rb +98 -45
  105. data/lib/active_support/core_ext/object/inclusion.rb +5 -3
  106. data/lib/active_support/core_ext/object/instance_variables.rb +3 -1
  107. data/lib/active_support/core_ext/object/json.rb +49 -19
  108. data/lib/active_support/core_ext/object/to_param.rb +3 -1
  109. data/lib/active_support/core_ext/object/to_query.rb +6 -4
  110. data/lib/active_support/core_ext/object/try.rb +70 -22
  111. data/lib/active_support/core_ext/object/with_options.rb +16 -3
  112. data/lib/active_support/core_ext/range.rb +7 -4
  113. data/lib/active_support/core_ext/range/conversions.rb +27 -7
  114. data/lib/active_support/core_ext/range/each.rb +19 -17
  115. data/lib/active_support/core_ext/range/include_range.rb +21 -19
  116. data/lib/active_support/core_ext/range/include_time_with_zone.rb +23 -0
  117. data/lib/active_support/core_ext/range/overlaps.rb +2 -0
  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.rb +15 -13
  121. data/lib/active_support/core_ext/string/access.rb +9 -7
  122. data/lib/active_support/core_ext/string/behavior.rb +3 -1
  123. data/lib/active_support/core_ext/string/conversions.rb +8 -5
  124. data/lib/active_support/core_ext/string/exclude.rb +2 -0
  125. data/lib/active_support/core_ext/string/filters.rb +10 -8
  126. data/lib/active_support/core_ext/string/indent.rb +6 -4
  127. data/lib/active_support/core_ext/string/inflections.rb +61 -24
  128. data/lib/active_support/core_ext/string/inquiry.rb +3 -1
  129. data/lib/active_support/core_ext/string/multibyte.rb +15 -7
  130. data/lib/active_support/core_ext/string/output_safety.rb +35 -35
  131. data/lib/active_support/core_ext/string/starts_ends_with.rb +2 -0
  132. data/lib/active_support/core_ext/string/strip.rb +4 -5
  133. data/lib/active_support/core_ext/string/zones.rb +4 -2
  134. data/lib/active_support/core_ext/time.rb +7 -5
  135. data/lib/active_support/core_ext/time/acts_like.rb +3 -1
  136. data/lib/active_support/core_ext/time/calculations.rb +101 -51
  137. data/lib/active_support/core_ext/time/compatibility.rb +16 -0
  138. data/lib/active_support/core_ext/time/conversions.rb +20 -13
  139. data/lib/active_support/core_ext/time/zones.rb +41 -7
  140. data/lib/active_support/core_ext/uri.rb +5 -4
  141. data/lib/active_support/current_attributes.rb +195 -0
  142. data/lib/active_support/dependencies.rb +143 -160
  143. data/lib/active_support/dependencies/autoload.rb +2 -0
  144. data/lib/active_support/dependencies/interlock.rb +57 -0
  145. data/lib/active_support/deprecation.rb +12 -9
  146. data/lib/active_support/deprecation/behaviors.rb +41 -12
  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 +54 -21
  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/descendants_tracker.rb +2 -0
  153. data/lib/active_support/digest.rb +20 -0
  154. data/lib/active_support/duration.rb +326 -30
  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/encrypted_configuration.rb +49 -0
  158. data/lib/active_support/encrypted_file.rb +99 -0
  159. data/lib/active_support/evented_file_update_checker.rb +205 -0
  160. data/lib/active_support/execution_wrapper.rb +128 -0
  161. data/lib/active_support/executor.rb +8 -0
  162. data/lib/active_support/file_update_checker.rb +63 -37
  163. data/lib/active_support/gem_version.rb +4 -2
  164. data/lib/active_support/gzip.rb +7 -5
  165. data/lib/active_support/hash_with_indifferent_access.rb +130 -30
  166. data/lib/active_support/i18n.rb +8 -6
  167. data/lib/active_support/i18n_railtie.rb +34 -14
  168. data/lib/active_support/inflections.rb +13 -11
  169. data/lib/active_support/inflector.rb +7 -5
  170. data/lib/active_support/inflector/inflections.rb +61 -12
  171. data/lib/active_support/inflector/methods.rb +161 -136
  172. data/lib/active_support/inflector/transliterate.rb +48 -27
  173. data/lib/active_support/json.rb +4 -2
  174. data/lib/active_support/json/decoding.rb +16 -13
  175. data/lib/active_support/json/encoding.rb +15 -57
  176. data/lib/active_support/key_generator.rb +25 -25
  177. data/lib/active_support/lazy_load_hooks.rb +50 -20
  178. data/lib/active_support/locale/en.yml +2 -0
  179. data/lib/active_support/log_subscriber.rb +13 -10
  180. data/lib/active_support/log_subscriber/test_helper.rb +14 -12
  181. data/lib/active_support/logger.rb +54 -3
  182. data/lib/active_support/logger_silence.rb +12 -7
  183. data/lib/active_support/logger_thread_safe_level.rb +33 -0
  184. data/lib/active_support/message_encryptor.rb +173 -51
  185. data/lib/active_support/message_verifier.rb +150 -17
  186. data/lib/active_support/messages/metadata.rb +71 -0
  187. data/lib/active_support/messages/rotation_configuration.rb +22 -0
  188. data/lib/active_support/messages/rotator.rb +56 -0
  189. data/lib/active_support/multibyte.rb +4 -2
  190. data/lib/active_support/multibyte/chars.rb +37 -24
  191. data/lib/active_support/multibyte/unicode.rb +100 -96
  192. data/lib/active_support/notifications.rb +11 -7
  193. data/lib/active_support/notifications/fanout.rb +10 -8
  194. data/lib/active_support/notifications/instrumenter.rb +27 -7
  195. data/lib/active_support/number_helper.rb +94 -68
  196. data/lib/active_support/number_helper/number_converter.rb +13 -11
  197. data/lib/active_support/number_helper/number_to_currency_converter.rb +9 -9
  198. data/lib/active_support/number_helper/number_to_delimited_converter.rb +9 -3
  199. data/lib/active_support/number_helper/number_to_human_converter.rb +11 -9
  200. data/lib/active_support/number_helper/number_to_human_size_converter.rb +9 -8
  201. data/lib/active_support/number_helper/number_to_percentage_converter.rb +3 -1
  202. data/lib/active_support/number_helper/number_to_phone_converter.rb +13 -4
  203. data/lib/active_support/number_helper/number_to_rounded_converter.rb +23 -56
  204. data/lib/active_support/number_helper/rounding_helper.rb +66 -0
  205. data/lib/active_support/option_merger.rb +3 -1
  206. data/lib/active_support/ordered_hash.rb +6 -4
  207. data/lib/active_support/ordered_options.rb +22 -4
  208. data/lib/active_support/per_thread_registry.rb +13 -6
  209. data/lib/active_support/proxy_object.rb +2 -0
  210. data/lib/active_support/rails.rb +16 -8
  211. data/lib/active_support/railtie.rb +43 -9
  212. data/lib/active_support/reloader.rb +131 -0
  213. data/lib/active_support/rescuable.rb +108 -53
  214. data/lib/active_support/security_utils.rb +17 -6
  215. data/lib/active_support/string_inquirer.rb +11 -3
  216. data/lib/active_support/subscriber.rb +15 -14
  217. data/lib/active_support/tagged_logging.rb +14 -11
  218. data/lib/active_support/test_case.rb +18 -46
  219. data/lib/active_support/testing/assertions.rb +137 -20
  220. data/lib/active_support/testing/autorun.rb +4 -2
  221. data/lib/active_support/testing/constant_lookup.rb +2 -1
  222. data/lib/active_support/testing/declarative.rb +3 -1
  223. data/lib/active_support/testing/deprecation.rb +14 -10
  224. data/lib/active_support/testing/file_fixtures.rb +36 -0
  225. data/lib/active_support/testing/isolation.rb +34 -25
  226. data/lib/active_support/testing/method_call_assertions.rb +43 -0
  227. data/lib/active_support/testing/setup_and_teardown.rb +12 -3
  228. data/lib/active_support/testing/stream.rb +44 -0
  229. data/lib/active_support/testing/tagged_logging.rb +3 -1
  230. data/lib/active_support/testing/time_helpers.rb +96 -27
  231. data/lib/active_support/time.rb +14 -12
  232. data/lib/active_support/time_with_zone.rb +195 -53
  233. data/lib/active_support/values/time_zone.rb +200 -61
  234. data/lib/active_support/values/unicode_tables.dat +0 -0
  235. data/lib/active_support/version.rb +3 -1
  236. data/lib/active_support/xml_mini.rb +69 -51
  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. metadata +55 -43
  244. data/lib/active_support/concurrency/latch.rb +0 -27
  245. data/lib/active_support/core_ext/big_decimal/yaml_conversions.rb +0 -14
  246. data/lib/active_support/core_ext/class/delegating_attributes.rb +0 -45
  247. data/lib/active_support/core_ext/date_time/zones.rb +0 -6
  248. data/lib/active_support/core_ext/kernel/debugger.rb +0 -10
  249. data/lib/active_support/core_ext/module/method_transplanting.rb +0 -11
  250. data/lib/active_support/core_ext/module/qualified_const.rb +0 -52
  251. data/lib/active_support/core_ext/object/itself.rb +0 -15
  252. data/lib/active_support/core_ext/struct.rb +0 -6
  253. data/lib/active_support/core_ext/thread.rb +0 -86
  254. data/lib/active_support/core_ext/time/marshal.rb +0 -30
@@ -1,18 +1,19 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'uri'
3
+ require "uri"
4
4
  str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japanese.
5
5
  parser = URI::Parser.new
6
6
 
7
7
  unless str == parser.unescape(parser.escape(str))
8
+ require "active_support/core_ext/module/redefine_method"
8
9
  URI::Parser.class_eval do
9
- remove_method :unescape
10
+ silence_redefinition_of_method :unescape
10
11
  def unescape(str, escaped = /%[a-fA-F\d]{2}/)
11
12
  # TODO: Are we actually sure that ASCII == UTF-8?
12
13
  # YK: My initial experiments say yes, but let's be sure please
13
14
  enc = str.encoding
14
15
  enc = Encoding::UTF_8 if enc == Encoding::US_ASCII
15
- str.gsub(escaped) { [$&[1, 2].hex].pack('C') }.force_encoding(enc)
16
+ str.gsub(escaped) { |match| [match[1, 2].hex].pack("C") }.force_encoding(enc)
16
17
  end
17
18
  end
18
19
  end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveSupport
4
+ # Abstract super class that provides a thread-isolated attributes singleton, which resets automatically
5
+ # before and after each request. This allows you to keep all the per-request attributes easily
6
+ # available to the whole system.
7
+ #
8
+ # The following full app-like example demonstrates how to use a Current class to
9
+ # facilitate easy access to the global, per-request attributes without passing them deeply
10
+ # around everywhere:
11
+ #
12
+ # # app/models/current.rb
13
+ # class Current < ActiveSupport::CurrentAttributes
14
+ # attribute :account, :user
15
+ # attribute :request_id, :user_agent, :ip_address
16
+ #
17
+ # resets { Time.zone = nil }
18
+ #
19
+ # def user=(user)
20
+ # super
21
+ # self.account = user.account
22
+ # Time.zone = user.time_zone
23
+ # end
24
+ # end
25
+ #
26
+ # # app/controllers/concerns/authentication.rb
27
+ # module Authentication
28
+ # extend ActiveSupport::Concern
29
+ #
30
+ # included do
31
+ # before_action :authenticate
32
+ # end
33
+ #
34
+ # private
35
+ # def authenticate
36
+ # if authenticated_user = User.find_by(id: cookies.encrypted[:user_id])
37
+ # Current.user = authenticated_user
38
+ # else
39
+ # redirect_to new_session_url
40
+ # end
41
+ # end
42
+ # end
43
+ #
44
+ # # app/controllers/concerns/set_current_request_details.rb
45
+ # module SetCurrentRequestDetails
46
+ # extend ActiveSupport::Concern
47
+ #
48
+ # included do
49
+ # before_action do
50
+ # Current.request_id = request.uuid
51
+ # Current.user_agent = request.user_agent
52
+ # Current.ip_address = request.ip
53
+ # end
54
+ # end
55
+ # end
56
+ #
57
+ # class ApplicationController < ActionController::Base
58
+ # include Authentication
59
+ # include SetCurrentRequestDetails
60
+ # end
61
+ #
62
+ # class MessagesController < ApplicationController
63
+ # def create
64
+ # Current.account.messages.create(message_params)
65
+ # end
66
+ # end
67
+ #
68
+ # class Message < ApplicationRecord
69
+ # belongs_to :creator, default: -> { Current.user }
70
+ # after_create { |message| Event.create(record: message) }
71
+ # end
72
+ #
73
+ # class Event < ApplicationRecord
74
+ # before_create do
75
+ # self.request_id = Current.request_id
76
+ # self.user_agent = Current.user_agent
77
+ # self.ip_address = Current.ip_address
78
+ # end
79
+ # end
80
+ #
81
+ # A word of caution: It's easy to overdo a global singleton like Current and tangle your model as a result.
82
+ # Current should only be used for a few, top-level globals, like account, user, and request details.
83
+ # The attributes stuck in Current should be used by more or less all actions on all requests. If you start
84
+ # sticking controller-specific attributes in there, you're going to create a mess.
85
+ class CurrentAttributes
86
+ include ActiveSupport::Callbacks
87
+ define_callbacks :reset
88
+
89
+ class << self
90
+ # Returns singleton instance for this class in this thread. If none exists, one is created.
91
+ def instance
92
+ current_instances[name] ||= new
93
+ end
94
+
95
+ # Declares one or more attributes that will be given both class and instance accessor methods.
96
+ def attribute(*names)
97
+ generated_attribute_methods.module_eval do
98
+ names.each do |name|
99
+ define_method(name) do
100
+ attributes[name.to_sym]
101
+ end
102
+
103
+ define_method("#{name}=") do |attribute|
104
+ attributes[name.to_sym] = attribute
105
+ end
106
+ end
107
+ end
108
+
109
+ names.each do |name|
110
+ define_singleton_method(name) do
111
+ instance.public_send(name)
112
+ end
113
+
114
+ define_singleton_method("#{name}=") do |attribute|
115
+ instance.public_send("#{name}=", attribute)
116
+ end
117
+ end
118
+ end
119
+
120
+ # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone.
121
+ def resets(&block)
122
+ set_callback :reset, :after, &block
123
+ end
124
+
125
+ delegate :set, :reset, to: :instance
126
+
127
+ def reset_all # :nodoc:
128
+ current_instances.each_value(&:reset)
129
+ end
130
+
131
+ def clear_all # :nodoc:
132
+ reset_all
133
+ current_instances.clear
134
+ end
135
+
136
+ private
137
+ def generated_attribute_methods
138
+ @generated_attribute_methods ||= Module.new.tap { |mod| include mod }
139
+ end
140
+
141
+ def current_instances
142
+ Thread.current[:current_attributes_instances] ||= {}
143
+ end
144
+
145
+ def method_missing(name, *args, &block)
146
+ # Caches the method definition as a singleton method of the receiver.
147
+ #
148
+ # By letting #delegate handle it, we avoid an enclosure that'll capture args.
149
+ singleton_class.delegate name, to: :instance
150
+
151
+ send(name, *args, &block)
152
+ end
153
+ end
154
+
155
+ attr_accessor :attributes
156
+
157
+ def initialize
158
+ @attributes = {}
159
+ end
160
+
161
+ # Expose one or more attributes within a block. Old values are returned after the block concludes.
162
+ # Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
163
+ #
164
+ # class Chat::PublicationJob < ApplicationJob
165
+ # def perform(attributes, room_number, creator)
166
+ # Current.set(person: creator) do
167
+ # Chat::Publisher.publish(attributes: attributes, room_number: room_number)
168
+ # end
169
+ # end
170
+ # end
171
+ def set(set_attributes)
172
+ old_attributes = compute_attributes(set_attributes.keys)
173
+ assign_attributes(set_attributes)
174
+ yield
175
+ ensure
176
+ assign_attributes(old_attributes)
177
+ end
178
+
179
+ # Reset all attributes. Should be called before and after actions, when used as a per-request singleton.
180
+ def reset
181
+ run_callbacks :reset do
182
+ self.attributes = {}
183
+ end
184
+ end
185
+
186
+ private
187
+ def assign_attributes(new_attributes)
188
+ new_attributes.each { |key, value| public_send("#{key}=", value) }
189
+ end
190
+
191
+ def compute_attributes(keys)
192
+ keys.collect { |key| [ key, public_send(key) ] }.to_h
193
+ end
194
+ end
195
+ end
@@ -1,73 +1,83 @@
1
- require 'set'
2
- require 'thread'
3
- require 'thread_safe'
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/inflector'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ require "thread"
5
+ require "concurrent/map"
6
+ require "pathname"
7
+ require "active_support/core_ext/module/aliasing"
8
+ require "active_support/core_ext/module/attribute_accessors"
9
+ require "active_support/core_ext/module/introspection"
10
+ require "active_support/core_ext/module/anonymous"
11
+ require "active_support/core_ext/object/blank"
12
+ require "active_support/core_ext/kernel/reporting"
13
+ require "active_support/core_ext/load_error"
14
+ require "active_support/core_ext/name_error"
15
+ require "active_support/core_ext/string/starts_ends_with"
16
+ require "active_support/dependencies/interlock"
17
+ require "active_support/inflector"
16
18
 
17
19
  module ActiveSupport #:nodoc:
18
20
  module Dependencies #:nodoc:
19
21
  extend self
20
22
 
23
+ mattr_accessor :interlock, default: 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
+
21
49
  # Should we turn on Ruby warnings on the first load of dependent files?
22
- mattr_accessor :warnings_on_first_load
23
- self.warnings_on_first_load = false
50
+ mattr_accessor :warnings_on_first_load, default: false
24
51
 
25
52
  # All files ever loaded.
26
- mattr_accessor :history
27
- self.history = Set.new
53
+ mattr_accessor :history, default: Set.new
28
54
 
29
55
  # All files currently loaded.
30
- mattr_accessor :loaded
31
- self.loaded = Set.new
56
+ mattr_accessor :loaded, default: Set.new
32
57
 
33
58
  # Stack of files being loaded.
34
- mattr_accessor :loading
35
- self.loading = []
59
+ mattr_accessor :loading, default: []
36
60
 
37
61
  # Should we load files or require them?
38
- mattr_accessor :mechanism
39
- self.mechanism = ENV['NO_RELOAD'] ? :require : :load
62
+ mattr_accessor :mechanism, default: ENV["NO_RELOAD"] ? :require : :load
40
63
 
41
64
  # The set of directories from which we may automatically load files. Files
42
65
  # under these directories will be reloaded on each request in development mode,
43
66
  # unless the directory also appears in autoload_once_paths.
44
- mattr_accessor :autoload_paths
45
- self.autoload_paths = []
67
+ mattr_accessor :autoload_paths, default: []
46
68
 
47
69
  # The set of directories from which automatically loaded constants are loaded
48
70
  # only once. All directories in this set must also be present in +autoload_paths+.
49
- mattr_accessor :autoload_once_paths
50
- self.autoload_once_paths = []
71
+ mattr_accessor :autoload_once_paths, default: []
51
72
 
52
73
  # An array of qualified constant names that have been loaded. Adding a name
53
74
  # to this array will cause it to be unloaded the next time Dependencies are
54
75
  # cleared.
55
- mattr_accessor :autoloaded_constants
56
- self.autoloaded_constants = []
76
+ mattr_accessor :autoloaded_constants, default: []
57
77
 
58
78
  # An array of constant names that need to be unloaded on every request. Used
59
79
  # to allow arbitrary constants to be marked for unloading.
60
- mattr_accessor :explicitly_unloadable_constants
61
- self.explicitly_unloadable_constants = []
62
-
63
- # The logger is used for generating information on the action run-time
64
- # (including benchmarking) if available. Can be set to nil for no logging.
65
- # Compatible with both Ruby's own Logger and Log4r loggers.
66
- mattr_accessor :logger
67
-
68
- # Set to +true+ to enable logging of const_missing and file loads.
69
- mattr_accessor :log_activity
70
- self.log_activity = false
80
+ mattr_accessor :explicitly_unloadable_constants, default: []
71
81
 
72
82
  # The WatchStack keeps a stack of the modules being watched as files are
73
83
  # loaded. If a file in the process of being loaded (parent.rb) triggers the
@@ -75,7 +85,7 @@ module ActiveSupport #:nodoc:
75
85
  # handles the new constants.
76
86
  #
77
87
  # If child.rb is being autoloaded, its constants will be added to
78
- # autoloaded_constants. If it was being `require`d, they will be discarded.
88
+ # autoloaded_constants. If it was being required, they will be discarded.
79
89
  #
80
90
  # This is handled by walking back up the watch stack and adding the constants
81
91
  # found by child.rb to the list of original constants in parent.rb.
@@ -89,7 +99,7 @@ module ActiveSupport #:nodoc:
89
99
 
90
100
  def initialize
91
101
  @watching = []
92
- @stack = Hash.new { |h,k| h[k] = [] }
102
+ @stack = Hash.new { |h, k| h[k] = [] }
93
103
  end
94
104
 
95
105
  def each(&block)
@@ -115,11 +125,11 @@ module ActiveSupport #:nodoc:
115
125
  next unless mod.is_a?(Module)
116
126
 
117
127
  # Get a list of the constants that were added
118
- new_constants = mod.local_constants - original_constants
128
+ new_constants = mod.constants(false) - original_constants
119
129
 
120
- # self[namespace] returns an Array of the constants that are being evaluated
130
+ # @stack[namespace] returns an Array of the constants that are being evaluated
121
131
  # for that namespace. For instance, if parent.rb requires child.rb, the first
122
- # element of self[Object] will be an Array of the constants that were present
132
+ # element of @stack[Object] will be an Array of the constants that were present
123
133
  # before parent.rb was required. The second element will be an Array of the
124
134
  # constants that were present before child.rb was required.
125
135
  @stack[namespace].each do |namespace_constants|
@@ -128,7 +138,7 @@ module ActiveSupport #:nodoc:
128
138
 
129
139
  # Normalize the list of new constants, and add them to the list we will return
130
140
  new_constants.each do |suffix|
131
- constants << ([namespace, suffix] - ["Object"]).join("::")
141
+ constants << ([namespace, suffix] - ["Object"]).join("::".freeze)
132
142
  end
133
143
  end
134
144
  constants
@@ -143,7 +153,7 @@ module ActiveSupport #:nodoc:
143
153
  @watching << namespaces.map do |namespace|
144
154
  module_name = Dependencies.to_constant_name(namespace)
145
155
  original_constants = Dependencies.qualified_const_defined?(module_name) ?
146
- Inflector.constantize(module_name).local_constants : []
156
+ Inflector.constantize(module_name).constants(false) : []
147
157
 
148
158
  @stack[module_name] << original_constants
149
159
  module_name
@@ -151,14 +161,13 @@ module ActiveSupport #:nodoc:
151
161
  end
152
162
 
153
163
  private
154
- def pop_modules(modules)
155
- modules.each { |mod| @stack[mod].pop }
156
- end
164
+ def pop_modules(modules)
165
+ modules.each { |mod| @stack[mod].pop }
166
+ end
157
167
  end
158
168
 
159
169
  # An internal stack used to record which constants are loaded by any block.
160
- mattr_accessor :constant_watch_stack
161
- self.constant_watch_stack = WatchStack.new
170
+ mattr_accessor :constant_watch_stack, default: WatchStack.new
162
171
 
163
172
  # Module includes this module.
164
173
  module ModuleConstMissing #:nodoc:
@@ -224,7 +233,7 @@ module ActiveSupport #:nodoc:
224
233
  # resolution deterministic for constants with the same relative name in
225
234
  # different namespaces whose evaluation would depend on load order
226
235
  # otherwise.
227
- def require_dependency(file_name, message = "No such file to load -- %s")
236
+ def require_dependency(file_name, message = "No such file to load -- %s.rb")
228
237
  file_name = file_name.to_path if file_name.respond_to?(:to_path)
229
238
  unless file_name.is_a?(String)
230
239
  raise ArgumentError, "the file name must either be a String or implement #to_path -- you passed #{file_name.inspect}"
@@ -234,7 +243,7 @@ module ActiveSupport #:nodoc:
234
243
  end
235
244
 
236
245
  def load_dependency(file)
237
- if Dependencies.load? && ActiveSupport::Dependencies.constant_watch_stack.watching?
246
+ if Dependencies.load? && Dependencies.constant_watch_stack.watching?
238
247
  Dependencies.new_constants_in(Object) { yield }
239
248
  else
240
249
  yield
@@ -263,17 +272,17 @@ module ActiveSupport #:nodoc:
263
272
 
264
273
  private
265
274
 
266
- def load(file, wrap = false)
267
- result = false
268
- load_dependency(file) { result = super }
269
- result
270
- end
275
+ def load(file, wrap = false)
276
+ result = false
277
+ load_dependency(file) { result = super }
278
+ result
279
+ end
271
280
 
272
- def require(file)
273
- result = false
274
- load_dependency(file) { result = super }
275
- result
276
- end
281
+ def require(file)
282
+ result = false
283
+ load_dependency(file) { result = super }
284
+ result
285
+ end
277
286
  end
278
287
 
279
288
  # Exception file-blaming.
@@ -324,56 +333,58 @@ module ActiveSupport #:nodoc:
324
333
  end
325
334
 
326
335
  def clear
327
- log_call
328
- loaded.clear
329
- loading.clear
330
- remove_unloadable_constants!
336
+ Dependencies.unload_interlock do
337
+ loaded.clear
338
+ loading.clear
339
+ remove_unloadable_constants!
340
+ end
331
341
  end
332
342
 
333
343
  def require_or_load(file_name, const_path = nil)
334
- log_call file_name, const_path
335
344
  file_name = $` if file_name =~ /\.rb\z/
336
345
  expanded = File.expand_path(file_name)
337
346
  return if loaded.include?(expanded)
338
347
 
339
- # Record that we've seen this file *before* loading it to avoid an
340
- # infinite loop with mutual dependencies.
341
- loaded << expanded
342
- loading << expanded
343
-
344
- begin
345
- if load?
346
- log "loading #{file_name}"
348
+ Dependencies.load_interlock do
349
+ # Maybe it got loaded while we were waiting for our lock:
350
+ return if loaded.include?(expanded)
347
351
 
348
- # Enable warnings if this file has not been loaded before and
349
- # warnings_on_first_load is set.
350
- load_args = ["#{file_name}.rb"]
351
- load_args << const_path unless const_path.nil?
352
+ # Record that we've seen this file *before* loading it to avoid an
353
+ # infinite loop with mutual dependencies.
354
+ loaded << expanded
355
+ loading << expanded
352
356
 
353
- if !warnings_on_first_load or history.include?(expanded)
354
- result = load_file(*load_args)
357
+ begin
358
+ if load?
359
+ # Enable warnings if this file has not been loaded before and
360
+ # warnings_on_first_load is set.
361
+ load_args = ["#{file_name}.rb"]
362
+ load_args << const_path unless const_path.nil?
363
+
364
+ if !warnings_on_first_load || history.include?(expanded)
365
+ result = load_file(*load_args)
366
+ else
367
+ enable_warnings { result = load_file(*load_args) }
368
+ end
355
369
  else
356
- enable_warnings { result = load_file(*load_args) }
370
+ result = require file_name
357
371
  end
358
- else
359
- log "requiring #{file_name}"
360
- result = require file_name
372
+ rescue Exception
373
+ loaded.delete expanded
374
+ raise
375
+ ensure
376
+ loading.pop
361
377
  end
362
- rescue Exception
363
- loaded.delete expanded
364
- raise
365
- ensure
366
- loading.pop
367
- end
368
378
 
369
- # Record history *after* loading so first load gets warnings.
370
- history << expanded
371
- result
379
+ # Record history *after* loading so first load gets warnings.
380
+ history << expanded
381
+ result
382
+ end
372
383
  end
373
384
 
374
385
  # Is the provided constant path defined?
375
386
  def qualified_const_defined?(path)
376
- Object.qualified_const_defined?(path.sub(/^::/, ''), false)
387
+ Object.const_defined?(path, false)
377
388
  end
378
389
 
379
390
  # Given +path+, a filesystem path to a ruby file, return an array of
@@ -386,13 +397,13 @@ module ActiveSupport #:nodoc:
386
397
 
387
398
  bases.each do |root|
388
399
  expanded_root = File.expand_path(root)
389
- next unless %r{\A#{Regexp.escape(expanded_root)}(/|\\)} =~ expanded_path
400
+ next unless expanded_path.start_with?(expanded_root)
390
401
 
391
- nesting = expanded_path[(expanded_root.size)..-1]
392
- nesting = nesting[1..-1] if nesting && nesting[0] == ?/
393
- next if nesting.blank?
402
+ root_size = expanded_root.size
403
+ next if expanded_path[root_size] != ?/.freeze
394
404
 
395
- paths << nesting.camelize
405
+ nesting = expanded_path[(root_size + 1)..-1]
406
+ paths << nesting.camelize unless nesting.blank?
396
407
  end
397
408
 
398
409
  paths.uniq!
@@ -401,7 +412,7 @@ module ActiveSupport #:nodoc:
401
412
 
402
413
  # Search for a file in autoload_paths matching the provided suffix.
403
414
  def search_for_file(path_suffix)
404
- path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb")
415
+ path_suffix = path_suffix.sub(/(\.rb)?$/, ".rb".freeze)
405
416
 
406
417
  autoload_paths.each do |root|
407
418
  path = File.join(root, path_suffix)
@@ -421,7 +432,7 @@ module ActiveSupport #:nodoc:
421
432
  end
422
433
 
423
434
  def load_once_path?(path)
424
- # to_s works around a ruby1.9 issue where String#starts_with?(Pathname)
435
+ # to_s works around a ruby issue where String#starts_with?(Pathname)
425
436
  # will raise a TypeError: no implicit conversion of Pathname into String
426
437
  autoload_once_paths.any? { |base| path.starts_with? base.to_s }
427
438
  end
@@ -436,6 +447,7 @@ module ActiveSupport #:nodoc:
436
447
  mod = Module.new
437
448
  into.const_set const_name, mod
438
449
  autoloaded_constants << qualified_name unless autoload_once_paths.include?(base_path)
450
+ autoloaded_constants.uniq!
439
451
  mod
440
452
  end
441
453
 
@@ -448,7 +460,6 @@ module ActiveSupport #:nodoc:
448
460
  # set of names that the file at +path+ may define. See
449
461
  # +loadable_constants_for_path+ for more details.
450
462
  def load_file(path, const_paths = loadable_constants_for_path(path))
451
- log_call path, const_paths
452
463
  const_paths = [const_paths].compact unless const_paths.is_a? Array
453
464
  parent_paths = const_paths.collect { |const_path| const_path[/.*(?=::)/] || ::Object }
454
465
 
@@ -459,7 +470,6 @@ module ActiveSupport #:nodoc:
459
470
 
460
471
  autoloaded_constants.concat newly_defined_paths unless load_once_path?(path)
461
472
  autoloaded_constants.uniq!
462
- log "loading #{path} defined #{newly_defined_paths * ', '}" unless newly_defined_paths.empty?
463
473
  result
464
474
  end
465
475
 
@@ -473,8 +483,6 @@ module ActiveSupport #:nodoc:
473
483
  # it is not possible to load the constant into from_mod, try its parent
474
484
  # module using +const_missing+.
475
485
  def load_missing_constant(from_mod, const_name)
476
- log_call from_mod, const_name
477
-
478
486
  unless qualified_const_defined?(from_mod.name) && Inflector.constantize(from_mod.name).equal?(from_mod)
479
487
  raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!"
480
488
  end
@@ -486,7 +494,7 @@ module ActiveSupport #:nodoc:
486
494
 
487
495
  if file_path
488
496
  expanded = File.expand_path(file_path)
489
- expanded.sub!(/\.rb\z/, '')
497
+ expanded.sub!(/\.rb\z/, "".freeze)
490
498
 
491
499
  if loading.include?(expanded)
492
500
  raise "Circular dependency detected while autoloading constant #{qualified_name}"
@@ -530,7 +538,7 @@ module ActiveSupport #:nodoc:
530
538
  end
531
539
 
532
540
  name_error = NameError.new("uninitialized constant #{qualified_name}", const_name)
533
- name_error.set_backtrace(caller.reject {|l| l.starts_with? __FILE__ })
541
+ name_error.set_backtrace(caller.reject { |l| l.starts_with? __FILE__ })
534
542
  raise name_error
535
543
  end
536
544
 
@@ -550,7 +558,7 @@ module ActiveSupport #:nodoc:
550
558
 
551
559
  class ClassCache
552
560
  def initialize
553
- @store = ThreadSafe::Cache.new
561
+ @store = Concurrent::Map.new
554
562
  end
555
563
 
556
564
  def empty?
@@ -574,7 +582,7 @@ module ActiveSupport #:nodoc:
574
582
 
575
583
  def store(klass)
576
584
  return self unless klass.respond_to?(:name)
577
- raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty?
585
+ raise(ArgumentError, "anonymous classes cannot be cached") if klass.name.empty?
578
586
  @store[klass.name] = klass
579
587
  self
580
588
  end
@@ -607,8 +615,8 @@ module ActiveSupport #:nodoc:
607
615
  def autoloaded?(desc)
608
616
  return false if desc.is_a?(Module) && desc.anonymous?
609
617
  name = to_constant_name desc
610
- return false unless qualified_const_defined? name
611
- return autoloaded_constants.include?(name)
618
+ return false unless qualified_const_defined?(name)
619
+ autoloaded_constants.include?(name)
612
620
  end
613
621
 
614
622
  # Will the provided constant descriptor be unloaded?
@@ -638,54 +646,49 @@ module ActiveSupport #:nodoc:
638
646
  # exception, any new constants are regarded as being only partially defined
639
647
  # and will be removed immediately.
640
648
  def new_constants_in(*descs)
641
- log_call(*descs)
642
-
643
649
  constant_watch_stack.watch_namespaces(descs)
644
- aborting = true
650
+ success = false
645
651
 
646
652
  begin
647
653
  yield # Now yield to the code that is to define new constants.
648
- aborting = false
654
+ success = true
649
655
  ensure
650
656
  new_constants = constant_watch_stack.new_constants
651
657
 
652
- log "New constants: #{new_constants * ', '}"
653
- return new_constants unless aborting
658
+ return new_constants if success
654
659
 
655
- log "Error during loading, removing partially loaded constants "
656
- new_constants.each { |c| remove_constant(c) }.clear
660
+ # Remove partially loaded constants.
661
+ new_constants.each { |c| remove_constant(c) }
657
662
  end
658
-
659
- []
660
663
  end
661
664
 
662
665
  # Convert the provided const desc to a qualified constant name (as a string).
663
666
  # A module, class, symbol, or string may be provided.
664
667
  def to_constant_name(desc) #:nodoc:
665
668
  case desc
666
- when String then desc.sub(/^::/, '')
667
- when Symbol then desc.to_s
668
- when Module
669
- desc.name ||
670
- raise(ArgumentError, "Anonymous modules have no name to be referenced by")
671
- else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
669
+ when String then desc.sub(/^::/, "")
670
+ when Symbol then desc.to_s
671
+ when Module
672
+ desc.name ||
673
+ raise(ArgumentError, "Anonymous modules have no name to be referenced by")
674
+ else raise TypeError, "Not a valid constant descriptor: #{desc.inspect}"
672
675
  end
673
676
  end
674
677
 
675
678
  def remove_constant(const) #:nodoc:
676
679
  # Normalize ::Foo, ::Object::Foo, Object::Foo, Object::Object::Foo, etc. as Foo.
677
- normalized = const.to_s.sub(/\A::/, '')
678
- normalized.sub!(/\A(Object::)+/, '')
680
+ normalized = const.to_s.sub(/\A::/, "")
681
+ normalized.sub!(/\A(Object::)+/, "")
679
682
 
680
- constants = normalized.split('::')
683
+ constants = normalized.split("::")
681
684
  to_remove = constants.pop
682
685
 
683
686
  # Remove the file path from the loaded list.
684
687
  file_path = search_for_file(const.underscore)
685
688
  if file_path
686
689
  expanded = File.expand_path(file_path)
687
- expanded.sub!(/\.rb\z/, '')
688
- self.loaded.delete(expanded)
690
+ expanded.sub!(/\.rb\z/, "")
691
+ loaded.delete(expanded)
689
692
  end
690
693
 
691
694
  if constants.empty?
@@ -698,13 +701,11 @@ module ActiveSupport #:nodoc:
698
701
  # here than require the caller to be clever. We check the parent
699
702
  # rather than the very const argument because we do not want to
700
703
  # trigger Kernel#autoloads, see the comment below.
701
- parent_name = constants.join('::')
704
+ parent_name = constants.join("::")
702
705
  return unless qualified_const_defined?(parent_name)
703
706
  parent = constantize(parent_name)
704
707
  end
705
708
 
706
- log "removing constant #{const}"
707
-
708
709
  # In an autoloaded user.rb like this
709
710
  #
710
711
  # autoload :Foo, 'foo'
@@ -725,7 +726,7 @@ module ActiveSupport #:nodoc:
725
726
  begin
726
727
  constantized = parent.const_get(to_remove, false)
727
728
  rescue NameError
728
- log "the constant #{const} is not reachable anymore, skipping"
729
+ # The constant is no longer reachable, just skip it.
729
730
  return
730
731
  else
731
732
  constantized.before_remove_const if constantized.respond_to?(:before_remove_const)
@@ -735,27 +736,9 @@ module ActiveSupport #:nodoc:
735
736
  begin
736
737
  parent.instance_eval { remove_const to_remove }
737
738
  rescue NameError
738
- log "the constant #{const} is not reachable anymore, skipping"
739
+ # The constant is no longer reachable, just skip it.
739
740
  end
740
741
  end
741
-
742
- protected
743
- def log_call(*args)
744
- if log_activity?
745
- arg_str = args.collect { |arg| arg.inspect } * ', '
746
- /in `([a-z_\?\!]+)'/ =~ caller(1).first
747
- selector = $1 || '<unknown>'
748
- log "called #{selector}(#{arg_str})"
749
- end
750
- end
751
-
752
- def log(msg)
753
- logger.debug "Dependencies: #{msg}" if log_activity?
754
- end
755
-
756
- def log_activity?
757
- logger && log_activity
758
- end
759
742
  end
760
743
  end
761
744