Module: XMLUtilities

Overview

Various utilities for dealing with Ruby classes and XML mappings. Normally included in an existing module.

Instance Method Summary collapse

Instance Method Details

#collapse_namespaces(el, existing_namespaces = nil) ⇒ Object



217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/voruby/misc.rb', line 217

def collapse_namespaces(el, existing_namespaces=nil)
  return if !el or !el.has_elements?

  existing_namespaces ||= el.namespaces

  el.children.each do |child|
    child.namespaces.each do |prefix, uri|
      child.delete_namespace(prefix) if existing_namespaces.has_key?(prefix)
    end

    collapse_namespaces(child, existing_namespaces.merge(child.namespaces))
  end
end

#current_moduleObject



136
137
138
139
# File 'lib/voruby/misc.rb', line 136

def current_module
  target = self.is_a?(Class) ? self : self.class
  target.to_s.split('::')[0..-2].inject(Object){ |x, y| x.const_get(y) }
end

#element_from(xml) ⇒ Object



128
129
130
# File 'lib/voruby/misc.rb', line 128

def element_from(xml)
  xml.is_a?(REXML::Element) ? xml : REXML::Document.new(xml).root
end

#element_name_from(klass) ⇒ Object



132
133
134
# File 'lib/voruby/misc.rb', line 132

def element_name_from(klass)
  klass.respond_to?(:element_name) ? klass.element_name : klass.to_s.split('::').last
end

#find_class_with_name(name, mod = nil) ⇒ Object



149
150
151
152
153
154
155
156
157
# File 'lib/voruby/misc.rb', line 149

def find_class_with_name(name, mod=nil)
  mod ||= current_module
  mod.const_get(
    mod.constants.find { |c|
      obj = mod.const_get(c)
      obj.is_a?(Class) and name == element_name_from(obj)
    }
  )
end

#find_classes_of_type(type, mod = nil) ⇒ Object



141
142
143
144
145
146
147
# File 'lib/voruby/misc.rb', line 141

def find_classes_of_type(type, mod=nil)
  mod ||= current_module
  mod.constants.find_all{ |c|
    obj = mod.const_get(c)
    obj.is_a?(Class) and obj.allocate().is_a?(type)
  }
end

#find_elements(root, path, ns = nil) ⇒ Object



173
174
175
176
177
178
# File 'lib/voruby/misc.rb', line 173

def find_elements(root, path, ns=nil)
  ns ||= obj_ns
  root.children.find_all{ |n|
    n.respond_to?(:name) and path.include?(n.name) and n.namespace == ns.uri
  }
end

#obj_ns(mod_name = nil) ⇒ Object



168
169
170
171
# File 'lib/voruby/misc.rb', line 168

def obj_ns(mod_name=nil)
  mod_name ||= current_module.to_s
  NAMESPACES[mod_name]
end

#xml_to_obj(node, klass = nil, first_only = false, mod = nil) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/voruby/misc.rb', line 180

def xml_to_obj(node, klass=nil, first_only=false, mod=nil)
  raise 'xml node is required' if !node
  
  if klass # style 1 (as in STC): element name + substitution groups determines object
    mod ||= current_module
    objects = find_elements(node, xpath_for(klass, mod), obj_ns(mod.to_s)).collect{ |n| find_class_with_name(n.name, mod).from_xml(n) }
    first_only ? objects.first : objects
  else # style 2 (as in ADQL): xsi:type determines object
    # find the xsi:type attribute
    xsi_type_def = node.attributes.get_attribute_ns(XSI_NAMESPACE.uri, 'type')
    raise "unable to find xsi:type attribute for #{node}" if !xsi_type_def

    # extract its namespace prefix (if none is present, use the default)
    # and the type name.
    type_def = xsi_type_def.value.split(':')
    type_ns_prefix = type_def.size == 2 ? type_def.first : nil
    type_ns_uri = node.namespace(type_ns_prefix)
    type_name = type_def.size == 2 ? type_def.last : type_def.first

    # find the module that corresponds to the namespace prefix
    ns_info = NAMESPACES.find { |key, value| value.uri == type_ns_uri }
    raise "unable to find module corresponding to #{type_ns_uri}" if !ns_info
    mod = ns_info.first.to_s.split('::').inject(Object){ |x, y| x.const_get(y) }

    # search through the module for the class who's xml_type is equal
    # to the type name
    klass = mod.constants.find { |c|
      obj = mod.const_get(c)
      obj.is_a?(Class) and obj.respond_to?(:xml_type) and obj.respond_to?(:from_xml) and obj.xml_type == type_name
    }.split('::').inject(Object){ |x, y| x.const_get(y) }
    raise "unable to find class with an xml_type of #{type_name}" if !klass

    # instantiate the class
    klass.from_xml(node)
  end
end

#xpath_for(type, mod = nil) ⇒ Object



159
160
161
162
163
164
165
166
# File 'lib/voruby/misc.rb', line 159

def xpath_for(type, mod=nil)
  mod ||= current_module
  find_classes_of_type(type, mod).collect { |klass_name|
    element_name_from(
      klass_name.split('::').inject(Object){ |x, y| x.const_get(y) }
    )
  }
end