class Sheng::MergeField
Constants
- MATH_TOKENS
- REGEXES
Attributes
element[R]
errors[R]
xml_document[R]
Public Class Methods
from_element(element)
click to toggle source
# File lib/sheng/merge_field.rb, line 14 def from_element(element) new(element) rescue NotAMergeFieldError => e nil end
new(element)
click to toggle source
# File lib/sheng/merge_field.rb, line 23 def initialize(element) @element = element @xml_document = element.document @instruction_text = Sheng::Support.extract_mergefield_instruction_text(element) @errors = [] end
Public Instance Methods
==(other)
click to toggle source
# File lib/sheng/merge_field.rb, line 30 def ==(other) other.is_a?(self.class) && other.element == element end
add_previous_sibling(fragment_to_add)
click to toggle source
# File lib/sheng/merge_field.rb, line 148 def add_previous_sibling(fragment_to_add) if inline? [xml].flatten.first.add_previous_sibling(fragment_to_add) elsif is_table_row_marker? containing_element.ancestors[1].add_previous_sibling(fragment_to_add) else containing_element.add_previous_sibling(fragment_to_add) end end
block_prefix()
click to toggle source
# File lib/sheng/merge_field.rb, line 86 def block_prefix @potential_prefix ||= begin potential_prefix = raw_key.match(REGEXES[:key_string])[:prefix] potential_prefix && potential_prefix.gsub(/\:$/, '') end end
block_type()
click to toggle source
# File lib/sheng/merge_field.rb, line 77 def block_type return nil unless block_prefix if ["start", "end"].include?(block_prefix) Sequence else ConditionalBlock end end
comma_series_conjunction()
click to toggle source
# File lib/sheng/merge_field.rb, line 109 def comma_series_conjunction if filters.detect { |f| f =~ /^series_with_commas\((.*)\)$/ } $1 else "and" end end
containing_element()
click to toggle source
# File lib/sheng/merge_field.rb, line 131 def containing_element parents_until_container = new_style? ? 2 : 1 element.ancestors[parents_until_container - 1] end
filter_value(value)
click to toggle source
# File lib/sheng/merge_field.rb, line 252 def filter_value(value) filters.inject(value) { |val, filter_string| filterer = Filters.filter_for(filter_string) filterer.filter(val) } end
filters()
click to toggle source
# File lib/sheng/merge_field.rb, line 42 def filters match = raw_key.match(REGEXES[:key_string]) match[:filters].split("|").map(&:strip) end
get_value(data_set)
click to toggle source
# File lib/sheng/merge_field.rb, line 227 def get_value(data_set) interpolated_string = key_parts.map { |token| if Support.is_numeric?(token) || MATH_TOKENS.include?(token) token else data_set.fetch(token) end }.join(" ") return interpolated_string unless key_has_math? Dentaku::Calculator.new.evaluate!(interpolated_string.gsub(",", "")) end
in_table_row?()
click to toggle source
# File lib/sheng/merge_field.rb, line 140 def in_table_row? containing_element.ancestors[1] && containing_element.ancestors[1].name == "tr" end
inline?()
click to toggle source
# File lib/sheng/merge_field.rb, line 144 def inline? containing_element.children.text != xml.text end
interpolate(data_set)
click to toggle source
# File lib/sheng/merge_field.rb, line 241 def interpolate(data_set) value = get_value(data_set) replace_mergefield(filter_value(value)) rescue DataSet::KeyNotFound, Dentaku::UnboundVariableError, Filters::UnsupportedFilterError => e @errors << e # Ignore this error; we'll collect all uninterpolated fields later and # raise a new exception, so we can list all the fields in an error # message. nil end
is_end?()
click to toggle source
# File lib/sheng/merge_field.rb, line 117 def is_end? block_prefix && block_prefix.match(/^end/) end
is_start?()
click to toggle source
# File lib/sheng/merge_field.rb, line 93 def is_start? block_prefix && !block_prefix.match(/^end/) end
is_table_row_marker?()
click to toggle source
# File lib/sheng/merge_field.rb, line 136 def is_table_row_marker? in_table_row? && (is_start? || is_end?) end
iteration_variable()
click to toggle source
# File lib/sheng/merge_field.rb, line 97 def iteration_variable if filters.detect { |f| f =~ /^as\((.*)\)$/ } $1.to_sym else :item end end
key()
click to toggle source
# File lib/sheng/merge_field.rb, line 38 def key raw_key.match(REGEXES[:key_string])[:key].strip end
key_has_math?()
click to toggle source
# File lib/sheng/merge_field.rb, line 223 def key_has_math? !(MATH_TOKENS & key_parts).empty? end
key_parts()
click to toggle source
# File lib/sheng/merge_field.rb, line 195 def key_parts @key_parts ||= key.gsub(",", ""). gsub(".", "_DOTSEPARATOR_"). split(/\b|\s/). map(&:strip). reject(&:empty?). map { |token| token.gsub("_DOTSEPARATOR_", ".") } end
new_style?()
click to toggle source
# File lib/sheng/merge_field.rb, line 34 def new_style? element.name == 'fldChar' end
next_element()
click to toggle source
# File lib/sheng/merge_field.rb, line 158 def next_element if inline? [xml].flatten.last.next_element elsif is_table_row_marker? containing_element.ancestors[1].next_element else containing_element.next_element end end
raw_key()
click to toggle source
# File lib/sheng/merge_field.rb, line 47 def raw_key @raw_key ||= @instruction_text.gsub(REGEXES[:instruction_text], '\1').strip end
remove()
click to toggle source
# File lib/sheng/merge_field.rb, line 121 def remove if inline? xml.remove elsif is_table_row_marker? containing_element.ancestors[1].remove else containing_element.remove end end
replace_mergefield(value)
click to toggle source
# File lib/sheng/merge_field.rb, line 181 def replace_mergefield(value) value_as_string = if value.is_a?(BigDecimal) value.to_s("F") else value.to_s end new_run = Sheng::Support.new_text_run( value_as_string, xml_document: xml_document, style_run: styling_run ) xml.before(new_run) xml.remove end
required_hash(placeholder: nil)
click to toggle source
# File lib/sheng/merge_field.rb, line 212 def required_hash(placeholder: nil) required_variables.inject({}) { |assembled, variable| parts = variable.split(/\./) last_key = parts.pop hash = parts.reverse.inject(last_key => placeholder) do |memo, key| memo = { key => memo }; memo end Sheng::Support.merge_required_hashes(assembled, hash) } end
required_variables()
click to toggle source
# File lib/sheng/merge_field.rb, line 206 def required_variables key_parts.reject { |token| Support.is_numeric?(token) || MATH_TOKENS.include?(token) } end
series_with_commas?()
click to toggle source
# File lib/sheng/merge_field.rb, line 105 def series_with_commas? filters.detect { |f| f =~ /^series_with_commas/ } end
start_key()
click to toggle source
# File lib/sheng/merge_field.rb, line 67 def start_key if is_start? "#{block_prefix}:#{key}" elsif block_prefix == "end" "start:#{key}" else "#{block_prefix.gsub(/^end_/, '')}:#{key}" end end
styling_paragraph()
click to toggle source
# File lib/sheng/merge_field.rb, line 51 def styling_paragraph return nil if inline? containing_element.at_xpath(".//w:pPr") end
styling_run()
click to toggle source
# File lib/sheng/merge_field.rb, line 56 def styling_run if new_style? separator_field = element.ancestors[1].at_xpath(".//w:fldChar[contains(@w:fldCharType, 'separate')]") if separator_field separator_field.parent.next_element.at_xpath(".//w:rPr") end else element.at_xpath(".//w:rPr") end end
xml()
click to toggle source
# File lib/sheng/merge_field.rb, line 168 def xml return element unless new_style? nodeset = Nokogiri::XML::NodeSet.new(xml_document) current_node = element.parent nodeset << current_node loop do current_node = current_node.next_element nodeset << current_node break if current_node.at_xpath("./w:fldChar[contains(@w:fldCharType, 'end')]") end nodeset end