view_component 4.0.0.alpha1 → 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 +21 -1
- data/lib/view_component/base.rb +63 -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 +3 -2
- 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,7 +10,19 @@ nav_order: 6
|
|
10
10
|
|
11
11
|
## main
|
12
12
|
|
13
|
-
## 4.0.0
|
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
|
+
|
19
|
+
## 4.0.0.alpha2
|
20
|
+
|
21
|
+
* Add `#current_template` accessor and `Template#path` for diagnostic usage.
|
22
|
+
|
23
|
+
*Joel Hawksley*
|
24
|
+
|
25
|
+
## 4.0.0.alpha1
|
14
26
|
|
15
27
|
Almost six years after releasing [v1.0.0](https://github.com/ViewComponent/view_component/releases/tag/v1.0.0), we're proud to ship ViewComponent 4. This release marks a shift towards a Long Term Support model for the project, having reached significant feature maturity. While contributions are always welcome, we're unlikely to accept further breaking changes or major feature additions.
|
16
28
|
|
@@ -112,6 +124,14 @@ This release makes the following breaking changes:
|
|
112
124
|
|
113
125
|
*Tiago Menegaz*, *Joel Hawksley*
|
114
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
|
+
|
115
135
|
* Do not include internal `DocsBuilderComponent` or `YARD::MattrAccessorHandler` in published gem.
|
116
136
|
|
117
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,22 +48,28 @@ 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
|
72
|
+
attr_reader :current_template
|
53
73
|
|
54
74
|
# Components render in their own view context. Helpers and other functionality
|
55
75
|
# require a reference to the original Rails view context, an instance of
|
@@ -61,7 +81,7 @@ module ViewComponent
|
|
61
81
|
# @param view_context [ActionView::Base] The original view context.
|
62
82
|
# @return [void]
|
63
83
|
def set_original_view_context(view_context)
|
64
|
-
|
84
|
+
# noop
|
65
85
|
end
|
66
86
|
|
67
87
|
using RequestDetails
|
@@ -80,7 +100,7 @@ module ViewComponent
|
|
80
100
|
@view_context = view_context
|
81
101
|
self.__vc_original_view_context ||= view_context
|
82
102
|
|
83
|
-
@output_buffer =
|
103
|
+
@output_buffer = view_context.output_buffer
|
84
104
|
|
85
105
|
@lookup_context ||= view_context.lookup_context
|
86
106
|
|
@@ -107,14 +127,20 @@ module ViewComponent
|
|
107
127
|
before_render
|
108
128
|
|
109
129
|
if render?
|
110
|
-
|
130
|
+
value = nil
|
111
131
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
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
|
117
141
|
end
|
142
|
+
|
143
|
+
value
|
118
144
|
else
|
119
145
|
""
|
120
146
|
end
|
@@ -206,7 +232,7 @@ module ViewComponent
|
|
206
232
|
def render(options = {}, args = {}, &block)
|
207
233
|
if options.respond_to?(:set_original_view_context)
|
208
234
|
options.set_original_view_context(self.__vc_original_view_context)
|
209
|
-
|
235
|
+
@view_context.render(options, args, &block)
|
210
236
|
else
|
211
237
|
__vc_original_view_context.render(options, args, &block)
|
212
238
|
end
|
@@ -593,16 +619,38 @@ module ViewComponent
|
|
593
619
|
# end
|
594
620
|
# ```
|
595
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
|
+
#
|
596
632
|
# @param value [Boolean] Whether to strip newlines.
|
597
633
|
def strip_trailing_whitespace(value = true)
|
598
|
-
|
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
|
599
647
|
end
|
600
648
|
|
601
649
|
# Whether trailing whitespace will be stripped before compilation.
|
602
650
|
#
|
603
651
|
# @return [Boolean]
|
604
652
|
def strip_trailing_whitespace?
|
605
|
-
|
653
|
+
view_component_config.strip_trailing_whitespace
|
606
654
|
end
|
607
655
|
|
608
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)
|
@@ -7,7 +7,7 @@ module ViewComponent
|
|
7
7
|
|
8
8
|
DataWithSource = Struct.new(:format, :identifier, :short_identifier, :type, keyword_init: true)
|
9
9
|
|
10
|
-
attr_reader :details
|
10
|
+
attr_reader :details, :path
|
11
11
|
|
12
12
|
delegate :virtual_path, to: :@component
|
13
13
|
delegate :format, :variant, to: :@details
|
@@ -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
|