lib/glyph/macro.rb in glyph-0.4.2 vs lib/glyph/macro.rb in glyph-0.5.0

- old
+ new

@@ -6,18 +6,18 @@ # The Macro class contains shortcut methods to access the current node and document, as well as other # useful methods to be used in macro definitions. class Macro include Validators - include Helpers include Utils - attr_reader :node, :source_name, :source_file, :source_topic + attr_reader :node, :source_name, :source_file, :source_topic, :name # Creates a new macro instance from a Node # @param [Node] node a node populated with macro data def initialize(node) + @data = {} @node = node @name = @node[:name] @updated_source = nil @source_name = @node[:source][:name] || nil rescue "--" @source_topic = @node[:source][:topic] || nil rescue "--" @@ -32,11 +32,11 @@ # @since 0.3.0 def update_source(name, file=nil, topic=nil) file ||= @node[:source][:file] rescue nil @updated_source = {:name => name, :file => file, :topic => topic} end - + # Returns a Glyph code representation of the specified parameter # @param [Fixnum] n the index of the parameter # @return [String, nil] the string representation of the parameter # @since 0.3.0 def raw_parameter(n) @@ -107,12 +107,12 @@ def parameters(options={:strip => true, :null_if_blank => true}) return @parameters if @parameters @parameters = [] @node.parameters.each do |value| @parameters << value.evaluate(@node, :params => true) - @parameters.last.strip! if options[:strip] - @parameters.last = nil if @parameters.last.blank? && options[:null_if_blank] + @parameters[@parameters.length-1].strip! if options[:strip] + @parameters[@parameters.length-1] = nil if @parameters.last.blank? && options[:null_if_blank] end @parameters end alias params parameters @@ -161,11 +161,11 @@ end macros << name end macros.reverse.compact.join('/') end - + # Returns a todo message to include in the document in case of errors. # @param [String] message the message to include in the document # @return [String] the resulting todo message # @since 0.2.0 def macro_todo(message) @@ -207,27 +207,14 @@ # Instantiates a Glyph::Interpreter and interprets a string # @param [String] string the string to interpret # @return [String] the interpreted output def interpret(string) - if @node[:escape] then - result = string - else - context = {} - context[:source] = @updated_source || @node[:source] - context[:embedded] = true - context[:document] = @node[:document] - interpreter = Glyph::Interpreter.new string, context - subtree = interpreter.parse - subtree[:source] = context[:source] - @node << subtree - result = interpreter.document.output - end - result + @node[:escape] ? string : inject(string).document.output end - # @see Glyph::Document#placeholder + # @see Glyph::Document#placeholder def placeholder(&block) @node[:document].placeholder &block end # @see Glyph::Document#bookmark @@ -248,16 +235,101 @@ # @see Glyph::Document#header def header(hash) @node[:document].header hash end + # @see Glyph::Document#snippet + def snippet(key, value) + @node[:document].snippet key, value + end + + # @see Glyph::Document#snippet? + def snippet?(ident) + @node[:document].snippet? ident + end + + # @since 0.5.0 + # Renders a macro representation + # @param [Symbol, String] rep the representation to render + # @param [Hash] data the data to pass to the representation + def render(rep=nil, data=nil) + rep ||= @name + data ||= @data + block = Glyph::REPS[rep.to_sym] + macro_error "No macro representation for '#{rep}'", e unless block + instance_exec(data, &block).to_s + end + + # Stores a block of code to be "dispatched" via macro composition + # @since 0.5.0 + # @example + # # Macro definition (Ruby) + # macro :greet do + # dispatch do |node| + # "#{node[:name]}, #{node.param 0}!" + # end + # end + # + # # macro usage (Glyph) + # greet/Hello[World] --[Outputs: Hello, World!] + # greet/GoodBye[John] --[Outputs: Goodbye, John!] + def dispatch(&block) + @node[:dispatch] = block + value + end + + # Performs parameter/attribute substitution and interprets text + # @since 0.5.0 + # @param [String] text the text to interpret + # @return [String] the interpreted output + def apply(text) + body = text.dup + # Parameters + body.gsub!(/\{\{(\d+)\}\}/) do + raw_param($1.to_i).to_s.strip + end + # Attributes + body.gsub!(/\{\{([^\[\]\|\\\s]+)\}\}/) do + raw_attr($1.to_sym).to_s.strip + end + interpret body + end + + # Parses text and injects the syntax tree into the current node + # @since 0.5.0 + # @param [String] text the text to parse + # @return [Glyph::Interpreter] the interpreter instance used to parse text + def inject(text) + context = create_context + interpreter = Glyph::Interpreter.new text, context + subtree = interpreter.parse + subtree[:source] = context[:source] + @node << subtree + interpreter + end + + # Parses text + # @since 0.5.0 + # @param [String] text the text to parse + # @return [Glyph::Node] the syntax tree generated by parsing + def parse(text) + Glyph::Interpreter.new(text, create_context).parse + end + # Executes a macro definition in the context of self def expand block = Glyph::MACROS[@name] - macro_error "Undefined macro '#@name'}" unless block - res = instance_exec(@node, &block).to_s - res.gsub!(/\\?([\[\]\|])/){"\\#$1"} - res + macro_error "Undefined macro '#@name'" unless block + instance_exec(@node, &block).to_s.gsub(/\\?([\[\]\|])/){"\\#$1"} end + private + + def create_context + context = {} + context[:source] = @updated_source || @node[:source] + context[:embedded] = true + context[:document] = @node[:document] + context + end end end