Module: HappyMapper::ClassMethods
- Defined in:
- lib/happymapper.rb
Instance Method Summary collapse
-
#attribute(name, type, options = {}) ⇒ Object
The xml has the following attributes defined.
-
#attributes ⇒ Array<Attribute>
The elements defined through #attribute.
-
#content(name, type = String, options = {}) ⇒ Object
The value stored in the text node of the current element.
-
#element(name, type, options = {}) ⇒ Object
An element defined in the XML that is parsed.
- #elements ⇒ Array<Element>
-
#has_many(name, type, options = {}) ⇒ Object
The object has many of these elements in the XML.
-
#has_one(name, type, options = {}) ⇒ Object
The object has one of these elements in the XML.
-
#has_xml_content ⇒ Object
Sets the object to have xml content, this will assign the XML contents that are parsed to the attribute accessor xml_content.
-
#namespace(namespace = nil) ⇒ Object
Specify a namespace if a node and all its children are all namespaced elements.
- #parse(xml, options = {}) ⇒ Object
-
#register_namespace(namespace, ns) ⇒ Object
Register a namespace that is used to persist the object namespace back to XML.
- #tag(new_tag_name) ⇒ Object
-
#tag_name ⇒ String
The name of the tag.
-
#wrap(name, &blk) ⇒ Object
There is an XML tag that needs to be known for parsing and should be generated during a to_xml.
Instance Method Details
#attribute(name, type, options = {}) ⇒ Object
The xml has the following attributes defined.
40 41 42 43 44 45 |
# File 'lib/happymapper.rb', line 40 def attribute(name, type, ={}) attribute = Attribute.new(name, type, ) @attributes[to_s] ||= [] @attributes[to_s] << attribute attr_accessor attribute.method_name.intern end |
#attributes ⇒ Array<Attribute>
The elements defined through #attribute.
53 54 55 |
# File 'lib/happymapper.rb', line 53 def attributes @attributes[to_s] || [] end |
#content(name, type = String, options = {}) ⇒ Object
The value stored in the text node of the current element.
130 131 132 133 |
# File 'lib/happymapper.rb', line 130 def content(name, type=String, ={}) @content = TextNode.new(name, type, ) attr_accessor @content.method_name.intern end |
#element(name, type, options = {}) ⇒ Object
An element defined in the XML that is parsed.
96 97 98 99 100 101 |
# File 'lib/happymapper.rb', line 96 def element(name, type, ={}) element = Element.new(name, type, ) @elements[to_s] ||= [] @elements[to_s] << element attr_accessor element.method_name.intern end |
#elements ⇒ Array<Element>
110 111 112 |
# File 'lib/happymapper.rb', line 110 def elements @elements[to_s] || [] end |
#has_many(name, type, options = {}) ⇒ Object
The object has many of these elements in the XML.
170 171 172 |
# File 'lib/happymapper.rb', line 170 def has_many(name, type, ={}) element name, type, {:single => false}.merge() end |
#has_one(name, type, options = {}) ⇒ Object
The object has one of these elements in the XML. If there are multiple, the last one will be set to this value.
156 157 158 |
# File 'lib/happymapper.rb', line 156 def has_one(name, type, ={}) element name, type, {:single => true}.merge() end |
#has_xml_content ⇒ Object
Sets the object to have xml content, this will assign the XML contents that are parsed to the attribute accessor xml_content. The object will respond to the method #xml_content and will return the XML data that it has parsed.
141 142 143 |
# File 'lib/happymapper.rb', line 141 def has_xml_content attr_accessor :xml_content end |
#namespace(namespace = nil) ⇒ Object
Specify a namespace if a node and all its children are all namespaced elements. This is simpler than passing the :namespace option to each defined element.
182 183 184 185 |
# File 'lib/happymapper.rb', line 182 def namespace(namespace = nil) @namespace = namespace if namespace @namespace end |
#parse(xml, options = {}) ⇒ Object
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 |
# File 'lib/happymapper.rb', line 243 def parse(xml, = {}) # create a local copy of the objects namespace value for this parse execution namespace = @namespace # If the XML specified is an Node then we have what we need. if xml.is_a?(Nokogiri::XML::Node) && !xml.is_a?(Nokogiri::XML::Document) node = xml else # If xml is an XML document select the root node of the document if xml.is_a?(Nokogiri::XML::Document) node = xml.root else # Attempt to parse the xml value with Nokogiri XML as a document # and select the root element xml = Nokogiri::XML(xml, nil, nil, Nokogiri::XML::ParseOptions::STRICT) node = xml.root end # if the node name is equal to the tag name then the we are parsing the # root element and that is important to record so that we can apply # the correct xpath on the elements of this document. root = node.name == tag_name end # if any namespaces have been provied then we should capture those and then # merge them with any namespaces found on the xml node and merge all that # with any namespaces that have been registered on the object namespaces = [:namespaces] || {} namespaces = namespaces.merge(xml.collect_namespaces) if xml.respond_to?(:collect_namespaces) namespaces = namespaces.merge(@registered_namespaces) # if a namespace has been provided then set the current namespace to it # or set the default namespace to the one defined under 'xmlns' # or set the default namespace to the namespace that matches 'happymapper's if [:namespace] namespace = [:namespace] elsif namespaces.has_key?("xmlns") namespace ||= DEFAULT_NS default_namespace = namespaces.delete("xmlns") namespaces[namespace] ||= default_namespace namespaces["xmlns:#{namespaces.key(default_namespace)}"] = default_namespace elsif namespaces.has_key?(DEFAULT_NS) namespace ||= DEFAULT_NS end # from the options grab any nodes present and if none are present then # perform the following to find the nodes for the given class nodes = .fetch(:nodes) do # when at the root use the xpath '/' otherwise use a more gready './/' # unless an xpath has been specified, which should overwrite default # and finally attach the current namespace if one has been defined # xpath = (root ? '/' : './/') xpath = [:xpath].to_s.sub(/([^\/])$/, '\1/') if [:xpath] xpath += "#{namespace}:" if namespace nodes = [] # when finding nodes, do it in this order: # 1. specified tag # 2. name of element # 3. tag_name (derived from class name by default) [[:tag], [:name], tag_name].compact.each do |xpath_ext| begin nodes = node.xpath(xpath + xpath_ext.to_s, namespaces) rescue break end break if nodes && !nodes.empty? end nodes end # Nothing matching found, we can go ahead and return return ( ( [:single] || root ) ? nil : [] ) if nodes.size == 0 # If the :limit option has been specified then we are going to slice # our node results by that amount to allow us the ability to deal with # a large result set of data. limit = [:in_groups_of] || nodes.size # If the limit of 0 has been specified then the user obviously wants # none of the nodes that we are serving within this batch of nodes. return [] if limit == 0 collection = [] nodes.each_slice(limit) do |slice| part = slice.map do |n| # If an existing HappyMapper object is provided, update it with the # values from the xml being parsed. Otherwise, create a new object obj = [:update] ? [:update] : new attributes.each do |attr| obj.send("#{attr.method_name}=",attr.from_xml_node(n, namespace, namespaces)) end elements.each do |elem| obj.send("#{elem.method_name}=",elem.from_xml_node(n, namespace, namespaces)) end if @content obj.send("#{@content.method_name}=",@content.from_xml_node(n, namespace, namespaces)) end # If the HappyMapper class has the method #xml_value=, # attr_writer :xml_value, or attr_accessor :xml_value then we want to # assign the current xml that we just parsed to the xml_value if obj.respond_to?('xml_value=') n.namespaces.each {|name,path| n[name] = path } obj.xml_value = n.to_xml end # If the HappyMapper class has the method #xml_content=, # attr_write :xml_content, or attr_accessor :xml_content then we want to # assign the child xml that we just parsed to the xml_content if obj.respond_to?('xml_content=') n = n.children if n.respond_to?(:children) obj.xml_content = n.to_xml end # collect the object that we have created obj end # If a block has been provided and the user has requested that the objects # be handled in groups then we should yield the slice of the objects to them # otherwise continue to lump them together if block_given? and [:in_groups_of] yield part else collection += part end end # per http://libxml.rubyforge.org/rdoc/classes/LibXML/XML/Document.html#M000354 nodes = nil # If the :single option has been specified or we are at the root element # then we are going to return the first item in the collection. Otherwise # the return response is going to be an entire array of items. if [:single] or root collection.first else collection end end |
#register_namespace(namespace, ns) ⇒ Object
Register a namespace that is used to persist the object namespace back to XML.
74 75 76 |
# File 'lib/happymapper.rb', line 74 def register_namespace(namespace, ns) @registered_namespaces.merge!({namespace => ns}) end |
#tag(new_tag_name) ⇒ Object
190 191 192 |
# File 'lib/happymapper.rb', line 190 def tag(new_tag_name) @tag_name = new_tag_name.to_s unless new_tag_name.nil? || new_tag_name.to_s.empty? end |
#tag_name ⇒ String
The name of the tag
199 200 201 |
# File 'lib/happymapper.rb', line 199 def tag_name @tag_name ||= to_s.split('::')[-1].downcase end |
#wrap(name, &blk) ⇒ Object
There is an XML tag that needs to be known for parsing and should be generated during a to_xml. But it doesn’t need to be a class and the contained elements should be made available on the parent class
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/happymapper.rb', line 210 def wrap(name, &blk) # Get an anonymous HappyMapper that has 'name' as its tag and defined # in '&blk'. Then save that to a class instance variable for later use wrapper = AnonymousWrapperClassFactory.get(name, &blk) @wrapper_anonymous_classes[wrapper.inspect] = wrapper # Create getter/setter for each element and attribute defined on the anonymous HappyMapper # onto this class. They get/set the value by passing thru to the anonymous class. passthrus = wrapper.attributes + wrapper.elements passthrus.each do |item| class_eval %{ def #{item.method_name} @#{name} ||= self.class.instance_variable_get('@wrapper_anonymous_classes')['#{wrapper.inspect}'].new @#{name}.#{item.method_name} end def #{item.method_name}=(value) @#{name} ||= self.class.instance_variable_get('@wrapper_anonymous_classes')['#{wrapper.inspect}'].new @#{name}.#{item.method_name} = value end } end has_one name, wrapper end |