Skip to content

Commit

Permalink
Merge branch 'main' into support-freezing-template-string-literals
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellhenke authored Nov 9, 2023
2 parents 7961511 + 0820ce8 commit f35aa5c
Show file tree
Hide file tree
Showing 23 changed files with 224 additions and 28 deletions.
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ gem "rails", (rails_version == "main") ? {git: "https://github.com/rails/rails",
gem "rspec-rails", "~> 5"

group :test do
gem "cuprite", "~> 0.8"
gem "cuprite", "~> 0.15"
gem "puma", "~> 6"

gem "selenium-webdriver", "4.9.0" # 4.9.1 requires Ruby 3+
Expand Down
21 changes: 12 additions & 9 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.4)
addressable (2.8.5)
public_suffix (>= 2.0.2, < 6.0)
ansi (1.5.0)
appraisal (2.5.0)
Expand Down Expand Up @@ -104,9 +104,9 @@ GEM
coderay (1.1.3)
concurrent-ruby (1.2.2)
crass (1.0.6)
cuprite (0.14.3)
cuprite (0.15)
capybara (~> 3.0)
ferrum (~> 0.13.0)
ferrum (~> 0.14.0)
date (3.3.3)
debug (1.8.0)
irb (>= 1.5.0)
Expand All @@ -121,7 +121,7 @@ GEM
rubocop
smart_properties
erubi (1.12.0)
ferrum (0.13)
ferrum (0.14)
addressable (~> 2.5)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
Expand Down Expand Up @@ -158,9 +158,9 @@ GEM
matrix (0.4.2)
method_source (1.0.0)
mini_mime (1.1.5)
mini_portile2 (2.8.4)
mini_portile2 (2.8.5)
minitest (5.20.0)
net-imap (0.4.2)
net-imap (0.4.4)
date
net-protocol
net-pop (0.1.2)
Expand All @@ -180,10 +180,10 @@ GEM
pry (0.14.2)
coderay (~> 1.1)
method_source (~> 1.0)
public_suffix (5.0.1)
public_suffix (5.0.3)
puma (6.4.0)
nio4r (~> 2.0)
racc (1.7.1)
racc (1.7.3)
rack (2.2.8)
rack-test (2.1.0)
rack (>= 1.3)
Expand Down Expand Up @@ -326,13 +326,16 @@ DEPENDENCIES
better_html
bundler (~> 2)
capybara (~> 3)
cuprite (~> 0.8)
cuprite (~> 0.15)
debug
erb_lint
haml (~> 6)
jbuilder (~> 2)
m (~> 1)
minitest (~> 5.18)
net-imap
net-pop
net-smtp
pry (~> 0.13)
puma (~> 6)
rails (~> 7.0.0)
Expand Down
14 changes: 14 additions & 0 deletions app/helpers/preview_helper.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# frozen_string_literal: true

module PreviewHelper
include ActionView::Helpers::AssetUrlHelper if Rails.version.to_f < 6.1

AVAILABLE_PRISM_LANGUAGES = %w[ruby erb haml]
FALLBACK_LANGUAGE = "ruby"

Expand All @@ -10,6 +12,14 @@ def preview_source
render "preview_source"
end

def prism_css_source_url
serve_static_preview_assets? ? asset_path("prism.css", skip_pipeline: true) : "https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.min.css"
end

def prism_js_source_url
serve_static_preview_assets? ? asset_path("prism.min.js", skip_pipeline: true) : "https://cdn.jsdelivr.net/npm/[email protected]/prism.min.js"
end

def find_template_data(lookup_context:, template_identifier:)
template = lookup_context.find_template(template_identifier)

Expand Down Expand Up @@ -62,4 +72,8 @@ def prism_language_name_by_template_path(template_file_path:)

language
end

def serve_static_preview_assets?
ViewComponent::Base.config.show_previews && Rails.application.config.public_file_server.enabled
end
end
4 changes: 2 additions & 2 deletions app/views/view_components/_preview_source.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<link href="<%= asset_path('prism.css', skip_pipeline: true) %>" media="screen" rel="stylesheet" type="text/css">
<link href="<%= prism_css_source_url %>" media="screen" rel="stylesheet" type="text/css">
<div class="view-component-source-example">
<h2>Source:</h2>
<pre class="source">
Expand All @@ -14,4 +14,4 @@
<% end %>
</pre>
</div>
<script type="text/javascript" src="<%= asset_path('prism.min.js', skip_pipeline: true) %>"></script>
<script type="text/javascript" src="<%= prism_js_source_url %>"></script>
25 changes: 25 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,27 @@ nav_order: 5

## main

* Replace usage of `String#ends_with?` with `String#end_with?` to reduce the dependency on ActiveSupport core extensions.

*halo*

* Don't add ActionDispatch::Static middleware unless `public_file_server.enabled`.

*Daniel Gonzalez*
*Reegan Viljoen*

* Resolve an issue where slots starting with `call` would cause a `NameError`

*Blake Williams*

* Add `use_helper` API.

*Reegan Viljoen*

* Fix bug where the `Rails` module wasn't being searched from the root namespace.

*Zenéixe*

* Add configuration and support for compiling templates with `frozen_string_literal` magic comment.

*Mitchell Henke*
Expand All @@ -18,6 +39,10 @@ nav_order: 5

*Nachiket Pusalkar*

* Allow setting method when using the `with_request_url` test helper.

*Andrew Duthie*

## 3.7.0

* Support Rails 7.1 in CI.
Expand Down
20 changes: 20 additions & 0 deletions docs/guide/helpers.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,26 @@ class UserComponent < ViewComponent::Base
end
```

## UseHelpers setter

By default, ViewComponents don't have access to helper methods defined externally. The `use_helpers` method allows external helpers to be called from the component.

To use the `use_helpers` method, include `ViewComponent::UseHelpers`.
`UseHelpers` defines the helper on the component and is similar in use to using `delegate` on helpers.

```ruby
class UseHelpersComponent < ViewComponent::Base
include ViewComponent::UseHelpers
use_helpers :icon

erb_template <<-ERB
<div class="icon">
<%= icon :user %>
</div>
ERB
end
```

## Nested URL helpers

Rails nested URL helpers implicitly depend on the current `request` in certain cases. Since ViewComponent is built to enable reusing components in different contexts, nested URL helpers should be passed their options explicitly:
Expand Down
2 changes: 2 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,13 +204,15 @@ ViewComponent is built by over a hundred members of the community, including:
<img src="https://avatars.githubusercontent.com/xronos-i-am?s=64" alt="xronos-i-am" width="32" />
<img src="https://avatars.githubusercontent.com/yykamei?s=64" alt="yykamei" width="32" />
<img src="https://avatars.githubusercontent.com/matheuspolicamilo?s=64" alt="matheuspolicamilo" width="32" />
<img src="https://avatars.githubusercontent.com/danigonza?s=64" alt="danigonza" width="32" />
<img src="https://avatars.githubusercontent.com/erinnachen?s=64" alt="erinnachen" width="32" />
<img src="https://avatars.githubusercontent.com/ihollander?s=64" alt="ihollander" width="32" />
<img src="https://avatars.githubusercontent.com/svetlins?s=64" alt="svetlins" width="32" />
<img src="https://avatars.githubusercontent.com/nickmalcolm?s=64" alt="nickmalcolm" width="32" />
<img src="https://avatars.githubusercontent.com/reeganviljoen?s=64" alt="reeganviljoen" width="32" />
<img src="https://avatars.githubusercontent.com/thomascchen?s=64" alt="thomascchen" width="32" />
<img src="https://avatars.githubusercontent.com/milk1000cc?s=64" alt="milk1000cc" width="32" />
<img src="https://avatars.githubusercontent.com/aduth?s=64" alt="aduth" width="32" />

## Who uses ViewComponent?

Expand Down
3 changes: 2 additions & 1 deletion lib/view_component/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
require "view_component/slotable"
require "view_component/translatable"
require "view_component/with_content_helper"
require "view_component/use_helpers"

module ViewComponent
class Base < ActionView::Base
Expand Down Expand Up @@ -219,7 +220,7 @@ def helpers
@__vc_helpers ||= __vc_original_view_context || controller.view_context
end

if Rails.env.development? || Rails.env.test?
if ::Rails.env.development? || ::Rails.env.test?
def method_missing(method_name, *args) # rubocop:disable Style/MissingRespondToMissing
super
rescue => e # rubocop:disable Style/RescueStandardError
Expand Down
4 changes: 2 additions & 2 deletions lib/view_component/compiler.rb
Original file line number Diff line number Diff line change
Expand Up @@ -229,12 +229,12 @@ def inline_calls
component_class.included_modules
)

view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call/) }.uniq
view_component_ancestors.flat_map { |ancestor| ancestor.instance_methods(false).grep(/^call(_|$)/) }.uniq
end
end

def inline_calls_defined_on_self
@inline_calls_defined_on_self ||= component_class.instance_methods(false).grep(/^call/)
@inline_calls_defined_on_self ||= component_class.instance_methods(false).grep(/^call(_|$)/)
end

def variants
Expand Down
6 changes: 5 additions & 1 deletion lib/view_component/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -113,11 +113,15 @@ class Engine < Rails::Engine # :nodoc:
end

initializer "static assets" do |app|
if app.config.view_component.show_previews
if serve_static_preview_assets?(app.config)
app.middleware.use(::ActionDispatch::Static, "#{root}/app/assets/vendor")
end
end

def serve_static_preview_assets?(app_config)
app_config.view_component.show_previews && app_config.public_file_server.enabled
end

initializer "compiler mode" do |_app|
ViewComponent::Compiler.mode = if Rails.env.development? || Rails.env.test?
ViewComponent::Compiler::DEVELOPMENT_MODE
Expand Down
9 changes: 6 additions & 3 deletions lib/view_component/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ class InvalidSlotDefinitionError < BaseError
"string, or callable (that is proc, lambda, etc)"
end

class SlotPredicateNameError < StandardError
class InvalidSlotNameError < StandardError
end

class SlotPredicateNameError < InvalidSlotNameError
MESSAGE =
"COMPONENT declares a slot named SLOT_NAME, which ends with a question mark.\n\n" \
"This isn't allowed because the ViewComponent framework already provides predicate " \
Expand All @@ -126,7 +129,7 @@ def initialize(klass_name, slot_name)
end
end

class ReservedSingularSlotNameError < StandardError
class ReservedSingularSlotNameError < InvalidSlotNameError
MESSAGE =
"COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
"To fix this issue, choose a different name."
Expand All @@ -136,7 +139,7 @@ def initialize(klass_name, slot_name)
end
end

class ReservedPluralSlotNameError < StandardError
class ReservedPluralSlotNameError < InvalidSlotNameError
MESSAGE =
"COMPONENT declares a slot named SLOT_NAME, which is a reserved word in the ViewComponent framework.\n\n" \
"To fix this issue, choose a different name."
Expand Down
10 changes: 9 additions & 1 deletion lib/view_component/slotable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ def validate_plural_slot_name(slot_name)
raise ReservedPluralSlotNameError.new(name, slot_name)
end

raise_if_slot_conflicts_with_call(slot_name)
raise_if_slot_ends_with_question_mark(slot_name)
raise_if_slot_registered(slot_name)
end
Expand All @@ -308,6 +309,7 @@ def validate_singular_slot_name(slot_name)
raise ReservedSingularSlotNameError.new(name, slot_name)
end

raise_if_slot_conflicts_with_call(slot_name)
raise_if_slot_ends_with_question_mark(slot_name)
raise_if_slot_registered(slot_name)
end
Expand All @@ -320,7 +322,13 @@ def raise_if_slot_registered(slot_name)
end

def raise_if_slot_ends_with_question_mark(slot_name)
raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.ends_with?("?")
raise SlotPredicateNameError.new(name, slot_name) if slot_name.to_s.end_with?("?")
end

def raise_if_slot_conflicts_with_call(slot_name)
if slot_name.start_with?("call_")
raise InvalidSlotNameError, "Slot cannot start with 'call_'. Please rename #{slot_name}"
end
end
end

Expand Down
14 changes: 13 additions & 1 deletion lib/view_component/test_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -163,10 +163,20 @@ def with_controller_class(klass)
# end
# ```
#
# To specify a request method, pass the method param:
#
# ```ruby
# with_request_url("/users/42", method: "POST") do
# render_inline(MyComponent.new)
# end
# ```
#
# @param path [String] The path to set for the current request.
# @param host [String] The host to set for the current request.
def with_request_url(full_path, host: nil)
# @param method [String] The request method to set for the current request.
def with_request_url(full_path, host: nil, method: nil)
old_request_host = vc_test_request.host
old_request_method = vc_test_request.request_method
old_request_path_info = vc_test_request.path_info
old_request_path_parameters = vc_test_request.path_parameters
old_request_query_parameters = vc_test_request.query_parameters
Expand All @@ -177,6 +187,7 @@ def with_request_url(full_path, host: nil)
vc_test_request.instance_variable_set(:@fullpath, full_path)
vc_test_request.instance_variable_set(:@original_fullpath, full_path)
vc_test_request.host = host if host
vc_test_request.request_method = method if method
vc_test_request.path_info = path
vc_test_request.path_parameters = Rails.application.routes.recognize_path_with_request(vc_test_request, path, {})
vc_test_request.set_header("action_dispatch.request.query_parameters",
Expand All @@ -185,6 +196,7 @@ def with_request_url(full_path, host: nil)
yield
ensure
vc_test_request.host = old_request_host
vc_test_request.request_method = old_request_method
vc_test_request.path_info = old_request_path_info
vc_test_request.path_parameters = old_request_path_parameters
vc_test_request.set_header("action_dispatch.request.query_parameters", old_request_query_parameters)
Expand Down
20 changes: 20 additions & 0 deletions lib/view_component/use_helpers.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# frozen_string_literal: true

module ViewComponent::UseHelpers
extend ActiveSupport::Concern

class_methods do
def use_helpers(*args)
args.each do |helper_method|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
def #{helper_method}(*args, &block)
raise HelpersCalledBeforeRenderError if view_context.nil?
__vc_original_view_context.#{helper_method}(*args, &block)
end
RUBY

ruby2_keywords(helper_method) if respond_to?(:ruby2_keywords, true)
end
end
end
end
6 changes: 1 addition & 5 deletions test/sandbox/app/components/current_page_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,6 @@

class CurrentPageComponent < ViewComponent::Base
def text
if current_page?("/slots")
"Inside /slots"
else
"Outside /slots"
end
"#{current_page?("/slots") ? "Inside" : "Outside"} /slots (#{request.method} #{request.path})"
end
end
3 changes: 3 additions & 0 deletions test/sandbox/app/components/use_helpers_component.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<div class="helper__message">
<%= message %>
</div>
Loading

0 comments on commit f35aa5c

Please sign in to comment.