# encoding: utf-8
# frozen_string_literal: true
module Brandish
module Processors
module All
# Provides both an `if` and an `unless` block into the output. The
# processor takes one option: `:embed`. If embed _is_ `true` (using
# strict equalivalence `equal?`), then the if statement takes a single
# pair, named `"condition"`. This condition is executed using
# {Brandish::Execute}, and if it is true, and if it is an `if` block,
# the contents are included; otherwise, if it is false, and if it is
# an `unless` block, the contents are included; otherwise, they are
# ignored. If embed is `false`, the block can take multiple pairs, and
# each pair is matched to a name. If the pair's value equals the matched
# name's value, then the condition is true. If the condition is true,
# and the block is an `if` block, the contents are included; otherwise,
# if it is false, and if it is an `unless` block, the contents are
# included; otherwise, they are not. The pair conditions that are
# matched by default are `"format"` and `"form"`. If pairs are included
# that are not provided, it errors.
#
# Options:
#
# - `:embed` - Optional. Whether or not the condition should be
# treaded like an embedded condition. This must be set to the exact
# value of `true` for it to be accepted.
#
# Pairs:
#
# - `"condition"` - Optional. The embedded condition for the `if` or
# `unless` block. Only applies if the `:embed` option is set.
# - `"format"` - Optional. If this is provided, and the `:embed` option
# is not set, it is added as a condition.
# - `"form"` - Optional. If this is provided, and the `:embed` option
# is not set, it is added as a condition.
#
# @example non-embed
#
#
#
# @example embed
#
#
#
# @note
# Using the `:embed` option is **very dangerous** - ONLY USE IF YOU
# TRUST THE SOURCE. `:embed` provides no sandboxing by default. This
# is why its value _must_ be set to be `true` exactly, and not
# any other value.
class If < Processor::Base
include Processor::Block
self.names = [:if, :unless]
register %i(all if) => self
unrestricted_pairs!
# If {#meets_conditions?} is true, this accepts the body of the block
# for processing; otherwise, it returns `nil`, effectively causing
# the body to be ignored.
#
# @return [Parser::Node, nil]
def perform
accept(@body) if meets_conditions?
end
# If the name of this block is `"if"`, and all of the conditions are
# matched as outlined in the class direction, then this returns
# `true`; otherwise, if the name of this block is `"if"`, and any of
# the conditions are not matched as outlined in the class description,
# then this returns `false`; otherwise, if the name of this block is
# anything else (i.e. `"unless"`), and all of the conditions are
# matched as outlined in the class description, then this returns
# `false`; otherwise, if any of the conditions are not matched as
# outlined in the class description, then this returns `true`.
#
# @return [::Boolean]
def meets_conditions?
@name == "if" ? match_conditions : !match_conditions
end
private
def match_conditions
true.equal?(@options[:embed]) ? exec_condition : pair_condition
end
def exec_condition
context = Brandish::Execute.new(execute_context)
context.exec(@pairs.fetch("condition"))
end
def execute_context
{ context: @context, pairs: @pairs, options: @options,
format: @context.form.format, form: @context.form.name }
end
def pair_condition
@pairs.all? { |k, v| match_pair.fetch(k.to_s).to_s == v.to_s }
end
def match_pair
@_match_pair ||= {
"format" => @context.form.format, "form" => @context.form.name
}.freeze
end
end
end
end
end