actionpack 6.0.4 → 6.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +242 -297
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/lib/abstract_controller.rb +1 -0
  6. data/lib/abstract_controller/base.rb +35 -2
  7. data/lib/abstract_controller/callbacks.rb +2 -2
  8. data/lib/abstract_controller/helpers.rb +105 -90
  9. data/lib/abstract_controller/rendering.rb +9 -9
  10. data/lib/abstract_controller/translation.rb +8 -2
  11. data/lib/action_controller.rb +2 -3
  12. data/lib/action_controller/api.rb +2 -2
  13. data/lib/action_controller/base.rb +4 -2
  14. data/lib/action_controller/caching.rb +0 -1
  15. data/lib/action_controller/log_subscriber.rb +3 -3
  16. data/lib/action_controller/metal.rb +2 -2
  17. data/lib/action_controller/metal/conditional_get.rb +10 -2
  18. data/lib/action_controller/metal/content_security_policy.rb +1 -1
  19. data/lib/action_controller/metal/data_streaming.rb +1 -1
  20. data/lib/action_controller/metal/etag_with_template_digest.rb +2 -4
  21. data/lib/action_controller/metal/exceptions.rb +33 -0
  22. data/lib/action_controller/metal/feature_policy.rb +46 -0
  23. data/lib/action_controller/metal/head.rb +7 -4
  24. data/lib/action_controller/metal/helpers.rb +11 -1
  25. data/lib/action_controller/metal/http_authentication.rb +5 -3
  26. data/lib/action_controller/metal/implicit_render.rb +1 -1
  27. data/lib/action_controller/metal/instrumentation.rb +11 -9
  28. data/lib/action_controller/metal/live.rb +1 -1
  29. data/lib/action_controller/metal/logging.rb +20 -0
  30. data/lib/action_controller/metal/mime_responds.rb +6 -2
  31. data/lib/action_controller/metal/parameter_encoding.rb +35 -4
  32. data/lib/action_controller/metal/params_wrapper.rb +16 -11
  33. data/lib/action_controller/metal/redirecting.rb +1 -1
  34. data/lib/action_controller/metal/rendering.rb +6 -0
  35. data/lib/action_controller/metal/request_forgery_protection.rb +1 -1
  36. data/lib/action_controller/metal/rescue.rb +1 -1
  37. data/lib/action_controller/metal/strong_parameters.rb +103 -15
  38. data/lib/action_controller/renderer.rb +24 -13
  39. data/lib/action_controller/test_case.rb +62 -56
  40. data/lib/action_dispatch.rb +3 -2
  41. data/lib/action_dispatch/http/cache.rb +12 -10
  42. data/lib/action_dispatch/http/content_security_policy.rb +5 -1
  43. data/lib/action_dispatch/http/feature_policy.rb +168 -0
  44. data/lib/action_dispatch/http/filter_parameters.rb +1 -1
  45. data/lib/action_dispatch/http/filter_redirect.rb +1 -1
  46. data/lib/action_dispatch/http/headers.rb +3 -2
  47. data/lib/action_dispatch/http/mime_negotiation.rb +14 -8
  48. data/lib/action_dispatch/http/mime_type.rb +29 -16
  49. data/lib/action_dispatch/http/parameters.rb +1 -19
  50. data/lib/action_dispatch/http/request.rb +24 -8
  51. data/lib/action_dispatch/http/response.rb +17 -16
  52. data/lib/action_dispatch/http/url.rb +3 -2
  53. data/lib/action_dispatch/journey.rb +0 -2
  54. data/lib/action_dispatch/journey/formatter.rb +53 -28
  55. data/lib/action_dispatch/journey/gtg/builder.rb +22 -36
  56. data/lib/action_dispatch/journey/gtg/simulator.rb +8 -7
  57. data/lib/action_dispatch/journey/gtg/transition_table.rb +6 -4
  58. data/lib/action_dispatch/journey/nfa/dot.rb +0 -11
  59. data/lib/action_dispatch/journey/nodes/node.rb +4 -3
  60. data/lib/action_dispatch/journey/parser.rb +13 -13
  61. data/lib/action_dispatch/journey/parser.y +1 -1
  62. data/lib/action_dispatch/journey/path/pattern.rb +13 -18
  63. data/lib/action_dispatch/journey/route.rb +7 -18
  64. data/lib/action_dispatch/journey/router.rb +26 -30
  65. data/lib/action_dispatch/journey/router/utils.rb +6 -4
  66. data/lib/action_dispatch/middleware/actionable_exceptions.rb +1 -1
  67. data/lib/action_dispatch/middleware/cookies.rb +67 -32
  68. data/lib/action_dispatch/middleware/debug_exceptions.rb +8 -15
  69. data/lib/action_dispatch/middleware/debug_view.rb +1 -1
  70. data/lib/action_dispatch/middleware/exception_wrapper.rb +28 -16
  71. data/lib/action_dispatch/middleware/host_authorization.rb +30 -19
  72. data/lib/action_dispatch/middleware/remote_ip.rb +5 -4
  73. data/lib/action_dispatch/middleware/request_id.rb +4 -5
  74. data/lib/action_dispatch/middleware/session/abstract_store.rb +2 -2
  75. data/lib/action_dispatch/middleware/session/cookie_store.rb +2 -2
  76. data/lib/action_dispatch/middleware/ssl.rb +9 -6
  77. data/lib/action_dispatch/middleware/stack.rb +18 -0
  78. data/lib/action_dispatch/middleware/static.rb +154 -93
  79. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +18 -0
  80. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +2 -5
  81. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +2 -2
  82. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +2 -3
  83. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +88 -8
  84. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +1 -1
  85. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +12 -1
  86. data/lib/action_dispatch/railtie.rb +3 -2
  87. data/lib/action_dispatch/request/session.rb +2 -8
  88. data/lib/action_dispatch/request/utils.rb +26 -2
  89. data/lib/action_dispatch/routing/inspector.rb +8 -7
  90. data/lib/action_dispatch/routing/mapper.rb +102 -71
  91. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -19
  92. data/lib/action_dispatch/routing/redirection.rb +3 -3
  93. data/lib/action_dispatch/routing/route_set.rb +49 -41
  94. data/lib/action_dispatch/system_test_case.rb +29 -24
  95. data/lib/action_dispatch/system_testing/browser.rb +33 -27
  96. data/lib/action_dispatch/system_testing/driver.rb +6 -7
  97. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +47 -6
  98. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +4 -7
  99. data/lib/action_dispatch/testing/assertions.rb +1 -1
  100. data/lib/action_dispatch/testing/assertions/response.rb +2 -4
  101. data/lib/action_dispatch/testing/assertions/routing.rb +5 -5
  102. data/lib/action_dispatch/testing/integration.rb +38 -27
  103. data/lib/action_dispatch/testing/test_process.rb +29 -4
  104. data/lib/action_dispatch/testing/test_request.rb +3 -3
  105. data/lib/action_pack.rb +1 -1
  106. data/lib/action_pack/gem_version.rb +3 -3
  107. metadata +20 -21
  108. data/lib/action_controller/metal/force_ssl.rb +0 -58
  109. data/lib/action_dispatch/http/parameter_filter.rb +0 -12
  110. data/lib/action_dispatch/journey/nfa/builder.rb +0 -78
  111. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  112. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -119
data/MIT-LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2004-2019 David Heinemeier Hansson
1
+ Copyright (c) 2004-2020 David Heinemeier Hansson
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.rdoc CHANGED
@@ -33,7 +33,7 @@ The latest version of Action Pack can be installed with RubyGems:
33
33
 
34
34
  Source code can be downloaded as part of the Rails project on GitHub:
35
35
 
36
- * https://github.com/rails/rails/tree/main/actionpack
36
+ * https://github.com/rails/rails/tree/master/actionpack
37
37
 
38
38
 
39
39
  == License
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "action_pack"
4
+ require "active_support"
4
5
  require "active_support/rails"
5
6
  require "active_support/i18n"
6
7
 
@@ -9,6 +9,35 @@ require "active_support/core_ext/module/attr_internal"
9
9
  module AbstractController
10
10
  # Raised when a non-existing controller action is triggered.
11
11
  class ActionNotFound < StandardError
12
+ attr_reader :controller, :action
13
+ def initialize(message = nil, controller = nil, action = nil)
14
+ @controller = controller
15
+ @action = action
16
+ super(message)
17
+ end
18
+
19
+ class Correction
20
+ def initialize(error)
21
+ @error = error
22
+ end
23
+
24
+ def corrections
25
+ if @error.action
26
+ maybe_these = @error.controller.class.action_methods
27
+
28
+ maybe_these.sort_by { |n|
29
+ DidYouMean::Jaro.distance(@error.action.to_s, n)
30
+ }.reverse.first(4)
31
+ else
32
+ []
33
+ end
34
+ end
35
+ end
36
+
37
+ # We may not have DYM, and DYM might not let us register error handlers
38
+ if defined?(DidYouMean) && DidYouMean.respond_to?(:correct_error)
39
+ DidYouMean.correct_error(self, Correction)
40
+ end
12
41
  end
13
42
 
14
43
  # AbstractController::Base is a low-level API. Nobody should be
@@ -104,7 +133,7 @@ module AbstractController
104
133
  # ==== Returns
105
134
  # * <tt>String</tt>
106
135
  def controller_path
107
- @controller_path ||= name.sub(/Controller$/, "").underscore unless anonymous?
136
+ @controller_path ||= name.delete_suffix("Controller").underscore unless anonymous?
108
137
  end
109
138
 
110
139
  # Refresh the cached action_methods when a new action_method is added.
@@ -128,7 +157,7 @@ module AbstractController
128
157
  @_action_name = action.to_s
129
158
 
130
159
  unless action_name = _find_action_name(@_action_name)
131
- raise ActionNotFound, "The action '#{action}' could not be found for #{self.class.name}"
160
+ raise ActionNotFound.new("The action '#{action}' could not be found for #{self.class.name}", self, action)
132
161
  end
133
162
 
134
163
  @_response_body = nil
@@ -175,6 +204,10 @@ module AbstractController
175
204
  true
176
205
  end
177
206
 
207
+ def inspect # :nodoc:
208
+ "#<#{self.class.name}:#{'%#016x' % (object_id << 1)}>"
209
+ end
210
+
178
211
  private
179
212
  # Returns true if the name can be considered an action because
180
213
  # it has a method defined in the controller.
@@ -37,7 +37,7 @@ module AbstractController
37
37
 
38
38
  # Override <tt>AbstractController::Base#process_action</tt> to run the
39
39
  # <tt>process_action</tt> callbacks around the normal behavior.
40
- def process_action(*args)
40
+ def process_action(*)
41
41
  run_callbacks(:process_action) do
42
42
  super
43
43
  end
@@ -69,7 +69,7 @@ module AbstractController
69
69
  end
70
70
 
71
71
  def _normalize_callback_option(options, from, to) # :nodoc:
72
- if from = options[from]
72
+ if from = options.delete(from)
73
73
  _from = Array(from).map(&:to_s).to_set
74
74
  from = proc { |c| _from.include? c.action_name }
75
75
  options[to] = Array(options[to]).unshift(from)
@@ -7,8 +7,19 @@ module AbstractController
7
7
  extend ActiveSupport::Concern
8
8
 
9
9
  included do
10
- class_attribute :_helpers, default: Module.new
11
10
  class_attribute :_helper_methods, default: Array.new
11
+
12
+ # This is here so that it is always higher in the inheritance chain than
13
+ # the definition in lib/action_view/rendering.rb
14
+ redefine_singleton_method(:_helpers) do
15
+ if @_helpers ||= nil
16
+ @_helpers
17
+ else
18
+ superclass._helpers
19
+ end
20
+ end
21
+
22
+ self._helpers = define_helpers_module(self)
12
23
  end
13
24
 
14
25
  class MissingHelperError < LoadError
@@ -25,17 +36,24 @@ module AbstractController
25
36
  end
26
37
  end
27
38
 
39
+ def _helpers
40
+ self.class._helpers
41
+ end
42
+
28
43
  module ClassMethods
29
44
  # When a class is inherited, wrap its helper module in a new module.
30
45
  # This ensures that the parent class's module can be changed
31
46
  # independently of the child class's.
32
47
  def inherited(klass)
33
- helpers = _helpers
34
- klass._helpers = Module.new { include helpers }
48
+ # Inherited from parent by default
49
+ klass._helpers = nil
50
+
35
51
  klass.class_eval { default_helper_module! } unless klass.anonymous?
36
52
  super
37
53
  end
38
54
 
55
+ attr_writer :_helpers
56
+
39
57
  # Declare a controller method as a helper. For example, the following
40
58
  # makes the +current_user+ and +logged_in?+ controller methods available
41
59
  # to the view:
@@ -57,60 +75,81 @@ module AbstractController
57
75
  # ==== Parameters
58
76
  # * <tt>method[, method]</tt> - A name or names of a method on the controller
59
77
  # to be made available on the view.
60
- def helper_method(*meths)
61
- meths.flatten!
62
- self._helper_methods += meths
63
-
64
- meths.each do |method|
65
- _helpers.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
66
- def #{method}(*args, &blk) # def current_user(*args, &blk)
67
- controller.send(%(#{method}), *args, &blk) # controller.send(:current_user, *args, &blk)
68
- end # end
69
- ruby2_keywords(%(#{method})) if respond_to?(:ruby2_keywords, true)
78
+ def helper_method(*methods)
79
+ methods.flatten!
80
+ self._helper_methods += methods
81
+
82
+ location = caller_locations(1, 1).first
83
+ file, line = location.path, location.lineno
84
+
85
+ methods.each do |method|
86
+ _helpers_for_modification.class_eval <<-ruby_eval, file, line
87
+ def #{method}(*args, &block) # def current_user(*args, &block)
88
+ controller.send(:'#{method}', *args, &block) # controller.send(:'current_user', *args, &block)
89
+ end # end
90
+ ruby2_keywords(:'#{method}') if respond_to?(:ruby2_keywords, true)
70
91
  ruby_eval
71
92
  end
72
93
  end
73
94
 
74
- # The +helper+ class method can take a series of helper module names, a block, or both.
95
+ # Includes the given modules in the template class.
96
+ #
97
+ # Modules can be specified in different ways. All of the following calls
98
+ # include +FooHelper+:
99
+ #
100
+ # # Module, recommended.
101
+ # helper FooHelper
102
+ #
103
+ # # String/symbol without the "helper" suffix, camel or snake case.
104
+ # helper "Foo"
105
+ # helper :Foo
106
+ # helper "foo"
107
+ # helper :foo
108
+ #
109
+ # The last two assume that <tt>"foo".camelize</tt> returns "Foo".
75
110
  #
76
- # ==== Options
77
- # * <tt>*args</tt> - Module, Symbol, String
78
- # * <tt>block</tt> - A block defining helper methods
111
+ # When strings or symbols are passed, the method finds the actual module
112
+ # object using +String#constantize+. Therefore, if the module has not been
113
+ # yet loaded, it has to be autoloadable, which is normally the case.
79
114
  #
80
- # When the argument is a module it will be included directly in the template class.
81
- # helper FooHelper # => includes FooHelper
115
+ # Namespaces are supported. The following calls include +Foo::BarHelper+:
82
116
  #
83
- # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
84
- # and include the module in the template class. The second form illustrates how to include custom helpers
85
- # when working with namespaced controllers, or other cases where the file containing the helper definition is not
86
- # in one of Rails' standard load paths:
87
- # helper :foo # => requires 'foo_helper' and includes FooHelper
88
- # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
117
+ # # Module, recommended.
118
+ # helper Foo::BarHelper
89
119
  #
90
- # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
91
- # to the template.
120
+ # # String/symbol without the "helper" suffix, camel or snake case.
121
+ # helper "Foo::Bar"
122
+ # helper :"Foo::Bar"
123
+ # helper "foo/bar"
124
+ # helper :"foo/bar"
92
125
  #
93
- # # One line
94
- # helper { def hello() "Hello, world!" end }
126
+ # The last two assume that <tt>"foo/bar".camelize</tt> returns "Foo::Bar".
127
+ #
128
+ # The method accepts a block too. If present, the block is evaluated in
129
+ # the context of the controller helper module. This simple call makes the
130
+ # +wadus+ method available in templates of the enclosing controller:
95
131
  #
96
- # # Multi-line
97
132
  # helper do
98
- # def foo(bar)
99
- # "#{bar} is the very best"
133
+ # def wadus
134
+ # "wadus"
100
135
  # end
101
136
  # end
102
137
  #
103
- # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
104
- # +symbols+, +strings+, +modules+ and blocks.
138
+ # Furthermore, all the above styles can be mixed together:
105
139
  #
106
- # helper(:three, BlindHelper) { def mice() 'mice' end }
140
+ # helper FooHelper, "woo", "bar/baz" do
141
+ # def wadus
142
+ # "wadus"
143
+ # end
144
+ # end
107
145
  #
108
146
  def helper(*args, &block)
109
147
  modules_for_helpers(args).each do |mod|
110
- add_template_helper(mod)
148
+ next if _helpers.include?(mod)
149
+ _helpers_for_modification.include(mod)
111
150
  end
112
151
 
113
- _helpers.module_eval(&block) if block_given?
152
+ _helpers_for_modification.module_eval(&block) if block_given?
114
153
  end
115
154
 
116
155
  # Clears up all existing helpers in this class, only keeping the helper
@@ -124,71 +163,47 @@ module AbstractController
124
163
  default_helper_module! unless anonymous?
125
164
  end
126
165
 
127
- # Returns a list of modules, normalized from the acceptable kinds of
128
- # helpers with the following behavior:
129
- #
130
- # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
131
- # and "foo_bar_helper.rb" is loaded using require_dependency.
132
- #
133
- # Module:: No further processing
134
- #
135
- # After loading the appropriate files, the corresponding modules
136
- # are returned.
137
- #
138
- # ==== Parameters
139
- # * <tt>args</tt> - An array of helpers
140
- #
141
- # ==== Returns
142
- # * <tt>Array</tt> - A normalized list of modules for the list of
143
- # helpers provided.
144
- def modules_for_helpers(args)
145
- args.flatten.map! do |arg|
146
- case arg
147
- when String, Symbol
148
- file_name = "#{arg.to_s.underscore}_helper"
149
- begin
150
- require_dependency(file_name)
151
- rescue LoadError => e
152
- raise AbstractController::Helpers::MissingHelperError.new(e, file_name)
153
- end
154
-
155
- mod_name = file_name.camelize
156
- begin
157
- mod_name.constantize
158
- rescue LoadError
159
- # dependencies.rb gives a similar error message but its wording is
160
- # not as clear because it mentions autoloading. To the user all it
161
- # matters is that a helper module couldn't be loaded, autoloading
162
- # is an internal mechanism that should not leak.
163
- raise NameError, "Couldn't find #{mod_name}, expected it to be defined in helpers/#{file_name}.rb"
164
- end
166
+ # Given an array of values like the ones accepted by +helper+, this method
167
+ # returns an array with the corresponding modules, in the same order.
168
+ def modules_for_helpers(modules_or_helper_prefixes)
169
+ modules_or_helper_prefixes.flatten.map! do |module_or_helper_prefix|
170
+ case module_or_helper_prefix
165
171
  when Module
166
- arg
172
+ module_or_helper_prefix
173
+ when String, Symbol
174
+ helper_prefix = module_or_helper_prefix.to_s
175
+ helper_prefix = helper_prefix.camelize unless helper_prefix.start_with?(/[A-Z]/)
176
+ "#{helper_prefix}Helper".constantize
167
177
  else
168
178
  raise ArgumentError, "helper must be a String, Symbol, or Module"
169
179
  end
170
180
  end
171
181
  end
172
182
 
183
+ def _helpers_for_modification
184
+ unless @_helpers
185
+ self._helpers = define_helpers_module(self, superclass._helpers)
186
+ end
187
+ _helpers
188
+ end
189
+
173
190
  private
174
- # Makes all the (instance) methods in the helper module available to templates
175
- # rendered through this controller.
176
- #
177
- # ==== Parameters
178
- # * <tt>module</tt> - The module to include into the current helper module
179
- # for the class
180
- def add_template_helper(mod)
181
- _helpers.module_eval { include mod }
191
+ def define_helpers_module(klass, helpers = nil)
192
+ # In some tests inherited is called explicitly. In that case, just
193
+ # return the module from the first time it was defined
194
+ return klass.const_get(:HelperMethods) if klass.const_defined?(:HelperMethods, false)
195
+
196
+ mod = Module.new
197
+ klass.const_set(:HelperMethods, mod)
198
+ mod.include(helpers) if helpers
199
+ mod
182
200
  end
183
201
 
184
202
  def default_helper_module!
185
- module_name = name.sub(/Controller$/, "")
186
- module_path = module_name.underscore
187
- helper module_path
188
- rescue LoadError => e
189
- raise e unless e.is_missing? "helpers/#{module_path}_helper"
203
+ helper_prefix = name.delete_suffix("Controller")
204
+ helper(helper_prefix)
190
205
  rescue NameError => e
191
- raise e unless e.missing_name? "#{module_name}Helper"
206
+ raise unless e.missing_name?("#{helper_prefix}Helper")
192
207
  end
193
208
  end
194
209
  end
@@ -28,6 +28,7 @@ module AbstractController
28
28
  else
29
29
  _set_rendered_content_type rendered_format
30
30
  end
31
+ _set_vary_header
31
32
  self.response_body = rendered_body
32
33
  end
33
34
 
@@ -55,20 +56,16 @@ module AbstractController
55
56
  Mime[:text]
56
57
  end
57
58
 
58
- DEFAULT_PROTECTED_INSTANCE_VARIABLES = Set.new %i(
59
- @_action_name @_response_body @_formats @_prefixes
60
- )
59
+ DEFAULT_PROTECTED_INSTANCE_VARIABLES = %i(@_action_name @_response_body @_formats @_prefixes)
61
60
 
62
61
  # This method should return a hash with assigns.
63
62
  # You can overwrite this configuration per controller.
64
63
  def view_assigns
65
- protected_vars = _protected_ivars
66
- variables = instance_variables
64
+ variables = instance_variables - _protected_ivars
67
65
 
68
- variables.reject! { |s| protected_vars.include? s }
69
- variables.each_with_object({}) { |name, hash|
66
+ variables.each_with_object({}) do |name, hash|
70
67
  hash[name.slice(1, name.length)] = instance_variable_get(name)
71
- }
68
+ end
72
69
  end
73
70
 
74
71
  private
@@ -109,6 +106,9 @@ module AbstractController
109
106
  def _set_html_content_type # :nodoc:
110
107
  end
111
108
 
109
+ def _set_vary_header # :nodoc:
110
+ end
111
+
112
112
  def _set_rendered_content_type(format) # :nodoc:
113
113
  end
114
114
 
@@ -120,7 +120,7 @@ module AbstractController
120
120
  options
121
121
  end
122
122
 
123
- def _protected_ivars # :nodoc:
123
+ def _protected_ivars
124
124
  DEFAULT_PROTECTED_INSTANCE_VARIABLES
125
125
  end
126
126
  end
@@ -1,7 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support/core_ext/symbol/starts_ends_with"
4
+
3
5
  module AbstractController
4
6
  module Translation
7
+ mattr_accessor :raise_on_missing_translations, default: false
8
+
5
9
  # Delegates to <tt>I18n.translate</tt>. Also aliased as <tt>t</tt>.
6
10
  #
7
11
  # When the given key starts with a period, it will be scoped by the current
@@ -11,14 +15,16 @@ module AbstractController
11
15
  # to translate many keys within the same controller / action and gives you a
12
16
  # simple framework for scoping them consistently.
13
17
  def translate(key, **options)
14
- if key.to_s.first == "."
18
+ if key.start_with?(".")
15
19
  path = controller_path.tr("/", ".")
16
20
  defaults = [:"#{path}#{key}"]
17
21
  defaults << options[:default] if options[:default]
18
22
  options[:default] = defaults.flatten
19
23
  key = "#{path}.#{action_name}#{key}"
20
24
  end
21
- I18n.translate(key, **options)
25
+
26
+ i18n_raise = options.fetch(:raise, self.raise_on_missing_translations)
27
+ I18n.translate(key, **options, raise: i18n_raise)
22
28
  end
23
29
  alias :t :translate
24
30