view_component 3.23.2 → 4.0.0

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 (66) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/view_component/preview_actions.rb +11 -14
  3. data/app/controllers/view_components_system_test_controller.rb +15 -20
  4. data/app/views/test_mailer/test_asset_email.html.erb +1 -0
  5. data/app/views/test_mailer/test_url_email.html.erb +1 -0
  6. data/app/views/view_components/preview.html.erb +1 -9
  7. data/docs/CHANGELOG.md +404 -0
  8. data/lib/{rails/generators → generators/view_component}/abstract_generator.rb +2 -2
  9. data/lib/{rails/generators → generators/view_component}/component/component_generator.rb +16 -3
  10. data/lib/{rails/generators → generators/view_component}/component/templates/component.rb.tt +6 -1
  11. data/lib/{rails/generators/erb/component_generator.rb → generators/view_component/erb/erb_generator.rb} +4 -3
  12. data/lib/{rails/generators/haml/component_generator.rb → generators/view_component/haml/haml_generator.rb} +3 -3
  13. data/lib/{rails/generators/locale/component_generator.rb → generators/view_component/locale/locale_generator.rb} +3 -3
  14. data/lib/{rails/generators/preview/component_generator.rb → generators/view_component/preview/preview_generator.rb} +3 -3
  15. data/lib/{rails/generators/rspec/component_generator.rb → generators/view_component/rspec/rspec_generator.rb} +3 -3
  16. data/lib/{rails/generators/slim/component_generator.rb → generators/view_component/slim/slim_generator.rb} +3 -3
  17. data/lib/{rails/generators/stimulus/component_generator.rb → generators/view_component/stimulus/stimulus_generator.rb} +3 -3
  18. data/lib/generators/view_component/tailwindcss/tailwindcss_generator.rb +11 -0
  19. data/lib/{rails/generators/test_unit/component_generator.rb → generators/view_component/test_unit/test_unit_generator.rb} +2 -2
  20. data/lib/view_component/base.rb +154 -157
  21. data/lib/view_component/collection.rb +11 -25
  22. data/lib/view_component/compiler.rb +52 -79
  23. data/lib/view_component/config.rb +51 -85
  24. data/lib/view_component/configurable.rb +1 -1
  25. data/lib/view_component/deprecation.rb +1 -1
  26. data/lib/view_component/engine.rb +37 -107
  27. data/lib/view_component/errors.rb +16 -34
  28. data/lib/view_component/inline_template.rb +3 -4
  29. data/lib/view_component/instrumentation.rb +4 -10
  30. data/lib/view_component/preview.rb +4 -11
  31. data/lib/view_component/request_details.rb +30 -0
  32. data/lib/view_component/slot.rb +6 -13
  33. data/lib/view_component/slotable.rb +82 -77
  34. data/lib/view_component/system_spec_helpers.rb +11 -0
  35. data/lib/view_component/system_test_helpers.rb +1 -2
  36. data/lib/view_component/template.rb +106 -83
  37. data/lib/view_component/test_helpers.rb +37 -44
  38. data/lib/view_component/translatable.rb +33 -32
  39. data/lib/view_component/version.rb +3 -3
  40. data/lib/view_component.rb +8 -6
  41. metadata +30 -558
  42. data/app/assets/vendor/prism.css +0 -4
  43. data/app/assets/vendor/prism.min.js +0 -12
  44. data/app/helpers/preview_helper.rb +0 -85
  45. data/app/views/view_components/_preview_source.html.erb +0 -17
  46. data/lib/rails/generators/tailwindcss/component_generator.rb +0 -11
  47. data/lib/view_component/capture_compatibility.rb +0 -44
  48. data/lib/view_component/component_error.rb +0 -6
  49. data/lib/view_component/rails/tasks/view_component.rake +0 -20
  50. data/lib/view_component/render_component_helper.rb +0 -10
  51. data/lib/view_component/render_component_to_string_helper.rb +0 -9
  52. data/lib/view_component/render_monkey_patch.rb +0 -13
  53. data/lib/view_component/render_to_string_monkey_patch.rb +0 -13
  54. data/lib/view_component/rendering_component_helper.rb +0 -9
  55. data/lib/view_component/rendering_monkey_patch.rb +0 -13
  56. data/lib/view_component/slotable_default.rb +0 -20
  57. data/lib/view_component/use_helpers.rb +0 -42
  58. /data/lib/{rails/generators → generators/view_component}/erb/templates/component.html.erb.tt +0 -0
  59. /data/lib/{rails/generators → generators/view_component}/haml/templates/component.html.haml.tt +0 -0
  60. /data/lib/{rails/generators → generators/view_component}/preview/templates/component_preview.rb.tt +0 -0
  61. /data/lib/{rails/generators → generators/view_component}/rspec/templates/component_spec.rb.tt +0 -0
  62. /data/lib/{rails/generators → generators/view_component}/slim/templates/component.html.slim.tt +0 -0
  63. /data/lib/{rails/generators → generators/view_component}/stimulus/templates/component_controller.js.tt +0 -0
  64. /data/lib/{rails/generators → generators/view_component}/stimulus/templates/component_controller.ts.tt +0 -0
  65. /data/lib/{rails/generators → generators/view_component}/tailwindcss/templates/component.html.erb.tt +0 -0
  66. /data/lib/{rails/generators → generators/view_component}/test_unit/templates/component_test.rb.tt +0 -0
@@ -8,159 +8,99 @@ module ViewComponent
8
8
  class Engine < Rails::Engine # :nodoc:
9
9
  config.view_component = ViewComponent::Config.current
10
10
 
11
- if Rails.version.to_f < 8.0
12
- rake_tasks do
13
- load "view_component/rails/tasks/view_component.rake"
14
- end
15
- else
16
- initializer "view_component.stats_directories" do |app|
17
- require "rails/code_statistics"
18
-
19
- if Rails.root.join(ViewComponent::Base.view_component_path).directory?
20
- Rails::CodeStatistics.register_directory("ViewComponents", ViewComponent::Base.view_component_path)
21
- end
22
-
23
- if Rails.root.join("test/components").directory?
24
- Rails::CodeStatistics.register_directory("ViewComponent tests", "test/components", test_directory: true)
25
- end
26
- end
27
- end
28
-
29
11
  initializer "view_component.set_configs" do |app|
30
12
  options = app.config.view_component
31
13
 
32
- %i[generate preview_controller preview_route show_previews_source].each do |config_option|
14
+ %i[generate previews].each do |config_option|
33
15
  options[config_option] ||= ViewComponent::Base.public_send(config_option)
34
16
  end
35
17
  options.instrumentation_enabled = false if options.instrumentation_enabled.nil?
36
- options.render_monkey_patch_enabled = true if options.render_monkey_patch_enabled.nil?
37
- options.show_previews = (Rails.env.development? || Rails.env.test?) if options.show_previews.nil?
18
+ options.previews.enabled = (Rails.env.development? || Rails.env.test?) if options.previews.enabled.nil?
38
19
 
39
- if options.show_previews
20
+ if options.previews.enabled
40
21
  # This is still necessary because when `config.view_component` is declared, `Rails.root` is unspecified.
41
- options.preview_paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
22
+ options.previews.paths << "#{Rails.root}/test/components/previews" if defined?(Rails.root) && Dir.exist?(
42
23
  "#{Rails.root}/test/components/previews"
43
24
  )
44
-
45
- if options.show_previews_source
46
- require "method_source"
47
-
48
- app.config.to_prepare do
49
- MethodSource.instance_variable_set(:@lines_for_file, {})
50
- end
51
- end
52
25
  end
53
26
  end
54
27
 
55
28
  initializer "view_component.enable_instrumentation" do |app|
56
29
  ActiveSupport.on_load(:view_component) do
57
30
  if app.config.view_component.instrumentation_enabled.present?
58
- # :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures.
59
31
  ViewComponent::Base.prepend(ViewComponent::Instrumentation)
60
- if app.config.view_component.use_deprecated_instrumentation_name
61
- ViewComponent::Deprecation.deprecation_warning(
62
- "!render.view_component",
63
- "Use the new instrumentation key `render.view_component` instead. See https://viewcomponent.org/guide/instrumentation.html"
64
- )
65
- end
66
- # :nocov:
67
32
  end
68
33
  end
69
34
  end
70
35
 
71
- # :nocov:
72
- initializer "view_component.enable_capture_patch" do |app|
73
- ActiveSupport.on_load(:view_component) do
74
- ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
75
- end
76
- end
77
- # :nocov:
78
-
79
36
  initializer "view_component.set_autoload_paths" do |app|
80
37
  options = app.config.view_component
81
38
 
82
- if options.show_previews && !options.preview_paths.empty?
83
- paths_to_add = options.preview_paths - ActiveSupport::Dependencies.autoload_paths
39
+ if options.previews.enabled && !options.previews.paths.empty?
40
+ paths_to_add = options.previews.paths - ActiveSupport::Dependencies.autoload_paths
84
41
  ActiveSupport::Dependencies.autoload_paths.concat(paths_to_add) if paths_to_add.any?
85
42
  end
86
43
  end
87
44
 
88
- initializer "view_component.eager_load_actions" do
89
- ActiveSupport.on_load(:after_initialize) do
90
- ViewComponent::Base.descendants.each(&:compile) if Rails.application.config.eager_load
45
+ initializer "view_component.propshaft_support" do |_app|
46
+ ActiveSupport.on_load(:view_component) do
47
+ if defined?(Propshaft)
48
+ include Propshaft::Helper
49
+ end
91
50
  end
92
51
  end
93
52
 
94
- initializer "view_component.monkey_patch_render" do |app|
95
- next if Rails.version.to_f >= 6.1 || !app.config.view_component.render_monkey_patch_enabled
96
-
97
- # :nocov:
98
- ViewComponent::Deprecation.deprecation_warning("Monkey patching `render`", "ViewComponent 4.0 will remove the `render` monkey patch")
99
-
100
- ActiveSupport.on_load(:action_view) do
101
- require "view_component/render_monkey_patch"
102
- ActionView::Base.prepend ViewComponent::RenderMonkeyPatch
103
- end
104
-
105
- ActiveSupport.on_load(:action_controller) do
106
- require "view_component/rendering_monkey_patch"
107
- require "view_component/render_to_string_monkey_patch"
108
- ActionController::Base.prepend ViewComponent::RenderingMonkeyPatch
109
- ActionController::Base.prepend ViewComponent::RenderToStringMonkeyPatch
110
- end
111
- # :nocov:
112
- end
53
+ config.after_initialize do |app|
54
+ ActiveSupport.on_load(:view_component) do
55
+ if defined?(Sprockets::Rails)
56
+ include Sprockets::Rails::Helper
113
57
 
114
- initializer "view_component.include_render_component" do |_app|
115
- next if Rails.version.to_f >= 6.1
58
+ # Copy relevant config to VC context
59
+ # See: https://github.com/rails/sprockets-rails/blob/266ec49f3c7c96018dd75f9dc4f9b62fe3f7eecf/lib/sprockets/railtie.rb#L245
60
+ self.debug_assets = app.config.assets.debug
61
+ self.digest_assets = app.config.assets.digest
62
+ self.assets_prefix = app.config.assets.prefix
63
+ self.assets_precompile = app.config.assets.precompile
116
64
 
117
- # :nocov:
118
- ViewComponent::Deprecation.deprecation_warning("using `render_component`", "ViewComponent 4.0 will remove `render_component`")
65
+ self.assets_environment = app.assets
66
+ self.assets_manifest = app.assets_manifest
119
67
 
120
- ActiveSupport.on_load(:action_view) do
121
- require "view_component/render_component_helper"
122
- ActionView::Base.include ViewComponent::RenderComponentHelper
123
- end
68
+ self.resolve_assets_with = app.config.assets.resolve_with
124
69
 
125
- ActiveSupport.on_load(:action_controller) do
126
- require "view_component/rendering_component_helper"
127
- require "view_component/render_component_to_string_helper"
128
- ActionController::Base.include ViewComponent::RenderingComponentHelper
129
- ActionController::Base.include ViewComponent::RenderComponentToStringHelper
70
+ self.check_precompiled_asset = app.config.assets.check_precompiled_asset
71
+ self.unknown_asset_fallback = app.config.assets.unknown_asset_fallback
72
+ # Expose the app precompiled asset check to the view
73
+ self.precompiled_asset_checker = ->(logical_path) { app.asset_precompiled? logical_path }
74
+ end
130
75
  end
131
- # :nocov:
132
76
  end
133
77
 
134
- initializer "static assets" do |app|
135
- if serve_static_preview_assets?(app.config)
136
- app.middleware.use(::ActionDispatch::Static, "#{root}/app/assets/vendor")
78
+ initializer "view_component.eager_load_actions" do
79
+ ActiveSupport.on_load(:after_initialize) do
80
+ ViewComponent::Base.descendants.each(&:__vc_compile) if Rails.application.config.eager_load
137
81
  end
138
82
  end
139
83
 
140
- def serve_static_preview_assets?(app_config)
141
- app_config.view_component.show_previews && app_config.public_file_server.enabled
142
- end
143
-
144
84
  initializer "compiler mode" do |_app|
145
- ViewComponent::Compiler.development_mode = (Rails.env.development? || Rails.env.test?)
85
+ ViewComponent::Compiler.__vc_development_mode = (Rails.env.development? || Rails.env.test?)
146
86
  end
147
87
 
148
88
  config.after_initialize do |app|
149
89
  options = app.config.view_component
150
90
 
151
- if options.show_previews
91
+ if options.previews.enabled
152
92
  app.routes.prepend do
153
- preview_controller = options.preview_controller.sub(/Controller$/, "").underscore
93
+ preview_controller = options.previews.controller.sub(/Controller$/, "").underscore
154
94
 
155
95
  get(
156
- options.preview_route,
96
+ options.previews.route,
157
97
  to: "#{preview_controller}#index",
158
98
  as: :preview_view_components,
159
99
  internal: true
160
100
  )
161
101
 
162
102
  get(
163
- "#{options.preview_route}/*path",
103
+ "#{options.previews.route}/*path",
164
104
  to: "#{preview_controller}#previews",
165
105
  as: :preview_view_component,
166
106
  internal: true
@@ -174,16 +114,6 @@ module ViewComponent
174
114
  end
175
115
  end
176
116
 
177
- # :nocov:
178
- if RUBY_VERSION < "3.2.0"
179
- ViewComponent::Deprecation.deprecation_warning("Support for Ruby versions < 3.2.0", "ViewComponent v4 will remove support for Ruby versions < 3.2.0 no earlier than April 1, 2025")
180
- end
181
-
182
- if Rails.version.to_f < 7.1
183
- ViewComponent::Deprecation.deprecation_warning("Support for Rails versions < 7.1", "ViewComponent v4 will remove support for Rails versions < 7.1 no earlier than April 1, 2025")
184
- end
185
- # :nocov:
186
-
187
117
  app.executor.to_run :before do
188
118
  CompileCache.invalidate! unless ActionView::Base.cache_template_loading
189
119
  end
@@ -38,6 +38,22 @@ module ViewComponent
38
38
  end
39
39
  end
40
40
 
41
+ class MissingTemplateError < StandardError
42
+ MESSAGE =
43
+ "No templates for COMPONENT match the request DETAIL.\n\n" \
44
+ "To fix this issue, provide a suitable template."
45
+
46
+ def initialize(component, request_detail)
47
+ detail = {
48
+ locale: request_detail.locale,
49
+ formats: request_detail.formats,
50
+ variants: request_detail.variants,
51
+ handlers: request_detail.handlers
52
+ }
53
+ super(MESSAGE.gsub("COMPONENT", component).gsub("DETAIL", detail.inspect))
54
+ end
55
+ end
56
+
41
57
  class DuplicateContentError < StandardError
42
58
  MESSAGE =
43
59
  "It looks like a block was provided after calling `with_content` on COMPONENT, " \
@@ -49,18 +65,6 @@ module ViewComponent
49
65
  end
50
66
  end
51
67
 
52
- class EmptyOrInvalidInitializerError < StandardError
53
- MESSAGE =
54
- "The COMPONENT initializer is empty or invalid. " \
55
- "It must accept the parameter `PARAMETER` to render it as a collection.\n\n" \
56
- "To fix this issue, update the initializer to accept `PARAMETER`.\n\n" \
57
- "See [the collections docs](https://viewcomponent.org/guide/collections.html) for more information on rendering collections."
58
-
59
- def initialize(klass_name, parameter)
60
- super(MESSAGE.gsub("COMPONENT", klass_name.to_s).gsub("PARAMETER", parameter.to_s))
61
- end
62
- end
63
-
64
68
  class MissingCollectionArgumentError < StandardError
65
69
  MESSAGE =
66
70
  "The initializer for COMPONENT doesn't accept the parameter `PARAMETER`, " \
@@ -202,28 +206,6 @@ module ViewComponent
202
206
  "`#controller` to a [`#before_render` method](https://viewcomponent.org/api.html#before_render--void)."
203
207
  end
204
208
 
205
- # :nocov:
206
- class NoMatchingTemplatesForPreviewError < StandardError
207
- MESSAGE = "Found 0 matches for templates for TEMPLATE_IDENTIFIER."
208
-
209
- def initialize(template_identifier)
210
- super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
211
- end
212
- end
213
-
214
- class MultipleMatchingTemplatesForPreviewError < StandardError
215
- MESSAGE = "Found multiple templates for TEMPLATE_IDENTIFIER."
216
-
217
- def initialize(template_identifier)
218
- super(MESSAGE.gsub("TEMPLATE_IDENTIFIER", template_identifier))
219
- end
220
- end
221
- # :nocov:
222
-
223
- class SystemTestControllerOnlyAllowedInTestError < BaseError
224
- MESSAGE = "ViewComponent SystemTest controller must only be called in a test environment for security reasons."
225
- end
226
-
227
209
  class SystemTestControllerNefariousPathError < BaseError
228
210
  MESSAGE = "ViewComponent SystemTest controller attempted to load a file outside of the expected directory."
229
211
  end
@@ -32,23 +32,22 @@ module ViewComponent # :nodoc:
32
32
 
33
33
  @__vc_inline_template_defined = true
34
34
  end
35
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
36
35
 
37
36
  def respond_to_missing?(method, include_all = false)
38
37
  method.end_with?("_template") || super
39
38
  end
40
39
 
41
- def inline_template
40
+ def __vc_inline_template
42
41
  @__vc_inline_template if defined?(@__vc_inline_template)
43
42
  end
44
43
 
45
- def inline_template_language
44
+ def __vc_inline_template_language
46
45
  @__vc_inline_template_language if defined?(@__vc_inline_template_language)
47
46
  end
48
47
 
49
48
  def inherited(subclass)
50
49
  super
51
- subclass.instance_variable_set(:@__vc_inline_template_language, inline_template_language)
50
+ subclass.instance_variable_set(:@__vc_inline_template_language, __vc_inline_template_language)
52
51
  end
53
52
  end
54
53
  end
@@ -5,12 +5,14 @@ require "active_support/notifications"
5
5
  module ViewComponent # :nodoc:
6
6
  module Instrumentation
7
7
  def self.included(mod)
8
- mod.prepend(self) unless ancestors.include?(ViewComponent::Instrumentation)
8
+ mod.prepend(self) unless self <= ViewComponent::Instrumentation
9
9
  end
10
10
 
11
11
  def render_in(view_context, &block)
12
+ return super if !Rails.application.config.view_component.instrumentation_enabled.present?
13
+
12
14
  ActiveSupport::Notifications.instrument(
13
- notification_name,
15
+ "render.view_component",
14
16
  {
15
17
  name: self.class.name,
16
18
  identifier: self.class.identifier
@@ -19,13 +21,5 @@ module ViewComponent # :nodoc:
19
21
  super
20
22
  end
21
23
  end
22
-
23
- private
24
-
25
- def notification_name
26
- return "!render.view_component" if ViewComponent::Base.config.use_deprecated_instrumentation_name
27
-
28
- "render.view_component"
29
- end
30
24
  end
31
25
  end
@@ -30,12 +30,10 @@ module ViewComponent # :nodoc:
30
30
  }
31
31
  end
32
32
 
33
- alias_method :render_component, :render
34
-
35
33
  class << self
36
34
  # Returns all component preview classes.
37
35
  def all
38
- load_previews
36
+ __vc_load_previews
39
37
 
40
38
  descendants
41
39
  end
@@ -94,13 +92,8 @@ module ViewComponent # :nodoc:
94
92
  .sub(/\..*$/, "")
95
93
  end
96
94
 
97
- # Returns the method body for the example from the preview file.
98
- def preview_source(example)
99
- source = instance_method(example.to_sym).source.split("\n")
100
- source[1...(source.size - 1)].join("\n")
101
- end
102
-
103
- def load_previews
95
+ # @private
96
+ def __vc_load_previews
104
97
  Array(preview_paths).each do |preview_path|
105
98
  Dir["#{preview_path}/**/*preview.rb"].sort.each { |file| require_dependency file }
106
99
  end
@@ -109,7 +102,7 @@ module ViewComponent # :nodoc:
109
102
  private
110
103
 
111
104
  def preview_paths
112
- Base.preview_paths
105
+ Base.previews.paths
113
106
  end
114
107
  end
115
108
  end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ # LookupContext computes and encapsulates @details for each request
5
+ # so that it doesn't need to be recomputed on each partial render.
6
+ # This data is wrapped in ActionView::TemplateDetails::Requested and
7
+ # used by instances of ActionView::Resolver to choose which template
8
+ # best matches the request.
9
+ #
10
+ # ActionView considers this logic internal to template/partial resolution.
11
+ # We're exposing it to the compiler via `refine` so that ViewComponent
12
+ # can match Rails' template picking logic.
13
+ module RequestDetails
14
+ refine ActionView::LookupContext do
15
+ # Return an abstraction for matching and sorting available templates
16
+ # based on the current lookup context details.
17
+ #
18
+ # @return ActionView::TemplateDetails::Requested
19
+ # @see ActionView::LookupContext#detail_args_for
20
+ # @see ActionView::FileSystemResolver#_find_all
21
+ def vc_requested_details(user_details = {})
22
+ # The hash `user_details` would normally be the standard arguments that
23
+ # `render` accepts, but there's currently no mechanism for users to
24
+ # provide these when calling render on a ViewComponent.
25
+ details, cached = detail_args_for(user_details)
26
+ cached || ActionView::TemplateDetails::Requested.new(**details)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -58,13 +58,7 @@ module ViewComponent
58
58
  if defined?(@__vc_content_block)
59
59
  # render_in is faster than `parent.render`
60
60
  @__vc_component_instance.render_in(view_context) do |*args|
61
- return @__vc_content_block.call(*args) if @__vc_content_block&.source_location.nil?
62
-
63
- block_context = @__vc_content_block.binding.receiver
64
-
65
- if block_context.class < ActionView::Base
66
- block_context.capture(*args, &@__vc_content_block)
67
- else
61
+ @parent.with_original_virtual_path do
68
62
  @__vc_content_block.call(*args)
69
63
  end
70
64
  end
@@ -74,7 +68,9 @@ module ViewComponent
74
68
  elsif defined?(@__vc_content)
75
69
  @__vc_content
76
70
  elsif defined?(@__vc_content_block)
77
- view_context.capture(&@__vc_content_block)
71
+ @parent.with_original_virtual_path do
72
+ view_context.capture(&@__vc_content_block)
73
+ end
78
74
  elsif defined?(@__vc_content_set_by_with_content)
79
75
  @__vc_content_set_by_with_content
80
76
  end
@@ -101,15 +97,12 @@ module ViewComponent
101
97
  # end
102
98
  # end
103
99
  #
104
- def method_missing(symbol, *args, &block)
105
- @__vc_component_instance.public_send(symbol, *args, &block)
100
+ def method_missing(symbol, *args, **kwargs, &block)
101
+ @__vc_component_instance.public_send(symbol, *args, **kwargs, &block)
106
102
  end
107
- ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
108
103
 
109
104
  def html_safe?
110
- # :nocov:
111
105
  to_s.html_safe?
112
- # :nocov:
113
106
  end
114
107
 
115
108
  def respond_to_missing?(symbol, include_all = false)