module RubyXL::OOXMLObjectClassMethods

Public Instance Methods

define_attribute(attr_name, attr_type, extra_params = {}) click to toggle source

Defines an attribute of OOXML object.

Parameters

  • attribute_name - Name of the element attribute as seen in the source XML. Can be either "String" or :Symbol

    • Special attibute name '_' (underscore) denotes the value of the element rather than attribute.

  • attribute_type - Specifies the conversion type for the attribute when parsing. Available options are:

    • :int - Integer

    • :uint - Unsigned Integer

    • :double - Float</u>

    • :string - String (no conversion)

    • :sqref - RubyXL::Sqref

    • :ref - RubyXL::Reference

    • :bool - Boolean (“1” and “true” convert to true, others to false)

    • one of simple_types - String, plus the list of acceptable values is saved for future validation (not used yet).

  • extra_parameters - Hash of optional parameters as follows:

    • :accessor - Name of the accessor for this attribute to be defined on the object. If not provided, defaults to classidied attribute_name.

    • :default - Value this attribute defaults to if not explicitly provided.

    • :required - Whether this attribute is required when writing XML. If the value of the attrinute is not explicitly provided, :default is written instead.

    • :computed - Do not store this attribute on parse, but do call the object-provided read accessor on write_xml.

Examples

define_attribute(:outline, :bool, :default => true)

A Boolean attribute 'outline' with default value true will be accessible by calling obj.outline

define_attribute(:uniqueCount,  :int)

An Integer attribute 'uniqueCount' accessible as obj.unique_count

define_attribute(:_,  :string, :accessor => :expression)

The value of the element will be accessible as a String by calling obj.expression

define_attribute(:errorStyle, %w{ stop warning information }, :default => 'stop',)

A String attribute named 'errorStyle' will be accessible as obj.error_style, valid values are "stop", "warning", "information"

# File lib/rubyXL/objects/ooxml_object.rb, line 47
def define_attribute(attr_name, attr_type, extra_params = {})
  attrs = obtain_class_variable(:@@ooxml_attributes)
  attr_hash = extra_params.merge({ :attr_type => attr_type })
  attr_hash[:accessor] ||= accessorize(attr_name)
  attrs[attr_name.to_s] = attr_hash
  self.send(:attr_accessor, attr_hash[:accessor]) unless attr_hash[:computed]
end
define_child_node(klass, extra_params = {}) click to toggle source

Defines a child node of OOXML object.

Parameters

  • klass - Class (descendant of RubyXL::OOXMLObject) of the child nodes. Child node objects will be produced by calling parse method of that class.

  • extra_parameters - Hash of optional parameters as follows:

    • :accessor - Name of the accessor for this attribute to be defined on the object. If not provided, defaults to classidied attribute_name.

    • :node_name - Node name for the child node, in case it does not match the one defined by the klass.

    • :collection - Whether the child node should be treated as a single node or a collection of nodes:

      • false (default) - child node is directly accessible through the respective accessor;

      • true - a collection of child nodes is accessed as Array through the respective accessor;

      • :with_count - same as true, but in addition, the attribute count is defined on the current object, that will be automatically set to the number of elements in the collection at the start of write_xml call.

Examples

define_child_node(RubyXL::Alignment)

Define a singular child node parsed by the RubyXL::BorderEdge.parse() and accessed by the default obj.alignment accessor

define_child_node(RubyXL::Hyperlink, :collection => true, :accessor => :hyperlinks)

Define an array of nodes accessed by obj.hyperlinks accessor, each of which will be parsed by the RubyXL::Hyperlink.parse()

define_child_node(RubyXL::BorderEdge, :node_name => :left)
define_child_node(RubyXL::BorderEdge, :node_name => :right)

Use class RubyXL::BorderEdge when parsing both the elements <left ...> and <right ...> elements.

define_child_node(RubyXL::Font, :collection => :with_count, :accessor => :fonts)

Upon writing of the object this was defined on, its count attribute will be set to the count of nodes in fonts array

# File lib/rubyXL/objects/ooxml_object.rb, line 75
def define_child_node(klass, extra_params = {})
  child_nodes = obtain_class_variable(:@@ooxml_child_nodes)
  child_node_name = (extra_params[:node_name] || klass.class_variable_get(:@@ooxml_tag_name)).to_s
  accessor = (extra_params[:accessor] || accessorize(child_node_name)).to_sym

  child_nodes[child_node_name] = {
    :class => klass,
    :is_array => extra_params[:collection],
    :accessor => accessor
  }

  define_count_attribute if extra_params[:collection] == :with_count

  self.send(:attr_accessor, accessor)
end
define_element_name(element_name) click to toggle source

Defines the name of the element that represents the current OOXML object. Should only be used once per object. In case of different objects represented by the same class in different parts of OOXML tree, :node_name extra parameter can be used to override the default element name.

Parameters

  • element_name

Examples

define_element_name 'externalReference'
# File lib/rubyXL/objects/ooxml_object.rb, line 103
def define_element_name(element_name)
  self.class_variable_set(:@@ooxml_tag_name, element_name)
end
obtain_class_variable(var_name, default = {}) click to toggle source

Get the value of a [sub]class variable if it exists, or create the respective variable with the passed-in default (or +{}+, if not specified)

Throughout this class, we are setting class variables through explicit method calls rather than by directly addressing the name of the variable because of context issues: addressing variable by name creates it in the context of defining class, while calling the setter/getter method addresses it in the context of descendant class, which is what we need.

# File lib/rubyXL/objects/ooxml_object.rb, line 14
def obtain_class_variable(var_name, default = {})
  self.class_variable_get(var_name)
rescue NameError
  self.class_variable_set(var_name, default)
end
parse(node, known_namespaces = nil) click to toggle source
# File lib/rubyXL/objects/ooxml_object.rb, line 107
    def parse(node, known_namespaces = nil)
      case node
      when String, IO, Zip::InputStream then node = Nokogiri::XML.parse(node)
      end

      if node.is_a?(Nokogiri::XML::Document) then
        @namespaces = node.namespaces
        node = node.root
#        ignorable_attr = node.attributes['Ignorable']
#        @ignorables << ignorable_attr.value if ignorable_attr
      end

      obj = self.new

      known_attributes = obtain_class_variable(:@@ooxml_attributes)

      content_params = known_attributes['_']
      process_attribute(obj, node.text, content_params) if content_params

      node.attributes.each_pair { |attr_name, attr|
        attr_name = if attr.namespace then "#{attr.namespace.prefix}:#{attr.name}"
                    else attr.name
                    end

        attr_params = known_attributes[attr_name]

        next if attr_params.nil?
        # raise "Unknown attribute [#{attr_name}] for element [#{node.name}]" if attr_params.nil?
        process_attribute(obj, attr.value, attr_params) unless attr_params[:computed]
      }

      known_child_nodes = obtain_class_variable(:@@ooxml_child_nodes)

      unless known_child_nodes.empty?
        known_namespaces ||= obtain_class_variable(:@@ooxml_namespaces)

        node.element_children.each { |child_node|

          ns = child_node.namespace
          prefix = known_namespaces[ns.href] || ns.prefix

          child_node_name = case prefix
                            when '', nil then child_node.name
                            else "#{prefix}:#{child_node.name}"
                            end

          child_node_params = known_child_nodes[child_node_name]
          raise "Unknown child node [#{child_node_name}] for element [#{node.name}]" if child_node_params.nil?
          parsed_object = child_node_params[:class].parse(child_node, known_namespaces)
          if child_node_params[:is_array] then
            index = parsed_object.index_in_collection

            collection = if (self < RubyXL::OOXMLContainerObject) then obj
                         else obj.send(child_node_params[:accessor])
                         end

            if index.nil? then
              collection << parsed_object
            else
              collection[index] = parsed_object
            end
          else
            obj.send("#{child_node_params[:accessor]}=", parsed_object)
          end
        }
      end

      obj
    end