activesupport 8.0.5.2 → 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.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +220 -278
  3. data/lib/active_support/backtrace_cleaner.rb +71 -0
  4. data/lib/active_support/cache/mem_cache_store.rb +14 -17
  5. data/lib/active_support/cache/redis_cache_store.rb +37 -31
  6. data/lib/active_support/cache/strategy/local_cache.rb +16 -7
  7. data/lib/active_support/cache/strategy/local_cache_middleware.rb +7 -7
  8. data/lib/active_support/cache.rb +69 -6
  9. data/lib/active_support/configurable.rb +28 -0
  10. data/lib/active_support/continuous_integration.rb +145 -0
  11. data/lib/active_support/core_ext/benchmark.rb +0 -1
  12. data/lib/active_support/core_ext/class/attribute.rb +6 -8
  13. data/lib/active_support/core_ext/date_and_time/compatibility.rb +1 -1
  14. data/lib/active_support/core_ext/enumerable.rb +2 -2
  15. data/lib/active_support/core_ext/erb/util.rb +3 -3
  16. data/lib/active_support/core_ext/object/json.rb +9 -2
  17. data/lib/active_support/core_ext/object/to_query.rb +5 -0
  18. data/lib/active_support/core_ext/range.rb +0 -1
  19. data/lib/active_support/core_ext/string/inflections.rb +1 -1
  20. data/lib/active_support/core_ext/string/multibyte.rb +10 -1
  21. data/lib/active_support/core_ext/string/output_safety.rb +20 -15
  22. data/lib/active_support/current_attributes/test_helper.rb +2 -2
  23. data/lib/active_support/current_attributes.rb +13 -10
  24. data/lib/active_support/delegation.rb +42 -25
  25. data/lib/active_support/deprecation/reporting.rb +4 -2
  26. data/lib/active_support/deprecation.rb +1 -1
  27. data/lib/active_support/editor.rb +70 -0
  28. data/lib/active_support/error_reporter.rb +50 -6
  29. data/lib/active_support/event_reporter/test_helper.rb +32 -0
  30. data/lib/active_support/event_reporter.rb +570 -0
  31. data/lib/active_support/evented_file_update_checker.rb +5 -1
  32. data/lib/active_support/execution_context.rb +64 -7
  33. data/lib/active_support/file_update_checker.rb +8 -6
  34. data/lib/active_support/gem_version.rb +3 -3
  35. data/lib/active_support/gzip.rb +1 -0
  36. data/lib/active_support/hash_with_indifferent_access.rb +27 -7
  37. data/lib/active_support/i18n_railtie.rb +1 -2
  38. data/lib/active_support/inflector/inflections.rb +32 -17
  39. data/lib/active_support/inflector/methods.rb +21 -5
  40. data/lib/active_support/inflector/transliterate.rb +6 -8
  41. data/lib/active_support/isolated_execution_state.rb +9 -17
  42. data/lib/active_support/json/decoding.rb +2 -2
  43. data/lib/active_support/json/encoding.rb +103 -14
  44. data/lib/active_support/log_subscriber.rb +2 -0
  45. data/lib/active_support/message_encryptors.rb +52 -0
  46. data/lib/active_support/message_pack/extensions.rb +6 -1
  47. data/lib/active_support/message_verifiers.rb +52 -0
  48. data/lib/active_support/messages/rotation_coordinator.rb +9 -0
  49. data/lib/active_support/messages/rotator.rb +5 -0
  50. data/lib/active_support/multibyte/chars.rb +8 -1
  51. data/lib/active_support/multibyte.rb +4 -0
  52. data/lib/active_support/notifications/instrumenter.rb +1 -1
  53. data/lib/active_support/number_helper/number_converter.rb +1 -1
  54. data/lib/active_support/number_helper/number_to_delimited_converter.rb +2 -17
  55. data/lib/active_support/railtie.rb +26 -12
  56. data/lib/active_support/syntax_error_proxy.rb +3 -0
  57. data/lib/active_support/test_case.rb +61 -12
  58. data/lib/active_support/testing/assertions.rb +37 -14
  59. data/lib/active_support/testing/autorun.rb +0 -5
  60. data/lib/active_support/testing/error_reporter_assertions.rb +18 -1
  61. data/lib/active_support/testing/event_reporter_assertions.rb +217 -0
  62. data/lib/active_support/testing/notification_assertions.rb +92 -0
  63. data/lib/active_support/testing/parallelization/server.rb +2 -15
  64. data/lib/active_support/testing/parallelization/worker.rb +5 -7
  65. data/lib/active_support/testing/parallelization.rb +14 -12
  66. data/lib/active_support/testing/tests_without_assertions.rb +1 -1
  67. data/lib/active_support/testing/time_helpers.rb +7 -3
  68. data/lib/active_support/time_with_zone.rb +19 -5
  69. data/lib/active_support/values/time_zone.rb +8 -1
  70. data/lib/active_support/xml_mini.rb +1 -4
  71. data/lib/active_support.rb +11 -0
  72. metadata +11 -6
  73. 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
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "benchmark"
4
- return if Benchmark.respond_to?(:ms)
5
4
 
6
5
  class << Benchmark
7
6
  def ms(&block) # :nodoc
@@ -96,16 +96,14 @@ class Class
96
96
  namespaced_name = :"__class_attr_#{name}"
97
97
  ::ActiveSupport::ClassAttribute.redefine(self, name, namespaced_name, default)
98
98
 
99
- class_methods << "def #{name}; #{namespaced_name}; end"
100
- class_methods << "def #{name}=(value); self.#{namespaced_name} = value; end"
99
+ delegators = [
100
+ "def #{name}; #{namespaced_name}; end",
101
+ "def #{name}=(value); self.#{namespaced_name} = value; end",
102
+ ]
101
103
 
104
+ class_methods.concat(delegators)
102
105
  if singleton_class?
103
- methods << <<~RUBY if instance_reader
104
- silence_redefinition_of_method(:#{name})
105
- def #{name}
106
- self.singleton_class.#{name}
107
- end
108
- RUBY
106
+ methods.concat(delegators)
109
107
  else
110
108
  methods << <<~RUBY if instance_reader
111
109
  silence_redefinition_of_method def #{name}
@@ -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 behaviour of the mattr_reader, instead
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.
@@ -212,12 +212,12 @@ module Enumerable
212
212
  result = nil
213
213
  found = false
214
214
 
215
- each do |*element|
215
+ each do |element|
216
216
  if found
217
217
  raise SoleItemExpectedError, "multiple items found"
218
218
  end
219
219
 
220
- result = element.size == 1 ? element[0] : element
220
+ result = element
221
221
  found = true
222
222
  end
223
223
 
@@ -12,7 +12,7 @@ module ActiveSupport
12
12
  if s.html_safe?
13
13
  s
14
14
  else
15
- super(ActiveSupport::Multibyte::Unicode.tidy_bytes(s))
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('&lt;&lt; Accept & Checkout')
62
62
  # # => "&lt;&lt; Accept &amp; Checkout"
63
63
  def html_escape_once(s)
64
- ActiveSupport::Multibyte::Unicode.tidy_bytes(s.to_s).gsub(HTML_ESCAPE_ONCE_REGEXP, HTML_ESCAPE).html_safe
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
- raise NotImplementedError if source.matched.nil?
172
+ return [[:PLAIN, source.string]] unless source.matched?
173
173
  len = source.pos - source.matched.bytesize - pos
174
174
 
175
175
  case source.matched
@@ -4,7 +4,7 @@
4
4
  require "json"
5
5
  require "bigdecimal"
6
6
  require "ipaddr"
7
- require "uri"
7
+ require "uri/generic"
8
8
  require "pathname"
9
9
  require "active_support/core_ext/big_decimal/conversions" # for #to_s
10
10
  require "active_support/core_ext/hash/except"
@@ -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
- to_s
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
@@ -17,6 +17,11 @@ class Object
17
17
  end
18
18
 
19
19
  class NilClass
20
+ # Returns a CGI-escaped +key+.
21
+ def to_query(key)
22
+ CGI.escape(key.to_param)
23
+ end
24
+
20
25
  # Returns +self+.
21
26
  def to_param
22
27
  self
@@ -3,5 +3,4 @@
3
3
  require "active_support/core_ext/range/conversions"
4
4
  require "active_support/core_ext/range/compare_range"
5
5
  require "active_support/core_ext/range/overlap"
6
- require "active_support/core_ext/range/each"
7
6
  require "active_support/core_ext/range/sole"
@@ -248,7 +248,7 @@ class String
248
248
  # optional parameter +capitalize+ to false.
249
249
  # By default, this parameter is true.
250
250
  #
251
- # The trailing '_id' can be kept by setting the
251
+ # The trailing '_id' can be kept and capitalized by setting the
252
252
  # optional parameter +keep_id_suffix+ to true.
253
253
  # By default, this parameter is false.
254
254
  #
@@ -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::Multibyte.proxy_class.new(self)
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(str = "")
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
- @html_safe = other.html_safe?
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
- new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
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
 
@@ -128,19 +129,21 @@ module ActiveSupport # :nodoc:
128
129
  escaped_args = Array(args).map { |arg| explicit_html_escape_interpolated_argument(arg) }
129
130
  end
130
131
 
131
- new_safe_buffer = self.class.new(super(escaped_args))
132
- new_safe_buffer.instance_variable_set(:@html_safe, @html_safe)
133
- new_safe_buffer
132
+ self.class.new(super(escaped_args))
134
133
  end
135
134
 
136
- attr_reader :html_safe
137
- alias_method :html_safe?, :html_safe
138
- remove_method :html_safe
135
+ def html_safe?
136
+ @html_unsafe.nil?
137
+ end
139
138
 
140
139
  def to_s
141
140
  self
142
141
  end
143
142
 
143
+ def as_json(*)
144
+ to_str
145
+ end
146
+
144
147
  def to_param
145
148
  to_str
146
149
  end
@@ -157,7 +160,7 @@ module ActiveSupport # :nodoc:
157
160
  end # end
158
161
 
159
162
  def #{unsafe_method}!(*args) # def capitalize!(*args)
160
- @html_safe = false # @html_safe = false
163
+ @html_unsafe = true # @html_unsafe = true
161
164
  super # super
162
165
  end # end
163
166
  EOT
@@ -178,7 +181,7 @@ module ActiveSupport # :nodoc:
178
181
  end # end
179
182
 
180
183
  def #{unsafe_method}!(*args, &block) # def gsub!(*args, &block)
181
- @html_safe = false # @html_safe = false
184
+ @html_unsafe = true # @html_unsafe = true
182
185
  if block # if block
183
186
  super(*args) { |*params| # super(*args) { |*params|
184
187
  set_block_back_references(block, $~) # set_block_back_references(block, $~)
@@ -193,14 +196,14 @@ module ActiveSupport # :nodoc:
193
196
 
194
197
  private
195
198
  def explicit_html_escape_interpolated_argument(arg)
196
- (!html_safe? || arg.html_safe?) ? arg : CGI.escapeHTML(arg.to_s)
199
+ (!html_safe? || arg.html_safe?) ? arg : ERB::Util.unwrapped_html_escape(arg)
197
200
  end
198
201
 
199
202
  def implicit_html_escape_interpolated_argument(arg)
200
203
  if !html_safe? || arg.html_safe?
201
204
  arg
202
205
  else
203
- CGI.escapeHTML(arg.to_str)
206
+ ERB::Util.unwrapped_html_escape(arg.to_str)
204
207
  end
205
208
  end
206
209
 
@@ -212,7 +215,9 @@ module ActiveSupport # :nodoc:
212
215
 
213
216
  def string_into_safe_buffer(new_string, is_html_safe)
214
217
  new_safe_buffer = new_string.is_a?(SafeBuffer) ? new_string : SafeBuffer.new(new_string)
215
- new_safe_buffer.instance_variable_set :@html_safe, is_html_safe
218
+ unless is_html_safe
219
+ new_safe_buffer.instance_variable_set :@html_unsafe, true
220
+ end
216
221
  new_safe_buffer
217
222
  end
218
223
  end
@@ -2,12 +2,12 @@
2
2
 
3
3
  module ActiveSupport::CurrentAttributes::TestHelper # :nodoc:
4
4
  def before_setup
5
- ActiveSupport::CurrentAttributes.reset_all
5
+ ActiveSupport::CurrentAttributes.clear_all
6
6
  super
7
7
  end
8
8
 
9
9
  def after_teardown
10
10
  super
11
- ActiveSupport::CurrentAttributes.reset_all
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"
@@ -125,13 +126,13 @@ module ActiveSupport
125
126
  owner.define_cached_method(name, namespace: :current_attributes) do |batch|
126
127
  batch <<
127
128
  "def #{name}" <<
128
- "attributes[:#{name}]" <<
129
+ "@attributes[:#{name}]" <<
129
130
  "end"
130
131
  end
131
132
  owner.define_cached_method("#{name}=", namespace: :current_attributes) do |batch|
132
133
  batch <<
133
134
  "def #{name}=(value)" <<
134
- "attributes[:#{name}] = value" <<
135
+ "@attributes[:#{name}] = value" <<
135
136
  "end"
136
137
  end
137
138
  end
@@ -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
- reset_all
162
- current_instances.clear
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
- IsolatedExecutionState[:current_attributes_instances] ||= {}
170
+ ExecutionContext.current_attributes_instances
172
171
  end
173
172
 
174
173
  def current_instances_key
@@ -201,12 +200,16 @@ module ActiveSupport
201
200
 
202
201
  class_attribute :defaults, instance_writer: false, default: {}.freeze
203
202
 
204
- attr_accessor :attributes
203
+ attr_writer :attributes
205
204
 
206
205
  def initialize
207
206
  @attributes = resolve_defaults
208
207
  end
209
208
 
209
+ def attributes
210
+ @attributes.dup
211
+ end
212
+
210
213
  # Expose one or more attributes within a block. Old values are returned after the block concludes.
211
214
  # Example demonstrating the common use of needing to set Current attributes outside the request-cycle:
212
215
  #
@@ -134,11 +134,11 @@ module ActiveSupport
134
134
  "def #{method_name}(#{definition})" <<
135
135
  " _ = #{receiver}" <<
136
136
  " _.#{method}(#{definition})" <<
137
- "rescue ::NoMethodError => e" <<
137
+ "rescue NoMethodError => e" <<
138
138
  " if _.nil? && e.name == :#{method}" <<
139
- " ::Kernel.raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
139
+ " raise ::ActiveSupport::DelegationError.nil_target(:#{method_name}, :'#{receiver}')" <<
140
140
  " else" <<
141
- " ::Kernel.raise" <<
141
+ " raise" <<
142
142
  " end" <<
143
143
  "end"
144
144
  end
@@ -151,32 +151,49 @@ module ActiveSupport
151
151
  target = target.to_s
152
152
  target = "self.#{target}" if RESERVED_METHOD_NAMES.include?(target) || target == "__target"
153
153
 
154
- nil_behavior = if allow_nil
155
- "nil"
156
- else
157
- "::Kernel.raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')"
158
- end
154
+ if allow_nil
155
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
156
+ def respond_to_missing?(name, include_private = false)
157
+ # It may look like an oversight, but we deliberately do not pass
158
+ # +include_private+, because they do not get delegated.
159
159
 
160
- owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
161
- def respond_to_missing?(name, include_private = false)
162
- # It may look like an oversight, but we deliberately do not pass
163
- # +include_private+, because they do not get delegated.
160
+ return false if name == :marshal_dump || name == :_dump
161
+ #{target}.respond_to?(name) || super
162
+ end
164
163
 
165
- return false if name == :marshal_dump || name == :_dump
166
- #{target}.respond_to?(name) || super
167
- end
164
+ def method_missing(method, ...)
165
+ __target = #{target}
166
+ if __target.nil? && !nil.respond_to?(method)
167
+ nil
168
+ elsif __target.respond_to?(method)
169
+ __target.public_send(method, ...)
170
+ else
171
+ super
172
+ end
173
+ end
174
+ RUBY
175
+ else
176
+ owner.module_eval <<~RUBY, __FILE__, __LINE__ + 1
177
+ def respond_to_missing?(name, include_private = false)
178
+ # It may look like an oversight, but we deliberately do not pass
179
+ # +include_private+, because they do not get delegated.
168
180
 
169
- def method_missing(method, ...)
170
- __target = #{target}
171
- if __target.nil? && !nil.respond_to?(method)
172
- #{nil_behavior}
173
- elsif __target.respond_to?(method)
174
- __target.public_send(method, ...)
175
- else
176
- super
181
+ return false if name == :marshal_dump || name == :_dump
182
+ #{target}.respond_to?(name) || super
177
183
  end
178
- end
179
- RUBY
184
+
185
+ def method_missing(method, ...)
186
+ __target = #{target}
187
+ if __target.nil? && !nil.respond_to?(method)
188
+ raise ::ActiveSupport::DelegationError.nil_target(method, :'#{target}')
189
+ elsif __target.respond_to?(method)
190
+ __target.public_send(method, ...)
191
+ else
192
+ super
193
+ end
194
+ end
195
+ RUBY
196
+ end
180
197
  end
181
198
  end
182
199
  end
@@ -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__) + "/" # :nodoc:
153
- LIB_DIR = RbConfig::CONFIG["libdir"] # :nodoc:
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.1", gem_name = "Rails")
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.
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActiveSupport
6
+ class Editor
7
+ @editors = {}
8
+ @current = false
9
+
10
+ class << self
11
+ # Registers a URL pattern for opening file in a given editor.
12
+ # This allows Rails to generate clickable links to control known editors.
13
+ #
14
+ # Example:
15
+ #
16
+ # ActiveSupport::Editor.register("myeditor", "myeditor://%s:%d")
17
+ def register(name, url_pattern, aliases: [])
18
+ editor = new(url_pattern)
19
+ @editors[name] = editor
20
+ aliases.each do |a|
21
+ @editors[a] = editor
22
+ end
23
+ end
24
+
25
+ # Returns the current editor pattern if it is known.
26
+ # First check for the `RAILS_EDITOR` environment variable, and if it's
27
+ # missing, check for the `EDITOR` environment variable.
28
+ def current
29
+ if @current == false
30
+ @current = if editor_name = ENV["RAILS_EDITOR"] || ENV["EDITOR"]
31
+ @editors[editor_name]
32
+ end
33
+ end
34
+ @current
35
+ end
36
+
37
+ # :nodoc:
38
+
39
+ def find(name)
40
+ @editors[name]
41
+ end
42
+
43
+ def reset
44
+ @current = false
45
+ end
46
+ end
47
+
48
+ def initialize(url_pattern)
49
+ @url_pattern = url_pattern
50
+ end
51
+
52
+ def url_for(path, line)
53
+ sprintf(@url_pattern, path, line)
54
+ end
55
+
56
+ register "atom", "atom://core/open/file?filename=%s&line=%d"
57
+ register "cursor", "cursor://file/%s:%f"
58
+ register "emacs", "emacs://open?url=file://%s&line=%d", aliases: ["emacsclient"]
59
+ register "idea", "idea://open?file=%s&line=%d"
60
+ register "macvim", "mvim://open?url=file://%s&line=%d", aliases: ["mvim"]
61
+ register "nova", "nova://open?path=%s&line=%d"
62
+ register "rubymine", "x-mine://open?file=%s&line=%d"
63
+ register "sublime", "subl://open?url=file://%s&line=%d", aliases: ["subl"]
64
+ register "textmate", "txmt://open?url=file://%s&line=%d", aliases: ["mate"]
65
+ register "vscode", "vscode://file/%s:%d", aliases: ["code"]
66
+ register "vscodium", "vscodium://file/%s:%d", aliases: ["codium"]
67
+ register "windsurf", "windsurf://file/%s:%d"
68
+ register "zed", "zed://file/%s:%d"
69
+ end
70
+ end