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