From 00c4d92965eb24c3307ca9462a3c6e7376c0a312 Mon Sep 17 00:00:00 2001
From: Marcell Cruz <0000marcell@gmail.com>
Date: Sat, 16 May 2026 15:36:54 -0300
Subject: [PATCH 01/10] Add configurable project_path option
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Add config.project_path setting with Rails.root.to_s fallback
- Use configured path in debug visitor, meta tags, and template checking
- Useful for Docker mounts and monorepo structures
💘 Generated with Crush
Assisted-by: Crush:claude-sonnet-4.5
---
docs/docs/installation.md | 28 ++++++++++++++++++++++
lib/reactionview/config.rb | 2 ++
lib/reactionview/template/handlers/herb.rb | 12 ++++++----
3 files changed, 38 insertions(+), 4 deletions(-)
diff --git a/docs/docs/installation.md b/docs/docs/installation.md
index 50c58fd..181f16b 100644
--- a/docs/docs/installation.md
+++ b/docs/docs/installation.md
@@ -67,6 +67,34 @@ end
This gives you all the benefits of Herb's validation, security features, and debugging tools for your existing templates.
+### Advanced Configuration
+
+#### Custom Project Path
+
+If your project path differs from `Rails.root` (e.g., Docker mounts, monorepos), you can configure a custom path:
+
+:::code-group
+```ruby [config/initializers/reactionview.rb]
+ReActionView.configure do |config|
+ # Custom project path for editor integration and dev tools
+ config.project_path = "/custom/path/to/project"
+
+ # Docker example: map container path to host path
+ # config.project_path = "/Users/you/myapp"
+
+ # Monorepo example: parent directory
+ # config.project_path = File.expand_path("../../", Rails.root)
+end
+```
+:::
+
+This affects:
+- Editor integration when clicking source locations in the browser
+- The `herb-project-path` meta tag for dev tools
+- Template path resolution in debug mode
+
+**Default**: `Rails.root.to_s`
+
## Verify Installation
Create a test template to verify ReActionView is working:
diff --git a/lib/reactionview/config.rb b/lib/reactionview/config.rb
index 0c8e9b5..0c6f095 100644
--- a/lib/reactionview/config.rb
+++ b/lib/reactionview/config.rb
@@ -5,12 +5,14 @@ class Config
attr_accessor :intercept_erb
attr_accessor :debug_mode
attr_accessor :transform_visitors
+ attr_accessor :project_path
attr_writer :validation_mode
def initialize
@intercept_erb = false
@debug_mode = nil
@transform_visitors = []
+ @project_path = nil
end
def validation_mode
diff --git a/lib/reactionview/template/handlers/herb.rb b/lib/reactionview/template/handlers/herb.rb
index b34f4c2..47cef87 100644
--- a/lib/reactionview/template/handlers/herb.rb
+++ b/lib/reactionview/template/handlers/herb.rb
@@ -14,13 +14,13 @@ def call(template, source)
if ::ReActionView.config.debug_mode_enabled? && local_template?(template)
visitors << ::Herb::Engine::DebugVisitor.new(
file_path: template.identifier,
- project_path: Rails.root.to_s
+ project_path: project_path
)
end
config = {
filename: template.identifier,
- project_path: Rails.root.to_s,
+ project_path: project_path,
validation_mode: ReActionView.config.validation_mode,
content_for_head: reactionview_dev_tools_markup(template),
visitors: visitors + ReActionView.config.transform_visitors,
@@ -40,7 +40,7 @@ def layout_template?(template)
def local_template?(template)
return true unless template.respond_to?(:identifier) && template.identifier
- template.identifier.start_with?(Rails.root.to_s)
+ template.identifier.start_with?(project_path)
end
def active_support_editor
@@ -58,6 +58,10 @@ def editor_meta_tag
%()
end
+ def project_path
+ ::ReActionView.config.project_path || Rails.root.to_s
+ end
+
def reactionview_dev_tools_markup(template)
return nil unless layout_template?(template)
return nil unless local_template?(template)
@@ -67,7 +71,7 @@ def reactionview_dev_tools_markup(template)
if ::ReActionView.config.debug_mode_enabled?
markup << <<~HTML
-
+
#{editor_meta_tag}
#{ActionController::Base.new.view_context.javascript_include_tag "reactionview-dev-tools.umd.js", defer: true}
From 01f305a9fa6fc5277d9cbc76c41a071c206d8723 Mon Sep 17 00:00:00 2001
From: Marcell Cruz <0000marcell@gmail.com>
Date: Sat, 23 May 2026 15:29:12 -0300
Subject: [PATCH 02/10] Fix project_fullpath
---
README.md | 3 +++
docs/docs/installation.md | 17 +++++++-------
.../reactionview/install_generator.rb | 3 +++
lib/reactionview/config.rb | 10 +++++++--
lib/reactionview/template/handlers/herb.rb | 22 ++++++++++++++-----
5 files changed, 40 insertions(+), 15 deletions(-)
diff --git a/README.md b/README.md
index 778f191..703e1d3 100644
--- a/README.md
+++ b/README.md
@@ -46,6 +46,9 @@ ReActionView.configure do |config|
# Enable debug mode
config.debug_mode = Rails.env.development?
+ # Custom path for editor "open in editor" links (optional, defaults to Rails.root)
+ # config.project_fullpath = ENV.fetch('PROJECT_FULLPATH', Rails.root.to_s)
+
# Validation mode (:raise, :overlay, or :none) — defaults to :raise in test, :overlay otherwise
# config.validation_mode = :overlay
end
diff --git a/docs/docs/installation.md b/docs/docs/installation.md
index 181f16b..caf5d27 100644
--- a/docs/docs/installation.md
+++ b/docs/docs/installation.md
@@ -69,32 +69,33 @@ This gives you all the benefits of Herb's validation, security features, and deb
### Advanced Configuration
-#### Custom Project Path
+#### Custom Project Path for Editor Links
-If your project path differs from `Rails.root` (e.g., Docker mounts, monorepos), you can configure a custom path:
+If your project path differs from `Rails.root` (e.g., Docker mounts, monorepos), you can configure a custom path for editor "open in editor" links:
:::code-group
```ruby [config/initializers/reactionview.rb]
ReActionView.configure do |config|
- # Custom project path for editor integration and dev tools
- config.project_path = "/custom/path/to/project"
+ # Custom project path for editor integration (optional)
+ config.project_fullpath = "/custom/path/to/project"
# Docker example: map container path to host path
- # config.project_path = "/Users/you/myapp"
+ # config.project_fullpath = "/Users/you/myapp"
# Monorepo example: parent directory
- # config.project_path = File.expand_path("../../", Rails.root)
+ # config.project_fullpath = File.expand_path("../../", Rails.root)
end
```
:::
This affects:
- Editor integration when clicking source locations in the browser
-- The `herb-project-path` meta tag for dev tools
-- Template path resolution in debug mode
+- The `herb-rails-root` meta tag for dev tools
**Default**: `Rails.root.to_s`
+**Note**: `project_path` (used for local template detection) is automatically set to `Rails.root` and cannot be configured.
+
## Verify Installation
Create a test template to verify ReActionView is working:
diff --git a/lib/generators/reactionview/install_generator.rb b/lib/generators/reactionview/install_generator.rb
index ccc5b1b..ac4f5bd 100644
--- a/lib/generators/reactionview/install_generator.rb
+++ b/lib/generators/reactionview/install_generator.rb
@@ -23,6 +23,9 @@ def create_initializer
# Enable debug mode in development (adds debug attributes to HTML)
config.debug_mode = Rails.env.development?
+ # Path used for editor "open in editor" links (optional, defaults to Rails.root)
+ # config.project_fullpath = ENV.fetch('PROJECT_FULLPATH', Rails.root.to_s)
+
# Validation mode (:raise, :overlay, or :none) — defaults to :raise in test, :overlay otherwise
# config.validation_mode = :overlay
diff --git a/lib/reactionview/config.rb b/lib/reactionview/config.rb
index 0c6f095..905be26 100644
--- a/lib/reactionview/config.rb
+++ b/lib/reactionview/config.rb
@@ -5,14 +5,20 @@ class Config
attr_accessor :intercept_erb
attr_accessor :debug_mode
attr_accessor :transform_visitors
- attr_accessor :project_path
+ attr_accessor :project_fullpath
attr_writer :validation_mode
def initialize
@intercept_erb = false
@debug_mode = nil
@transform_visitors = []
- @project_path = nil
+ @project_fullpath = nil
+ end
+
+ def project_path
+ return nil unless defined?(Rails)
+
+ Rails.root.to_s
end
def validation_mode
diff --git a/lib/reactionview/template/handlers/herb.rb b/lib/reactionview/template/handlers/herb.rb
index 47cef87..6b976bc 100644
--- a/lib/reactionview/template/handlers/herb.rb
+++ b/lib/reactionview/template/handlers/herb.rb
@@ -13,14 +13,14 @@ def call(template, source)
if ::ReActionView.config.debug_mode_enabled? && local_template?(template)
visitors << ::Herb::Engine::DebugVisitor.new(
- file_path: template.identifier,
- project_path: project_path
+ file_path: translate_path_for_editor(template.identifier),
+ project_path: project_fullpath
)
end
config = {
filename: template.identifier,
- project_path: project_path,
+ project_path: project_fullpath,
validation_mode: ReActionView.config.validation_mode,
content_for_head: reactionview_dev_tools_markup(template),
visitors: visitors + ReActionView.config.transform_visitors,
@@ -59,7 +59,19 @@ def editor_meta_tag
end
def project_path
- ::ReActionView.config.project_path || Rails.root.to_s
+ ::ReActionView.config.project_path
+ end
+
+ def project_fullpath
+ ::ReActionView.config.project_fullpath || project_path
+ end
+
+ def translate_path_for_editor(template_path)
+ return template_path unless project_fullpath && project_path
+ return template_path if project_fullpath == project_path
+
+ # Replace Rails.root (container path) with project_fullpath (host path)
+ template_path.to_s.sub(project_path, project_fullpath)
end
def reactionview_dev_tools_markup(template)
@@ -71,7 +83,7 @@ def reactionview_dev_tools_markup(template)
if ::ReActionView.config.debug_mode_enabled?
markup << <<~HTML
-
+
#{editor_meta_tag}
#{ActionController::Base.new.view_context.javascript_include_tag "reactionview-dev-tools.umd.js", defer: true}
From 54478b0176fc389e39433bf488cff50b00616c6d Mon Sep 17 00:00:00 2001
From: Marcell Cruz <0000marcell@gmail.com>
Date: Sat, 23 May 2026 15:30:16 -0300
Subject: [PATCH 03/10] update tests
---
test/reactionview_test.rb | 2 +-
test/snapshot_utils.rb | 28 ++++++++++++++--------------
2 files changed, 15 insertions(+), 15 deletions(-)
diff --git a/test/reactionview_test.rb b/test/reactionview_test.rb
index 384b49e..0f27abf 100644
--- a/test/reactionview_test.rb
+++ b/test/reactionview_test.rb
@@ -4,6 +4,6 @@
class TestReActionView < Minitest::Spec
test "has version number" do
- refute_nil ::ReActionView::VERSION
+ assert_not_nil ::ReActionView::VERSION
end
end
diff --git a/test/snapshot_utils.rb b/test/snapshot_utils.rb
index 578d430..0d0c904 100644
--- a/test/snapshot_utils.rb
+++ b/test/snapshot_utils.rb
@@ -107,17 +107,17 @@ def snapshot_changed?(content, source, options = {})
previous_content = snapshot_file(source, options).read
if previous_content == content
- puts "\n\nSnapshot for '#{self.class.name} #{name}' didn't change: \n#{snapshot_file(source, options)}\n"
+ Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' didn't change: \n#{snapshot_file(source, options)}\n" }
false
else
- puts "\n\nSnapshot for '#{self.class.name} #{name}' changed:\n"
+ Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' changed:\n" }
- puts Difftastic::Differ.new(color: :always).diff_strings(previous_content, content)
- puts "==============="
+ Rails.logger.debug Difftastic::Differ.new(color: :always).diff_strings(previous_content, content)
+ Rails.logger.debug "==============="
true
end
else
- puts "\n\nSnapshot for '#{self.class.name} #{name}' doesn't exist at: \n#{snapshot_file(source, options)}\n"
+ Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' doesn't exist at: \n#{snapshot_file(source, options)}\n" }
true
end
end
@@ -125,21 +125,21 @@ def snapshot_changed?(content, source, options = {})
def save_failures_to_snapshot(content, source, options = {})
return unless snapshot_changed?(content, source, options)
- puts "\n==== [ Input for '#{self.class.name} #{name}' ] ====="
- puts source
- puts "\n\n"
+ Rails.logger.debug { "\n==== [ Input for '#{self.class.name} #{name}' ] =====" }
+ Rails.logger.debug source
+ Rails.logger.debug "\n\n"
if !ENV["FORCE_UPDATE_SNAPSHOTS"].nil? ||
ask?("Do you want to update (or create) the snapshot for '#{self.class.name} #{name}'?")
- puts "\nUpdating Snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}\n"
+ Rails.logger.debug { "\nUpdating Snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}\n" }
FileUtils.mkdir_p(snapshot_file(source, options).dirname)
snapshot_file(source, options).write(content)
- puts "\nSnapshot for '#{self.class.name} #{name}' written: \n#{snapshot_file(source, options)}\n"
+ Rails.logger.debug { "\nSnapshot for '#{self.class.name} #{name}' written: \n#{snapshot_file(source, options)}\n" }
else
- puts "\nNot updating snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}.\n"
+ Rails.logger.debug { "\nNot updating snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}.\n" }
end
end
@@ -157,7 +157,7 @@ def assert_snapshot_matches(actual, source, options = {}, mode: nil)
raise unless snapshot_file(source, snapshot_opts).exist?
if snapshot_file(source, snapshot_opts)&.read != actual
- puts
+ Rails.logger.debug
divider = "=" * `tput cols`.strip.to_i
@@ -191,7 +191,7 @@ def snapshot_file(source, options = {}) # rubocop:disable Metrics/MethodLength
opts_for_hash = options.except(:mode)
- if opts_for_hash && !opts_for_hash.empty?
+ if opts_for_hash.present?
options_hash = Digest::MD5.hexdigest(opts_for_hash.inspect)
expected_snapshot_filename = "#{test_name}#{mode_suffix}_#{content_hash}-#{options_hash}.txt"
else
@@ -203,7 +203,7 @@ def snapshot_file(source, options = {}) # rubocop:disable Metrics/MethodLength
return expected_snapshot_path if expected_snapshot_path.exist?
- matching_md5_files = if opts_for_hash && !opts_for_hash.empty?
+ matching_md5_files = if opts_for_hash.present?
Dir[base_path / "*#{mode_suffix}_#{content_hash}-#{options_hash}.txt"]
else
Dir[base_path / "*#{mode_suffix}_#{content_hash}.txt"]
From eff88392d64df4204e3387c28d1531be03aa5752 Mon Sep 17 00:00:00 2001
From: Marcell Cruz <0000marcell@gmail.com>
Date: Sat, 23 May 2026 15:36:20 -0300
Subject: [PATCH 04/10] revert snapshot tests rails specific configurations
---
test/snapshot_utils.rb | 28 ++++++++++++++--------------
1 file changed, 14 insertions(+), 14 deletions(-)
diff --git a/test/snapshot_utils.rb b/test/snapshot_utils.rb
index 0d0c904..578d430 100644
--- a/test/snapshot_utils.rb
+++ b/test/snapshot_utils.rb
@@ -107,17 +107,17 @@ def snapshot_changed?(content, source, options = {})
previous_content = snapshot_file(source, options).read
if previous_content == content
- Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' didn't change: \n#{snapshot_file(source, options)}\n" }
+ puts "\n\nSnapshot for '#{self.class.name} #{name}' didn't change: \n#{snapshot_file(source, options)}\n"
false
else
- Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' changed:\n" }
+ puts "\n\nSnapshot for '#{self.class.name} #{name}' changed:\n"
- Rails.logger.debug Difftastic::Differ.new(color: :always).diff_strings(previous_content, content)
- Rails.logger.debug "==============="
+ puts Difftastic::Differ.new(color: :always).diff_strings(previous_content, content)
+ puts "==============="
true
end
else
- Rails.logger.debug { "\n\nSnapshot for '#{self.class.name} #{name}' doesn't exist at: \n#{snapshot_file(source, options)}\n" }
+ puts "\n\nSnapshot for '#{self.class.name} #{name}' doesn't exist at: \n#{snapshot_file(source, options)}\n"
true
end
end
@@ -125,21 +125,21 @@ def snapshot_changed?(content, source, options = {})
def save_failures_to_snapshot(content, source, options = {})
return unless snapshot_changed?(content, source, options)
- Rails.logger.debug { "\n==== [ Input for '#{self.class.name} #{name}' ] =====" }
- Rails.logger.debug source
- Rails.logger.debug "\n\n"
+ puts "\n==== [ Input for '#{self.class.name} #{name}' ] ====="
+ puts source
+ puts "\n\n"
if !ENV["FORCE_UPDATE_SNAPSHOTS"].nil? ||
ask?("Do you want to update (or create) the snapshot for '#{self.class.name} #{name}'?")
- Rails.logger.debug { "\nUpdating Snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}\n" }
+ puts "\nUpdating Snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}\n"
FileUtils.mkdir_p(snapshot_file(source, options).dirname)
snapshot_file(source, options).write(content)
- Rails.logger.debug { "\nSnapshot for '#{self.class.name} #{name}' written: \n#{snapshot_file(source, options)}\n" }
+ puts "\nSnapshot for '#{self.class.name} #{name}' written: \n#{snapshot_file(source, options)}\n"
else
- Rails.logger.debug { "\nNot updating snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}.\n" }
+ puts "\nNot updating snapshot for '#{self.class.name} #{name}' at: \n#{snapshot_file(source, options)}.\n"
end
end
@@ -157,7 +157,7 @@ def assert_snapshot_matches(actual, source, options = {}, mode: nil)
raise unless snapshot_file(source, snapshot_opts).exist?
if snapshot_file(source, snapshot_opts)&.read != actual
- Rails.logger.debug
+ puts
divider = "=" * `tput cols`.strip.to_i
@@ -191,7 +191,7 @@ def snapshot_file(source, options = {}) # rubocop:disable Metrics/MethodLength
opts_for_hash = options.except(:mode)
- if opts_for_hash.present?
+ if opts_for_hash && !opts_for_hash.empty?
options_hash = Digest::MD5.hexdigest(opts_for_hash.inspect)
expected_snapshot_filename = "#{test_name}#{mode_suffix}_#{content_hash}-#{options_hash}.txt"
else
@@ -203,7 +203,7 @@ def snapshot_file(source, options = {}) # rubocop:disable Metrics/MethodLength
return expected_snapshot_path if expected_snapshot_path.exist?
- matching_md5_files = if opts_for_hash.present?
+ matching_md5_files = if opts_for_hash && !opts_for_hash.empty?
Dir[base_path / "*#{mode_suffix}_#{content_hash}-#{options_hash}.txt"]
else
Dir[base_path / "*#{mode_suffix}_#{content_hash}.txt"]
From ac14984d6972876b366354b6c428e64041729ec3 Mon Sep 17 00:00:00 2001
From: Marcell Cruz <0000marcell@gmail.com>
Date: Mon, 1 Jun 2026 18:00:59 -0300
Subject: [PATCH 05/10] revert test change
---
test/reactionview_test.rb | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/test/reactionview_test.rb b/test/reactionview_test.rb
index 0f27abf..384b49e 100644
--- a/test/reactionview_test.rb
+++ b/test/reactionview_test.rb
@@ -4,6 +4,6 @@
class TestReActionView < Minitest::Spec
test "has version number" do
- assert_not_nil ::ReActionView::VERSION
+ refute_nil ::ReActionView::VERSION
end
end
From f7cc93a70b04fbfa6822838bb40effe514ff5ea3 Mon Sep 17 00:00:00 2001
From: Marco Roth
Date: Sat, 8 Aug 2026 02:01:28 +0200
Subject: [PATCH 06/10] Rename back to `herb-project-path`
---
docs/docs/installation.md | 2 +-
javascript/packages/dev-tools/src/index.ts | 6 +-----
lib/reactionview/template/handlers/herb.rb | 2 +-
3 files changed, 3 insertions(+), 7 deletions(-)
diff --git a/docs/docs/installation.md b/docs/docs/installation.md
index caf5d27..925bfdd 100644
--- a/docs/docs/installation.md
+++ b/docs/docs/installation.md
@@ -90,7 +90,7 @@ end
This affects:
- Editor integration when clicking source locations in the browser
-- The `herb-rails-root` meta tag for dev tools
+- The `herb-project-path` meta tag for dev tools
**Default**: `Rails.root.to_s`
diff --git a/javascript/packages/dev-tools/src/index.ts b/javascript/packages/dev-tools/src/index.ts
index 7adf7b7..f415d73 100644
--- a/javascript/packages/dev-tools/src/index.ts
+++ b/javascript/packages/dev-tools/src/index.ts
@@ -86,11 +86,7 @@ if (typeof window !== "undefined" && typeof document !== "undefined") {
isInitializing = true
try {
- let projectPath: string | undefined
- const railsRoot = document.querySelector(`meta[name="herb-rails-root"]`)?.getAttribute("content")
- if (railsRoot) {
- projectPath = railsRoot
- }
+ const projectPath = document.querySelector(`meta[name="herb-project-path"]`)?.getAttribute("content") ?? undefined
initReActionViewDevTools({
projectPath,
diff --git a/lib/reactionview/template/handlers/herb.rb b/lib/reactionview/template/handlers/herb.rb
index 6b976bc..645e492 100644
--- a/lib/reactionview/template/handlers/herb.rb
+++ b/lib/reactionview/template/handlers/herb.rb
@@ -83,7 +83,7 @@ def reactionview_dev_tools_markup(template)
if ::ReActionView.config.debug_mode_enabled?
markup << <<~HTML
-
+
#{editor_meta_tag}
#{ActionController::Base.new.view_context.javascript_include_tag "reactionview-dev-tools.umd.js", defer: true}
From f92e3303f241e02a44e10fc669513e6cd64399df Mon Sep 17 00:00:00 2001
From: Marco Roth
Date: Sat, 8 Aug 2026 02:27:10 +0200
Subject: [PATCH 07/10] Rename `project_fullpath` to `project_path`
---
Gemfile | 1 +
Gemfile.lock | 2 +
README.md | 2 +-
docs/docs/installation.md | 12 +-
gemfiles/rails_7_0.gemfile | 3 +-
gemfiles/rails_7_0.gemfile.lock | 2 +
gemfiles/rails_7_1.gemfile | 3 +-
gemfiles/rails_7_1.gemfile.lock | 2 +
gemfiles/rails_7_2.gemfile | 3 +-
gemfiles/rails_7_2.gemfile.lock | 2 +
gemfiles/rails_8_0.gemfile | 3 +-
gemfiles/rails_8_0.gemfile.lock | 2 +
gemfiles/rails_8_1.gemfile | 3 +-
gemfiles/rails_8_1.gemfile.lock | 2 +
gemfiles/rails_8_2.gemfile | 3 +-
gemfiles/rails_8_2.gemfile.lock | 2 +
.../reactionview/install_generator.rb | 2 +-
lib/reactionview/config.rb | 8 +-
lib/reactionview/template/handlers/herb.rb | 25 ++--
test/reactionview/config_test.rb | 27 ++++
test/snapshot_utils.rb | 8 +-
...piled_23f372c5835bd8e5809427a8bfe433ca.txt | 2 +
...piled_15299f7b9059f08ba4360b398f86c031.txt | 2 +
...uated_4d3a28975f5f90eefab8a7f79760fa4e.txt | 1 +
test/template/handlers/project_path_test.rb | 118 ++++++++++++++++++
test/test_helper.rb | 1 +
26 files changed, 202 insertions(+), 39 deletions(-)
create mode 100644 test/snapshots/re_action_view/project_path_test/test_0006_compiled_output_without_a_configured_project_path_compiled_23f372c5835bd8e5809427a8bfe433ca.txt
create mode 100644 test/snapshots/re_action_view/project_path_test/test_0007_compiled_output_with_a_configured_project_path_compiled_15299f7b9059f08ba4360b398f86c031.txt
create mode 100644 test/snapshots/re_action_view/project_path_test/test_0008_evaluated_output_with_a_configured_project_path_evaluated_4d3a28975f5f90eefab8a7f79760fa4e.txt
create mode 100644 test/template/handlers/project_path_test.rb
diff --git a/Gemfile b/Gemfile
index c0bea54..c9fa00f 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,6 +9,7 @@ gem "appraisal"
gem "actionview", "~> 8.1"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 8.1"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/Gemfile.lock b/Gemfile.lock
index cc04bb4..18cac3d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -93,6 +93,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
@@ -203,6 +204,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 8.1)
rake (~> 13.0)
reactionview!
diff --git a/README.md b/README.md
index 703e1d3..2c80b10 100644
--- a/README.md
+++ b/README.md
@@ -47,7 +47,7 @@ ReActionView.configure do |config|
config.debug_mode = Rails.env.development?
# Custom path for editor "open in editor" links (optional, defaults to Rails.root)
- # config.project_fullpath = ENV.fetch('PROJECT_FULLPATH', Rails.root.to_s)
+ # config.project_path = ENV.fetch('PROJECT_PATH', Rails.root.to_s)
# Validation mode (:raise, :overlay, or :none) — defaults to :raise in test, :overlay otherwise
# config.validation_mode = :overlay
diff --git a/docs/docs/installation.md b/docs/docs/installation.md
index 925bfdd..e70b407 100644
--- a/docs/docs/installation.md
+++ b/docs/docs/installation.md
@@ -77,13 +77,13 @@ If your project path differs from `Rails.root` (e.g., Docker mounts, monorepos),
```ruby [config/initializers/reactionview.rb]
ReActionView.configure do |config|
# Custom project path for editor integration (optional)
- config.project_fullpath = "/custom/path/to/project"
-
+ config.project_path = "/custom/path/to/project"
+
# Docker example: map container path to host path
- # config.project_fullpath = "/Users/you/myapp"
-
+ # config.project_path = "/Users/you/myapp"
+
# Monorepo example: parent directory
- # config.project_fullpath = File.expand_path("../../", Rails.root)
+ # config.project_path = File.expand_path("../../", Rails.root)
end
```
:::
@@ -94,7 +94,7 @@ This affects:
**Default**: `Rails.root.to_s`
-**Note**: `project_path` (used for local template detection) is automatically set to `Rails.root` and cannot be configured.
+**Note**: local template detection always uses `Rails.root` and is unaffected by this setting.
## Verify Installation
diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile
index 16b7619..40e52e6 100644
--- a/gemfiles/rails_7_0.gemfile
+++ b/gemfiles/rails_7_0.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", "~> 7.0.0"
gem "appraisal"
+gem "actionview", "~> 7.0.0"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 7.0.0"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_7_0.gemfile.lock b/gemfiles/rails_7_0.gemfile.lock
index 22eeca0..f2dcfdf 100644
--- a/gemfiles/rails_7_0.gemfile.lock
+++ b/gemfiles/rails_7_0.gemfile.lock
@@ -85,6 +85,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
mutex_m (0.3.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
@@ -175,6 +176,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 7.0.0)
rake (~> 13.0)
reactionview!
diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile
index 98f051b..274dbc8 100644
--- a/gemfiles/rails_7_1.gemfile
+++ b/gemfiles/rails_7_1.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", "~> 7.1.0"
gem "appraisal"
+gem "actionview", "~> 7.1.0"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 7.1.0"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_7_1.gemfile.lock b/gemfiles/rails_7_1.gemfile.lock
index 21da859..5992830 100644
--- a/gemfiles/rails_7_1.gemfile.lock
+++ b/gemfiles/rails_7_1.gemfile.lock
@@ -97,6 +97,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
mutex_m (0.3.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
@@ -207,6 +208,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 7.1.0)
rake (~> 13.0)
reactionview!
diff --git a/gemfiles/rails_7_2.gemfile b/gemfiles/rails_7_2.gemfile
index c54166e..b0f8a0f 100644
--- a/gemfiles/rails_7_2.gemfile
+++ b/gemfiles/rails_7_2.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", "~> 7.2.0"
gem "appraisal"
+gem "actionview", "~> 7.2.0"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 7.2.0"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_7_2.gemfile.lock b/gemfiles/rails_7_2.gemfile.lock
index 4d94390..209cb29 100644
--- a/gemfiles/rails_7_2.gemfile.lock
+++ b/gemfiles/rails_7_2.gemfile.lock
@@ -97,6 +97,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
@@ -207,6 +208,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 7.2.0)
rake (~> 13.0)
reactionview!
diff --git a/gemfiles/rails_8_0.gemfile b/gemfiles/rails_8_0.gemfile
index a2a9980..f2e4e76 100644
--- a/gemfiles/rails_8_0.gemfile
+++ b/gemfiles/rails_8_0.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", "~> 8.0.0"
gem "appraisal"
+gem "actionview", "~> 8.0.0"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 8.0.0"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_8_0.gemfile.lock b/gemfiles/rails_8_0.gemfile.lock
index ad37d48..b3cb8e8 100644
--- a/gemfiles/rails_8_0.gemfile.lock
+++ b/gemfiles/rails_8_0.gemfile.lock
@@ -94,6 +94,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
@@ -204,6 +205,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 8.0.0)
rake (~> 13.0)
reactionview!
diff --git a/gemfiles/rails_8_1.gemfile b/gemfiles/rails_8_1.gemfile
index 0207b8e..16a644e 100644
--- a/gemfiles/rails_8_1.gemfile
+++ b/gemfiles/rails_8_1.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", "~> 8.1.0"
gem "appraisal"
+gem "actionview", "~> 8.1.0"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", "~> 8.1.0"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_8_1.gemfile.lock b/gemfiles/rails_8_1.gemfile.lock
index 81a473a..4043cdf 100644
--- a/gemfiles/rails_8_1.gemfile.lock
+++ b/gemfiles/rails_8_1.gemfile.lock
@@ -93,6 +93,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
@@ -203,6 +204,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties (~> 8.1.0)
rake (~> 13.0)
reactionview!
diff --git a/gemfiles/rails_8_2.gemfile b/gemfiles/rails_8_2.gemfile
index 913bcaf..4b8caf2 100644
--- a/gemfiles/rails_8_2.gemfile
+++ b/gemfiles/rails_8_2.gemfile
@@ -2,10 +2,11 @@
source "https://rubygems.org"
-gem "actionview", github: "rails/rails"
gem "appraisal"
+gem "actionview", github: "rails/rails"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
+gem "minitest-mock"
gem "railties", github: "rails/rails"
gem "rake", "~> 13.0"
gem "readline", "~> 0.0.4"
diff --git a/gemfiles/rails_8_2.gemfile.lock b/gemfiles/rails_8_2.gemfile.lock
index 7d28a5b..b230149 100644
--- a/gemfiles/rails_8_2.gemfile.lock
+++ b/gemfiles/rails_8_2.gemfile.lock
@@ -110,6 +110,7 @@ GEM
prism (~> 1.5)
minitest-difftastic (0.2.1)
difftastic (~> 0.6)
+ minitest-mock (5.27.0)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
@@ -216,6 +217,7 @@ DEPENDENCIES
appraisal
maxitest (~> 7.0)
minitest-difftastic
+ minitest-mock
railties!
rake (~> 13.0)
reactionview!
diff --git a/lib/generators/reactionview/install_generator.rb b/lib/generators/reactionview/install_generator.rb
index ac4f5bd..4ca6060 100644
--- a/lib/generators/reactionview/install_generator.rb
+++ b/lib/generators/reactionview/install_generator.rb
@@ -24,7 +24,7 @@ def create_initializer
config.debug_mode = Rails.env.development?
# Path used for editor "open in editor" links (optional, defaults to Rails.root)
- # config.project_fullpath = ENV.fetch('PROJECT_FULLPATH', Rails.root.to_s)
+ # config.project_path = ENV.fetch('PROJECT_PATH', Rails.root.to_s)
# Validation mode (:raise, :overlay, or :none) — defaults to :raise in test, :overlay otherwise
# config.validation_mode = :overlay
diff --git a/lib/reactionview/config.rb b/lib/reactionview/config.rb
index 905be26..d903af4 100644
--- a/lib/reactionview/config.rb
+++ b/lib/reactionview/config.rb
@@ -5,20 +5,18 @@ class Config
attr_accessor :intercept_erb
attr_accessor :debug_mode
attr_accessor :transform_visitors
- attr_accessor :project_fullpath
+ attr_writer :project_path
attr_writer :validation_mode
def initialize
@intercept_erb = false
@debug_mode = nil
@transform_visitors = []
- @project_fullpath = nil
+ @project_path = nil
end
def project_path
- return nil unless defined?(Rails)
-
- Rails.root.to_s
+ @project_path || Rails.root.to_s
end
def validation_mode
diff --git a/lib/reactionview/template/handlers/herb.rb b/lib/reactionview/template/handlers/herb.rb
index 645e492..bb6ac61 100644
--- a/lib/reactionview/template/handlers/herb.rb
+++ b/lib/reactionview/template/handlers/herb.rb
@@ -14,13 +14,13 @@ def call(template, source)
if ::ReActionView.config.debug_mode_enabled? && local_template?(template)
visitors << ::Herb::Engine::DebugVisitor.new(
file_path: translate_path_for_editor(template.identifier),
- project_path: project_fullpath
+ project_path: ::ReActionView.config.project_path
)
end
config = {
filename: template.identifier,
- project_path: project_fullpath,
+ project_path: Rails.root.to_s,
validation_mode: ReActionView.config.validation_mode,
content_for_head: reactionview_dev_tools_markup(template),
visitors: visitors + ReActionView.config.transform_visitors,
@@ -40,7 +40,7 @@ def layout_template?(template)
def local_template?(template)
return true unless template.respond_to?(:identifier) && template.identifier
- template.identifier.start_with?(project_path)
+ template.identifier.start_with?(Rails.root.to_s)
end
def active_support_editor
@@ -58,20 +58,13 @@ def editor_meta_tag
%()
end
- def project_path
- ::ReActionView.config.project_path
- end
-
- def project_fullpath
- ::ReActionView.config.project_fullpath || project_path
- end
-
def translate_path_for_editor(template_path)
- return template_path unless project_fullpath && project_path
- return template_path if project_fullpath == project_path
+ rails_root = Rails.root.to_s
+ project_path = ::ReActionView.config.project_path
+
+ return template_path if project_path == rails_root
- # Replace Rails.root (container path) with project_fullpath (host path)
- template_path.to_s.sub(project_path, project_fullpath)
+ template_path.to_s.sub(rails_root, project_path)
end
def reactionview_dev_tools_markup(template)
@@ -83,7 +76,7 @@ def reactionview_dev_tools_markup(template)
if ::ReActionView.config.debug_mode_enabled?
markup << <<~HTML
-
+
#{editor_meta_tag}
#{ActionController::Base.new.view_context.javascript_include_tag "reactionview-dev-tools.umd.js", defer: true}
diff --git a/test/reactionview/config_test.rb b/test/reactionview/config_test.rb
index 613e003..7164c53 100644
--- a/test/reactionview/config_test.rb
+++ b/test/reactionview/config_test.rb
@@ -58,4 +58,31 @@ def config.test?
assert_equal :raise, config.validation_mode
end
+
+ test "project_path defaults to Rails.root" do
+ config = ReActionView::Config.new
+
+ Rails.stub(:root, Pathname.new("/app")) do
+ assert_equal "/app", config.project_path
+ end
+ end
+
+ test "project_path returns the configured value" do
+ config = ReActionView::Config.new
+ config.project_path = "/Users/you/myapp"
+
+ Rails.stub(:root, Pathname.new("/app")) do
+ assert_equal "/Users/you/myapp", config.project_path
+ end
+ end
+
+ test "project_path falls back to Rails.root when reset to nil" do
+ config = ReActionView::Config.new
+ config.project_path = "/Users/you/myapp"
+ config.project_path = nil
+
+ Rails.stub(:root, Pathname.new("/app")) do
+ assert_equal "/app", config.project_path
+ end
+ end
end
diff --git a/test/snapshot_utils.rb b/test/snapshot_utils.rb
index 578d430..8ccc6fd 100644
--- a/test/snapshot_utils.rb
+++ b/test/snapshot_utils.rb
@@ -15,10 +15,10 @@ def ask?(prompt = "")
end
module SnapshotUtils # rubocop:disable Metrics/ModuleLength
- def assert_compiled_snapshot(source, handler: ReActionView::Template::Handlers::ERB, virtual_path: "test", format: :html, locals: [], options: {}) # rubocop:disable Metrics/ParameterLists
+ def assert_compiled_snapshot(source, handler: ReActionView::Template::Handlers::ERB, virtual_path: "test", identifier: "test_template", format: :html, locals: [], options: {}) # rubocop:disable Metrics/ParameterLists,Layout/LineLength
template = ActionView::Template.new(
source,
- "test_template",
+ identifier,
handler,
virtual_path: virtual_path,
format: format,
@@ -53,10 +53,10 @@ def assert_compiled_snapshot(source, handler: ReActionView::Template::Handlers::
compiled_source
end
- def assert_evaluated_snapshot(source, ivars: {}, options: {}, handler: ReActionView::Template::Handlers::ERB, virtual_path: "test", format: :html, locals: []) # rubocop:disable Metrics/ParameterLists,Layout/LineLength,Metrics/MethodLength
+ def assert_evaluated_snapshot(source, ivars: {}, options: {}, handler: ReActionView::Template::Handlers::ERB, virtual_path: "test", identifier: "test_template", format: :html, locals: []) # rubocop:disable Metrics/ParameterLists,Layout/LineLength,Metrics/MethodLength
template = ActionView::Template.new(
source,
- "test_template",
+ identifier,
handler,
virtual_path: virtual_path,
format: format,
diff --git a/test/snapshots/re_action_view/project_path_test/test_0006_compiled_output_without_a_configured_project_path_compiled_23f372c5835bd8e5809427a8bfe433ca.txt b/test/snapshots/re_action_view/project_path_test/test_0006_compiled_output_without_a_configured_project_path_compiled_23f372c5835bd8e5809427a8bfe433ca.txt
new file mode 100644
index 0000000..777a6b6
--- /dev/null
+++ b/test/snapshots/re_action_view/project_path_test/test_0006_compiled_output_without_a_configured_project_path_compiled_23f372c5835bd8e5809427a8bfe433ca.txt
@@ -0,0 +1,2 @@
+ @output_buffer.safe_append='Hello
'.freeze;
+@output_buffer
\ No newline at end of file
diff --git a/test/snapshots/re_action_view/project_path_test/test_0007_compiled_output_with_a_configured_project_path_compiled_15299f7b9059f08ba4360b398f86c031.txt b/test/snapshots/re_action_view/project_path_test/test_0007_compiled_output_with_a_configured_project_path_compiled_15299f7b9059f08ba4360b398f86c031.txt
new file mode 100644
index 0000000..8f926eb
--- /dev/null
+++ b/test/snapshots/re_action_view/project_path_test/test_0007_compiled_output_with_a_configured_project_path_compiled_15299f7b9059f08ba4360b398f86c031.txt
@@ -0,0 +1,2 @@
+ @output_buffer.safe_append='Hello
'.freeze;
+@output_buffer
\ No newline at end of file
diff --git a/test/snapshots/re_action_view/project_path_test/test_0008_evaluated_output_with_a_configured_project_path_evaluated_4d3a28975f5f90eefab8a7f79760fa4e.txt b/test/snapshots/re_action_view/project_path_test/test_0008_evaluated_output_with_a_configured_project_path_evaluated_4d3a28975f5f90eefab8a7f79760fa4e.txt
new file mode 100644
index 0000000..19e465b
--- /dev/null
+++ b/test/snapshots/re_action_view/project_path_test/test_0008_evaluated_output_with_a_configured_project_path_evaluated_4d3a28975f5f90eefab8a7f79760fa4e.txt
@@ -0,0 +1 @@
+Hello
\ No newline at end of file
diff --git a/test/template/handlers/project_path_test.rb b/test/template/handlers/project_path_test.rb
new file mode 100644
index 0000000..241ca65
--- /dev/null
+++ b/test/template/handlers/project_path_test.rb
@@ -0,0 +1,118 @@
+# frozen_string_literal: true
+
+require_relative "../../test_helper"
+
+require "action_controller"
+
+class ReActionView::ProjectPathTest < Minitest::Spec
+ RAILS_ROOT = "/app"
+ HOST_PATH = "/Users/you/myapp"
+ VIEW = "/app/app/views/users/show.html.erb"
+ LAYOUT = "/app/app/views/layouts/application.html.erb"
+
+ before do
+ @previous_debug_mode = ReActionView.config.debug_mode
+
+ ReActionView.config.debug_mode = true
+ ReActionView.config.project_path = nil
+ end
+
+ after do
+ ReActionView.config.debug_mode = @previous_debug_mode
+ ReActionView.config.project_path = nil
+ end
+
+ def compile(source, identifier: VIEW, virtual_path: "users/show")
+ template = ActionView::Template.new(
+ source,
+ identifier,
+ ReActionView::Template::Handlers::Herb,
+ virtual_path: virtual_path,
+ format: :html,
+ locals: []
+ )
+
+ Rails.stub(:root, Pathname.new(RAILS_ROOT)) do
+ ReActionView::Template::Handlers::Herb.call(template, source)
+ end
+ end
+
+ test "editor path is left alone when project_path is not configured" do
+ compiled = compile("Hello
")
+
+ assert_includes compiled, %(data-herb-debug-file-full-path="/app/app/views/users/show.html.erb")
+ end
+
+ test "editor path is rewritten to the configured project_path" do
+ ReActionView.config.project_path = HOST_PATH
+
+ compiled = compile("Hello
")
+
+ assert_includes compiled, %(data-herb-debug-file-full-path="/Users/you/myapp/app/views/users/show.html.erb")
+ end
+
+ test "relative path stays correct when the editor path is rewritten" do
+ ReActionView.config.project_path = HOST_PATH
+
+ compiled = compile("Hello
")
+
+ assert_includes compiled, %(data-herb-debug-file-relative-path="app/views/users/show.html.erb")
+ end
+
+ test "herb-project-path meta tag keeps Rails.root so it matches the herb dev server" do
+ ReActionView.config.project_path = HOST_PATH
+
+ compiled = compile("", identifier: LAYOUT, virtual_path: "layouts/application")
+
+ assert_includes compiled, %()
+ refute_includes compiled, %()
+ end
+
+ test "templates outside Rails.root stay undecorated even when project_path matches them" do
+ ReActionView.config.project_path = HOST_PATH
+
+ compiled = compile("Hello
", identifier: "#{HOST_PATH}/app/views/users/show.html.erb")
+
+ refute_includes compiled, "data-herb-debug"
+ end
+
+ test "compiled output without a configured project_path" do
+ Rails.stub(:root, Pathname.new(RAILS_ROOT)) do
+ assert_compiled_snapshot(
+ "Hello
",
+ handler: ReActionView::Template::Handlers::Herb,
+ identifier: VIEW,
+ virtual_path: "users/show",
+ options: { project_path: nil }
+ )
+ end
+ end
+
+ test "compiled output with a configured project_path" do
+ ReActionView.config.project_path = HOST_PATH
+
+ Rails.stub(:root, Pathname.new(RAILS_ROOT)) do
+ assert_compiled_snapshot(
+ "Hello
",
+ handler: ReActionView::Template::Handlers::Herb,
+ identifier: VIEW,
+ virtual_path: "users/show",
+ options: { project_path: HOST_PATH }
+ )
+ end
+ end
+
+ test "evaluated output with a configured project_path" do
+ ReActionView.config.project_path = HOST_PATH
+
+ Rails.stub(:root, Pathname.new(RAILS_ROOT)) do
+ assert_evaluated_snapshot(
+ "Hello
",
+ handler: ReActionView::Template::Handlers::Herb,
+ identifier: VIEW,
+ virtual_path: "users/show",
+ options: { project_path: HOST_PATH }
+ )
+ end
+ end
+end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index c8dfc5f..2199d3e 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -10,6 +10,7 @@
require "maxitest/autorun"
require "minitest/spec"
+require "minitest/mock"
require_relative "snapshot_utils"
From 86b8323d4ff05a19b685e18eb4dca11102f54fff Mon Sep 17 00:00:00 2001
From: Marco Roth
Date: Sat, 8 Aug 2026 02:31:34 +0200
Subject: [PATCH 08/10] Cleanup
---
gemfiles/rails_7_1.gemfile | 2 +-
gemfiles/rails_7_2.gemfile | 2 +-
gemfiles/rails_8_0.gemfile | 2 +-
gemfiles/rails_8_1.gemfile | 2 +-
gemfiles/rails_8_2.gemfile | 2 +-
5 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/gemfiles/rails_7_1.gemfile b/gemfiles/rails_7_1.gemfile
index 274dbc8..6d0b61f 100644
--- a/gemfiles/rails_7_1.gemfile
+++ b/gemfiles/rails_7_1.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", "~> 7.1.0"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"
diff --git a/gemfiles/rails_7_2.gemfile b/gemfiles/rails_7_2.gemfile
index b0f8a0f..a9b174f 100644
--- a/gemfiles/rails_7_2.gemfile
+++ b/gemfiles/rails_7_2.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", "~> 7.2.0"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"
diff --git a/gemfiles/rails_8_0.gemfile b/gemfiles/rails_8_0.gemfile
index f2e4e76..21b154a 100644
--- a/gemfiles/rails_8_0.gemfile
+++ b/gemfiles/rails_8_0.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", "~> 8.0.0"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"
diff --git a/gemfiles/rails_8_1.gemfile b/gemfiles/rails_8_1.gemfile
index 16a644e..977063f 100644
--- a/gemfiles/rails_8_1.gemfile
+++ b/gemfiles/rails_8_1.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", "~> 8.1.0"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"
diff --git a/gemfiles/rails_8_2.gemfile b/gemfiles/rails_8_2.gemfile
index 4b8caf2..32130c6 100644
--- a/gemfiles/rails_8_2.gemfile
+++ b/gemfiles/rails_8_2.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", github: "rails/rails"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"
From a2ce0fc9776d84ef5877d37ed606c73dc2e6d63b Mon Sep 17 00:00:00 2001
From: Marco Roth
Date: Sat, 8 Aug 2026 02:32:11 +0200
Subject: [PATCH 09/10] Update docs
---
docs/docs/installation.md | 23 ++++++++++-------------
1 file changed, 10 insertions(+), 13 deletions(-)
diff --git a/docs/docs/installation.md b/docs/docs/installation.md
index e70b407..e16182a 100644
--- a/docs/docs/installation.md
+++ b/docs/docs/installation.md
@@ -69,32 +69,29 @@ This gives you all the benefits of Herb's validation, security features, and deb
### Advanced Configuration
-#### Custom Project Path for Editor Links
+#### Custom Project Path for Editor Links
-If your project path differs from `Rails.root` (e.g., Docker mounts, monorepos), you can configure a custom path for editor "open in editor" links:
+When your app runs somewhere other than where its files live, such as a Docker bind mount, a devcontainer, or a VM, the paths Rails sees aren't paths your editor can open. `config.project_path` says where `Rails.root` is mounted on the machine running your editor, and rewrites "open in editor" links to match:
:::code-group
```ruby [config/initializers/reactionview.rb]
ReActionView.configure do |config|
- # Custom project path for editor integration (optional)
- config.project_path = "/custom/path/to/project"
+ # Where Rails.root is mounted on the machine running your editor
+ config.project_path = "/Users/you/myapp"
- # Docker example: map container path to host path
- # config.project_path = "/Users/you/myapp"
-
- # Monorepo example: parent directory
- # config.project_path = File.expand_path("../../", Rails.root)
+ # Or take it from the environment
+ # config.project_path = ENV.fetch("PROJECT_PATH", Rails.root.to_s)
end
```
:::
-This affects:
-- Editor integration when clicking source locations in the browser
-- The `herb-project-path` meta tag for dev tools
+With `Rails.root` at `/app` inside the container, a template at `/app/app/views/users/show.html.erb` then opens as `/Users/you/myapp/app/views/users/show.html.erb`.
**Default**: `Rails.root.to_s`
-**Note**: local template detection always uses `Rails.root` and is unaffected by this setting.
+::: info Only editor links are affected
+Local template detection and the `herb-project-path` meta tag stay on `Rails.root`. The meta tag is compared against the path the `herb dev` server reports, so overriding it would make the dev tools treat the page as a different project and ignore it.
+:::
## Verify Installation
From 8bb7f035f37e7e0d52ecf77eca4deb37c0bc92c9 Mon Sep 17 00:00:00 2001
From: Marco Roth
Date: Sat, 8 Aug 2026 02:33:24 +0200
Subject: [PATCH 10/10] Cleanup
---
gemfiles/rails_7_0.gemfile | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gemfiles/rails_7_0.gemfile b/gemfiles/rails_7_0.gemfile
index 40e52e6..3d69615 100644
--- a/gemfiles/rails_7_0.gemfile
+++ b/gemfiles/rails_7_0.gemfile
@@ -2,8 +2,8 @@
source "https://rubygems.org"
-gem "appraisal"
gem "actionview", "~> 7.0.0"
+gem "appraisal"
gem "maxitest", "~> 7.0"
gem "minitest-difftastic"
gem "minitest-mock"