Closed sashman closed 6 years ago
In mail_merge.rb
I've messed about with parse_fields
by adding the env
as a parameter, then I was able to do the following:
Add an extra check:
# line 144
fields << field if field && field.valid? && field_in_env?(field, env)
and check the env
if the context contains the key:
def field_in_env?(field, env)
env.context.key?(field.expression.gsub(/^=/, ''))
end
For the above I've had to change document.rb
line 73 to:
operations = build_operations(@parser.parse_fields(xml_node, env))
But I'm not sure where else @parser.parse_fields
might be called...
The main catch with your approach is that the code allows for chained access using context keys such as article.title
which might evaluate out to a proper value but searching explicitly for that key will throw a false negative (further complicating things is that title
could be a key in a nested hash or a method call on the article object).
You could design a recursive version using the same logic in the LookupOrMethodCall
class (see lib/sablon/operations.rb
), and if you only care about hash lookups tweak the code accordingly.
As for a different solution, a quick test shows the following change to the Sablon::Statement::Insertion
class is sufficient to preserve any expressions that evaluate to a "falsey" value. However, this will preserve both missing keys and keys that are set to a "falsey" value alike which might not be what you want.
class Insertion < Struct.new(:expr, :field)
def evaluate(env)
if content = expr.evaluate(env.context)
field.replace(Sablon::Content.wrap(content), env)
else
#field.remove
end
end
end
That being said, I think your best bet might be defining a kind of "optional" field expression and registering it with the document processor. See lib/sablon/processor/document/field_handlers.rb
and lib/sablon/processor/document.rb
for examples on how the "builtin" handlers are done.
For example you could create an "OptionalInsertion" statement class and field handler using the expression ?=keyname
instead of =keyname
. Although that would require modifying your templates which is never fun.
One key caveat is that field handlers are not applied in any particular order and the first one that "handles" the given field wins. This does pose an issue where a more specific handler might never get invoked because a more general handler gets checked first. It was something I overlooked until now and probably should fix.
Thanks for the swift reply @stadelmanma! You're awesome! Currently, this is a one-off feature I require so will probably leave it as a local change to the gem, especially as this has no quick idiomatic fix. But thanks a lot for the help!
Closing this as resolved.
I have a layout file which contains some merge fields that I'd like to fill in later. But I do want to fill in the
body
merge field for example. My context would look like:But in the docx file, I have additional merge field I'd like not to be replaced and stay as merge fields for the next templating stage. What would be the easiest way to achieve this? Maybe somehow skipping merge fields which do not have a matching key in the context hash?