diff --git a/lib/reactionview/template/handlers/erb.rb b/lib/reactionview/template/handlers/erb.rb index f932c1d..e5eb2d1 100644 --- a/lib/reactionview/template/handlers/erb.rb +++ b/lib/reactionview/template/handlers/erb.rb @@ -7,12 +7,31 @@ class ERB < ActionView::Template::Handlers::ERB autoload :Herb, "reactionview/template/handlers/herb/herb" def call(template, source) - if template.format == :html && ReActionView.config.intercept_erb + if intercept_template?(template) ::ReActionView::Template::Handlers::Herb.call(template, source) else super end end + + private + + def intercept_template?(template) + template.format == :html && ReActionView.config.intercept_erb && local_template?(template) + end + + def local_template?(template) + return true unless template.respond_to?(:identifier) && template.identifier + return false if vendored_template?(template) + + template.identifier.start_with?(Rails.root.to_s) + end + + def vendored_template?(template) + return false unless defined?(Bundler) + + template.identifier.start_with?(Bundler.bundle_path.to_s) + end end end end diff --git a/test/template/handlers/herb_test.rb b/test/template/handlers/herb_test.rb index b01a4b8..44cee8d 100644 --- a/test/template/handlers/herb_test.rb +++ b/test/template/handlers/herb_test.rb @@ -99,7 +99,7 @@ def @view_context.user_path(id) "/users/#{id}" end - template_obj = ActionView::Template.new( + template_object = ActionView::Template.new( template, "test_template", ReActionView::Template::Handlers::ERB, @@ -107,7 +107,7 @@ def @view_context.user_path(id) format: :html, locals: [] ) - compiled_source = template_obj.handler.call(template_obj, template) + compiled_source = template_object.handler.call(template_object, template) @view_context.instance_variable_set(:@user, { name: "John Doe", @@ -160,7 +160,7 @@ def @view_context.ui_badge(count, **_options) "#{count}".html_safe end - template_obj = ActionView::Template.new( + template_object = ActionView::Template.new( template, "test_template", ReActionView::Template::Handlers::ERB, @@ -168,7 +168,7 @@ def @view_context.ui_badge(count, **_options) format: :html, locals: [] ) - compiled_source = template_obj.handler.call(template_obj, template) + compiled_source = template_object.handler.call(template_object, template) @view_context.instance_variable_set(:@show_countries, true) result = @view_context.instance_eval(compiled_source).to_s @@ -365,4 +365,75 @@ def @view_context.ui_badge(count, **_options) assert_compiled_snapshot(template) end + + test "does not process templates that are not local" do + ReActionView.config.intercept_erb = true + + template = %(

I am invalid

) + template_object = ActionView::Template.new( + template, + "test_template", + ReActionView::Template::Handlers::ERB, + virtual_path: "test", + format: :html, + locals: [] + ) + + compiled_source = Rails.stub(:root, Pathname.new("/local/template")) do + template_object.handler.call(template_object, template) + end + + result = @view_context.instance_eval(compiled_source).to_s + + normalized_result = result.gsub(/>\s+<").gsub(/\s+/, " ").strip + assert_equal "

I am invalid

", normalized_result + end + + test "does not process templates from gems vendored inside the application" do + ReActionView.config.intercept_erb = true + + template = %(

I am invalid

) + template_object = ActionView::Template.new( + template, + "/app/vendor/bundle/ruby/3.4.0/gems/actionpack-8.1.2/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb", + ReActionView::Template::Handlers::ERB, + virtual_path: "rescues/routing_error", + format: :html, + locals: [] + ) + + compiled_source = Rails.stub(:root, Pathname.new("/app")) do + Bundler.stub(:bundle_path, Pathname.new("/app/vendor/bundle")) do + template_object.handler.call(template_object, template) + end + end + + result = @view_context.instance_eval(compiled_source).to_s + + normalized_result = result.gsub(/>\s+<").gsub(/\s+/, " ").strip + assert_equal "

I am invalid

", normalized_result + end + + test "processes application templates when gems are vendored inside the application" do + ReActionView.config.intercept_erb = true + ReActionView.config.debug_mode = true + + template = %(

Hello

) + template_object = ActionView::Template.new( + template, + "/app/app/views/users/show.html.erb", + ReActionView::Template::Handlers::ERB, + virtual_path: "users/show", + format: :html, + locals: [] + ) + + compiled_source = Rails.stub(:root, Pathname.new("/app")) do + Bundler.stub(:bundle_path, Pathname.new("/app/vendor/bundle")) do + template_object.handler.call(template_object, template) + end + end + + assert_includes compiled_source, %(data-herb-debug-file-full-path="/app/app/views/users/show.html.erb") + end end