lib/lutaml/model/serialize.rb in lutaml-model-0.2.1 vs lib/lutaml/model/serialize.rb in lutaml-model-0.3.0
- old
+ new
@@ -1,16 +1,13 @@
# lib/lutaml/model/serialize.rb
-require_relative "json_adapter/standard"
-require_relative "json_adapter/multi_json"
require_relative "yaml_adapter"
require_relative "xml_adapter"
-require_relative "toml_adapter/toml_rb_adapter"
-require_relative "toml_adapter/tomlib_adapter"
require_relative "config"
require_relative "type"
require_relative "attribute"
require_relative "mapping_rule"
+require_relative "mapping_hash"
require_relative "xml_mapping"
require_relative "key_value_mapping"
require_relative "json_adapter"
module Lutaml
@@ -20,18 +17,19 @@
def self.included(base)
base.extend(ClassMethods)
end
- # rubocop:disable Metrics/MethodLength
- # rubocop:disable Metrics/BlockLength
- # rubocop:disable Metrics/AbcSize
- # rubocop:disable Metrics/CyclomaticComplexity
- # rubocop:disable Metrics/PerceivedComplexity
module ClassMethods
attr_accessor :attributes, :mappings
+ def inherited(subclass)
+ super
+
+ subclass.instance_variable_set(:@attributes, @attributes.dup)
+ end
+
def attribute(name, type, options = {})
self.attributes ||= {}
attr = Attribute.new(name, type, options)
attributes[name] = attr
@@ -48,17 +46,21 @@
define_method(format) do |&block|
self.mappings ||= {}
klass = format == :xml ? XmlMapping : KeyValueMapping
self.mappings[format] = klass.new
self.mappings[format].instance_eval(&block)
+
+ if format == :xml && !self.mappings[format].root_element
+ self.mappings[format].root(to_s)
+ end
end
define_method(:"from_#{format}") do |data|
adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
doc = adapter.parse(data)
mapped_attrs = apply_mappings(doc.to_h, format)
- apply_content_mapping(doc, mapped_attrs) if format == :xml
+ # apply_content_mapping(doc, mapped_attrs) if format == :xml
new(mapped_attrs)
end
end
def mappings_for(format)
@@ -77,11 +79,11 @@
def apply_mappings(doc, format)
return apply_xml_mapping(doc) if format == :xml
mappings = mappings_for(format).mappings
- mappings.each_with_object({}) do |rule, hash|
+ mappings.each_with_object(Lutaml::Model::MappingHash.new) do |rule, hash|
attr = if rule.delegate
attributes[rule.delegate].type.attributes[rule.to]
else
attributes[rule.to]
end
@@ -93,19 +95,10 @@
elsif doc.key?(rule.name) || doc.key?(rule.name.to_sym)
doc[rule.name] || doc[rule.name.to_sym]
else
attr.default
end
- # if attr.collection?
- # value = (value || []).map do |v|
- # attr.type <= Serialize ? attr.type.new(v) : v
- # end
- # elsif value.is_a?(Hash) && attr.type <= Serialize
- # value = attr.type.new(value)
- # else
- # value = attr.type.cast(value)
- # end
if attr.collection?
value = (value || []).map do |v|
attr.type <= Serialize ? attr.type.apply_mappings(v, format) : v
end
@@ -120,98 +113,121 @@
hash[rule.to] = value
end
end
end
- def apply_xml_mapping(doc)
+ def apply_xml_mapping(doc, caller_class: nil, mixed_content: false)
+ return unless doc
+
mappings = mappings_for(:xml).mappings
- mappings.each_with_object({}) do |rule, hash|
+ if doc.is_a?(Array)
+ raise "May be `collection: true` is" \
+ "missing for #{self} in #{caller_class}"
+ end
+
+ mapping_hash = Lutaml::Model::MappingHash.new
+ mapping_hash.item_order = doc.item_order
+ mapping_hash.ordered = mappings_for(:xml).mixed_content? || mixed_content
+
+ mappings.each_with_object(mapping_hash) do |rule, hash|
attr = attributes[rule.to]
raise "Attribute '#{rule.to}' not found in #{self}" unless attr
- value = if rule.name
- doc[rule.name.to_s] || doc[rule.name.to_sym]
- else
+ is_content_mapping = rule.name.nil?
+ value = if is_content_mapping
doc["text"]
+ else
+ doc[rule.name.to_s] || doc[rule.name.to_sym]
end
- # if attr.collection?
- # value = (value || []).map do |v|
- # attr.type <= Serialize ? attr.type.from_hash(v) : v
- # end
- # elsif value.is_a?(Hash) && attr.type <= Serialize
- # value = attr.type.cast(value)
- # elsif value.is_a?(Array)
- # value = attr.type.cast(value.first["text"]&.first)
- # end
-
if attr.collection?
+ if value && !value.is_a?(Array)
+ value = [value]
+ end
+
value = (value || []).map do |v|
if attr.type <= Serialize
- attr.type.apply_xml_mapping(v)
- else
+ attr.type.apply_xml_mapping(v, caller_class: self, mixed_content: rule.mixed_content)
+ elsif v.is_a?(Hash)
v["text"]
+ else
+ v
end
end
elsif attr.type <= Serialize
- value = attr.type.apply_xml_mapping(value) if value
+ value = attr.type.apply_xml_mapping(value, caller_class: self, mixed_content: rule.mixed_content)
else
if value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
value = value["text"]
end
- value = attr.type.cast(value)
+ value = attr.type.cast(value) unless is_content_mapping
end
+
hash[rule.to] = value
end
end
-
- def apply_content_mapping(doc, mapped_attrs)
- content_mapping = mappings_for(:xml).content_mapping
- return unless content_mapping
-
- content = doc.root.children.select(&:text?).map(&:text)
- mapped_attrs[content_mapping.to] = content
- end
end
- # rubocop:disable Layout/LineLength
+ attr_reader :element_order
+
def initialize(attrs = {})
return unless self.class.attributes
+ if attrs.is_a?(Lutaml::Model::MappingHash)
+ @ordered = attrs.ordered?
+ @element_order = attrs.item_order
+ end
+
self.class.attributes.each do |name, attr|
- value = if attrs.key?(name)
- attrs[name]
- elsif attrs.key?(name.to_sym)
- attrs[name.to_sym]
- elsif attrs.key?(name.to_s)
- attrs[name.to_s]
- else
- attr.default
- end
+ value = attr_value(attrs, name, attr)
- value = if attr.collection?
- (value || []).map do |v|
- if v.is_a?(Hash)
- attr.type.new(v)
- else
- Lutaml::Model::Type.cast(
- v, attr.type
- )
- end
- end
- elsif value.is_a?(Hash) && attr.type != Lutaml::Model::Type::Hash
- attr.type.new(value)
- else
- Lutaml::Model::Type.cast(value, attr.type)
- end
send(:"#{name}=", ensure_utf8(value))
end
end
- # rubocop:enable Layout/LineLength
+ def attr_value(attrs, name, attr_rule)
+ value = if attrs.key?(name)
+ attrs[name]
+ elsif attrs.key?(name.to_sym)
+ attrs[name.to_sym]
+ elsif attrs.key?(name.to_s)
+ attrs[name.to_s]
+ else
+ attr_rule.default
+ end
+
+ if attr_rule.collection? || value.is_a?(Array)
+ (value || []).map do |v|
+ if v.is_a?(Hash)
+ attr_rule.type.new(v)
+ else
+ Lutaml::Model::Type.cast(
+ v, attr_rule.type
+ )
+ end
+ end
+ elsif value.is_a?(Hash) && attr_rule.type != Lutaml::Model::Type::Hash
+ attr_rule.type.new(value)
+ else
+ Lutaml::Model::Type.cast(value, attr_rule.type)
+ end
+ end
+
+ def ordered?
+ @ordered
+ end
+
+ def key_exist?(hash, key)
+ hash.key?(key) || hash.key?(key.to_sym) || hash.key?(key.to_s)
+ end
+
+ def key_value(hash, key)
+ hash[key] || hash[key.to_sym] || hash[key.to_s]
+ end
+
# TODO: Make this work
# FORMATS.each do |format|
# define_method("to_#{format}") do |options = {}|
# adapter = Lutaml::Model::Config.send("#{format}_adapter")
# representation = if format == :yaml
@@ -323,13 +339,8 @@
end.transform_values { |v| ensure_utf8(v) }
else
value
end
end
- # rubocop:enable Metrics/MethodLength
- # rubocop:enable Metrics/BlockLength
- # rubocop:enable Metrics/AbcSize
- # rubocop:enable Metrics/CyclomaticComplexity
- # rubocop:enable Metrics/PerceivedComplexity
end
end
end