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



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

def clear_xml_load_state
  xml_state.pop
end

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



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

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



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

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



164
165
166
167
# File 'lib/firm/serializer/xml.rb', line 164

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



123
124
125
126
127
128
129
130
# File 'lib/firm/serializer/xml.rb', line 123

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



34
35
36
37
38
# File 'lib/firm/serializer/xml.rb', line 34

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

.load(source) ⇒ Object



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

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)


117
118
119
120
# File 'lib/firm/serializer/xml.rb', line 117

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



150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/firm/serializer/xml.rb', line 150

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



112
113
114
# File 'lib/firm/serializer/xml.rb', line 112

def xml_handlers
  @xml_handlers ||= {}
end

.xml_stateObject



29
30
31
# File 'lib/firm/serializer/xml.rb', line 29

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

.xml_tag_allowed?(xml) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
# File 'lib/firm/serializer/xml.rb', line 44

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