view_component 2.30.0 → 2.31.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.

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