|
module LiquidInterpolatable
extend ActiveSupport::Concern
included do
validate :validate_interpolation
end
def valid?(context = nil)
super
rescue Liquid::Error
errors.empty?
end
def validate_interpolation
interpolated
rescue Liquid::Error => e
errors.add(:options, "has an error with Liquid templating: #{e.message}")
false
end
# Return the current interpolation context. Use this in your Agent
# class to manipulate interpolation context for user.
#
# For example, to provide local variables:
#
# # Create a new scope to define variables in:
# interpolation_context.stack {
# interpolation_context['_something_'] = 42
# # And user can say "{{_something_}}" in their options.
# value = interpolated['some_key']
# }
#
def interpolation_context
@interpolation_context ||= Context.new(self)
end
# Take the given object as "self" in the current interpolation
# context while running a given block.
#
# The most typical use case for this is to evaluate options for each
# received event like this:
#
# def receive(incoming_events)
# incoming_events.each do |event|
# interpolate_with(event) do
# # Handle each event based on "interpolated" options.
# end
# end
# end
def interpolate_with(self_object)
case self_object
when nil
yield
else
context = interpolation_context
begin
context.environments.unshift(self_object.to_liquid)
yield
ensure
context.environments.shift
end
end
end
def interpolate_options(options, self_object = nil)
interpolate_with(self_object) do
case options
when String
interpolate_string(options)
when ActiveSupport::HashWithIndifferentAccess, Hash
options.each_with_object(ActiveSupport::HashWithIndifferentAccess.new) { |(key, value), memo|
memo[key] = interpolate_options(value)
}
when Array
options.map { |value| interpolate_options(value) }
else
options
end
end
end
def interpolated(self_object = nil)
interpolate_with(self_object) do
(@interpolated_cache ||= {})[[options, interpolation_context]] ||=
interpolate_options(options)
end
end
def interpolate_string(string, self_object = nil)
interpolate_with(self_object) do
Liquid::Template.parse(string).render!(interpolation_context)
end
end
class Context < Liquid::Context
def initialize(agent)
super({}, {}, { agent: agent }, true)
end
def hash
[@environments, @scopes, @registers].hash
end
def eql?(other)
other.environments == @environments &&
other.scopes == @scopes &&
other.registers == @registers
end
end
require 'uri'
module Filters
# Percent encoding for URI conforming to RFC 3986.
# Ref: http://tools.ietf.org/html/rfc3986#page-12
def uri_escape(string)
CGI.escape(string) rescue string
end
# Parse an input into a URI object, optionally resolving it
# against a base URI if given.
#
# A URI object will have the following properties: scheme,
# userinfo, host, port, registry, path, opaque, query, and
# fragment.
def to_uri(uri, base_uri = nil)
if base_uri
URI(base_uri) + uri.to_s
else
URI(uri.to_s)
end
rescue URI::Error
nil
end
# Escape a string for use in XPath expression
def to_xpath(string)
subs = string.to_s.scan(/\G(?:\A\z|[^"]+|[^']+)/).map { |x|
case x
when /"/
%Q{'#{x}'}
else
%Q{"#{x}"}
end
}
if subs.size == 1
subs.first
else
'concat(' << subs.join(', ') << ')'
end
end
end
Liquid::Template.register_filter(LiquidInterpolatable::Filters)
module Tags
class Credential < Liquid::Tag
def initialize(tag_name, name, tokens)
super
@credential_name = name.strip
end
def render(context)
credential = context.registers[:agent].credential(@credential_name)
raise "No user credential named '#{@credential_name}' defined" if credential.nil?
credential
end
end
end
Liquid::Template.register_tag('credential', LiquidInterpolatable::Tags::Credential)
end
|