Module: ASpaceImport::XML::SAX

Defined in:
backend/app/converters/lib/xml_sax.rb

Defined Under Namespace

Modules: ClassMethods

Class Method Summary collapse

Instance Method Summary collapse

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(*args) ⇒ Object



61
62
# File 'backend/app/converters/lib/xml_sax.rb', line 61

def method_missing(*args)
end

Class Method Details

.included(base) ⇒ Object



56
57
58
# File 'backend/app/converters/lib/xml_sax.rb', line 56

def self.included(base)
  base.extend(ClassMethods)
end

Instance Method Details

#ancestor(*types) ⇒ Object



272
273
274
275
276
277
# File 'backend/app/converters/lib/xml_sax.rb', line 272

def ancestor(*types)
  queue_offset = (@context_nodes.has_key?(@node_name) && @context_nodes[@node_name][@node_depth]) ? -2 : -1

  obj = @batch.working_area[0..queue_offset].reverse.find { |o| types.map {|t| t.to_s }.include?(o.class.record_type)}
  block_given? ? yield(obj) : obj
end

#append(obj = context_obj, property, value) ⇒ Object



203
204
205
206
207
208
209
210
211
212
# File 'backend/app/converters/lib/xml_sax.rb', line 203

def append(obj = context_obj, property, value)
  property_type = ASpaceImport::Utils.get_property_type(obj.class.schema['properties'][property.to_s])
  return unless property_type[0].match(/string/) && value.is_a?(String)
  filtered_value = ASpaceImport::Utils.value_filter(property_type[0]).call(value)
  if obj.send(property)
    obj.send(property).send(:<<, filtered_value)
  else
    obj.send("#{property}=", filtered_value)
  end
end

#att(attribute, namespace = nil) ⇒ Object



280
281
282
283
284
285
286
287
288
289
290
# File 'backend/app/converters/lib/xml_sax.rb', line 280

def att(attribute, namespace = nil)
  if namespace && @namespace_mappings && @namespace_mappings[namespace]
    attribute = "#{@namespace_mappings[namespace]}:#{attribute}"
  end
  att_pair = @node.attributes.find {|a| a[0] == attribute}
  if att_pair.nil?
    nil
  else
    att_pair[1]
  end
end

#close_context(type) ⇒ Object



177
178
179
180
181
182
183
184
185
186
# File 'backend/app/converters/lib/xml_sax.rb', line 177

def close_context(type)
  if @batch.working_area.last.jsonmodel_type != type.to_s
    Log.debug(@batch.working_area.last.inspect)
    raise "Unexpected Object Type in Queue: Expected #{type} got #{@batch.working_area.last.jsonmodel_type}"
  end

  @proxies.discharge_proxy("#{@batch.working_area.last.jsonmodel_type}-#{@contexts.length}", @batch.working_area.last)
  @contexts.pop
  @batch.flush_last
end

#contextObject



293
294
295
# File 'backend/app/converters/lib/xml_sax.rb', line 293

def context
  @contexts.last
end

#context_objObject



303
304
305
# File 'backend/app/converters/lib/xml_sax.rb', line 303

def context_obj
  @batch.working_area.last
end

#full_contextObject



298
299
300
# File 'backend/app/converters/lib/xml_sax.rb', line 298

def full_context
  @contexts
end

#handle_closer(node) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# File 'backend/app/converters/lib/xml_sax.rb', line 137

def handle_closer(node)
  @node_shadow = nil
  @empty_node = false

  node_info = node.is_a?(Array) ? node : [node.local_name, node.depth]

  if self.respond_to?("_closing_#{@node_name}")
    self.send("_closing_#{@node_name}", node)
  end

  if @context_nodes[node_info[0]] && @context_nodes[node_info[0]][node_info[1]]
    @context_nodes[node_info[0]][node_info[1]].reverse.each do |type|
      close_context(type)
    end
    @context_nodes[node_info[0]].delete_at(node_info[1])
  end
  @stickies.pop if @stickies.last == node_info[0]
end

#handle_opener(node, empty_node) ⇒ Object



108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'backend/app/converters/lib/xml_sax.rb', line 108

def handle_opener(node, empty_node)
  @node_name = node.local_name
  @node_depth = node.depth
  @node_shadow = [node.local_name, node.depth]

  @node = node
  @empty_node = empty_node


  # constrained handlers, e.g. publication/date
  @stickies.each_with_index do |prefix, i|
    self.send("_#{@stickies[i..@stickies.length].join('_')}_#{@node_name}", node)
  end

  # unconstrained handlers, e.g., date
  self.send("_#{@node_name}", node)

  # config calls for constrained handlers on this path
  make_sticky(@node_name) if self.class.make_sticky?(@node_name)

  @node = nil
end

#handle_text(node) ⇒ Object



132
133
134
# File 'backend/app/converters/lib/xml_sax.rb', line 132

def handle_text(node)
  @proxies.discharge_proxy(:text, node.value)
end

#inner_xmlObject



189
190
191
# File 'backend/app/converters/lib/xml_sax.rb', line 189

def inner_xml
  @node.inner_xml.gsub("&", "&amp;").strip
end

#make_sticky(node_name) ⇒ Object



308
309
310
# File 'backend/app/converters/lib/xml_sax.rb', line 308

def make_sticky(node_name)
  @stickies << node_name
end

#nodeObject



267
268
269
# File 'backend/app/converters/lib/xml_sax.rb', line 267

def node
  @node
end

#open_context(type, properties = {}) {|obj| ... } ⇒ Object Also known as: make

Yields:

  • (obj)


157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'backend/app/converters/lib/xml_sax.rb', line 157

def open_context(type, properties = {})
  obj = ASpaceImport::JSONModel(type).new
  obj["import_context"]= pprint_current_node

  @contexts.push(type)
  @batch << obj
  @context_nodes[@node_name] ||= []
  @context_nodes[@node_name][@node_depth] ||= []
  @context_nodes[@node_name][@node_depth] << type
  properties.each do |k, v|
    set obj, k, v
  end

  yield obj if block_given?
end

#outer_xmlObject



193
194
195
# File 'backend/app/converters/lib/xml_sax.rb', line 193

def outer_xml
  @node.outer_xml.strip
end

#pprint_current_nodeObject



197
198
199
200
201
# File 'backend/app/converters/lib/xml_sax.rb', line 197

def pprint_current_node
  Nokogiri::XML::Builder.new( :encoding => 'UTF-8' ) {|b|
    b.send(@node.name.intern, @node.attributes).cdata(" ... ")
  }.doc.root.to_s
end

#proxy(record_type = context) ⇒ Object

Since it won’t do to push subrecords into parent records until the subrecords are complete, a proxy can be assigned instead, and the proxy will discharge the JSON subrecord once it is complete



262
263
264
# File 'backend/app/converters/lib/xml_sax.rb', line 262

def proxy(record_type = context)
  @proxies.get_proxy_for("#{record_type}-#{@contexts.length}", record_type)
end

#runObject



65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'backend/app/converters/lib/xml_sax.rb', line 65

def run
  reader = SAXXMLReader.new(IO.read(@input_file).gsub(/\s\s+/, " "))

  @contexts = []
  @context_nodes = {}
  @proxies = ASpaceImport::RecordProxyMgr.new
  @stickies = []
  # another hack for noko:
  @node_shadow = nil
  @empty_node = false

  self.class.ensure_configuration

  reader.each_with_index do |(node, is_node_empty), i|
    case node.node_type

    when 1

      next if @ignore

      # Nokogiri Reader won't create events for closing tags on empty nodes
      # https://github.com/sparklemotion/nokogiri/issues/928
      # handle_closer(node) if node.self_closing? #<--- don't do this it's horribly slow
      if @node_shadow && @empty_node
        handle_closer(@node_shadow)
      end

      #we do not bother with empty and attributesless nodes. however, a
      #node can be empty as long as it has attributes
      handle_opener(node, is_node_empty) unless ( is_node_empty && !node.attributes? )

    when 3
      handle_text(node)
    when 15
      if @node_shadow && node.local_name != @node_shadow[0]
        handle_closer(@node_shadow)
      end
      handle_closer(node)
    end
  end
end

#set(*args) ⇒ Object



215
216
217
# File 'backend/app/converters/lib/xml_sax.rb', line 215

def set(*args)
  set_property(*args)
end

#set_property(obj = context_obj, property, value) ⇒ Object



220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'backend/app/converters/lib/xml_sax.rb', line 220

def set_property(obj = context_obj, property, value)
  if obj.nil?
    Log.warn "Tried to set property #{property} on an object that couldn't be found"
    return false
  end

  if property.nil?
    Log.warn("Can't set <#{obj.class.record_type}> <#{property}>: nil value")
    return false
  end

  begin
    property_type = ASpaceImport::Utils.get_property_type(obj.class.schema['properties'][property.to_s])
  rescue NoMethodError
    raise "Having some trouble finding a property <#{property}> on a <#{obj.class.record_type}> object"
  end

  if value.is_a?(ASpaceImport::RecordProxy)
    value.on_discharge(self, :set_property, obj, property)
  else
    if value.nil?
      # Log.debug("Given a nil value for <#{obj.class.record_type}><#{property}>")
    else
      filtered_value = ASpaceImport::Utils.value_filter(property_type[0]).call(value)
      if property_type[0].match /list$/
        obj.send("#{property}").push(filtered_value)
      else
        # if obj.send("#{property}")
        #   Log.warn("Setting a property that has already been set")
        # end
        obj.send("#{property}=", filtered_value)
      end
    end
  end
end