Skip to content

Commit

Permalink
[1029] Preview the source of templates when exclusively used (#1047)
Browse files Browse the repository at this point in the history
  • Loading branch information
edwinthinks committed Dec 16, 2021
1 parent aedcd86 commit c7db148
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 23 deletions.
3 changes: 1 addition & 2 deletions app/controllers/view_components_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,11 @@ def previews
@example_name = File.basename(params[:path])
@render_args = @preview.render_args(@example_name, params: params.permit!)
layout = determine_layout(@render_args[:layout], prepend_views: false)[:layout]
template = @render_args[:template]
locals = @render_args[:locals]
opts = {}
opts[:layout] = layout if layout.present? || layout == false
opts[:locals] = locals if locals.present?
render template, opts # rubocop:disable GitHub/RailsControllerRenderLiteral
render "view_components/preview", opts # rubocop:disable GitHub/RailsControllerRenderLiteral
end
end

Expand Down
56 changes: 52 additions & 4 deletions app/helpers/preview_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,64 @@ module PreviewHelper
AVAILABLE_PRISM_LANGUAGES = ["ruby", "erb", "haml"]
FALLBACK_LANGUAGE = "ruby"

def prism_language_name(template:)
def preview_source
return if @render_args.nil?

render "preview_source" # rubocop:disable GitHub/RailsViewRenderPathsExist
end

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

if Rails.version.to_f >= 6.1 || template.source.present?
return {
source: template.source,
prism_language_name: prism_language_name_by_template(template: template)
}
else
# Fetch template source via finding it through preview paths
# to accomodate source view when exclusively using templates
# for previews for Rails < 6.1.
all_template_paths = ViewComponent::Base.preview_paths.map do |preview_path|
Dir.glob("#{preview_path}/**/*")
end.flatten

# Search for templates the contain `html`.
matching_templates = all_template_paths.find_all do |template|
template =~ /#{template_identifier}*.(html)/
end

# In-case of a conflict due to multiple template files with
# the same name
raise "found 0 matches for templates for #{template_identifier}." if matching_templates.empty?
raise "found multiple templates for #{template_identifier}." if matching_templates.size > 1

template_file_path = matching_templates.first
template_source = File.read(template_file_path)
prism_language_name = prism_language_name_by_template_path(template_file_path: template_file_path)

return {
source: template_source,
prism_language_name: prism_language_name
}
end
end

private

def prism_language_name_by_template(template:)
language = template.identifier.split(".").last

return FALLBACK_LANGUAGE unless AVAILABLE_PRISM_LANGUAGES.include? language

language
end

def preview_source
return if @render_args.nil?
def prism_language_name_by_template_path(template_file_path:)
language = template_file_path.gsub(".html", "").split(".").last

render "preview_source" # rubocop:disable GitHub/RailsViewRenderPathsExist
return FALLBACK_LANGUAGE unless AVAILABLE_PRISM_LANGUAGES.include? language

language
end
end
6 changes: 3 additions & 3 deletions app/views/view_components/_preview_source.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
<%= h @preview.preview_source(@example_name) %>
</code>
<% else %>
<% template = @view_renderer.lookup_context.find_template(@render_args[:template]) %>
<code class="language-<%= prism_language_name(template: template) %>">
<%= h template.source %>
<% template_data = find_template_data(lookup_context: @view_renderer.lookup_context, template_identifier: @render_args[:template]) %>
<code class="language-<%= template_data[:prism_language_name] %>">
<%= h template_data[:source] %>
</code>
<% end %>
</pre>
Expand Down
10 changes: 7 additions & 3 deletions app/views/view_components/preview.html.erb
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
<% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
<%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
<% if @render_args[:component] %>
<% if ViewComponent::Base.render_monkey_patch_enabled || Rails.version.to_f >= 6.1 %>
<%= render(@render_args[:component], @render_args[:args], &@render_args[:block]) %>
<% else %>
<%= render_component(@render_args[:component], &@render_args[:block]) %>
<% end %>
<% else %>
<%= render_component(@render_args[:component], &@render_args[:block]) %>
<%= render template: @render_args[:template], locals: @render_args[:locals] || {} %>
<% end %>
<% if ViewComponent::Base.show_previews_source %>
Expand Down
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ title: Changelog

## main

* Display preview source on previews that exclusively use templates.

*Edwin Mak*

* Add a test to ensure trailing newlines are stripped when rendering with `#render_in`.

*Simon Fish*
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@ ViewComponent is built by over a hundred members of the community, including:
<img src="https://avatars.githubusercontent.com/vinistock?s=64" alt="vinistock" width="32" />
<img src="https://avatars.githubusercontent.com/wdrexler?s=64" alt="wdrexler" width="32" />
<img src="https://avatars.githubusercontent.com/websebdev?s=64" alt="websebdev" width="32" />
<img src="https://avatars.githubusercontent.com/edwinthinks?s=64" alt="edwinthinks" width="32" />
<img src="https://avatars.githubusercontent.com/xkraty?s=64" alt="xkraty" width="32" />
<img src="https://avatars.githubusercontent.com/xronos-i-am?s=64" alt="xronos-i-am" width="32" />

Expand Down
8 changes: 7 additions & 1 deletion test/view_component/integration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -320,18 +320,24 @@ def test_preview_from_multiple_preview_paths
assert_select("div", "hello,world!")
end

def test_renders_preview_source
def test_renders_preview_source_without_template
get "/rails/view_components/preview_component/default"

assert_select ".view-component-source-example h2", "Source:"
assert_select ".view-component-source-example pre.source code"
assert_select ".language-ruby"
refute_match "&lt;%=", response.body
refute_match "%&gt", response.body
end

def test_renders_preview_source_with_template_from_layout
get "/rails/view_components/preview_source_from_layout_component/default_with_template"

assert_select ".view-component-source-example h2", "Source:"
assert_select ".view-component-source-example pre.source code"
assert_select ".language-erb"
assert_match "&lt;%=", response.body
assert_match "%&gt", response.body
end

def test_renders_collections
Expand Down
156 changes: 146 additions & 10 deletions test/view_component/preview_helper_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,155 @@
include PreviewHelper

class PreviewHelperTest < ActiveSupport::TestCase
def test_returns_the_language_from_the_file_extention
template = Minitest::Mock.new
template.expect :identifier, "template.html.erb"
def test_returns_template_data_with_no_template
template_identifier = "preview/no_template"

assert_equal(PreviewHelper.prism_language_name(template: template), "erb")
template.verify
expected_template_source = "expected_template"
mock_template = Minitest::Mock.new
mock_template.expect(:source, expected_template_source)
mock_template.expect(:source, expected_template_source)
mock_template.expect(:identifier, "unknown")

lookup_context = Minitest::Mock.new
lookup_context.expect(:find_template, mock_template, [template_identifier])

template_data = PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)

assert_equal(template_data[:source], "expected_template")
assert_equal(template_data[:prism_language_name], "ruby")
end

def test_returns_template_data_with_template_of_different_languages
template_identifier = "preview/template"

expected_template_source = "expected_template"

PreviewHelper::AVAILABLE_PRISM_LANGUAGES.each do |language|
mock_template = Minitest::Mock.new
mock_template.expect(:source, expected_template_source)
mock_template.expect(:source, expected_template_source)
mock_template.expect(:identifier, "html.#{language}")

lookup_context = Minitest::Mock.new
lookup_context.expect(:find_template, mock_template, [template_identifier])

template_data = PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)

assert_equal(template_data[:source], "expected_template")
assert_equal(template_data[:prism_language_name], language)
end
end

def test_returns_fallback_language_if_file_extention_unknown
template = Minitest::Mock.new
template.expect :identifier, "template.html.slim"
if Rails.version.to_f < 6.1
def test_returns_template_data_without_dedicated_template
template_identifier = "preview/template"
expected_source = "<%= PreviewTest %>"

PreviewHelper::AVAILABLE_PRISM_LANGUAGES.each do |language|
mock_template = Minitest::Mock.new
mock_template.expect(:source, expected_source)
mock_template.expect(:source, expected_source)
mock_template.expect(:identifier, "html.#{language}")

lookup_context = Minitest::Mock.new
expected_template_path = "some/path/#{template_identifier}.html.haml"
lookup_context.expect(:find_template, mock_template, [template_identifier])

mock = Minitest::Mock.new
mock.expect :map, [expected_template_path]
ViewComponent::Base.stub(:preview_paths, mock) do
template_data = PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)

assert_equal(template_data[:source], expected_source)
assert_equal(template_data[:prism_language_name], language)
end
end
end

def test_returns_template_data_with_dedicated_template
template_identifier = "preview/template"
expected_source = "<%= PreviewTest %>"

PreviewHelper::AVAILABLE_PRISM_LANGUAGES.each do |language|
mock_template = Minitest::Mock.new
mock_template.expect(:source, "")
mock_template.expect(:source, "")

lookup_context = Minitest::Mock.new
expected_template_path = "some/path/#{template_identifier}.html.#{language}"
lookup_context.expect(:find_template, mock_template, [template_identifier])

mock = Minitest::Mock.new
mock.expect :map, [expected_template_path]
ViewComponent::Base.stub(:preview_paths, mock) do
File.stub(:read, expected_source, [expected_template_path]) do
template_data = PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)

assert_equal(template_data[:source], expected_source)
assert_equal(template_data[:prism_language_name], language)
end
end
end
end

def test_raises_with_no_matching_template
template_identifier = "preview/template"

mock_template = Minitest::Mock.new
mock_template.expect(:source, "")
mock_template.expect(:source, "")

lookup_context = Minitest::Mock.new
lookup_context.expect(:find_template, mock_template, [template_identifier])

mock = Minitest::Mock.new
mock.expect :map, []
ViewComponent::Base.stub :preview_paths, mock do
exception = assert_raises RuntimeError do
PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)
end

assert_equal("found 0 matches for templates for #{template_identifier}.", exception.message)
end
end

def test_raises_with_conflict_in_template_resolution
template_identifier = "preview/template"

mock_template = Minitest::Mock.new
mock_template.expect(:source, "")
mock_template.expect(:source, "")

lookup_context = Minitest::Mock.new
lookup_context.expect(:find_template, mock_template, [template_identifier])

mock = Minitest::Mock.new
mock.expect :map, [template_identifier + ".html.haml", template_identifier + ".html.erb"]
ViewComponent::Base.stub :preview_paths, mock do
exception = assert_raises RuntimeError do
PreviewHelper.find_template_data(
lookup_context: lookup_context,
template_identifier: template_identifier
)
end

assert_equal(PreviewHelper.prism_language_name(template: template), "ruby")
template.verify
assert_equal("found multiple templates for #{template_identifier}.", exception.message)
end
end
end
end

0 comments on commit c7db148

Please sign in to comment.