<commit_before>require 'malt/engines/abstract'

module Malt::Engine

  # Builder
  #
  #   http://builder.rubyforge.org/
  #
  class Builder < Abstract

    default :builder, :rbml

    #
    def render(params, &content)
      into = parameters(params, :to) || :html

      case into
      when :xml, :html, :xhtml
        prepare_engine(params, &content) #.target!
      else
        super(params, &content)
      end
    end

  #private

    # Prepare engine for rendering.
    def prepare_engine(params={}, &content)
      prefix = parameters(params, :prefix)

      if prefix
        prepare_engine_prefix(params, &content)
      else
        prepare_engine_scope(params, &content)
      end
    end

    # TODO: Can Builder be cached?

    #
    def create_engine(params={})
      opts = engine_options(params)

      #cached(opts) do
        ::Builder::XmlMarkup.new(opts)
      #end
    end

    #
    def prepare_engine_prefix(params, &content)
      text, file, data, prefix = parameters(params, :text, :file, :data, :prefix)

      file ||= "(builder)"

      bind = make_binding(data, &content)

      #scope, locals = split_data(data)

      #scope  ||= Object.new
      #locals ||= {}

      engine = create_engine(params)

      code = %{
        lambda do |#{prefix}|
          #{text}
        end
      }

      eval(code, bind, file || '(builder)').call(engine)
    end

    #
    def prepare_engine_scope(params, &content)
      text, file, data = parameters(params, :text, :file, :data)

      scope, locals = external_scope_and_locals(data, &content)

      engine = create_engine

      locals.each do |k,v|
        next if k.to_sym == :target
        engine.instance_eval("@#{k} = v")
      end

      unless scope.respond_to?(:to_struct)
        scope.instance_variables.each do |k|
          next if k == "@target"
          v = scope.instance_variable_get(k)
          engine.instance_eval("#{k} = v") 
        end
      end

      engine.instance_eval(text, file || '(builder)')

      engine.target!
    end

  private

    # Load Builder library if not already loaded.
    def require_engine
      return if defined? ::Builder
      require_library 'builder'

      ::Builder::XmlBase.class_eval do
        undef_method :p
      end
    end

    #
    # :target=>target_object: Object receiving the markup. target_object must
    # respond to the <<(a_string) operator and return itself.
    # The default target is a plain string target.
    #<commit_msg>Only undefine #p method if not already undefined.<commit_after>require 'malt/engines/abstract'

module Malt::Engine

  # Builder
  #
  #   http://builder.rubyforge.org/
  #
  class Builder < Abstract

    default :builder, :rbml

    #
    def render(params, &content)
      into = parameters(params, :to) || :html

      case into
      when :xml, :html, :xhtml
        prepare_engine(params, &content) #.target!
      else
        super(params, &content)
      end
    end

  #private

    # Prepare engine for rendering.
    def prepare_engine(params={}, &content)
      prefix = parameters(params, :prefix)

      if prefix
        prepare_engine_prefix(params, &content)
      else
        prepare_engine_scope(params, &content)
      end
    end

    # TODO: Can Builder be cached?

    #
    def create_engine(params={})
      opts = engine_options(params)

      #cached(opts) do
        ::Builder::XmlMarkup.new(opts)
      #end
    end

    #
    def prepare_engine_prefix(params, &content)
      text, file, data, prefix = parameters(params, :text, :file, :data, :prefix)

      file ||= "(builder)"

      bind = make_binding(data, &content)

      #scope, locals = split_data(data)

      #scope  ||= Object.new
      #locals ||= {}

      engine = create_engine(params)

      code = %{
        lambda do |#{prefix}|
          #{text}
        end
      }

      eval(code, bind, file || '(builder)').call(engine)
    end

    #
    def prepare_engine_scope(params, &content)
      text, file, data = parameters(params, :text, :file, :data)

      scope, locals = external_scope_and_locals(data, &content)

      engine = create_engine

      locals.each do |k,v|
        next if k.to_sym == :target
        engine.instance_eval("@#{k} = v")
      end

      unless scope.respond_to?(:to_struct)
        scope.instance_variables.each do |k|
          next if k == "@target"
          v = scope.instance_variable_get(k)
          engine.instance_eval("#{k} = v") 
        end
      end

      engine.instance_eval(text, file || '(builder)')

      engine.target!
    end

  private

    # Load Builder library if not already loaded.
    def require_engine
      return if defined? ::Builder
      require_library 'builder'

      ::Builder::XmlBase.class_eval do
        undef_method :p if method_defined?(:p)
      end
    end

    #
    # :target=>target_object: Object receiving the markup. target_object must
    # respond to the <<(a_string) operator and return itself.
    # The default target is a plain string target.
    #
{'fraction_non_alphanumeric': 0.1133877063855182, 'fraction_numerical': 0.0, 'mean_word_length': 2.3013788575180563, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3367126', 'n_tokens_mistral': 1577, 'n_tokens_neox': 1511, 'n_words': 397}