view_component 2.30.0 → 2.31.0

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

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bb3c2781d5f07f404e5bf9582758537704a05c3937529c250ae657541d4f664
4
- data.tar.gz: 0efa6d2e3bbf03ed206e02b9dad11bea1f5649ab655dfff78d1f48c68414129f
3
+ metadata.gz: 37bb719bf3f89d9d71ce7879e12a55ccafb0b1705565d4256a0f825b68a24db2
4
+ data.tar.gz: 5a379783bcf6f170e151fd48f1524e942710b3dfbc5cf40e790812476f303ae0
5
5
  SHA512:
6
- metadata.gz: 9222381535158cc047e33e37341329c232ac984ee3f6895e95251cd9dabc9b92baebb69b336500932c264b48eb7f74ef0b7d3f918473d8fe83151ad73c18323b
7
- data.tar.gz: 4da1bd8980affd316288695a52e9fcdccad912f3447081f0186194190ad48e52d397f36c34b3bea70ea743d32f2eda8d37779ecb588b50cbd626f7d89a4f365c
6
+ metadata.gz: d0c18928b2cb3e6ae1e9a360eaeb05d278fee96c745bc4b17a0f498787862fb5619bf14165c37489d7d96bd064683fcd614166da3ee0c274453db0b47c270db8
7
+ data.tar.gz: 5381cd03176cfa7b9df47a86d801d7177c292c0f4460c8df70378e97052993f2bf17c9f96f140290d22d44a3a56794f4e9d9ac1378cb8c9666509d3f2fddac62
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
 
3
3
  ## main
4
4
 
5
+ ## 2.31.0
6
+
7
+ * Add `#with_content` to allow setting content without a block.
8
+
9
+ *Jordan Raine, Manuel Puyol*
10
+
11
+ * Add `with_request_url` test helper.
12
+
13
+ *Mario Schüttel*
14
+
15
+ * Improve feature parity with Rails translations
16
+ * Don't create a translation backend if the component has no translation file
17
+ * Mark translation keys ending with `html` as HTML-safe
18
+ * Always convert keys to String
19
+ * Support multiple keys
20
+
21
+ *Elia Schito*
22
+
23
+ * Fix errors on `asset_url` helpers when `asset_host` has no protocol.
24
+
25
+ *Elia Schito*
26
+
27
+ * Prevent slots from overriding the `#content` method when registering a slot with that name.
28
+
29
+ *Blake Williams*
30
+
31
+ * Deprecate `with_slot` in favor of the new [slots API](https://viewcomponent.org/guide/slots.html).
32
+
33
+ *Manuel Puyol*
34
+
5
35
  ## 2.30.0
6
36
 
7
37
  * Deprecate `with_content_areas` in favor of [slots](https://viewcomponent.org/guide/slots.html).
@@ -7,6 +7,7 @@ module ViewComponent
7
7
 
8
8
  autoload :Base
9
9
  autoload :Compiler
10
+ autoload :ComponentError
10
11
  autoload :Preview
11
12
  autoload :PreviewTemplateError
12
13
  autoload :TestHelpers
@@ -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
- template_compiler.compiled?
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
- template_compiler.compile(raise_errors: raise_errors)
297
+ compiler.compile(raise_errors: raise_errors)
292
298
  end
293
299
 
294
- def template_compiler
295
- @_template_compiler ||= Compiler.new(self)
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 ArgumentError.new(
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
- instance_method(:initialize).parameters.map(&:second).include?(collection_counter_parameter)
394
+ initialize_parameter_names.include?(collection_counter_parameter)
389
395
  end
390
396
 
391
397
  private
@@ -13,17 +13,23 @@ module ViewComponent
13
13
  def compile(raise_errors: false)
14
14
  return if compiled?
15
15
 
16
- if template_errors.present?
17
- raise ViewComponent::TemplateError.new(template_errors) if raise_errors
18
- return false
19
- end
16
+ subclass_instance_methods = component_class.instance_methods(false)
20
17
 
21
- if component_class.instance_methods(false).include?(:before_render_check)
18
+ if subclass_instance_methods.include?(:before_render_check)
22
19
  ActiveSupport::Deprecation.warn(
23
20
  "`before_render_check` will be removed in v3.0.0. Use `before_render` instead."
24
21
  )
25
22
  end
26
23
 
24
+ if subclass_instance_methods.include?(:with_content) && raise_errors
25
+ raise ViewComponent::ComponentError.new("#{component_class} implements a reserved method, `with_content`.")
26
+ end
27
+
28
+ if template_errors.present?
29
+ raise ViewComponent::TemplateError.new(template_errors) if raise_errors
30
+ return false
31
+ end
32
+
27
33
  if raise_errors
28
34
  component_class.validate_initialization_parameters!
29
35
  component_class.validate_collection_parameter!
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ViewComponent
4
+ class ComponentError < StandardError
5
+ end
6
+ end
@@ -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
- # render_in is faster than `parent.render`
31
- if defined?(@_content_block)
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
- nil
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
@@ -9,6 +9,8 @@ module ViewComponent
9
9
  module Translatable
10
10
  extend ActiveSupport::Concern
11
11
 
12
+ HTML_SAFE_TRANSLATION_KEY = /(?:_|\b)html\z/.freeze
13
+
12
14
  included do
13
15
  class_attribute :i18n_backend, instance_writer: false, instance_predicate: false
14
16
  end
@@ -21,11 +23,16 @@ module ViewComponent
21
23
  def _after_compile
22
24
  super
23
25
 
24
- unless CompileCache.compiled? self
26
+ return if CompileCache.compiled? self
27
+
28
+ if (translation_files = _sidecar_files(%w[yml yaml])).any?
25
29
  self.i18n_backend = I18nBackend.new(
26
30
  i18n_scope: i18n_scope,
27
- load_paths: _sidecar_files(%w[yml yaml]),
31
+ load_paths: translation_files,
28
32
  )
33
+ else
34
+ # Cleanup if translations file has been removed since the last compilation
35
+ self.i18n_backend = nil
29
36
  end
30
37
  end
31
38
  end
@@ -55,21 +62,28 @@ module ViewComponent
55
62
  end
56
63
  end
57
64
 
58
- def translate(key = nil, locale: nil, **options)
59
- locale ||= ::I18n.locale
65
+ def translate(key = nil, **options)
66
+ return super unless i18n_backend
67
+ return key.map { |k| translate(k, **options) } if key.is_a?(Array)
60
68
 
69
+ locale = options.delete(:locale) || ::I18n.locale
70
+ key = key&.to_s unless key.is_a?(String)
61
71
  key = "#{i18n_scope}#{key}" if key.start_with?(".")
62
72
 
63
- result = catch(:exception) do
73
+ translated = catch(:exception) do
64
74
  i18n_backend.translate(locale, key, options)
65
75
  end
66
76
 
67
77
  # Fallback to the global translations
68
- if result.is_a? ::I18n::MissingTranslation
69
- result = helpers.t(key, locale: locale, **options)
78
+ if translated.is_a? ::I18n::MissingTranslation
79
+ return super(key, locale: locale, **options)
80
+ end
81
+
82
+ if HTML_SAFE_TRANSLATION_KEY.match?(key)
83
+ translated = translated.html_safe
70
84
  end
71
85
 
72
- result
86
+ translated
73
87
  end
74
88
  alias :t :translate
75
89
 
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 2
6
- MINOR = 30
6
+ MINOR = 31
7
7
  PATCH = 0
8
8
 
9
9
  STRING = [MAJOR, MINOR, PATCH].join(".")
@@ -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
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.30.0
4
+ version: 2.31.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - GitHub Open Source
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-02 00:00:00.000000000 Z
11
+ date: 2021-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -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,6 +266,7 @@ 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