view_component 4.0.0.alpha2 → 4.0.0.alpha3
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/docs/CHANGELOG.md +14 -0
- data/lib/view_component/base.rb +62 -15
- data/lib/view_component/collection.rb +0 -8
- data/lib/view_component/component_local_config.rb +60 -0
- data/lib/view_component/config.rb +1 -9
- data/lib/view_component/engine.rb +33 -6
- data/lib/view_component/slot.rb +1 -9
- data/lib/view_component/template.rb +2 -1
- data/lib/view_component/translatable.rb +3 -3
- data/lib/view_component/version.rb +1 -1
- metadata +2 -2
- data/lib/view_component/capture_compatibility.rb +0 -44
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d926054ff51767aa08c688eef809327cbbda35e8b403a574af8baa642aefe7ed
|
4
|
+
data.tar.gz: ed0a01b7b1fe98080c4c7210c7bdde4b474d08f4b0c5ab0a36c39e9e6631f185
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54aa90e88b7fb9f0773842f0737739c15cfaff4609f62fe733badd14ab8676ec21532c5d00915947ce183aca678f7ed79573f15e8dec06f873067a9585a277ab
|
7
|
+
data.tar.gz: 3c4187ccc4f70942964fedd232fb387314e5f7cfa47624df5def3006ff9478947d4de9380c0baf29b5102e20217de0eb77a9bfe8a21240a79065cb719d02bf94
|
data/docs/CHANGELOG.md
CHANGED
@@ -10,6 +10,12 @@ nav_order: 6
|
|
10
10
|
|
11
11
|
## main
|
12
12
|
|
13
|
+
## 4.0.0.alpha3
|
14
|
+
|
15
|
+
* BREAKING: Remove dependency on `ActionView::Base`, eliminating the need for capture compatibility patch.
|
16
|
+
|
17
|
+
*Cameron Dutro*
|
18
|
+
|
13
19
|
## 4.0.0.alpha2
|
14
20
|
|
15
21
|
* Add `#current_template` accessor and `Template#path` for diagnostic usage.
|
@@ -118,6 +124,14 @@ This release makes the following breaking changes:
|
|
118
124
|
|
119
125
|
*Tiago Menegaz*, *Joel Hawksley*
|
120
126
|
|
127
|
+
* Introduce component-local config and migrate `strip_trailing_whitespace` to use it under the hood.
|
128
|
+
|
129
|
+
*Simon Fish*
|
130
|
+
|
131
|
+
* Add docs about Slack channel in Ruby Central workspace. (Join us! #oss-view-component). Email joelhawksley@github.com for an invite.
|
132
|
+
|
133
|
+
*Joel Hawksley
|
134
|
+
|
121
135
|
* Do not include internal `DocsBuilderComponent` or `YARD::MattrAccessorHandler` in published gem.
|
122
136
|
|
123
137
|
*Joel Hawksley*
|
data/lib/view_component/base.rb
CHANGED
@@ -5,6 +5,7 @@ require "active_support/configurable"
|
|
5
5
|
require "view_component/collection"
|
6
6
|
require "view_component/compile_cache"
|
7
7
|
require "view_component/compiler"
|
8
|
+
require "view_component/component_local_config"
|
8
9
|
require "view_component/config"
|
9
10
|
require "view_component/errors"
|
10
11
|
require "view_component/inline_template"
|
@@ -17,8 +18,21 @@ require "view_component/translatable"
|
|
17
18
|
require "view_component/with_content_helper"
|
18
19
|
require "view_component/use_helpers"
|
19
20
|
|
21
|
+
module ActionView
|
22
|
+
class OutputBuffer
|
23
|
+
def with_buffer(buf = nil)
|
24
|
+
new_buffer = buf || +""
|
25
|
+
old_buffer, @raw_buffer = @raw_buffer, new_buffer
|
26
|
+
yield
|
27
|
+
new_buffer
|
28
|
+
ensure
|
29
|
+
@raw_buffer = old_buffer
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
20
34
|
module ViewComponent
|
21
|
-
class Base
|
35
|
+
class Base
|
22
36
|
class << self
|
23
37
|
delegate(*ViewComponent::Config.defaults.keys, to: :config)
|
24
38
|
|
@@ -34,21 +48,26 @@ module ViewComponent
|
|
34
48
|
end
|
35
49
|
end
|
36
50
|
|
51
|
+
include ActionView::Helpers
|
52
|
+
include ERB::Escape
|
53
|
+
include ActiveSupport::CoreExt::ERBUtil
|
54
|
+
|
37
55
|
include ViewComponent::InlineTemplate
|
38
56
|
include ViewComponent::UseHelpers
|
39
57
|
include ViewComponent::Slotable
|
40
58
|
include ViewComponent::Translatable
|
41
59
|
include ViewComponent::WithContentHelper
|
60
|
+
include ViewComponent::ComponentLocalConfig
|
42
61
|
|
43
62
|
# For CSRF authenticity tokens in forms
|
44
63
|
delegate :form_authenticity_token, :protect_against_forgery?, :config, to: :helpers
|
45
64
|
|
65
|
+
# HTML construction methods
|
66
|
+
delegate :output_buffer, :lookup_context, :view_renderer, :view_flow, to: :helpers
|
67
|
+
|
46
68
|
# For Content Security Policy nonces
|
47
69
|
delegate :content_security_policy_nonce, to: :helpers
|
48
70
|
|
49
|
-
# Config option that strips trailing whitespace in templates before compiling them.
|
50
|
-
class_attribute :__vc_strip_trailing_whitespace, instance_accessor: false, instance_predicate: false, default: false
|
51
|
-
|
52
71
|
attr_accessor :__vc_original_view_context
|
53
72
|
attr_reader :current_template
|
54
73
|
|
@@ -62,7 +81,7 @@ module ViewComponent
|
|
62
81
|
# @param view_context [ActionView::Base] The original view context.
|
63
82
|
# @return [void]
|
64
83
|
def set_original_view_context(view_context)
|
65
|
-
|
84
|
+
# noop
|
66
85
|
end
|
67
86
|
|
68
87
|
using RequestDetails
|
@@ -81,7 +100,7 @@ module ViewComponent
|
|
81
100
|
@view_context = view_context
|
82
101
|
self.__vc_original_view_context ||= view_context
|
83
102
|
|
84
|
-
@output_buffer =
|
103
|
+
@output_buffer = view_context.output_buffer
|
85
104
|
|
86
105
|
@lookup_context ||= view_context.lookup_context
|
87
106
|
|
@@ -108,14 +127,20 @@ module ViewComponent
|
|
108
127
|
before_render
|
109
128
|
|
110
129
|
if render?
|
111
|
-
|
130
|
+
value = nil
|
112
131
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
132
|
+
@output_buffer.with_buffer do
|
133
|
+
rendered_template = render_template_for(@__vc_requested_details).to_s
|
134
|
+
|
135
|
+
# Avoid allocating new string when output_preamble and output_postamble are blank
|
136
|
+
value = if output_preamble.blank? && output_postamble.blank?
|
137
|
+
rendered_template
|
138
|
+
else
|
139
|
+
safe_output_preamble + rendered_template + safe_output_postamble
|
140
|
+
end
|
118
141
|
end
|
142
|
+
|
143
|
+
value
|
119
144
|
else
|
120
145
|
""
|
121
146
|
end
|
@@ -207,7 +232,7 @@ module ViewComponent
|
|
207
232
|
def render(options = {}, args = {}, &block)
|
208
233
|
if options.respond_to?(:set_original_view_context)
|
209
234
|
options.set_original_view_context(self.__vc_original_view_context)
|
210
|
-
|
235
|
+
@view_context.render(options, args, &block)
|
211
236
|
else
|
212
237
|
__vc_original_view_context.render(options, args, &block)
|
213
238
|
end
|
@@ -594,16 +619,38 @@ module ViewComponent
|
|
594
619
|
# end
|
595
620
|
# ```
|
596
621
|
#
|
622
|
+
# @deprecated Use the new component-local configuration option instead.
|
623
|
+
#
|
624
|
+
# ```ruby
|
625
|
+
# class MyComponent < ViewComponent::Base
|
626
|
+
# configure_view_component do |config|
|
627
|
+
# config.strip_trailing_whitespace = true
|
628
|
+
# end
|
629
|
+
# end
|
630
|
+
# ```
|
631
|
+
#
|
597
632
|
# @param value [Boolean] Whether to strip newlines.
|
598
633
|
def strip_trailing_whitespace(value = true)
|
599
|
-
|
634
|
+
ViewComponent::Deprecation.deprecation_warning(
|
635
|
+
"strip_trailing_whitespace",
|
636
|
+
<<~DOC
|
637
|
+
Use the new component-local configuration option instead:
|
638
|
+
|
639
|
+
class #{self.class.name} < ViewComponent::Base
|
640
|
+
configure_view_component do |config|
|
641
|
+
config.strip_trailing_whitespace = #{value}
|
642
|
+
end
|
643
|
+
end
|
644
|
+
DOC
|
645
|
+
)
|
646
|
+
view_component_config.strip_trailing_whitespace = value
|
600
647
|
end
|
601
648
|
|
602
649
|
# Whether trailing whitespace will be stripped before compilation.
|
603
650
|
#
|
604
651
|
# @return [Boolean]
|
605
652
|
def strip_trailing_whitespace?
|
606
|
-
|
653
|
+
view_component_config.strip_trailing_whitespace
|
607
654
|
end
|
608
655
|
|
609
656
|
# Ensure the component initializer accepts the
|
@@ -9,15 +9,8 @@ module ViewComponent
|
|
9
9
|
|
10
10
|
delegate :size, to: :@collection
|
11
11
|
|
12
|
-
attr_accessor :__vc_original_view_context
|
13
|
-
|
14
|
-
def set_original_view_context(view_context)
|
15
|
-
self.__vc_original_view_context = view_context
|
16
|
-
end
|
17
|
-
|
18
12
|
def render_in(view_context, &block)
|
19
13
|
components.map do |component|
|
20
|
-
component.set_original_view_context(__vc_original_view_context)
|
21
14
|
component.render_in(view_context, &block)
|
22
15
|
end.join(rendered_spacer(view_context)).html_safe
|
23
16
|
end
|
@@ -67,7 +60,6 @@ module ViewComponent
|
|
67
60
|
|
68
61
|
def rendered_spacer(view_context)
|
69
62
|
if @spacer_component
|
70
|
-
@spacer_component.set_original_view_context(__vc_original_view_context)
|
71
63
|
@spacer_component.render_in(view_context)
|
72
64
|
else
|
73
65
|
""
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module ComponentLocalConfig
|
5
|
+
class Configuration
|
6
|
+
def self.defaults
|
7
|
+
ActiveSupport::Configurable::Configuration[
|
8
|
+
strip_trailing_whitespace: false
|
9
|
+
]
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize(config = defaults)
|
13
|
+
@config = config
|
14
|
+
end
|
15
|
+
|
16
|
+
delegate_missing_to :@config
|
17
|
+
|
18
|
+
def inheritable_copy
|
19
|
+
self.class.new(@config.inheritable_copy)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
delegate :defaults, to: :class
|
25
|
+
end
|
26
|
+
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
included do
|
30
|
+
# :nocov:
|
31
|
+
def view_component_config
|
32
|
+
@__vc_config ||= self.class.view_component_config.inheritable_copy
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def inherited(child)
|
38
|
+
child.instance_variable_set(:@__vc_config, nil)
|
39
|
+
super
|
40
|
+
end
|
41
|
+
# :nocov:
|
42
|
+
end
|
43
|
+
|
44
|
+
class_methods do
|
45
|
+
def view_component_config
|
46
|
+
@__vc_config ||= if respond_to?(:superclass) && superclass.respond_to?(:view_component_config)
|
47
|
+
superclass.view_component_config.inheritable_copy
|
48
|
+
else
|
49
|
+
# create a new "anonymous" class that will host the compiled reader methods
|
50
|
+
ViewComponent::ComponentLocalConfig::Configuration.new
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def configure_view_component(&block)
|
55
|
+
view_component_config.instance_eval(&block)
|
56
|
+
view_component_config.compile_methods!
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -21,8 +21,7 @@ module ViewComponent
|
|
21
21
|
show_previews: Rails.env.development? || Rails.env.test?,
|
22
22
|
preview_paths: default_preview_paths,
|
23
23
|
test_controller: "ApplicationController",
|
24
|
-
default_preview_layout: nil
|
25
|
-
capture_compatibility_patch_enabled: false
|
24
|
+
default_preview_layout: nil
|
26
25
|
})
|
27
26
|
end
|
28
27
|
|
@@ -145,13 +144,6 @@ module ViewComponent
|
|
145
144
|
# previews.
|
146
145
|
# Defaults to `nil`. If this is falsy, `"component_preview"` is used.
|
147
146
|
|
148
|
-
# @!attribute capture_compatibility_patch_enabled
|
149
|
-
# @return [Boolean]
|
150
|
-
# Enables the experimental capture compatibility patch that makes ViewComponent
|
151
|
-
# compatible with forms, capture, and other built-ins.
|
152
|
-
# previews.
|
153
|
-
# Defaults to `false`.
|
154
|
-
|
155
147
|
def default_preview_paths
|
156
148
|
(default_rails_preview_paths + default_rails_engines_preview_paths).uniq
|
157
149
|
end
|
@@ -51,12 +51,6 @@ module ViewComponent
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
initializer "view_component.enable_capture_patch" do |app|
|
55
|
-
ActiveSupport.on_load(:view_component) do
|
56
|
-
ActionView::Base.include(ViewComponent::CaptureCompatibility) if app.config.view_component.capture_compatibility_patch_enabled
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
54
|
initializer "view_component.set_autoload_paths" do |app|
|
61
55
|
options = app.config.view_component
|
62
56
|
|
@@ -66,6 +60,39 @@ module ViewComponent
|
|
66
60
|
end
|
67
61
|
end
|
68
62
|
|
63
|
+
initializer "view_component.propshaft_support" do |_app|
|
64
|
+
ActiveSupport.on_load(:view_component) do
|
65
|
+
if defined?(Propshaft)
|
66
|
+
include Propshaft::Helper
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
config.after_initialize do |app|
|
72
|
+
ActiveSupport.on_load(:view_component) do
|
73
|
+
if defined?(Sprockets::Rails)
|
74
|
+
include Sprockets::Rails::Helper
|
75
|
+
|
76
|
+
# Copy relevant config to VC context
|
77
|
+
# See: https://github.com/rails/sprockets-rails/blob/266ec49f3c7c96018dd75f9dc4f9b62fe3f7eecf/lib/sprockets/railtie.rb#L245
|
78
|
+
self.debug_assets = app.config.assets.debug
|
79
|
+
self.digest_assets = app.config.assets.digest
|
80
|
+
self.assets_prefix = app.config.assets.prefix
|
81
|
+
self.assets_precompile = app.config.assets.precompile
|
82
|
+
|
83
|
+
self.assets_environment = app.assets
|
84
|
+
self.assets_manifest = app.assets_manifest
|
85
|
+
|
86
|
+
self.resolve_assets_with = app.config.assets.resolve_with
|
87
|
+
|
88
|
+
self.check_precompiled_asset = app.config.assets.check_precompiled_asset
|
89
|
+
self.unknown_asset_fallback = app.config.assets.unknown_asset_fallback
|
90
|
+
# Expose the app precompiled asset check to the view
|
91
|
+
self.precompiled_asset_checker = ->(logical_path) { app.asset_precompiled? logical_path }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
69
96
|
initializer "view_component.eager_load_actions" do
|
70
97
|
ActiveSupport.on_load(:after_initialize) do
|
71
98
|
ViewComponent::Base.descendants.each(&:__vc_compile) if Rails.application.config.eager_load
|
data/lib/view_component/slot.rb
CHANGED
@@ -58,15 +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
|
-
|
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
|
68
|
-
@__vc_content_block.call(*args)
|
69
|
-
end
|
61
|
+
@__vc_content_block.call(*args)
|
70
62
|
end
|
71
63
|
else
|
72
64
|
@__vc_component_instance.render_in(view_context)
|
@@ -98,8 +98,9 @@ module ViewComponent
|
|
98
98
|
@component.silence_redefinition_of_method(call_method_name)
|
99
99
|
|
100
100
|
# rubocop:disable Style/EvalWithLocation
|
101
|
-
@component.class_eval <<~RUBY, @path, @lineno
|
101
|
+
@component.class_eval <<~RUBY, @path, @lineno - 1
|
102
102
|
def #{call_method_name}
|
103
|
+
@view_context.instance_variable_set(:@virtual_path, virtual_path)
|
103
104
|
#{compiled_source}
|
104
105
|
end
|
105
106
|
RUBY
|
@@ -93,7 +93,7 @@ module ViewComponent
|
|
93
93
|
def translate(key = nil, **options)
|
94
94
|
raise ViewComponent::TranslateCalledBeforeRenderError if view_context.nil?
|
95
95
|
|
96
|
-
return
|
96
|
+
return @view_context.translate(key, **options) unless __vc_i18n_backend
|
97
97
|
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
98
98
|
|
99
99
|
locale = options.delete(:locale) || ::I18n.locale
|
@@ -110,13 +110,13 @@ module ViewComponent
|
|
110
110
|
|
111
111
|
# Fallback to the global translations
|
112
112
|
if translated.is_a? ::I18n::MissingTranslation
|
113
|
-
return
|
113
|
+
return @view_context.translate(key, locale: locale, **options)
|
114
114
|
end
|
115
115
|
|
116
116
|
translated = html_safe_translation(translated) if as_html
|
117
117
|
translated
|
118
118
|
else
|
119
|
-
|
119
|
+
@view_context.translate(key, locale: locale, **options)
|
120
120
|
end
|
121
121
|
end
|
122
122
|
alias_method :t, :translate
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0.
|
4
|
+
version: 4.0.0.alpha3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ViewComponent Team
|
@@ -466,10 +466,10 @@ files:
|
|
466
466
|
- docs/CHANGELOG.md
|
467
467
|
- lib/view_component.rb
|
468
468
|
- lib/view_component/base.rb
|
469
|
-
- lib/view_component/capture_compatibility.rb
|
470
469
|
- lib/view_component/collection.rb
|
471
470
|
- lib/view_component/compile_cache.rb
|
472
471
|
- lib/view_component/compiler.rb
|
472
|
+
- lib/view_component/component_local_config.rb
|
473
473
|
- lib/view_component/config.rb
|
474
474
|
- lib/view_component/configurable.rb
|
475
475
|
- lib/view_component/deprecation.rb
|
@@ -1,44 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module ViewComponent
|
4
|
-
# CaptureCompatibility is a module that patches #capture to fix issues
|
5
|
-
# related to ViewComponent and functionality that relies on `capture`
|
6
|
-
# like forms, capture itself, turbo frames, etc.
|
7
|
-
#
|
8
|
-
# This underlying incompatibility with ViewComponent and capture is
|
9
|
-
# that several features like forms keep a reference to the primary
|
10
|
-
# `ActionView::Base` instance which has its own @output_buffer. When
|
11
|
-
# `#capture` is called on the original `ActionView::Base` instance while
|
12
|
-
# evaluating a block from a ViewComponent the @output_buffer is overridden
|
13
|
-
# in the ActionView::Base instance, and *not* the component. This results
|
14
|
-
# in a double render due to `#capture` implementation details.
|
15
|
-
#
|
16
|
-
# To resolve the issue, we override `#capture` so that we can delegate
|
17
|
-
# the `capture` logic to the ViewComponent that created the block.
|
18
|
-
module CaptureCompatibility
|
19
|
-
def self.included(base)
|
20
|
-
return if base < InstanceMethods
|
21
|
-
|
22
|
-
base.class_eval do
|
23
|
-
alias_method :original_capture, :capture
|
24
|
-
end
|
25
|
-
|
26
|
-
base.prepend(InstanceMethods)
|
27
|
-
end
|
28
|
-
|
29
|
-
module InstanceMethods
|
30
|
-
def capture(*args, &block)
|
31
|
-
# Handle blocks that originate from C code and raise, such as `&:method`
|
32
|
-
return original_capture(*args, &block) if block.source_location.nil?
|
33
|
-
|
34
|
-
block_context = block.binding.receiver
|
35
|
-
|
36
|
-
if block_context != self && block_context.class < ActionView::Base
|
37
|
-
block_context.original_capture(*args, &block)
|
38
|
-
else
|
39
|
-
original_capture(*args, &block)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|