Class: Quarto::ElementWrapper::Base
- Inherits:
-
Object
- Object
- Quarto::ElementWrapper::Base
- Includes:
- InheritableAttributes
- Defined in:
- lib/quarto/element_wrapper.rb
Overview
Abstract base class for your models. Put your ElementWrapper::Base subclasses inside the “models” directory within your project. All files in that directory will be automatically required.
Each ElementWrapper::Base subclass corresponds to exactly one XML element. You can specify the model’s element name by calling element_name=, but generally, you just let ElementWrapper use the default, which is the subclass name in snake_case.
Inside each ElementWrapper::Base is a REXML::Element. ElementWrapper::Base implements method_missing, allowing you to call that REXML::Element’s methods on the ElementWrapper::Base instance.
Instance attributes corresponding to the XML attributes will automatically be defined. Hwoever, if you want an attribute defined by the text of a child element, you have to specify it yourself.
For example, suppose you have an XML document like this:
<programmers>
<programmer skill="genius">
<name>Linus Torvalds</name>
<programmer>
</programmers>
You could then subclass ElementWrapper like this:
class Programmer < ElementWrapper::Base
element_name = 'programmer'
element_attrs 'name'
end
You could then do something like this in your generate.rb file:
programmer = Programmer.find :first
puts programmer.name
puts programmer.skill
Also see the documentation for ElementWrapper::Children
Instance Attribute Summary collapse
-
#element ⇒ Object
readonly
Returns the REXML::Element from which the instance was created.
Class Method Summary collapse
-
.element_attrs(*element_names) ⇒ Object
Creates read-only attributes from the given strings.
-
.element_name ⇒ Object
Returns the XML element name.
-
.element_name=(el_name) ⇒ Object
Overrides the XML element name.
-
.find(quantifier, options = {}) ⇒ Object
Searches the XML document and returns instances of the class.
-
.inherited(subclass) ⇒ Object
:nodoc:.
-
.text_attr(attr_name) ⇒ Object
Creates a read-only attribute from the wrapped element’s text.
-
.xml_doc ⇒ Object
Returns the currently-loaded REXML::Document.
Instance Method Summary collapse
-
#==(other_wrapped_element) ⇒ Object
Returns true if both instances come from the same node in the source XML document.
-
#initialize(el) ⇒ Base
constructor
:nodoc:.
-
#method_missing(meth, *args, &block) ⇒ Object
:nodoc:.
-
#respond_to?(meth, include_private = false) ⇒ Boolean
:nodoc:.
Methods included from InheritableAttributes
Constructor Details
#initialize(el) ⇒ Base
:nodoc:
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 |
# File 'lib/quarto/element_wrapper.rb', line 116 def initialize(el) # :nodoc: unless el.is_a?(REXML::Element) raise ArgumentError, "Quarto::ElementWrapper.new must be passed a REXML::Element, but got #{el.inspect}" end @element = el @attributes = {} @element.attributes.each do |a_name, value| @attributes[a_name.to_sym] = typecast_text(value) end if element_attrs = self.class.read_inheritable_attribute(:element_attrs) element_attrs.each do |el_name| if child_el = @element.elements[el_name.to_s] if child_el.elements.empty? @attributes[el_name.to_sym] = typecast_text(child_el.text) else @attributes[el_name.to_sym] = child_el end else @attributes[el_name.to_sym] = nil end end end if text_attr = self.class.read_inheritable_attribute(:text_attr) @attributes[text_attr.to_sym] = typecast_text(@element.text) end end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(meth, *args, &block) ⇒ Object
:nodoc:
143 144 145 146 147 148 149 150 151 |
# File 'lib/quarto/element_wrapper.rb', line 143 def method_missing(meth, *args, &block) # :nodoc: if @attributes.has_key?(meth.to_sym) @attributes[meth.to_sym] elsif @element.respond_to?(meth) @element.send(meth, *args, &block) else super end end |
Instance Attribute Details
#element ⇒ Object (readonly)
Returns the REXML::Element from which the instance was created.
58 59 60 |
# File 'lib/quarto/element_wrapper.rb', line 58 def element @element end |
Class Method Details
.element_attrs(*element_names) ⇒ Object
Creates read-only attributes from the given strings. When a model is instantiated from an XML node, ElementWrapper will try to populate these attributes using the node’s child elements.
For example, if your “employee” element has a child element called “name,” you can use:
element_attrs 'name'
…which will then expose a #name method for every instance of your class. Also see the usage example in the class description.
Remember, XML attributes will automatically have corresponding ElementWrapper attributes. You only need to tell ElementWrapper which child elements to use.
71 72 73 |
# File 'lib/quarto/element_wrapper.rb', line 71 def self.element_attrs(*element_names) write_inheritable_array :element_attrs, element_names.collect { |en| en.to_sym} end |
.element_name ⇒ Object
Returns the XML element name.
76 77 78 |
# File 'lib/quarto/element_wrapper.rb', line 76 def self.element_name @element_name end |
.element_name=(el_name) ⇒ Object
Overrides the XML element name. The default is the class name in snake_case.
81 82 83 |
# File 'lib/quarto/element_wrapper.rb', line 81 def self.element_name=(el_name) @element_name = el_name end |
.find(quantifier, options = {}) ⇒ Object
Searches the XML document and returns instances of the class. The first parameter must be either :first, :last, or :all. If it’s :first or :last, the method returns a single instance or nil. If it’s :all, the method returns an array (which may be empty).
Options:
-
:xpath- An XPath expression to limit the search. If this option is not given, the default XPath is “//element_name”
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/quarto/element_wrapper.rb', line 91 def self.find(quantifier, = {}) raise 'You must call use_xml() in generate.rb before using the models' if xml_doc.nil? raise ArgumentError, "Quantifier must be :all, :first, or :last, but got #{quantifier.inspect}" unless [:all, :first, :last].include?(quantifier) raise ArgumentError, "Options must be a Hash, but got #{.inspect}" unless .is_a?(Hash) if .has_key?(:xpath) xpath = [:xpath] else xpath = "//#{@element_name}" # TODO: add support for :root and :conditions (XPath predicates) end all = xml_doc.elements.to_a(xpath) case quantifier when :all all.collect { |el| new(el) } when :first all.empty? ? nil : new(all.first) when :last all.empty? ? nil : new(all.last) end end |
.inherited(subclass) ⇒ Object
:nodoc:
112 113 114 |
# File 'lib/quarto/element_wrapper.rb', line 112 def self.inherited(subclass) # :nodoc: subclass.element_name = subclass.to_s.underscore end |
.text_attr(attr_name) ⇒ Object
Creates a read-only attribute from the wrapped element’s text. attr_name can be anything you want; it doesn’t have to correspond to anything in the XML. Example:
<company>
<product>Shoes</product>
<company>
class Product < ElementWrapper
text_attr :name
end
172 173 174 |
# File 'lib/quarto/element_wrapper.rb', line 172 def self.text_attr(attr_name) write_inheritable_attribute(:text_attr, attr_name) end |
Instance Method Details
#==(other_wrapped_element) ⇒ Object
Returns true if both instances come from the same node in the source XML document.
48 49 50 |
# File 'lib/quarto/element_wrapper.rb', line 48 def ==(other_wrapped_element) other_wrapped_element.is_a?(Quarto::ElementWrapper::Base) and @element == other_wrapped_element.element end |
#respond_to?(meth, include_private = false) ⇒ Boolean
:nodoc:
153 154 155 156 157 158 159 |
# File 'lib/quarto/element_wrapper.rb', line 153 def respond_to?(meth, include_private = false) # :nodoc: if @element.respond_to?(meth, include_private) or @attributes.has_key?(meth.to_sym) true else super end end |