view_component 2.30.0 → 2.31.2
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.
Potentially problematic release.
This version of view_component might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +42 -0
- data/lib/view_component/base.rb +14 -8
- data/lib/view_component/compiler.rb +7 -1
- data/lib/view_component/component_error.rb +6 -0
- data/lib/view_component/slot_v2.rb +16 -2
- data/lib/view_component/slotable.rb +5 -0
- data/lib/view_component/slotable_v2.rb +5 -1
- data/lib/view_component/test_helpers.rb +11 -0
- data/lib/view_component/translatable.rb +41 -8
- data/lib/view_component/version.rb +2 -2
- data/lib/view_component/with_content_helper.rb +15 -0
- data/lib/view_component.rb +1 -0
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aed268c7e5731efdfba6e0d5764f04bcca9e456329afa15f31ebe7198c6bc79e
|
4
|
+
data.tar.gz: bc455dc076927f9a374788cd4a043df0a8905f9e6b64f5475fa9b7f1b1f38cc3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 455f1f06841908fe6239c93d9f4e2f99aa42e2bba64a6293bd4f2d5e8823225322566962c35ca46c0788bbfa085096ab15ede650e229759a05aa0c2f0f6b5e5c
|
7
|
+
data.tar.gz: b2fde290f722b87ca420bfd2f722184e4f3d79a987bb3ea93c29ae8f7c587a090276e9cdcf94e7434147b6ff02b8fb8492db86945c107c9e328b1c3ab4798786
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,48 @@
|
|
2
2
|
|
3
3
|
## main
|
4
4
|
|
5
|
+
## 2.31.2
|
6
|
+
|
7
|
+
* Patch XSS vulnerability in `Translatable` module caused by improperly escaped interpolation arguments.
|
8
|
+
|
9
|
+
*Cameron Dutro*
|
10
|
+
|
11
|
+
## 2.31.1
|
12
|
+
|
13
|
+
* Fix `DEPRECATION WARNING: before_render_check` when compiling `ViewComponent::Base`
|
14
|
+
|
15
|
+
*Dave Kroondyk*
|
16
|
+
|
17
|
+
## 2.31.0
|
18
|
+
|
19
|
+
* Add `#with_content` to allow setting content without a block.
|
20
|
+
|
21
|
+
*Jordan Raine, Manuel Puyol*
|
22
|
+
|
23
|
+
* Add `with_request_url` test helper.
|
24
|
+
|
25
|
+
*Mario Schüttel*
|
26
|
+
|
27
|
+
* Improve feature parity with Rails translations
|
28
|
+
* Don't create a translation backend if the component has no translation file
|
29
|
+
* Mark translation keys ending with `html` as HTML-safe
|
30
|
+
* Always convert keys to String
|
31
|
+
* Support multiple keys
|
32
|
+
|
33
|
+
*Elia Schito*
|
34
|
+
|
35
|
+
* Fix errors on `asset_url` helpers when `asset_host` has no protocol.
|
36
|
+
|
37
|
+
*Elia Schito*
|
38
|
+
|
39
|
+
* Prevent slots from overriding the `#content` method when registering a slot with that name.
|
40
|
+
|
41
|
+
*Blake Williams*
|
42
|
+
|
43
|
+
* Deprecate `with_slot` in favor of the new [slots API](https://viewcomponent.org/guide/slots.html).
|
44
|
+
|
45
|
+
*Manuel Puyol*
|
46
|
+
|
5
47
|
## 2.30.0
|
6
48
|
|
7
49
|
* Deprecate `with_content_areas` in favor of [slots](https://viewcomponent.org/guide/slots.html).
|
data/lib/view_component/base.rb
CHANGED
@@ -7,12 +7,14 @@ require "view_component/compile_cache"
|
|
7
7
|
require "view_component/previewable"
|
8
8
|
require "view_component/slotable"
|
9
9
|
require "view_component/slotable_v2"
|
10
|
+
require "view_component/with_content_helper"
|
10
11
|
|
11
12
|
module ViewComponent
|
12
13
|
class Base < ActionView::Base
|
13
14
|
include ActiveSupport::Configurable
|
14
15
|
include ViewComponent::Previewable
|
15
16
|
include ViewComponent::SlotableV2
|
17
|
+
include ViewComponent::WithContentHelper
|
16
18
|
|
17
19
|
ViewContextCalledBeforeRenderError = Class.new(StandardError)
|
18
20
|
|
@@ -79,6 +81,8 @@ module ViewComponent
|
|
79
81
|
old_current_template = @current_template
|
80
82
|
@current_template = self
|
81
83
|
|
84
|
+
raise ArgumentError.new("Block provided after calling `with_content`. Use one or the other.") if block && defined?(@_content_set_by_with_content)
|
85
|
+
|
82
86
|
@_content_evaluated = false
|
83
87
|
@_render_in_block = block
|
84
88
|
|
@@ -169,8 +173,6 @@ module ViewComponent
|
|
169
173
|
self
|
170
174
|
end
|
171
175
|
|
172
|
-
private
|
173
|
-
|
174
176
|
# Exposes the current request to the component.
|
175
177
|
# Use sparingly as doing so introduces coupling
|
176
178
|
# that inhibits encapsulation & reuse.
|
@@ -178,6 +180,8 @@ module ViewComponent
|
|
178
180
|
@request ||= controller.request
|
179
181
|
end
|
180
182
|
|
183
|
+
private
|
184
|
+
|
181
185
|
attr_reader :view_context
|
182
186
|
|
183
187
|
def content
|
@@ -186,6 +190,8 @@ module ViewComponent
|
|
186
190
|
|
187
191
|
@_content = if @view_context && @_render_in_block
|
188
192
|
view_context.capture(self, &@_render_in_block)
|
193
|
+
elsif defined?(@_content_set_by_with_content)
|
194
|
+
@_content_set_by_with_content
|
189
195
|
end
|
190
196
|
end
|
191
197
|
|
@@ -280,7 +286,7 @@ module ViewComponent
|
|
280
286
|
end
|
281
287
|
|
282
288
|
def compiled?
|
283
|
-
|
289
|
+
compiler.compiled?
|
284
290
|
end
|
285
291
|
|
286
292
|
# Compile templates to instance methods, assuming they haven't been compiled already.
|
@@ -288,11 +294,11 @@ module ViewComponent
|
|
288
294
|
# Do as much work as possible in this step, as doing so reduces the amount
|
289
295
|
# of work done each time a component is rendered.
|
290
296
|
def compile(raise_errors: false)
|
291
|
-
|
297
|
+
compiler.compile(raise_errors: raise_errors)
|
292
298
|
end
|
293
299
|
|
294
|
-
def
|
295
|
-
@
|
300
|
+
def compiler
|
301
|
+
@_compiler ||= Compiler.new(self)
|
296
302
|
end
|
297
303
|
|
298
304
|
# we'll eventually want to update this to support other types
|
@@ -365,7 +371,7 @@ module ViewComponent
|
|
365
371
|
def validate_initialization_parameters!
|
366
372
|
return unless initialize_parameter_names.include?(RESERVED_PARAMETER)
|
367
373
|
|
368
|
-
raise
|
374
|
+
raise ViewComponent::ComponentError.new(
|
369
375
|
"#{self} initializer cannot contain " \
|
370
376
|
"`#{RESERVED_PARAMETER}` since it will override a " \
|
371
377
|
"public ViewComponent method."
|
@@ -385,7 +391,7 @@ module ViewComponent
|
|
385
391
|
end
|
386
392
|
|
387
393
|
def counter_argument_present?
|
388
|
-
|
394
|
+
initialize_parameter_names.include?(collection_counter_parameter)
|
389
395
|
end
|
390
396
|
|
391
397
|
private
|
@@ -13,12 +13,18 @@ module ViewComponent
|
|
13
13
|
def compile(raise_errors: false)
|
14
14
|
return if compiled?
|
15
15
|
|
16
|
+
subclass_instance_methods = component_class.instance_methods(false)
|
17
|
+
|
18
|
+
if subclass_instance_methods.include?(:with_content) && raise_errors
|
19
|
+
raise ViewComponent::ComponentError.new("#{component_class} implements a reserved method, `with_content`.")
|
20
|
+
end
|
21
|
+
|
16
22
|
if template_errors.present?
|
17
23
|
raise ViewComponent::TemplateError.new(template_errors) if raise_errors
|
18
24
|
return false
|
19
25
|
end
|
20
26
|
|
21
|
-
if
|
27
|
+
if subclass_instance_methods.include?(:before_render_check)
|
22
28
|
ActiveSupport::Deprecation.warn(
|
23
29
|
"`before_render_check` will be removed in v3.0.0. Use `before_render` instead."
|
24
30
|
)
|
@@ -1,7 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "view_component/with_content_helper"
|
4
|
+
|
3
5
|
module ViewComponent
|
4
6
|
class SlotV2
|
7
|
+
include ViewComponent::WithContentHelper
|
8
|
+
|
5
9
|
attr_writer :_component_instance, :_content_block, :_content
|
6
10
|
|
7
11
|
def initialize(parent)
|
@@ -26,10 +30,18 @@ module ViewComponent
|
|
26
30
|
|
27
31
|
view_context = @parent.send(:view_context)
|
28
32
|
|
33
|
+
raise ArgumentError.new("Block provided after calling `with_content`. Use one or the other.") if defined?(@_content_block) && defined?(@_content_set_by_with_content)
|
34
|
+
|
29
35
|
@content = if defined?(@_component_instance)
|
30
|
-
|
31
|
-
|
36
|
+
if defined?(@_content_set_by_with_content)
|
37
|
+
@_component_instance.with_content(@_content_set_by_with_content)
|
38
|
+
|
39
|
+
view_context.capture do
|
40
|
+
@_component_instance.render_in(view_context)
|
41
|
+
end
|
42
|
+
elsif defined?(@_content_block)
|
32
43
|
view_context.capture do
|
44
|
+
# render_in is faster than `parent.render`
|
33
45
|
@_component_instance.render_in(view_context, &@_content_block)
|
34
46
|
end
|
35
47
|
else
|
@@ -41,6 +53,8 @@ module ViewComponent
|
|
41
53
|
@_content
|
42
54
|
elsif defined?(@_content_block)
|
43
55
|
view_context.capture(&@_content_block)
|
56
|
+
elsif defined?(@_content_set_by_with_content)
|
57
|
+
@_content_set_by_with_content
|
44
58
|
end
|
45
59
|
|
46
60
|
@content
|
@@ -23,6 +23,11 @@ module ViewComponent
|
|
23
23
|
# class_name: "Header" # class name string, used to instantiate Slot
|
24
24
|
# )
|
25
25
|
def with_slot(*slot_names, collection: false, class_name: nil)
|
26
|
+
ActiveSupport::Deprecation.warn(
|
27
|
+
"`with_slot` is deprecated and will be removed in ViewComponent v3.0.0.\n" \
|
28
|
+
"Use the new slots API (https://viewcomponent.org/guide/slots.html) instead."
|
29
|
+
)
|
30
|
+
|
26
31
|
slot_names.each do |slot_name|
|
27
32
|
# Ensure slot_name is not already declared
|
28
33
|
if self.slots.key?(slot_name)
|
@@ -175,6 +175,10 @@ module ViewComponent
|
|
175
175
|
end
|
176
176
|
|
177
177
|
def validate_slot_name(slot_name)
|
178
|
+
if slot_name.to_sym == :content
|
179
|
+
raise ArgumentError.new("#{slot_name} is not a valid slot name.")
|
180
|
+
end
|
181
|
+
|
178
182
|
if self.registered_slots.key?(slot_name)
|
179
183
|
# TODO remove? This breaks overriding slots when slots are inherited
|
180
184
|
raise ArgumentError.new("#{slot_name} slot declared multiple times")
|
@@ -251,7 +255,7 @@ module ViewComponent
|
|
251
255
|
@_set_slots[slot_name] = slot
|
252
256
|
end
|
253
257
|
|
254
|
-
|
258
|
+
slot
|
255
259
|
end
|
256
260
|
end
|
257
261
|
end
|
@@ -60,6 +60,17 @@ module ViewComponent
|
|
60
60
|
@controller = old_controller
|
61
61
|
end
|
62
62
|
|
63
|
+
def with_request_url(path)
|
64
|
+
old_request_path_parameters = request.path_parameters
|
65
|
+
old_controller = defined?(@controller) && @controller
|
66
|
+
|
67
|
+
request.path_parameters = Rails.application.routes.recognize_path(path)
|
68
|
+
yield
|
69
|
+
ensure
|
70
|
+
request.path_parameters = old_request_path_parameters
|
71
|
+
@controller = old_controller
|
72
|
+
end
|
73
|
+
|
63
74
|
def build_controller(klass)
|
64
75
|
klass.new.tap { |c| c.request = request }.extend(Rails.application.routes.url_helpers)
|
65
76
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "erb"
|
3
4
|
require "set"
|
4
5
|
require "i18n"
|
5
6
|
require "action_view/helpers/translation_helper"
|
@@ -9,6 +10,8 @@ module ViewComponent
|
|
9
10
|
module Translatable
|
10
11
|
extend ActiveSupport::Concern
|
11
12
|
|
13
|
+
HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/.freeze
|
14
|
+
|
12
15
|
included do
|
13
16
|
class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
|
14
17
|
end
|
@@ -21,11 +24,16 @@ module ViewComponent
|
|
21
24
|
def _after_compile
|
22
25
|
super
|
23
26
|
|
24
|
-
|
27
|
+
return if CompileCache.compiled? self
|
28
|
+
|
29
|
+
if (translation_files = _sidecar_files(%w[yml yaml])).any?
|
25
30
|
self.i18n_backend = I18nBackend.new(
|
26
31
|
i18n_scope: i18n_scope,
|
27
|
-
load_paths:
|
32
|
+
load_paths: translation_files,
|
28
33
|
)
|
34
|
+
else
|
35
|
+
# Cleanup if translations file has been removed since the last compilation
|
36
|
+
self.i18n_backend = nil
|
29
37
|
end
|
30
38
|
end
|
31
39
|
end
|
@@ -55,21 +63,32 @@ module ViewComponent
|
|
55
63
|
end
|
56
64
|
end
|
57
65
|
|
58
|
-
def translate(key = nil,
|
59
|
-
|
66
|
+
def translate(key = nil, **options)
|
67
|
+
return super unless i18n_backend
|
68
|
+
return key.map { |k| translate(k, **options) } if key.is_a?(Array)
|
60
69
|
|
70
|
+
locale = options.delete(:locale) || ::I18n.locale
|
71
|
+
key = key&.to_s unless key.is_a?(String)
|
61
72
|
key = "#{i18n_scope}#{key}" if key.start_with?(".")
|
62
73
|
|
63
|
-
|
74
|
+
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
75
|
+
html_escape_translation_options!(options)
|
76
|
+
end
|
77
|
+
|
78
|
+
translated = catch(:exception) do
|
64
79
|
i18n_backend.translate(locale, key, options)
|
65
80
|
end
|
66
81
|
|
67
82
|
# Fallback to the global translations
|
68
|
-
if
|
69
|
-
|
83
|
+
if translated.is_a? ::I18n::MissingTranslation
|
84
|
+
return super(key, locale: locale, **options)
|
70
85
|
end
|
71
86
|
|
72
|
-
|
87
|
+
if HTML_SAFE_TRANSLATION_KEY.match?(key)
|
88
|
+
translated = translated.html_safe
|
89
|
+
end
|
90
|
+
|
91
|
+
translated
|
73
92
|
end
|
74
93
|
alias :t :translate
|
75
94
|
|
@@ -77,5 +96,19 @@ module ViewComponent
|
|
77
96
|
def i18n_scope
|
78
97
|
self.class.i18n_scope
|
79
98
|
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def html_escape_translation_options!(options)
|
103
|
+
options.each do |name, value|
|
104
|
+
unless i18n_option?(name) || (name == :count && value.is_a?(Numeric))
|
105
|
+
options[name] = ERB::Util.html_escape(value.to_s)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def i18n_option?(name)
|
111
|
+
(@i18n_option_names ||= I18n::RESERVED_KEYS.to_set).include?(name)
|
112
|
+
end
|
80
113
|
end
|
81
114
|
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ViewComponent
|
4
|
+
module WithContentHelper
|
5
|
+
def with_content(value)
|
6
|
+
if value.nil?
|
7
|
+
raise ArgumentError.new("No content provided.")
|
8
|
+
else
|
9
|
+
@_content_set_by_with_content = value
|
10
|
+
end
|
11
|
+
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/lib/view_component.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: view_component
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.31.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- GitHub Open Source
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-03-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -212,7 +212,7 @@ dependencies:
|
|
212
212
|
- - "~>"
|
213
213
|
- !ruby/object:Gem::Version
|
214
214
|
version: '0.13'
|
215
|
-
description:
|
215
|
+
description:
|
216
216
|
email:
|
217
217
|
- opensource+view_component@github.com
|
218
218
|
executables: []
|
@@ -246,6 +246,7 @@ files:
|
|
246
246
|
- lib/view_component/collection.rb
|
247
247
|
- lib/view_component/compile_cache.rb
|
248
248
|
- lib/view_component/compiler.rb
|
249
|
+
- lib/view_component/component_error.rb
|
249
250
|
- lib/view_component/engine.rb
|
250
251
|
- lib/view_component/preview.rb
|
251
252
|
- lib/view_component/preview_template_error.rb
|
@@ -265,12 +266,13 @@ files:
|
|
265
266
|
- lib/view_component/test_helpers.rb
|
266
267
|
- lib/view_component/translatable.rb
|
267
268
|
- lib/view_component/version.rb
|
269
|
+
- lib/view_component/with_content_helper.rb
|
268
270
|
homepage: https://github.com/github/view_component
|
269
271
|
licenses:
|
270
272
|
- MIT
|
271
273
|
metadata:
|
272
274
|
allowed_push_host: https://rubygems.org
|
273
|
-
post_install_message:
|
275
|
+
post_install_message:
|
274
276
|
rdoc_options: []
|
275
277
|
require_paths:
|
276
278
|
- lib
|
@@ -285,8 +287,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
285
287
|
- !ruby/object:Gem::Version
|
286
288
|
version: '0'
|
287
289
|
requirements: []
|
288
|
-
rubygems_version: 3.
|
289
|
-
signing_key:
|
290
|
+
rubygems_version: 3.2.22
|
291
|
+
signing_key:
|
290
292
|
specification_version: 4
|
291
293
|
summary: View components for Rails
|
292
294
|
test_files: []
|