module MasterView
# KeywordExpander is used to hold variables defined to be used in expansion of
# directive attributes.
#
# Keywords in a directive attribute value are expanded into their variable values
# in the current template processing contenxt prior to invocation of the
# directive processing implementation.
#
class KeywordExpander
#--
# keywords related to path information about the template being processed
#++
# relative path of the template being processed within the template src dir
# (does *not* include the template flie extension)
KW_TEMPLATE_PATH = '{template_path}'
# relative path of the directory containing the template being processed
# within the template src dir.
# ('' if template is directly contained in the templates src dir)
KW_TEMPLATE_DIR_PATH = '{template_dir_path}'
# base filename of the template being processed (no file extension)
KW_TEMPLATE_BASENAME = '{template_basename}'
# filename extension of the template being processed (ordinarily '.html')
KW_EXTENSION = '{extension}'
# the default extension for generated output (ordinarily '.rhthml')
KW_DEFAULT_EXTENSION = '{default_extension}'
# list of all supported keywords related to the template being processed
TEMPLATE_KEYWORDS = [ KW_TEMPLATE_PATH, KW_TEMPLATE_DIR_PATH, KW_TEMPLATE_BASENAME, KW_EXTENSION ]
def initialize(hash = {})
@hash = hash
build_key_value_array_sorted_by_desc_length
end
# Sets a variety of path related keywords:
#
# MasterView supports the following keyword expansions for
# mv:generate, mv:gen_partial, mv:import, and mv:import_render
#
#--
# The default generated output extension is MasterView::GeneratedFileDefaultExtension,
# per the masterview config.output_filename_extension setting.
#++
#
# For template file one/foo/bar.html
when default generated output extension '.rhtml':
# {template_path} == use original masterview template path with default output extension (one/foo/bar.rhtml)
# {template_path}.ext == use original masterview template path with the specified extension (one/foo/bar.ext)
# {template_dir_path} == direct_parent_dirname (one/foo)
# {template_basename} == basename (bar)
# {extension} == extension (.html)
#
# For example:
# with MasterView template file one/two/three.html
and default generated output extension '.rhtml'
# mv:generate="{template_path}" expands to mv:generate="one/two/three.rhtml" (default generation output extension used)
# mv:generate="{template_path}.rcss" expands to mv:generate="one/two/three.rcss"
# mv:generate="{template_dir_path}/{template_basename}" expands to mv:generate="one/two/three" (no extension)
# mv:generate="{template_dir_path}/{template_basename}.rcss" expands to mv:generate="one/two/three.rcss"
# mv:generate="{template_dir_path}/{template_basename}-bar.rtxt" expands to mv:generate="one/two/three-bar.rtxt"
# mv:generate="somewhere/{template_basename}-bar{extension}" expands to mv:generate="somewhere/three-bar.html"
#
# Note: All path values use forward slashes
#
def set_template_pathname(template_pathname, default_erb_extension)
return if template_pathname.nil?
pn = Pathname.for_path(template_pathname)
default_erb_extension = '' if default_erb_extension.nil?
@hash[KW_TEMPLATE_PATH] = convert_pathname_to_s(pn.path_no_ext) #defer decision: + default_erb_extension
@hash[KW_TEMPLATE_DIR_PATH] = convert_pathname_to_s(pn.dirname)
@hash[KW_TEMPLATE_BASENAME] = convert_pathname_to_s(pn.basename(pn.extname))
@hash[KW_EXTENSION] = pn.extname
@hash[KW_DEFAULT_EXTENSION] = default_erb_extension
build_key_value_array_sorted_by_desc_length
end
# set the value, regenerate a reverse sorted hash value array so that it will
# replace the longer replacement keys before the shorter ones, so FOO_BAR will be
# replaced before FOO
def set(key, value)
@hash[key] = value
build_key_value_array_sorted_by_desc_length
end
def [](key)
@hash[key]
end
# expand all keywords, return value
def expand_keywords(str)
# maybe consider: add parm check_template_path=false that mv:generate processor would turn on
# so that ext. defaulting cleverness only applies in that specific context.
# Might be overaggressive (or just unnecessary) to this this on *all* substitution mappings
# [DJL 18-Jun-2006]
return nil if str.nil?
append_default_ext = str.ends_with?(KW_TEMPLATE_PATH) and @hash.has_key?(KW_DEFAULT_EXTENSION)
val = str.clone
@hash_values_by_desc_key_length.each { |k,v| val.gsub!(k,v) }
val << @hash[KW_DEFAULT_EXTENSION] if append_default_ext
val
end
# finds the attribute using the key deleting it from hash, expands value using current binding
# which allows KEYWORD substitutions before returning string, return nil if not found
def resolveAttrAndDelete(attributes, key)
attr_value = attributes.delete(key)
attr_value = expand_keywords(attr_value) unless attr_value.nil?
attr_value
end
private
# convert pathname to string, for '.' we want empty string
def convert_pathname_to_s(pathname)
(pathname.to_s == '.') ? '' : pathname.to_s
end
def build_key_value_array_sorted_by_desc_length
@hash_values_by_desc_key_length = @hash.sort { |a,b| b[0].length <=> a[0].length } # longest keys first
end
end
end