lib/goldendocx/element.rb in goldendocx-0.2.2 vs lib/goldendocx/element.rb in goldendocx-0.2.3
- old
+ new
@@ -1,21 +1,15 @@
# frozen_string_literal: true
-require 'active_support/core_ext/string/inflections'
+require 'goldendocx/has_attributes'
+require 'goldendocx/has_children'
-ActiveSupport::Inflector.inflections do |inflect|
- inflect.uncountable 'extents', 'image_data', 'data'
- inflect.irregular 'axis', 'axes'
-
- inflect.uncountable 'values' # TODO: Find better names
-end
-
module Goldendocx
module Element
- def self.included(base)
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
+ include Goldendocx::HasAttributes
+ include Goldendocx::HasChildren
module ClassMethods
def tag(*args)
@tag = args.first if args.any?
@tag
@@ -24,169 +18,68 @@
def namespace(*args)
@namespace = args.first if args.any?
@namespace
end
- # alias_name: nil
- # readonly: false
- # default: nil
- # namespace: nil
- # setter: nil
- def attribute(name, **options)
- named = name.to_s
- attributes[named] = {
- alias_name: options[:alias_name],
- default: options[:default],
- namespace: options[:namespace],
- method: options[:method]
- }.compact
-
- readonly = options[:readonly] || false
- if readonly
- attr_reader named
- elsif options[:method]
- attr_writer named
- else
- attr_accessor named
- end
+ def tag_name
+ @tag_name ||= [namespace, tag].compact.join(':')
end
- def attributes
- @attributes ||= {}
+ def parse(xml_string)
+ root_node = Goldendocx.xml_serializer.parse(xml_string).root
+ read_from(root_node)
end
- def create_children_getter(name)
- options = children[name]
- class_name = options[:class_name]
- multiple = options[:multiple]
- auto_build = options[:auto_build]
-
- define_method name do
- return instance_variable_get("@#{name}") if instance_variable_defined?("@#{name}")
-
- default_value = if multiple
- []
- else
- auto_build ? Kernel.const_get(class_name).new : nil
- end
- instance_variable_set("@#{name}", default_value)
- end
+ def adapt?(xml_node)
+ tag_name == xml_node.tag_name
end
- def create_children_setter(name)
- options = children[name]
- class_name = options[:class_name]
+ def read_from(xml_node)
+ return unless adapt?(xml_node)
- define_method "#{name}=" do |value|
- value = value.to_s if value && class_name == 'String'
- instance_variable_set("@#{name}", value)
- end
+ instance = new
+ instance.read_attributes(xml_node)
+ instance.read_children(xml_node)
+ instance
end
- def create_children_builder(name)
- options = children[name]
- class_name = options[:class_name]
- multiple = options[:multiple]
-
- define_method "build_#{name.to_s.singularize}" do |**attributes|
- child = Kernel.const_get(class_name).new
- attributes.each { |key, value| child.send("#{key}=", value) if child.respond_to?("#{key}=") }
- multiple ? send(name) << child : instance_variable_set("@#{name}", child)
- child
- end
- end
-
- def embeds_one(name, class_name:, auto_build: false)
- warning_naming_suggestion(name, name.to_s.singularize)
-
- children[name] = { class_name: class_name, multiple: false, auto_build: auto_build }
- create_children_getter(name)
- create_children_setter(name)
- create_children_builder(name)
- end
-
- def embeds_many(name, class_name:)
- warning_naming_suggestion(name, name.to_s.pluralize)
-
- children[name] = { class_name: class_name, multiple: true, auto_build: false }
- create_children_getter(name)
- create_children_builder(name)
- end
-
- def children
- @children ||= {}
- end
-
def concerning_ancestors
ancestors.filter { |ancestor| ancestor.include?(Goldendocx::Element) }
end
-
- private
-
- # :nocov:
- def warning_naming_suggestion(name, suggestion_name)
- return if suggestion_name == name.to_s
-
- location = caller.find { |c| c.include?('goldendocx/') && !c.include?('goldendocx/element.rb') }
- warn "warning: [embeds_one] `#{name}` better be singular `#{suggestion_name}` at #{location}"
- end
- # :nocov:
end
- def attributes
- self.class.attributes.each_with_object({}) do |(name, options), result|
- value = public_send(options[:method] || name) || options[:default]
- next if value.nil?
-
- key = [options[:namespace], options[:alias_name] || name].compact.join(':')
- result[key] = value
- end
+ def initialize(attributes = nil)
+ attributes ||= {}
+ assign_attributes(**attributes)
end
- def assign_attributes(**attributes)
- attributes.each { |key, value| send("#{key}=", value) if respond_to?("#{key}=") }
- end
-
def tag
self.class.concerning_ancestors.find { |ancestor| ancestor.tag.present? }&.tag
end
def namespace
self.class.concerning_ancestors.find { |ancestor| ancestor.namespace.present? }&.namespace
end
- def root_tag
- @root_tag ||= [namespace, tag].compact.join(':')
+ def tag_name
+ @tag_name ||= [namespace, tag].compact.join(':')
end
- def siblings
- return [] unless self.class.superclass.include?(Goldendocx::Element)
-
- self.class.superclass.children.keys.flat_map { |name| send(name) }
+ def to_element(**context, &block)
+ Goldendocx.xml_serializer.build_element(tag_name, **context) { |xml| build_element(xml, &block) }
end
- def children
- self.class.children.keys.flat_map do |name|
- send(name)
- end.concat(siblings).compact
+ def to_xml(&block)
+ Goldendocx.xml_serializer.build_xml(tag_name) { |xml| build_element(xml, &block) }
end
- def to_element(**context)
- Goldendocx.xml_serializer.build_element(root_tag, **context) do |xml|
- attributes.each { |name, value| xml[name] = value }
- children.each { |child| xml << child }
+ def build_element(xml)
+ attributes.each { |name, value| xml[name] = value }
+ unparsed_attributes.each { |name, value| xml[name] = value }
- yield(xml) if block_given?
- end
- end
+ children.each { |child| xml << child }
+ unparsed_children.each { |child| xml << child }
- def to_xml
- Goldendocx.xml_serializer.build_xml(root_tag) do |xml|
- attributes.each { |name, value| xml[name] = value }
-
- yield(xml) if block_given?
-
- children.each { |child| xml << child }
- end
+ yield(xml) if block_given?
end
end
end