Module: HappyMapper::ClassMethods
- Defined in:
- lib/happymapper/class_methods.rb
Overview
Class methods to be applied to classes that include the HappyMapper module.
Instance Attribute Summary collapse
-
#nokogiri_config_callback ⇒ Proc
readonly
The callback defined through #with_nokogiri_config.
Instance Method Summary collapse
-
#after_parse {|object| ... } ⇒ Object
Register a new after_parse callback, given as a block.
-
#after_parse_callbacks ⇒ Object
The list of registered after_parse callbacks.
-
#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.
- #defined_content ⇒ Object
-
#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(name, href) ⇒ 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.
-
#with_nokogiri_config(&blk) ⇒ Object
Register a config callback according to the block Nokogori expects when calling Nokogiri::XML::Document.parse().
-
#wrap(name, options = {}, &blk) ⇒ Object
There is an XML tag that needs to be known for parsing and should be generated during a to_xml.
Instance Attribute Details
#nokogiri_config_callback ⇒ Proc (readonly)
The callback defined through #with_nokogiri_config.
246 247 248 |
# File 'lib/happymapper/class_methods.rb', line 246 def nokogiri_config_callback @nokogiri_config_callback end |
Instance Method Details
#after_parse {|object| ... } ⇒ Object
Register a new after_parse callback, given as a block.
167 168 169 |
# File 'lib/happymapper/class_methods.rb', line 167 def after_parse(&block) after_parse_callbacks.push(block) end |
#after_parse_callbacks ⇒ Object
The list of registered after_parse callbacks.
158 159 160 |
# File 'lib/happymapper/class_methods.rb', line 158 def after_parse_callbacks @after_parse_callbacks ||= [] end |
#attribute(name, type, options = {}) ⇒ Object
The xml has the following attributes defined.
23 24 25 26 27 |
# File 'lib/happymapper/class_methods.rb', line 23 def attribute(name, type, = {}) attribute = Attribute.new(name, type, ) @attributes[name] = attribute attr_accessor attribute.method_name.intern end |
#attributes ⇒ Array<Attribute>
The elements defined through #attribute.
35 36 37 |
# File 'lib/happymapper/class_methods.rb', line 35 def attributes @attributes.values end |
#content(name, type = String, options = {}) ⇒ Object
The value stored in the text node of the current element.
111 112 113 114 |
# File 'lib/happymapper/class_methods.rb', line 111 def content(name, type = String, = {}) @content = TextNode.new(name, type, ) attr_accessor @content.method_name.intern end |
#defined_content ⇒ Object
365 366 367 |
# File 'lib/happymapper/class_methods.rb', line 365 def defined_content @content if defined? @content end |
#element(name, type, options = {}) ⇒ Object
An element defined in the XML that is parsed.
78 79 80 81 82 |
# File 'lib/happymapper/class_methods.rb', line 78 def element(name, type, = {}) element = Element.new(name, type, ) attr_accessor element.method_name.intern unless @elements[name] @elements[name] = element end |
#elements ⇒ Array<Element>
91 92 93 |
# File 'lib/happymapper/class_methods.rb', line 91 def elements @elements.values end |
#has_many(name, type, options = {}) ⇒ Object
The object has many of these elements in the XML.
151 152 153 |
# File 'lib/happymapper/class_methods.rb', line 151 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.
137 138 139 |
# File 'lib/happymapper/class_methods.rb', line 137 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.
122 123 124 |
# File 'lib/happymapper/class_methods.rb', line 122 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.
179 180 181 182 |
# File 'lib/happymapper/class_methods.rb', line 179 def namespace(namespace = nil) @namespace = namespace if namespace @namespace if defined? @namespace end |
#parse(xml, options = {}) ⇒ Object
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 |
# File 'lib/happymapper/class_methods.rb', line 268 def parse(xml, = {}) # Capture any provided namespaces and merge in any namespaces that have # been registered on the object. namespaces = [:namespaces] || {} namespaces = namespaces.merge(@registered_namespaces) # 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 unless xml.is_a?(Nokogiri::XML::Document) # 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, &nokogiri_config_callback ) end # Now xml is certainly an XML document: Select the root node of the document node = xml.root # merge any namespaces found on the xml node into the namespace hash namespaces = namespaces.merge(xml.collect_namespaces) # 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 the :single option has been specified or we are at the root element # then we are going to return a single element or nil if no nodes are found single = root || [:single] # if a namespace has been provided then set the current namespace to it # or use the namespace provided by the class # or use the 'xmlns' namespace if defined namespace = [:namespace] || self.namespace || namespaces.key?("xmlns") && "xmlns" # 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 find_nodes_to_parse(, namespace, tag_name, namespaces, node, root) end # Nothing matching found, we can go ahead and return return (single ? nil : []) if nodes.empty? # 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| parse_node(n, , namespace, namespaces) 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? && [:in_groups_of] yield part else collection += part end end # If we're parsing a single 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 collection.first else collection end end |
#register_namespace(name, href) ⇒ Object
Register a namespace that is used to persist the object namespace back to XML.
56 57 58 |
# File 'lib/happymapper/class_methods.rb', line 56 def register_namespace(name, href) @registered_namespaces.merge!(name => href) end |
#tag(new_tag_name) ⇒ Object
187 188 189 190 191 192 193 |
# File 'lib/happymapper/class_methods.rb', line 187 def tag(new_tag_name) return if new_tag_name.nil? || (name = new_tag_name.to_s).empty? raise SyntaxError, "Unexpected ':' in tag name #{new_tag_name}" if name.include? ":" @tag_name = name end |
#tag_name ⇒ String
The name of the tag
200 201 202 |
# File 'lib/happymapper/class_methods.rb', line 200 def tag_name @tag_name ||= name && name.to_s.split("::")[-1].downcase end |
#with_nokogiri_config(&blk) ⇒ Object
Register a config callback according to the block Nokogori expects when calling Nokogiri::XML::Document.parse().
255 256 257 |
# File 'lib/happymapper/class_methods.rb', line 255 def with_nokogiri_config(&blk) @nokogiri_config_callback = blk end |
#wrap(name, options = {}, &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
211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/happymapper/class_methods.rb', line 211 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_key = wrapper.inspect @wrapper_anonymous_classes[wrapper_key] = 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| method_name = item.method_name class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method_name} # def property @#{name} ||= # @wrapper ||= wrapper_anonymous_classes['#{wrapper_key}'].new # wrapper_anonymous_classes['#<Class:0x0000555b7d0b9220>'].new @#{name}.#{method_name} # @wrapper.property end # end def #{method_name}=(value) # def property=(value) @#{name} ||= # @wrapper ||= wrapper_anonymous_classes['#{wrapper_key}'].new # wrapper_anonymous_classes['#<Class:0x0000555b7d0b9220>'].new @#{name}.#{method_name} = value # @wrapper.property = value end # end RUBY end has_one name, wrapper, end |