activesupport 8.0.2.1 → 8.1.0.beta1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +247 -136
- data/README.rdoc +1 -1
- data/lib/active_support/backtrace_cleaner.rb +71 -0
- data/lib/active_support/broadcast_logger.rb +46 -59
- data/lib/active_support/cache/mem_cache_store.rb +25 -27
- data/lib/active_support/cache/redis_cache_store.rb +36 -30
- data/lib/active_support/cache/strategy/local_cache.rb +16 -7
- data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
- data/lib/active_support/cache.rb +70 -6
- data/lib/active_support/configurable.rb +28 -0
- data/lib/active_support/continuous_integration.rb +145 -0
- data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
- data/lib/active_support/core_ext/date_time/conversions.rb +4 -2
- data/lib/active_support/core_ext/enumerable.rb +16 -4
- data/lib/active_support/core_ext/erb/util.rb +3 -3
- data/lib/active_support/core_ext/object/json.rb +8 -1
- data/lib/active_support/core_ext/object/to_query.rb +7 -1
- data/lib/active_support/core_ext/object/try.rb +2 -2
- data/lib/active_support/core_ext/range/overlap.rb +3 -3
- data/lib/active_support/core_ext/range/sole.rb +17 -0
- data/lib/active_support/core_ext/range.rb +1 -1
- data/lib/active_support/core_ext/string/filters.rb +3 -3
- data/lib/active_support/core_ext/string/multibyte.rb +12 -3
- data/lib/active_support/core_ext/string/output_safety.rb +19 -12
- data/lib/active_support/current_attributes/test_helper.rb +2 -2
- data/lib/active_support/current_attributes.rb +26 -16
- data/lib/active_support/deprecation/reporting.rb +4 -2
- data/lib/active_support/deprecation.rb +1 -1
- data/lib/active_support/editor.rb +70 -0
- data/lib/active_support/error_reporter.rb +50 -6
- data/lib/active_support/event_reporter/test_helper.rb +32 -0
- data/lib/active_support/event_reporter.rb +570 -0
- data/lib/active_support/evented_file_update_checker.rb +5 -1
- data/lib/active_support/execution_context.rb +64 -7
- data/lib/active_support/file_update_checker.rb +7 -5
- data/lib/active_support/gem_version.rb +3 -3
- data/lib/active_support/gzip.rb +1 -0
- data/lib/active_support/hash_with_indifferent_access.rb +47 -24
- data/lib/active_support/i18n_railtie.rb +1 -2
- data/lib/active_support/inflector/inflections.rb +31 -15
- data/lib/active_support/inflector/transliterate.rb +6 -8
- data/lib/active_support/isolated_execution_state.rb +7 -13
- data/lib/active_support/json/decoding.rb +6 -4
- data/lib/active_support/json/encoding.rb +103 -14
- data/lib/active_support/lazy_load_hooks.rb +1 -1
- data/lib/active_support/log_subscriber.rb +2 -0
- data/lib/active_support/logger_thread_safe_level.rb +6 -3
- data/lib/active_support/message_encryptors.rb +52 -0
- data/lib/active_support/message_pack/extensions.rb +5 -0
- data/lib/active_support/message_verifiers.rb +52 -0
- data/lib/active_support/messages/rotation_coordinator.rb +9 -0
- data/lib/active_support/messages/rotator.rb +5 -0
- data/lib/active_support/multibyte/chars.rb +8 -1
- data/lib/active_support/multibyte.rb +4 -0
- data/lib/active_support/railtie.rb +26 -12
- data/lib/active_support/syntax_error_proxy.rb +3 -0
- data/lib/active_support/test_case.rb +61 -6
- data/lib/active_support/testing/assertions.rb +34 -6
- data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
- data/lib/active_support/testing/event_reporter_assertions.rb +217 -0
- data/lib/active_support/testing/notification_assertions.rb +92 -0
- data/lib/active_support/testing/parallelization/worker.rb +2 -0
- data/lib/active_support/testing/parallelization.rb +13 -0
- data/lib/active_support/testing/tests_without_assertions.rb +1 -1
- data/lib/active_support/testing/time_helpers.rb +7 -3
- data/lib/active_support/time_with_zone.rb +19 -5
- data/lib/active_support/values/time_zone.rb +8 -1
- data/lib/active_support/xml_mini.rb +1 -2
- data/lib/active_support.rb +11 -0
- metadata +11 -5
- data/lib/active_support/core_ext/range/each.rb +0 -24
@@ -0,0 +1,145 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveSupport
|
4
|
+
# Provides a DSL for declaring a continuous integration workflow that can be run either locally or in the cloud.
|
5
|
+
# Each step is timed, reports success/error, and is aggregated into a collective report that reports total runtime,
|
6
|
+
# as well as whether the entire run was successful or not.
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# ActiveSupport::ContinuousIntegration.run do
|
11
|
+
# step "Setup", "bin/setup --skip-server"
|
12
|
+
# step "Style: Ruby", "bin/rubocop"
|
13
|
+
# step "Security: Gem audit", "bin/bundler-audit"
|
14
|
+
# step "Tests: Rails", "bin/rails test test:system"
|
15
|
+
#
|
16
|
+
# if success?
|
17
|
+
# step "Signoff: Ready for merge and deploy", "gh signoff"
|
18
|
+
# else
|
19
|
+
# failure "Skipping signoff; CI failed.", "Fix the issues and try again."
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Starting with Rails 8.1, a default `bin/ci` and `config/ci.rb` file are created to provide out-of-the-box CI.
|
24
|
+
class ContinuousIntegration
|
25
|
+
COLORS = {
|
26
|
+
banner: "\033[1;32m", # Green
|
27
|
+
title: "\033[1;35m", # Purple
|
28
|
+
subtitle: "\033[1;90m", # Medium Gray
|
29
|
+
error: "\033[1;31m", # Red
|
30
|
+
success: "\033[1;32m" # Green
|
31
|
+
}
|
32
|
+
|
33
|
+
attr_reader :results
|
34
|
+
|
35
|
+
# Perform a CI run. Execute each step, show their results and runtime, and exit with a non-zero status if there are any failures.
|
36
|
+
#
|
37
|
+
# Pass an optional title, subtitle, and a block that declares the steps to be executed.
|
38
|
+
#
|
39
|
+
# Sets the CI environment variable to "true" to allow for conditional behavior in the app, like enabling eager loading and disabling logging.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# ActiveSupport::ContinuousIntegration.run do
|
44
|
+
# step "Setup", "bin/setup --skip-server"
|
45
|
+
# step "Style: Ruby", "bin/rubocop"
|
46
|
+
# step "Security: Gem audit", "bin/bundler-audit"
|
47
|
+
# step "Tests: Rails", "bin/rails test test:system"
|
48
|
+
#
|
49
|
+
# if success?
|
50
|
+
# step "Signoff: Ready for merge and deploy", "gh signoff"
|
51
|
+
# else
|
52
|
+
# failure "Skipping signoff; CI failed.", "Fix the issues and try again."
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
def self.run(title = "Continuous Integration", subtitle = "Running tests, style checks, and security audits", &block)
|
56
|
+
new.tap do |ci|
|
57
|
+
ENV["CI"] = "true"
|
58
|
+
ci.heading title, subtitle, padding: false
|
59
|
+
ci.report(title, &block)
|
60
|
+
abort unless ci.success?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
@results = []
|
66
|
+
end
|
67
|
+
|
68
|
+
# Declare a step with a title and a command. The command can either be given as a single string or as multiple
|
69
|
+
# strings that will be passed to `system` as individual arguments (and therefore correctly escaped for paths etc).
|
70
|
+
#
|
71
|
+
# Examples:
|
72
|
+
#
|
73
|
+
# step "Setup", "bin/setup"
|
74
|
+
# step "Single test", "bin/rails", "test", "--name", "test_that_is_one"
|
75
|
+
def step(title, *command)
|
76
|
+
heading title, command.join(" "), type: :title
|
77
|
+
report(title) { results << system(*command) }
|
78
|
+
end
|
79
|
+
|
80
|
+
# Returns true if all steps were successful.
|
81
|
+
def success?
|
82
|
+
results.all?
|
83
|
+
end
|
84
|
+
|
85
|
+
# Display an error heading with the title and optional subtitle to reflect that the run failed.
|
86
|
+
def failure(title, subtitle = nil)
|
87
|
+
heading title, subtitle, type: :error
|
88
|
+
end
|
89
|
+
|
90
|
+
# Display a colorized heading followed by an optional subtitle.
|
91
|
+
#
|
92
|
+
# Examples:
|
93
|
+
#
|
94
|
+
# heading "Smoke Testing", "End-to-end tests verifying key functionality", padding: false
|
95
|
+
# heading "Skipping video encoding tests", "Install FFmpeg to run these tests", type: :error
|
96
|
+
#
|
97
|
+
# See ActiveSupport::ContinuousIntegration::COLORS for a complete list of options.
|
98
|
+
def heading(heading, subtitle = nil, type: :banner, padding: true)
|
99
|
+
echo "#{padding ? "\n\n" : ""}#{heading}", type: type
|
100
|
+
echo "#{subtitle}#{padding ? "\n" : ""}", type: :subtitle if subtitle
|
101
|
+
end
|
102
|
+
|
103
|
+
# Echo text to the terminal in the color corresponding to the type of the text.
|
104
|
+
#
|
105
|
+
# Examples:
|
106
|
+
#
|
107
|
+
# echo "This is going to be green!", type: :success
|
108
|
+
# echo "This is going to be red!", type: :error
|
109
|
+
#
|
110
|
+
# See ActiveSupport::ContinuousIntegration::COLORS for a complete list of options.
|
111
|
+
def echo(text, type:)
|
112
|
+
puts colorize(text, type)
|
113
|
+
end
|
114
|
+
|
115
|
+
# :nodoc:
|
116
|
+
def report(title, &block)
|
117
|
+
Signal.trap("INT") { abort colorize(:error, "\n❌ #{title} interrupted") }
|
118
|
+
|
119
|
+
ci = self.class.new
|
120
|
+
elapsed = timing { ci.instance_eval(&block) }
|
121
|
+
|
122
|
+
if ci.success?
|
123
|
+
echo "\n✅ #{title} passed in #{elapsed}", type: :success
|
124
|
+
else
|
125
|
+
echo "\n❌ #{title} failed in #{elapsed}", type: :error
|
126
|
+
end
|
127
|
+
|
128
|
+
results.concat ci.results
|
129
|
+
ensure
|
130
|
+
Signal.trap("INT", "-")
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
def timing
|
135
|
+
started_at = Time.now.to_f
|
136
|
+
yield
|
137
|
+
min, sec = (Time.now.to_f - started_at).divmod(60)
|
138
|
+
"#{"#{min}m" if min > 0}%.2fs" % sec
|
139
|
+
end
|
140
|
+
|
141
|
+
def colorize(text, type)
|
142
|
+
"#{COLORS.fetch(type)}#{text}\033[0m"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -17,7 +17,7 @@ module DateAndTime
|
|
17
17
|
singleton_class.silence_redefinition_of_method :preserve_timezone
|
18
18
|
|
19
19
|
#--
|
20
|
-
# This re-implements the
|
20
|
+
# This re-implements the behavior of the mattr_reader, instead
|
21
21
|
# of prepending on to it, to avoid overcomplicating a module that
|
22
22
|
# is in turn included in several places. This will all go away in
|
23
23
|
# Rails 8.0 anyway.
|
@@ -11,7 +11,8 @@ class DateTime
|
|
11
11
|
#
|
12
12
|
# This method is aliased to <tt>to_formatted_s</tt>.
|
13
13
|
#
|
14
|
-
#
|
14
|
+
# ==== Examples
|
15
|
+
#
|
15
16
|
# datetime = DateTime.civil(2007, 12, 4, 0, 0, 0, 0) # => Tue, 04 Dec 2007 00:00:00 +0000
|
16
17
|
#
|
17
18
|
# datetime.to_fs(:db) # => "2007-12-04 00:00:00"
|
@@ -23,7 +24,8 @@ class DateTime
|
|
23
24
|
# datetime.to_fs(:rfc822) # => "Tue, 04 Dec 2007 00:00:00 +0000"
|
24
25
|
# datetime.to_fs(:iso8601) # => "2007-12-04T00:00:00+00:00"
|
25
26
|
#
|
26
|
-
#
|
27
|
+
# ==== Adding your own datetime formats to +to_fs+
|
28
|
+
#
|
27
29
|
# DateTime formats are shared with Time. You can add your own to the
|
28
30
|
# Time::DATE_FORMATS hash. Use the format name as the hash key and
|
29
31
|
# either a strftime string or Proc instance that takes a time or
|
@@ -209,10 +209,22 @@ module Enumerable
|
|
209
209
|
# Set.new.sole # => Enumerable::SoleItemExpectedError: no item found
|
210
210
|
# { a: 1, b: 2 }.sole # => Enumerable::SoleItemExpectedError: multiple items found
|
211
211
|
def sole
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
212
|
+
result = nil
|
213
|
+
found = false
|
214
|
+
|
215
|
+
each do |element|
|
216
|
+
if found
|
217
|
+
raise SoleItemExpectedError, "multiple items found"
|
218
|
+
end
|
219
|
+
|
220
|
+
result = element
|
221
|
+
found = true
|
222
|
+
end
|
223
|
+
|
224
|
+
if found
|
225
|
+
result
|
226
|
+
else
|
227
|
+
raise SoleItemExpectedError, "no item found"
|
216
228
|
end
|
217
229
|
end
|
218
230
|
end
|
@@ -12,7 +12,7 @@ module ActiveSupport
|
|
12
12
|
if s.html_safe?
|
13
13
|
s
|
14
14
|
else
|
15
|
-
super(
|
15
|
+
super(s)
|
16
16
|
end
|
17
17
|
end
|
18
18
|
alias :unwrapped_html_escape :html_escape # :nodoc:
|
@@ -61,7 +61,7 @@ class ERB
|
|
61
61
|
# html_escape_once('<< Accept & Checkout')
|
62
62
|
# # => "<< Accept & Checkout"
|
63
63
|
def html_escape_once(s)
|
64
|
-
|
64
|
+
s.to_s.gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
|
65
65
|
end
|
66
66
|
|
67
67
|
module_function :html_escape_once
|
@@ -169,7 +169,7 @@ class ERB
|
|
169
169
|
while !source.eos?
|
170
170
|
pos = source.pos
|
171
171
|
source.scan_until(/(?:#{start_re}|#{finish_re})/)
|
172
|
-
|
172
|
+
return [[:PLAIN, source.string]] unless source.matched?
|
173
173
|
len = source.pos - source.matched.bytesize - pos
|
174
174
|
|
175
175
|
case source.matched
|
@@ -240,9 +240,16 @@ class Pathname # :nodoc:
|
|
240
240
|
end
|
241
241
|
|
242
242
|
unless IPAddr.method_defined?(:as_json, false)
|
243
|
+
# Use `IPAddr#as_json` from the IPAddr gem if the version is 1.2.7 or higher.
|
243
244
|
class IPAddr # :nodoc:
|
244
245
|
def as_json(options = nil)
|
245
|
-
|
246
|
+
if ipv4? && prefix == 32
|
247
|
+
to_s
|
248
|
+
elsif ipv6? && prefix == 128
|
249
|
+
to_s
|
250
|
+
else
|
251
|
+
"#{self}/#{prefix}"
|
252
|
+
end
|
246
253
|
end
|
247
254
|
end
|
248
255
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require "cgi"
|
3
|
+
require "cgi/escape"
|
4
|
+
require "cgi/util" if RUBY_VERSION < "3.5"
|
4
5
|
|
5
6
|
class Object
|
6
7
|
# Alias of <tt>to_s</tt>.
|
@@ -16,6 +17,11 @@ class Object
|
|
16
17
|
end
|
17
18
|
|
18
19
|
class NilClass
|
20
|
+
# Returns a CGI-escaped +key+.
|
21
|
+
def to_query(key)
|
22
|
+
CGI.escape(key.to_param)
|
23
|
+
end
|
24
|
+
|
19
25
|
# Returns +self+.
|
20
26
|
def to_param
|
21
27
|
self
|
@@ -145,14 +145,14 @@ class NilClass
|
|
145
145
|
#
|
146
146
|
# With +try+
|
147
147
|
# @person.try(:children).try(:first).try(:name)
|
148
|
-
def try(
|
148
|
+
def try(*, &)
|
149
149
|
nil
|
150
150
|
end
|
151
151
|
|
152
152
|
# Calling +try!+ on +nil+ always returns +nil+.
|
153
153
|
#
|
154
154
|
# nil.try!(:name) # => nil
|
155
|
-
def try!(
|
155
|
+
def try!(*, &)
|
156
156
|
nil
|
157
157
|
end
|
158
158
|
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class Range
|
4
|
-
# Compare two ranges and see if they overlap each other
|
5
|
-
# (1..5).overlap?(4..6) # => true
|
6
|
-
# (1..5).overlap?(7..9) # => false
|
7
4
|
unless Range.method_defined?(:overlap?) # Ruby 3.3+
|
5
|
+
# Compare two ranges and see if they overlap each other
|
6
|
+
# (1..5).overlap?(4..6) # => true
|
7
|
+
# (1..5).overlap?(7..9) # => false
|
8
8
|
def overlap?(other)
|
9
9
|
raise TypeError unless other.is_a? Range
|
10
10
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Range
|
4
|
+
# Returns the sole item in the range. If there are no items, or more
|
5
|
+
# than one item, raises Enumerable::SoleItemExpectedError.
|
6
|
+
#
|
7
|
+
# (1..1).sole # => 1
|
8
|
+
# (2..1).sole # => Enumerable::SoleItemExpectedError: no item found
|
9
|
+
# (..1).sole # => Enumerable::SoleItemExpectedError: infinite range cannot represent a sole item
|
10
|
+
def sole
|
11
|
+
if self.begin.nil? || self.end.nil?
|
12
|
+
raise ActiveSupport::EnumerableCoreExt::SoleItemExpectedError, "infinite range '#{inspect}' cannot represent a sole item"
|
13
|
+
end
|
14
|
+
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
@@ -88,11 +88,11 @@ class String
|
|
88
88
|
# characters.
|
89
89
|
#
|
90
90
|
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".size
|
91
|
-
# => 20
|
91
|
+
# # => 20
|
92
92
|
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".bytesize
|
93
|
-
# => 80
|
93
|
+
# # => 80
|
94
94
|
# >> "🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪🔪".truncate_bytes(20)
|
95
|
-
# => "🔪🔪🔪🔪…"
|
95
|
+
# # => "🔪🔪🔪🔪…"
|
96
96
|
#
|
97
97
|
# The truncated text ends with the <tt>:omission</tt> string, defaulting
|
98
98
|
# to "…", for a total length not exceeding <tt>truncate_to</tt>.
|
@@ -12,12 +12,12 @@ class String
|
|
12
12
|
# class. If the proxy class doesn't respond to a certain method, it's forwarded to the encapsulated string.
|
13
13
|
#
|
14
14
|
# >> "lj".mb_chars.upcase.to_s
|
15
|
-
# => "LJ"
|
15
|
+
# # => "LJ"
|
16
16
|
#
|
17
17
|
# NOTE: Ruby 2.4 and later support native Unicode case mappings:
|
18
18
|
#
|
19
19
|
# >> "lj".upcase
|
20
|
-
# => "LJ"
|
20
|
+
# # => "LJ"
|
21
21
|
#
|
22
22
|
# == \Method chaining
|
23
23
|
#
|
@@ -35,7 +35,16 @@ class String
|
|
35
35
|
# For more information about the methods defined on the Chars proxy see ActiveSupport::Multibyte::Chars. For
|
36
36
|
# information about how to change the default Multibyte behavior see ActiveSupport::Multibyte.
|
37
37
|
def mb_chars
|
38
|
-
ActiveSupport
|
38
|
+
ActiveSupport.deprecator.warn(
|
39
|
+
"String#mb_chars is deprecated and will be removed in Rails 8.2. " \
|
40
|
+
"Use normal string methods instead."
|
41
|
+
)
|
42
|
+
|
43
|
+
if ActiveSupport::Multibyte.proxy_class == ActiveSupport::Multibyte::Chars
|
44
|
+
ActiveSupport::Multibyte::Chars.new(self, deprecation: false)
|
45
|
+
else
|
46
|
+
ActiveSupport::Multibyte.proxy_class.new(self)
|
47
|
+
end
|
39
48
|
end
|
40
49
|
|
41
50
|
# Returns +true+ if string has utf_8 encoding.
|
@@ -67,14 +67,13 @@ module ActiveSupport # :nodoc:
|
|
67
67
|
original_concat(value)
|
68
68
|
end
|
69
69
|
|
70
|
-
def initialize(
|
71
|
-
@html_safe = true
|
70
|
+
def initialize(_str = "")
|
72
71
|
super
|
73
72
|
end
|
74
73
|
|
75
74
|
def initialize_copy(other)
|
76
75
|
super
|
77
|
-
@
|
76
|
+
@html_unsafe = true unless other.html_safe?
|
78
77
|
end
|
79
78
|
|
80
79
|
def concat(value)
|
@@ -116,7 +115,9 @@ module ActiveSupport # :nodoc:
|
|
116
115
|
def *(_)
|
117
116
|
new_string = super
|
118
117
|
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
|
119
|
-
|
118
|
+
if @html_unsafe
|
119
|
+
new_safe_buffer.instance_variable_set(:@html_unsafe, true)
|
120
|
+
end
|
120
121
|
new_safe_buffer
|
121
122
|
end
|
122
123
|
|
@@ -131,14 +132,18 @@ module ActiveSupport # :nodoc:
|
|
131
132
|
self.class.new(super(escaped_args))
|
132
133
|
end
|
133
134
|
|
134
|
-
|
135
|
-
|
136
|
-
|
135
|
+
def html_safe?
|
136
|
+
@html_unsafe.nil?
|
137
|
+
end
|
137
138
|
|
138
139
|
def to_s
|
139
140
|
self
|
140
141
|
end
|
141
142
|
|
143
|
+
def as_json(*)
|
144
|
+
to_str
|
145
|
+
end
|
146
|
+
|
142
147
|
def to_param
|
143
148
|
to_str
|
144
149
|
end
|
@@ -155,7 +160,7 @@ module ActiveSupport # :nodoc:
|
|
155
160
|
end # end
|
156
161
|
|
157
162
|
def #{unsafe_method}!(*args) # def capitalize!(*args)
|
158
|
-
@
|
163
|
+
@html_unsafe = true # @html_unsafe = true
|
159
164
|
super # super
|
160
165
|
end # end
|
161
166
|
EOT
|
@@ -176,7 +181,7 @@ module ActiveSupport # :nodoc:
|
|
176
181
|
end # end
|
177
182
|
|
178
183
|
def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
|
179
|
-
@
|
184
|
+
@html_unsafe = true # @html_unsafe = true
|
180
185
|
if block # if block
|
181
186
|
super(*args) { |*params| # super(*args) { |*params|
|
182
187
|
set_block_back_references(block, $~) # set_block_back_references(block, $~)
|
@@ -191,14 +196,14 @@ module ActiveSupport # :nodoc:
|
|
191
196
|
|
192
197
|
private
|
193
198
|
def explicit_html_escape_interpolated_argument(arg)
|
194
|
-
(!html_safe? || arg.html_safe?) ? arg :
|
199
|
+
(!html_safe? || arg.html_safe?) ? arg : ERB::Util.unwrapped_html_escape(arg)
|
195
200
|
end
|
196
201
|
|
197
202
|
def implicit_html_escape_interpolated_argument(arg)
|
198
203
|
if !html_safe? || arg.html_safe?
|
199
204
|
arg
|
200
205
|
else
|
201
|
-
|
206
|
+
ERB::Util.unwrapped_html_escape(arg.to_str)
|
202
207
|
end
|
203
208
|
end
|
204
209
|
|
@@ -210,7 +215,9 @@ module ActiveSupport # :nodoc:
|
|
210
215
|
|
211
216
|
def string_into_safe_buffer(new_string, is_html_safe)
|
212
217
|
new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
|
213
|
-
|
218
|
+
unless is_html_safe
|
219
|
+
new_safe_buffer.instance_variable_set :@html_unsafe, true
|
220
|
+
end
|
214
221
|
new_safe_buffer
|
215
222
|
end
|
216
223
|
end
|
@@ -2,12 +2,12 @@
|
|
2
2
|
|
3
3
|
module ActiveSupport::CurrentAttributes::TestHelper # :nodoc:
|
4
4
|
def before_setup
|
5
|
-
ActiveSupport::CurrentAttributes.
|
5
|
+
ActiveSupport::CurrentAttributes.clear_all
|
6
6
|
super
|
7
7
|
end
|
8
8
|
|
9
9
|
def after_teardown
|
10
10
|
super
|
11
|
-
ActiveSupport::CurrentAttributes.
|
11
|
+
ActiveSupport::CurrentAttributes.clear_all
|
12
12
|
end
|
13
13
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "active_support/callbacks"
|
4
|
+
require "active_support/execution_context"
|
4
5
|
require "active_support/core_ext/object/with"
|
5
6
|
require "active_support/core_ext/enumerable"
|
6
7
|
require "active_support/core_ext/module/delegation"
|
@@ -108,35 +109,35 @@ module ActiveSupport
|
|
108
109
|
# ==== Options
|
109
110
|
#
|
110
111
|
# * <tt>:default</tt> - The default value for the attributes. If the value
|
111
|
-
#
|
112
|
-
#
|
113
|
-
#
|
112
|
+
# is a proc or lambda, it will be called whenever an instance is
|
113
|
+
# constructed. Otherwise, the value will be duplicated with +#dup+.
|
114
|
+
# Default values are re-assigned when the attributes are reset.
|
114
115
|
def attribute(*names, default: NOT_SET)
|
115
116
|
invalid_attribute_names = names.map(&:to_sym) & INVALID_ATTRIBUTE_NAMES
|
116
117
|
if invalid_attribute_names.any?
|
117
118
|
raise ArgumentError, "Restricted attribute names: #{invalid_attribute_names.join(", ")}"
|
118
119
|
end
|
119
120
|
|
121
|
+
Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
|
122
|
+
Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
|
123
|
+
|
120
124
|
ActiveSupport::CodeGenerator.batch(generated_attribute_methods, __FILE__, __LINE__) do |owner|
|
121
125
|
names.each do |name|
|
122
126
|
owner.define_cached_method(name, namespace: :current_attributes) do |batch|
|
123
127
|
batch <<
|
124
128
|
"def #{name}" <<
|
125
|
-
"attributes[:#{name}]" <<
|
129
|
+
"@attributes[:#{name}]" <<
|
126
130
|
"end"
|
127
131
|
end
|
128
132
|
owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
|
129
133
|
batch <<
|
130
134
|
"def #{name}=(value)" <<
|
131
|
-
"attributes[:#{name}] = value" <<
|
135
|
+
"@attributes[:#{name}] = value" <<
|
132
136
|
"end"
|
133
137
|
end
|
134
138
|
end
|
135
139
|
end
|
136
140
|
|
137
|
-
Delegation.generate(singleton_class, names, to: :instance, nilable: false, signature: "")
|
138
|
-
Delegation.generate(singleton_class, names.map { |n| "#{n}=" }, to: :instance, nilable: false, signature: "value")
|
139
|
-
|
140
141
|
self.defaults = defaults.merge(names.index_with { default })
|
141
142
|
end
|
142
143
|
|
@@ -153,13 +154,11 @@ module ActiveSupport
|
|
153
154
|
|
154
155
|
delegate :set, :reset, to: :instance
|
155
156
|
|
156
|
-
def reset_all # :nodoc:
|
157
|
-
current_instances.each_value(&:reset)
|
158
|
-
end
|
159
|
-
|
160
157
|
def clear_all # :nodoc:
|
161
|
-
|
162
|
-
|
158
|
+
if instances = current_instances
|
159
|
+
instances.values.each(&:reset)
|
160
|
+
instances.clear
|
161
|
+
end
|
163
162
|
end
|
164
163
|
|
165
164
|
private
|
@@ -168,7 +167,7 @@ module ActiveSupport
|
|
168
167
|
end
|
169
168
|
|
170
169
|
def current_instances
|
171
|
-
|
170
|
+
ExecutionContext.current_attributes_instances
|
172
171
|
end
|
173
172
|
|
174
173
|
def current_instances_key
|
@@ -185,21 +184,32 @@ module ActiveSupport
|
|
185
184
|
|
186
185
|
def method_added(name)
|
187
186
|
super
|
187
|
+
|
188
|
+
# We try to generate instance delegators early to not rely on method_missing.
|
188
189
|
return if name == :initialize
|
190
|
+
|
191
|
+
# If the added method isn't public, we don't delegate it.
|
189
192
|
return unless public_method_defined?(name)
|
193
|
+
|
194
|
+
# If we already have a class method by that name, we don't override it.
|
190
195
|
return if singleton_class.method_defined?(name) || singleton_class.private_method_defined?(name)
|
196
|
+
|
191
197
|
Delegation.generate(singleton_class, [name], to: :instance, as: self, nilable: false)
|
192
198
|
end
|
193
199
|
end
|
194
200
|
|
195
201
|
class_attribute :defaults, instance_writer: false, default: {}.freeze
|
196
202
|
|
197
|
-
|
203
|
+
attr_writer :attributes
|
198
204
|
|
199
205
|
def initialize
|
200
206
|
@attributes = resolve_defaults
|
201
207
|
end
|
202
208
|
|
209
|
+
def attributes
|
210
|
+
@attributes.dup
|
211
|
+
end
|
212
|
+
|
203
213
|
# Expose one or more attributes within a block. Old values are returned after the block concludes.
|
204
214
|
# Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
|
205
215
|
#
|
@@ -149,8 +149,10 @@ module ActiveSupport
|
|
149
149
|
[offending_line.path, offending_line.lineno, offending_line.label]
|
150
150
|
end
|
151
151
|
|
152
|
-
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
153
|
-
|
152
|
+
RAILS_GEM_ROOT = File.expand_path("../../../..", __dir__) + "/"
|
153
|
+
private_constant :RAILS_GEM_ROOT
|
154
|
+
LIB_DIR = RbConfig::CONFIG["libdir"]
|
155
|
+
private_constant :LIB_DIR
|
154
156
|
|
155
157
|
def ignored_callstack?(path)
|
156
158
|
path.start_with?(RAILS_GEM_ROOT, LIB_DIR) || path.include?("<internal:")
|
@@ -68,7 +68,7 @@ module ActiveSupport
|
|
68
68
|
# and the second is a library name.
|
69
69
|
#
|
70
70
|
# ActiveSupport::Deprecation.new('2.0', 'MyLibrary')
|
71
|
-
def initialize(deprecation_horizon = "8.
|
71
|
+
def initialize(deprecation_horizon = "8.2", gem_name = "Rails")
|
72
72
|
self.gem_name = gem_name
|
73
73
|
self.deprecation_horizon = deprecation_horizon
|
74
74
|
# By default, warnings are not silenced and debugging is off.
|