Module: FIRM::Serializable::XML

Defined in:
lib/firm/serializer/xml.rb

Defined Under Namespace

Modules: SerializeClassMethods, SerializeInstanceMethods Classes: HashAdapter

Class Method Summary collapse

Class Method Details

.clear_xml_load_stateObject



37
38
39
# File 'lib/firm/serializer/xml.rb', line 37

def clear_xml_load_state
  xml_state.pop
end

.define_xml_handler(klass, tag = nil, aliasable: false, &block) ⇒ Object



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/firm/serializer/xml.rb', line 130

def define_xml_handler(klass, tag=nil, aliasable: false, &block)
  hnd_klass = Class.new
  hnd_klass.singleton_class.include(HandlerMethods)
  hnd_klass.singleton_class.include(AliasableHandler) if aliasable
  tag_code = if tag
               ::Symbol === tag ? ":#{tag}" : "'#{tag.to_s}'"
             else
               'klass'
             end
  hnd_klass.singleton_class.class_eval <<~__CODE
    def klass; #{klass};  end
    def tag; #{tag_code}; end
  __CODE
  hnd_klass.singleton_class.class_eval &block if block_given?
  register_xml_handler(hnd_klass)
end

.dump(obj, io = nil, pretty: false) ⇒ Object



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
# File 'lib/firm/serializer/xml.rb', line 482

def self.dump(obj, io=nil, pretty: false)
  begin
    # initialize anchor registry
    Serializable::Aliasing.start_anchor_object_registry
    # generate XML document
    xml = to_xml(Nokogiri::XML::Document.new, obj)
    opts = pretty ? { indent: 2 } : { save_with: 0 }
    if io || io.respond_to?(:write)
      xml.write_xml_to(io, opts)
      io
    else
      xml.to_xml(opts)
    end
  ensure
    # reset anchor registry
    Serializable::Aliasing.clear_anchor_object_registry
  end
end

.from_xml(xml) ⇒ Object



161
162
163
164
# File 'lib/firm/serializer/xml.rb', line 161

def from_xml(xml)
  raise Serializable::Exception, "Illegal XML tag #{xml.name}" unless xml_tag_allowed?(xml)
  get_xml_handler(xml.name).from_xml(xml)
end

.get_xml_handler(tag_or_value) ⇒ Object



120
121
122
123
124
125
126
127
# File 'lib/firm/serializer/xml.rb', line 120

def get_xml_handler(tag_or_value)
  h = xml_handlers[tag_or_value.to_s]
  unless h
    tag_or_value = ::Object.const_get(tag_or_value.to_s) unless tag_or_value.is_a?(::Class)
    h = xml_handlers.values.find { |hnd| hnd.klass > tag_or_value } || NullHandler.new(tag_or_value)
  end
  h
end

.init_xml_load_stateObject



31
32
33
34
35
# File 'lib/firm/serializer/xml.rb', line 31

def init_xml_load_state
  xml_state.push({
                   allowed_classes: ::Set.new(Serializable.serializables.to_a.map(&:to_s))
                 })
end

.load(source) ⇒ Object



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
# File 'lib/firm/serializer/xml.rb', line 501

def self.load(source)
  xml = Nokogiri::XML(source)
  return nil unless xml
  begin
    # initialize alias anchor restoration map
    Serializable::Aliasing.start_anchor_references
    # initialize XML loader state
    Serializable::XML.init_xml_load_state
    # load from xml doc
    xml.root ? Serializable::XML.from_xml(xml.root) : nil
  ensure
    # reset XML loader state
    Serializable::XML.clear_xml_load_state
    # reset alias anchor restoration map
    Serializable::Aliasing.clear_anchor_references
  end
end

.register_xml_handler(handler) ⇒ Object

Raises:

  • (RuntimeError)


114
115
116
117
# File 'lib/firm/serializer/xml.rb', line 114

def register_xml_handler(handler)
  raise RuntimeError, "Duplicate XML handler for tag #{handler.tag}" if xml_handlers.has_key?(handler.tag.to_s)
  xml_handlers[handler.tag.to_s] = handler
end

.to_xml(xml, value) ⇒ Object



147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/firm/serializer/xml.rb', line 147

def to_xml(xml, value)
  if Serializable === value
    value.to_xml(xml)
  else
    hk = case value
         when true, false
           value.to_s
         else
           value ? value.class : 'nil'
         end
    get_xml_handler(hk).to_xml(xml, value)
  end
end

.xml_handlersObject



109
110
111
# File 'lib/firm/serializer/xml.rb', line 109

def xml_handlers
  @xml_handlers ||= {}
end

.xml_stateObject



26
27
28
# File 'lib/firm/serializer/xml.rb', line 26

def xml_state
  Serializable.tls_vars[TLS_STATE_KEY] ||= []
end

.xml_tag_allowed?(xml) ⇒ Boolean

Returns:

  • (Boolean)


41
42
43
# File 'lib/firm/serializer/xml.rb', line 41

def xml_tag_allowed?(xml)
  xml.name != 'Object' || (xml.has_attribute?('class') && xml_state.last[:allowed_classes].include?(xml['class']))
end