Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/herb/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ def engine
@config["engine"] || {}
end

#: (String, untyped) -> untyped
def engine_option(key, default = nil)
engine.fetch(key.to_s, default)
end

def enabled_validators(overrides = {})
config = dig("engine", "validators") || {}

Expand Down
17 changes: 13 additions & 4 deletions lib/herb/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,13 @@ def initialize(input, properties = {})
@src = properties[:src] || String.new
@chain_appends = properties[:chain_appends]
@buffer_on_stack = false
@debug = properties.fetch(:debug, false)
@debug = properties.fetch(:debug, Herb.configuration.engine_option("debug", false))
@content_for_head = properties[:content_for_head]
@validation_error_template = nil
@validation_mode = properties.fetch(:validation_mode, :raise)
@enabled_validators = Herb.configuration.enabled_validators(properties[:validators] || {})
@strict = properties.fetch(:strict, true)
@optimize = properties.fetch(:optimize, false)
@optimize = properties.fetch(:optimize, Herb.configuration.engine_option("optimize", false))
@parser_options = properties.fetch(:parser_options, default_parser_options).transform_keys(&:to_sym)

if @optimize && !self.class.optimize_warning_issued
self.class.optimize_warning_issued = true
Expand Down Expand Up @@ -130,7 +130,9 @@ def initialize(input, properties = {})

action_view_helpers = @optimize && source_may_contain_action_view_helpers?(input)
transform_conditionals = @optimize && action_view_helpers
parse_result = ::Herb.parse(input, track_whitespace: true, strict: @strict, action_view_helpers: action_view_helpers, transform_conditionals: transform_conditionals)
parse_result = ::Herb.parse(input, **@parser_options, track_whitespace: true,
action_view_helpers: action_view_helpers,
transform_conditionals: transform_conditionals)
ast = parse_result.value
parser_errors = parse_result.errors

Expand Down Expand Up @@ -516,6 +518,13 @@ def default_visitors
[]
end

#: () -> Hash[Symbol, untyped]
def default_parser_options
fallback = {} #: Hash[Symbol, untyped]

Herb.configuration.engine_option("parser_options", fallback)
end

def ensure_valid_ruby!(source)
RubyVM::InstructionSequence.compile(source)
rescue SyntaxError => e
Expand Down
3 changes: 3 additions & 0 deletions sig/herb/configuration.rbs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions sig/herb/engine.rbs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

123 changes: 123 additions & 0 deletions test/configuration_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -567,4 +567,127 @@ def write_config(content, filename = ".herb.yml")
assert_equal false, result[:nesting]
assert_equal true, result[:accessibility]
end

test "engine_option returns default when no config" do
config = Herb::Configuration.load(@temp_dir)

assert_equal false, config.engine_option("optimize", false)
assert_equal true, config.engine_option("strict", true)
end

test "engine_option reads from config file" do
write_config(<<~YAML)
engine:
optimize: true
strict: false
YAML

config = Herb::Configuration.load(@temp_dir)

assert_equal true, config.engine_option("optimize", false)
assert_equal false, config.engine_option("strict", true)
end

test "engine_option returns default for missing keys" do
write_config(<<~YAML)
engine:
optimize: true
YAML

config = Herb::Configuration.load(@temp_dir)

assert_equal true, config.engine_option("optimize", false)
assert_equal true, config.engine_option("strict", true)
end

test "engine parser_options defaults to empty hash" do
config = Herb::Configuration.load(@temp_dir)

assert_equal({}, config.engine_option("parser_options", {}))
end

test "engine parser_options reads from config file" do
write_config(<<~YAML)
engine:
parser_options:
strict: false
render_nodes: true
YAML

config = Herb::Configuration.load(@temp_dir)

parser_options = config.engine_option("parser_options", {})
assert_equal false, parser_options["strict"]
assert_equal true, parser_options["render_nodes"]
end

test "engine parser_options are passed through to Herb.parse" do
engine = Herb::Engine.new("<div>Hello</div>", parser_options: { strict: false })
result = engine.src

assert_kind_of String, result
refute_empty result
end

test "engine parser_options from config are used" do
write_config(<<~YAML)
engine:
parser_options:
strict: false
YAML

Herb.configure(@temp_dir)

engine = Herb::Engine.new("<div>Hello</div>")
result = engine.src

assert_kind_of String, result
refute_empty result
end

test "engine parser_options from properties override config" do
write_config(<<~YAML)
engine:
parser_options:
strict: true
YAML

Herb.configure(@temp_dir)

engine = Herb::Engine.new("<div>Hello</div>", parser_options: { strict: false })

assert_kind_of String, engine.src
end

test "engine debug defaults to false" do
config = Herb::Configuration.load(@temp_dir)

assert_equal false, config.engine_option("debug", false)
end

test "engine debug reads from config file" do
write_config(<<~YAML)
engine:
debug: true
YAML

Herb.configure(@temp_dir)

engine = Herb::Engine.new("<div>Hello</div>")

assert_equal true, engine.debug
end

test "engine debug property overrides config" do
write_config(<<~YAML)
engine:
debug: true
YAML

Herb.configure(@temp_dir)

engine = Herb::Engine.new("<div>Hello</div>", debug: false)

assert_equal false, engine.debug
end
end
60 changes: 60 additions & 0 deletions test/engine/engine_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -277,5 +277,65 @@ class EngineTest < Minitest::Spec
assert_compiled_snapshot("<script><![CDATA[ alert(1) ]]></script>", enforce_erubi_equality: true)
assert_evaluated_snapshot("<script><![CDATA[ alert(1) ]]></script>")
end

test "compilation with optimize for tag helper" do
assert_compiled_snapshot('<%= tag.div class: "container" do %>Content<% end %>', optimize: true)
end

test "compilation with optimize for void element" do
assert_compiled_snapshot("<%= tag.br %>", optimize: true)
end

test "compilation with optimize for tag with data attributes" do
assert_compiled_snapshot('<%= tag.div data: { controller: "content", count: 42 } %>', optimize: true)
end

test "compilation with optimize for nested tag helpers" do
assert_compiled_snapshot(<<~ERB, optimize: true)
<%= tag.div class: "outer" do %>
<%= tag.span "Inner" %>
<% end %>
ERB
end

test "compilation with optimize for link_to" do
assert_compiled_snapshot('<%= link_to "Home", "/" %>', optimize: true)
end

test "compilation with optimize for image_tag" do
assert_compiled_snapshot('<%= image_tag "photo.jpg", alt: "Photo" %>', optimize: true)
end

test "compilation with optimize for content_tag" do
assert_compiled_snapshot('<%= content_tag :div, "Content", class: "box" %>', optimize: true)
end

test "compilation with optimize preserves non-helper ERB" do
assert_compiled_snapshot(<<~ERB, optimize: true)
<h1><%= title %></h1>
<%= tag.div class: "content" do %>
<p><%= body %></p>
<% end %>
ERB
end

test "compilation with optimize for tag with dynamic attribute" do
assert_compiled_snapshot("<%= tag.div class: css_class %>", optimize: true)
end

test "compilation without optimize leaves tag helpers as Ruby calls" do
template = '<%= tag.div class: "container" do %>Content<% end %>'

without_optimize = Herb::Engine.new(template).src
with_optimize = Herb::Engine.new(template, optimize: true).src

refute_equal without_optimize, with_optimize
assert_includes without_optimize, "tag.div"
refute_includes with_optimize, "tag.div"
end

test "compilation with parser_options strict false" do
assert_compiled_snapshot("<div>Hello</div>", parser_options: { strict: false })
end
end
end
22 changes: 11 additions & 11 deletions test/engine/evaluation/case_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,14 @@ class CaseTest < Minitest::Spec
ERB

error = assert_raises(Herb::Engine::CompilationError) do
Herb::Engine.new(template, strict: true)
Herb::Engine.new(template, parser_options: { strict: true })
end

assert_includes error.message, "ERBCaseWithConditions"

assert_evaluated_snapshot(template, { status: "pending" }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { status: "approved" }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { status: "other" }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { status: "pending" }, { escape: false, parser_options: { strict: false } })
assert_evaluated_snapshot(template, { status: "approved" }, { escape: false, parser_options: { strict: false } })
assert_evaluated_snapshot(template, { status: "other" }, { escape: false, parser_options: { strict: false } })
end

test "case in pattern in same ERB tag" do
Expand All @@ -42,7 +42,7 @@ class CaseTest < Minitest::Spec
ERB

error = assert_raises(Herb::Engine::CompilationError) do
Herb::Engine.new(template, strict: true)
Herb::Engine.new(template, parser_options: { strict: true })
end

assert_includes error.message, "ERBCaseWithConditions"
Expand All @@ -59,13 +59,13 @@ class CaseTest < Minitest::Spec
ERB

error = assert_raises(Herb::Engine::CompilationError) do
Herb::Engine.new(template, strict: true)
Herb::Engine.new(template, parser_options: { strict: true })
end

assert_includes error.message, "ERBCaseWithConditions"

assert_evaluated_snapshot(template, { status: "pending" }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { status: "approved" }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { status: "pending" }, { escape: false, parser_options: { strict: false } })
assert_evaluated_snapshot(template, { status: "approved" }, { escape: false, parser_options: { strict: false } })
end

test "case in on newline in same ERB tag" do
Expand All @@ -79,13 +79,13 @@ class CaseTest < Minitest::Spec
ERB

error = assert_raises(Herb::Engine::CompilationError) do
Herb::Engine.new(template, strict: true)
Herb::Engine.new(template, parser_options: { strict: true })
end

assert_includes error.message, "ERBCaseWithConditions"

assert_evaluated_snapshot(template, { count: 1 }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { count: 2 }, { escape: false, strict: false })
assert_evaluated_snapshot(template, { count: 1 }, { escape: false, parser_options: { strict: false } })
assert_evaluated_snapshot(template, { count: 2 }, { escape: false, parser_options: { strict: false } })
end
end
end
Loading
Loading