class HappyMapper::AnonymousMapper

Public Instance Methods

parse(xml_content) click to toggle source
# File lib/happymapper/anonymous_mapper.rb, line 5
def parse(xml_content)
  # TODO: this should be able to handle all the types of functionality that parse is able
  #   to handle which includes the text, xml document, node, fragment, etc.
  xml = Nokogiri::XML(xml_content)

  klass = create_happymapper_class_from_node(xml.root)

  # With all the elements and attributes defined on the class it is time
  # for the class to actually use the normal HappyMapper powers to parse
  # the content. At this point this code is utilizing all of the existing
  # code implemented for parsing.
  klass.parse(xml_content, single: true)
end

Private Instance Methods

create_happymapper_class_from_node(node) click to toggle source

Used internally to create and define the necessary happymapper elements.

# File lib/happymapper/anonymous_mapper.rb, line 53
def create_happymapper_class_from_node(node)
  klass = create_happymapper_class_with_tag(node.name)

  klass.namespace node.namespace.prefix if node.namespace

  node.namespaces.each do |prefix, namespace|
    klass.register_namespace prefix, namespace
  end

  node.attributes.each_value do |attribute|
    define_attribute_on_class(klass, attribute)
  end

  node.children.each do |child|
    define_element_on_class(klass, child)
  end

  klass
end
create_happymapper_class_with_tag(tag_name) click to toggle source

Used internally when parsing to create a class that is capable of parsing the content. The name of the class is of course not likely going to match the content it will be able to parse so the tag value is set to the one provided.

# File lib/happymapper/anonymous_mapper.rb, line 40
def create_happymapper_class_with_tag(tag_name)
  klass = Class.new
  klass.class_eval do
    include HappyMapper
    tag tag_name
  end
  klass
end
define_attribute_on_class(klass, attribute) click to toggle source

Define a HappyMapper attribute on the provided class based on the attribute provided.

# File lib/happymapper/anonymous_mapper.rb, line 113
def define_attribute_on_class(klass, attribute)
  klass.attribute underscore(attribute.name), String, tag: attribute.name
end
define_element_on_class(klass, node) click to toggle source

Define a HappyMapper element on the provided class based on the node provided.

# File lib/happymapper/anonymous_mapper.rb, line 77
def define_element_on_class(klass, node)
  # When a text node has been provided create the necessary
  # HappyMapper content attribute if the text happens to contain
  # some content.

  if node.text?
    klass.content :content, String if node.content.strip != ''
    return
  end

  # When the node has child elements, that are not text
  # nodes, then we want to recursively define a new HappyMapper
  # class that will have elements and attributes.

  element_type = if node.elements.any? || node.attributes.any?
                   create_happymapper_class_from_node(node)
                 else
                   String
                 end

  element_name = underscore(node.name)
  method = klass.elements.find { |e| e.name == element_name } ? :has_many : :has_one

  options = {}
  options[:tag] = node.name
  namespace = node.namespace
  options[:namespace] = namespace.prefix if namespace
  options[:xpath] = './' unless element_type == String

  klass.send(method, element_name, element_type, options)
end
underscore(camel_cased_word) click to toggle source

Borrowed from Active Support to convert unruly element names into a format known and loved by Rubyists.

# File lib/happymapper/anonymous_mapper.rb, line 25
def underscore(camel_cased_word)
  word = camel_cased_word.to_s.dup
  word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
  word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
  word.tr!('-', '_')
  word.downcase!
  word
end