view_component 3.2.0 → 3.5.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: e505722b47ce320345cbb123e06e5138362a3b9abc5fe8cc77c5849a71e8baf5
4
- data.tar.gz: c9f053e92c067b2743ae64d8135334518a14933710c81ad68e058d0cd076b610
3
+ metadata.gz: ce590ddce7011ae4362dde0733f78ba65992c31add6bac215555515d02ae1dc6
4
+ data.tar.gz: df4867599a61d2757e2f226a3c6b187981772e2d225fc442f9184484c0423e64
5
5
  SHA512:
6
- metadata.gz: 3e3aab08e8665557c94e0f8df5c7f0f484b056bc9e0a85247a7d718cedfc3ff8aa3a2a6875fd4660626b15c0f6e002ac50797fdcce822f610ca91581afff8c9e
7
- data.tar.gz: 2f25ccc3bcfefb4e3096a5c9289922b648353ba42c333ad963d59cc95f8bd1cf4d1db0f178494ad6cb4a59af6832a852b3b15f34f80dc3c2ebfa6cf78d65b445
6
+ metadata.gz: 0a39d71f089c0f4285dc1eecd324e44afc73f55565002a45c2d37292cc4fa8b05713f7237f04b530617ddc0bb2ccd08fab0c7d010400790dc00daf2d6c9c0546
7
+ data.tar.gz: 252a63a4ed7eefd7db529dc93081d3ccfb78555ac1d051540f7a69829a41d4d22fd94557fa2fe9eb88c012bd8dccfd2ceb813aae2582877e387a2df347e2d915
@@ -59,7 +59,7 @@ module ViewComponent
59
59
  def find_preview
60
60
  candidates = []
61
61
  params[:path].to_s.scan(%r{/|$}) { candidates << Regexp.last_match.pre_match }
62
- preview = candidates.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
62
+ preview = candidates.sort_by(&:length).reverse_each.detect { |candidate| ViewComponent::Preview.exists?(candidate) }
63
63
 
64
64
  if preview
65
65
  @preview = ViewComponent::Preview.find(preview)
data/docs/CHANGELOG.md CHANGED
@@ -10,6 +10,44 @@ nav_order: 5
10
10
 
11
11
  ## main
12
12
 
13
+ ## 3.5.0
14
+
15
+ * Add Skroutz to users list.
16
+
17
+ *Chris Nitsas*
18
+
19
+ * Improve implementation of `#render_parent` so it respects variants and deep inheritance hierarchies.
20
+
21
+ *Cameron Dutro*
22
+
23
+ * Add CharlieHR to users list.
24
+
25
+ *Alex Balhatchet*
26
+
27
+ ## 3.4.0
28
+
29
+ * Avoid including Rails `url_helpers` into `Preview` class when they're not defined.
30
+
31
+ *Richard Macklin*
32
+
33
+ * Allow instrumentation to be automatically included in Server-Timing headers generated by Rails. To enable this set the config `config.use_deprecated_instrumentation_name = false`. The old key `!render.view_component` is deprecated: update ActiveSupport::Notification subscriptions to `render.view_component`.
34
+
35
+ *Travis Gaff*
36
+
37
+ ## 3.3.0
38
+
39
+ * Include InlineTemplate by default in Base. **Note:** It's no longer necessary to include `ViewComponent::InlineTemplate` to use inline templates.
40
+
41
+ *Joel Hawksley*
42
+
43
+ * Allow Setting host when using the `with_request_url` test helper.
44
+
45
+ *Daniel Alfaro*
46
+
47
+ * Resolve ambiguous preview paths when using components without the Component suffix.
48
+
49
+ *Reed Law*
50
+
13
51
  ## 3.2.0
14
52
 
15
53
  * Fix viewcomponent.org Axe violations.
@@ -7,6 +7,7 @@ require "view_component/compile_cache"
7
7
  require "view_component/compiler"
8
8
  require "view_component/config"
9
9
  require "view_component/errors"
10
+ require "view_component/inline_template"
10
11
  require "view_component/preview"
11
12
  require "view_component/slotable"
12
13
  require "view_component/translatable"
@@ -29,6 +30,7 @@ module ViewComponent
29
30
  attr_writer :config
30
31
  end
31
32
 
33
+ include ViewComponent::InlineTemplate
32
34
  include ViewComponent::Slotable
33
35
  include ViewComponent::Translatable
34
36
  include ViewComponent::WithContentHelper
@@ -114,20 +116,44 @@ module ViewComponent
114
116
  end
115
117
 
116
118
  # Subclass components that call `super` inside their template code will cause a
117
- # double render if they emit the result:
119
+ # double render if they emit the result.
118
120
  #
119
121
  # ```erb
120
122
  # <%= super %> # double-renders
121
- # <% super %> # does not double-render
123
+ # <% super %> # doesn't double-render
122
124
  # ```
123
125
  #
124
- # Calls `super`, returning `nil` to avoid rendering the result twice.
126
+ # `super` also doesn't consider the current variant. `render_parent` renders the
127
+ # parent template considering the current variant and emits the result without
128
+ # double-rendering.
125
129
  def render_parent
126
- mtd = @__vc_variant ? "call_#{@__vc_variant}" : "call"
127
- method(mtd).super_method.call
130
+ render_parent_to_string
128
131
  nil
129
132
  end
130
133
 
134
+ # Renders the parent component to a string and returns it. This method is meant
135
+ # to be used inside custom #call methods when a string result is desired, eg.
136
+ #
137
+ # ```ruby
138
+ # def call
139
+ # "<div>#{render_parent_to_string}</div>"
140
+ # end
141
+ # ```
142
+ #
143
+ # When rendering the parent inside an .erb template, use `#render_parent` instead.
144
+ def render_parent_to_string
145
+ @__vc_parent_render_level ||= 0 # ensure a good starting value
146
+
147
+ begin
148
+ target_render = self.class.instance_variable_get(:@__vc_ancestor_calls)[@__vc_parent_render_level]
149
+ @__vc_parent_render_level += 1
150
+
151
+ target_render.bind_call(self, @__vc_variant)
152
+ ensure
153
+ @__vc_parent_render_level -= 1
154
+ end
155
+ end
156
+
131
157
  # Optional content to be returned after the rendered template.
132
158
  #
133
159
  # @return [String]
@@ -457,6 +483,13 @@ module ViewComponent
457
483
  # Set collection parameter to the extended component
458
484
  child.with_collection_parameter provided_collection_parameter
459
485
 
486
+ if instance_methods(false).include?(:render_template_for)
487
+ vc_ancestor_calls = defined?(@__vc_ancestor_calls) ? @__vc_ancestor_calls.dup : []
488
+
489
+ vc_ancestor_calls.unshift(instance_method(:render_template_for))
490
+ child.instance_variable_set(:@__vc_ancestor_calls, vc_ancestor_calls)
491
+ end
492
+
460
493
  super
461
494
  end
462
495
 
@@ -56,17 +56,17 @@ module ViewComponent
56
56
  RUBY
57
57
  # rubocop:enable Style/EvalWithLocation
58
58
 
59
+ component_class.define_method("_call_#{safe_class_name}", component_class.instance_method(:call))
60
+
59
61
  component_class.silence_redefinition_of_method("render_template_for")
60
62
  component_class.class_eval <<-RUBY, __FILE__, __LINE__ + 1
61
63
  def render_template_for(variant = nil)
62
- call
64
+ _call_#{safe_class_name}
63
65
  end
64
66
  RUBY
65
67
  end
66
68
  else
67
69
  templates.each do |template|
68
- # Remove existing compiled template methods,
69
- # as Ruby warns when redefining a method.
70
70
  method_name = call_method_name(template[:variant])
71
71
 
72
72
  redefinition_lock.synchronize do
@@ -95,15 +95,20 @@ module ViewComponent
95
95
 
96
96
  def define_render_template_for
97
97
  variant_elsifs = variants.compact.uniq.map do |variant|
98
- "elsif variant.to_sym == :'#{variant}'\n #{call_method_name(variant)}"
98
+ safe_name = "_call_variant_#{normalized_variant_name(variant)}_#{safe_class_name}"
99
+ component_class.define_method(safe_name, component_class.instance_method(call_method_name(variant)))
100
+
101
+ "elsif variant.to_sym == :'#{variant}'\n #{safe_name}"
99
102
  end.join("\n")
100
103
 
104
+ component_class.define_method("_call_#{safe_class_name}", component_class.instance_method(:call))
105
+
101
106
  body = <<-RUBY
102
107
  if variant.nil?
103
- call
108
+ _call_#{safe_class_name}
104
109
  #{variant_elsifs}
105
110
  else
106
- call
111
+ _call_#{safe_class_name}
107
112
  end
108
113
  RUBY
109
114
 
@@ -276,12 +281,17 @@ module ViewComponent
276
281
  variant.to_s.gsub("-", "__").gsub(".", "___")
277
282
  end
278
283
 
284
+ def safe_class_name
285
+ @safe_class_name ||= component_class.name.underscore.gsub("/", "__")
286
+ end
287
+
279
288
  def should_compile_superclass?
280
- development? && templates.empty? && !has_inline_template? &&
281
- !(
282
- component_class.instance_methods(false).include?(:call) ||
283
- component_class.private_instance_methods(false).include?(:call)
284
- )
289
+ development? && templates.empty? && !has_inline_template? && !call_defined?
290
+ end
291
+
292
+ def call_defined?
293
+ component_class.instance_methods(false).include?(:call) ||
294
+ component_class.private_instance_methods(false).include?(:call)
285
295
  end
286
296
  end
287
297
  end
@@ -17,6 +17,7 @@ module ViewComponent
17
17
  preview_route: "/rails/view_components",
18
18
  show_previews_source: false,
19
19
  instrumentation_enabled: false,
20
+ use_deprecated_instrumentation_name: true,
20
21
  render_monkey_patch_enabled: true,
21
22
  view_component_path: "app/components",
22
23
  component_parent_class: nil,
@@ -99,6 +100,13 @@ module ViewComponent
99
100
  # Whether ActiveSupport notifications are enabled.
100
101
  # Defaults to `false`.
101
102
 
103
+ # @!attribute use_deprecated_instrumentation_name
104
+ # @return [Boolean]
105
+ # Whether ActiveSupport Notifications use the private name `"!render.view_component"`
106
+ # or are made more publicly available via `"render.view_component"`.
107
+ # Will default to `false` in next major version.
108
+ # Defaults to `true`.
109
+
102
110
  # @!attribute render_monkey_patch_enabled
103
111
  # @return [Boolean] Whether the #render method should be monkey patched.
104
112
  # If this is disabled, use `#render_component` or
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "rails"
4
4
  require "view_component/config"
5
+ require "view_component/deprecation"
5
6
 
6
7
  module ViewComponent
7
8
  class Engine < Rails::Engine # :nodoc:
@@ -39,8 +40,14 @@ module ViewComponent
39
40
  Base.config = app.config.view_component
40
41
 
41
42
  if app.config.view_component.instrumentation_enabled.present?
42
- # :nocov:
43
+ # :nocov: Re-executing the below in tests duplicates initializers and causes order-dependent failures.
43
44
  ViewComponent::Base.prepend(ViewComponent::Instrumentation)
45
+ if app.config.view_component.use_deprecated_instrumentation_name
46
+ ViewComponent::Deprecation.deprecation_warning(
47
+ "!render.view_component",
48
+ "Use the new instrumentation key `render.view_component` instead. See https://viewcomponent.org/guide/instrumentation.html"
49
+ )
50
+ end
44
51
  # :nocov:
45
52
  end
46
53
  end
@@ -39,7 +39,7 @@ module ViewComponent # :nodoc:
39
39
  end
40
40
 
41
41
  def inline_template
42
- @__vc_inline_template
42
+ @__vc_inline_template if defined?(@__vc_inline_template)
43
43
  end
44
44
 
45
45
  def inline_template_language
@@ -10,7 +10,7 @@ module ViewComponent # :nodoc:
10
10
 
11
11
  def render_in(view_context, &block)
12
12
  ActiveSupport::Notifications.instrument(
13
- "!render.view_component",
13
+ notification_name,
14
14
  {
15
15
  name: self.class.name,
16
16
  identifier: self.class.identifier
@@ -19,5 +19,13 @@ module ViewComponent # :nodoc:
19
19
  super(view_context, &block)
20
20
  end
21
21
  end
22
+
23
+ private
24
+
25
+ def notification_name
26
+ return "!render.view_component" if ViewComponent::Base.config.use_deprecated_instrumentation_name
27
+
28
+ "render.view_component"
29
+ end
22
30
  end
23
31
  end
@@ -4,7 +4,7 @@ require "active_support/descendants_tracker"
4
4
 
5
5
  module ViewComponent # :nodoc:
6
6
  class Preview
7
- include Rails.application.routes.url_helpers
7
+ include Rails.application.routes.url_helpers if defined?(Rails.application.routes.url_helpers)
8
8
  include ActionView::Helpers::TagHelper
9
9
  include ActionView::Helpers::AssetTagHelper
10
10
  extend ActiveSupport::DescendantsTracker
@@ -4,8 +4,10 @@ task stats: "view_component:statsetup"
4
4
 
5
5
  namespace :view_component do
6
6
  task :statsetup do
7
+ # :nocov:
7
8
  require "rails/code_statistics"
8
9
 
9
10
  ::STATS_DIRECTORIES << ["ViewComponents", ViewComponent::Base.view_component_path]
11
+ # :nocov:
10
12
  end
11
13
  end
@@ -155,8 +155,18 @@ module ViewComponent
155
155
  # end
156
156
  # ```
157
157
  #
158
+ # To use a specific host, pass the host param:
159
+ #
160
+ # ```ruby
161
+ # with_request_url("/users/42", host: "app.example.com") do
162
+ # render_inline(MyComponent.new)
163
+ # end
164
+ # ```
165
+ #
158
166
  # @param path [String] The path to set for the current request.
159
- def with_request_url(path)
167
+ # @param host [String] The host to set for the current request.
168
+ def with_request_url(path, host: nil)
169
+ old_request_host = vc_test_request.host
160
170
  old_request_path_info = vc_test_request.path_info
161
171
  old_request_path_parameters = vc_test_request.path_parameters
162
172
  old_request_query_parameters = vc_test_request.query_parameters
@@ -164,12 +174,14 @@ module ViewComponent
164
174
  old_controller = defined?(@vc_test_controller) && @vc_test_controller
165
175
 
166
176
  path, query = path.split("?", 2)
177
+ vc_test_request.host = host if host
167
178
  vc_test_request.path_info = path
168
179
  vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
169
180
  vc_test_request.set_header("action_dispatch.request.query_parameters", Rack::Utils.parse_nested_query(query))
170
181
  vc_test_request.set_header(Rack::QUERY_STRING, query)
171
182
  yield
172
183
  ensure
184
+ vc_test_request.host = old_request_host
173
185
  vc_test_request.path_info = old_request_path_info
174
186
  vc_test_request.path_parameters = old_request_path_parameters
175
187
  vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
@@ -3,7 +3,7 @@
3
3
  module ViewComponent
4
4
  module VERSION
5
5
  MAJOR = 3
6
- MINOR = 2
6
+ MINOR = 5
7
7
  PATCH = 0
8
8
  PRE = nil
9
9
 
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: 3.2.0
4
+ version: 3.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ViewComponent Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-06-12 00:00:00.000000000 Z
11
+ date: 2023-07-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport