Module: ROXML::ClassMethods::Declarations
- Defined in:
- lib/roxml.rb
Overview
:nodoc:
Instance Method Summary collapse
-
#xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares an accesser to a certain xml element, whether an attribute, a node, or a typed collection of nodes.
-
#xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares a writable xml reference.
-
#xml_construct(*args) ⇒ Object
This method is deprecated, please use xml_initialize instead.
-
#xml_name(name) ⇒ Object
Sets the name of the XML element that represents this class.
-
#xml_name? ⇒ Boolean
A helper which enables us to detect when the xml_name has been explicitly set.
-
#xml_reader(sym, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares a read-only xml reference.
Instance Method Details
#xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares an accesser to a certain xml element, whether an attribute, a node, or a typed collection of nodes. Typically you should call xml_reader or xml_accessor rather than calling this method directly, but the instructions below apply to both.
Sym Option
- sym
-
Symbol representing the name of the accessor.
Default naming
This name will be the default node or attribute name searched for, if no other is declared. For example,
xml_reader :bob, :from => 'bob'
xml_accessor :pony, :attr => 'pony'
are equivalent to:
xml_reader :bob
xml_accessor :pony, :attr
Boolean attributes
If the name ends in a ?, ROXML will attempt to coerce the value to true or false, with True, TRUE, true and 1 mapping to true and False, FALSE, false and 0 mapping to false, as shown below:
xml_reader :desirable?
xml_reader :bizzare?, :attr => 'BIZZARE'
x = #from_xml(%{
<object BIZZARE="1">
<desirable>False</desirable>
</object>
})
x.desirable?
=> false
x.bizzare?
=> true
If an unexpected value is encountered, the attribute will be set to nil, unless you provide a block, in which case the block will recived the actual unexpected value.
#from_xml(%{
<object>
<desirable>Dunno</desirable>
</object>
}).desirable?
=> nil
xml_reader :strange? do |val|
val.upcase
end
#from_xml(%{
<object>
<strange>Dunno</strange>
</object>
}).strange?
=> DUNNO
Type options
All type arguments may be used as the type argument to indicate just type, or used as :from, pointing to a xml name to indicate both type and attribute name. Also, any type may be passed via an array to indicate that multiple instances of the object should be returned as an array.
:attr
Declare an accessor that represents an XML attribute.
Example:
class Book
xml_reader :isbn, :attr => "ISBN" # 'ISBN' is used to specify :from
xml_accessor :title, :attr # :from defaults to :title
end
To map:
<book ISBN="0974514055" title="Programming Ruby: the pragmatic programmers' guide" />
:text
The default type, if none is specified. Declares an accessor that represents a text node from XML.
Example:
class Book
xml :author, false, :text => 'Author'
xml_accessor :description, :text, :as => :cdata
xml_reader :title
end
To map:
<book>
<title>Programming Ruby: the pragmatic programmers' guide</title>
<description><![CDATA[Probably the best Ruby book out there]]></description>
<Author>David Thomas</author>
</book>
Likewise, a number of :text node values can be collected in an array like so:
Example:
class Library
xml_reader :books, [:text], :in => 'books'
end
To map:
<library>
<books>
<book>To kill a mockingbird</book>
<book>House of Leaves</book>
<book>Gödel, Escher, Bach</book>
</books>
</library>
:content
A special case of :text, this refers to the content of the current node, rather than a sub-node
Example:
class Contributor
xml_reader :name, :content
xml_reader :role, :attr
end
To map:
<contributor role="editor">James Wick</contributor>
Hash
Somewhere between the simplicity of a :text/:attr mapping, and the complexity of a full Object/Type mapping, lies the Hash mapping. It serves in the case where you have a collection of key-value pairs represented in your xml. You create a hash declaration by passing a hash mapping as the type argument. A few examples:
Hash of :attrs
For xml such as this:
<dictionary>
<definitions>
<definition dt="quaquaversally"
dd="adjective: (of a geological formation) sloping downward from the center in all directions." />
<definition dt="tergiversate"
dd="To use evasions or ambiguities; equivocate." />
</definitions>
</dictionary>
You can use the :attrs key in you has with a [:key, :value] name array:
xml_reader :definitions, {:attrs => ['dt', 'dd']}, :in => :definitions
Hash of :texts
For xml such as this:
<dictionary>
<definition>
<word/>
<meaning/>
</definition>
<definition>
<word/>
<meaning/>
</definition>
</dictionary>
You can individually declare your key and value names:
xml_reader :definitions, {:key => 'word',
:value => 'meaning'}
Hash of :content &c.
For xml such as this:
<dictionary>
<definition word="quaquaversally">adjective: (of a geological formation) sloping downward from the center in all directions.</definition>
<definition word="tergiversate">To use evasions or ambiguities; equivocate.</definition>
</dictionary>
You can individually declare the key and value, but with the attr, you need to provide both the type and name of that type (i.e. => :word), because omitting the type will result in ROXML defaulting to :text
xml_reader :definitions, {:key => {:attr => 'word'},
:value => :content}
Hash of :name &c.
For xml such as this:
<dictionary>
<quaquaversally>adjective: (of a geological formation) sloping downward from the center in all directions.</quaquaversally>
<tergiversate>To use evasions or ambiguities; equivocate.</tergiversate>
</dictionary>
You can pick up the node names (e.g. quaquaversally) using the :name keyword:
xml_reader :definitions, {:key => :name,
:value => :content}
Other ROXML Class
Declares an accessor that represents another ROXML class as child XML element (one-to-one or composition) or array of child elements (one-to-many or aggregation) of this type. Default is one-to-one. Use :array option for one-to-many, or simply pass the class in an array.
Composition example:
<book>
<publisher>
<name>Pragmatic Bookshelf</name>
</publisher>
</book>
Can be mapped using the following code:
class Book
xml_reader :publisher, Publisher
end
Aggregation example:
<library>
<books>
<book/>
<book/>
</books>
</library>
Can be mapped using the following code:
class Library
xml_reader :books, [Book], :in => "books"
end
If you don’t have the <books> tag to wrap around the list of <book> tags:
<library>
<name>Ruby books</name>
<book/>
<book/>
</library>
You can skip the wrapper argument:
xml_reader :books, [Book]
Blocks
You may also pass a block which manipulates the associated parsed value.
class Muffins
include ROXML
xml_reader(:count, :from => 'bakers_dozens') {|val| val.to_i * 13 }
end
For hash types, the block recieves the key and value as arguments, and they should be returned as an array of [key, value]
For array types, the entire array is passed in, and must be returned in the same fashion.
Block Shorthands
Alternatively, you may use block shorthands to specify common coercions, such that:
xml_reader :count, :as => Integer
is equivalent to:
xml_reader(:count) {|val| Integer(val) }
Block shorthands :float, Float, :integer and Integer are currently available, but only for non-Hash declarations.
Other options
- :from
-
The name by which the xml value will be found, either an attribute or tag name in XML. Default is sym, or the singular form of sym, in the case of arrays and hashes.
- :as
-
:cdata for character data; :integer, Integer, :float, Float to coerce to Integer or Float respectively
- :in
-
An optional name of a wrapping tag for this XML accessor
- :else
-
Default value for attribute, if missing
- :required
-
If true, throws RequiredElementMissing when the element isn’t present
377 378 379 380 381 382 383 384 385 386 387 388 389 390 |
# File 'lib/roxml.rb', line 377 def xml(sym, writable = false, type_and_or_opts = :text, opts = nil, &block) opts = Opts.new(sym, *[type_and_or_opts, opts].compact, &block) ref = case opts.type when :attr then XMLAttributeRef when :content then XMLTextRef when :text then XMLTextRef when :hash then XMLHashRef when Symbol then raise ArgumentError, "Invalid type argument #{opts.type}" else XMLObjectRef end.new(opts) add_accessor(ref, writable) end |
#xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares a writable xml reference. See xml for details.
398 399 400 |
# File 'lib/roxml.rb', line 398 def xml_accessor(sym, type_and_or_opts = :text, opts = nil, &block) xml sym, true, type_and_or_opts, opts, &block end |
#xml_construct(*args) ⇒ Object
This method is deprecated, please use xml_initialize instead
403 404 405 406 407 408 409 410 411 412 |
# File 'lib/roxml.rb', line 403 def xml_construct(*args) = tag_refs.map(&:accessor) = args - unless .empty? raise ArgumentError, "All construction tags must be declared first using xml, " + "xml_reader, or xml_accessor. #{.join(', ')} is missing. " + "#{.join(', ')} are declared." end @xml_construction_args = args end |
#xml_name(name) ⇒ Object
Sets the name of the XML element that represents this class. Use this to override the default lowercase class name.
Example:
class BookWithPublisher
xml_name :book
end
Without the xml_name annotation, the XML mapped tag would have been “bookwithpublisher”.
107 108 109 110 |
# File 'lib/roxml.rb', line 107 def xml_name(name) @xml_name = true @tag_name = name end |
#xml_name? ⇒ Boolean
A helper which enables us to detect when the xml_name has been explicitly set
93 94 95 |
# File 'lib/roxml.rb', line 93 def xml_name? #:nodoc: @xml_name end |
#xml_reader(sym, type_and_or_opts = :text, opts = nil, &block) ⇒ Object
Declares a read-only xml reference. See xml for details.
393 394 395 |
# File 'lib/roxml.rb', line 393 def xml_reader(sym, type_and_or_opts = :text, opts = nil, &block) xml sym, false, type_and_or_opts, opts, &block end |