xml-object 0.9.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2008 Jordi Bunster <jordi@bunster.org>
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,176 @@
1
+ = XMLObject
2
+
3
+ (This is inspired by Python's +xml_objectify+)
4
+
5
+ XMLObject attempts to make the accessing of small, well-formed XML structures
6
+ convenient, by using dot notation to represent both attributes and child
7
+ elements whenever possible.
8
+
9
+ XML parsing libraries (in general) have interfaces that are useful when one
10
+ is using XML for its intended purpose, but cumbersome when one always sends
11
+ the same XML structure, and always process all of it in the same way. This
12
+ one aims to be a bit different.
13
+
14
+ At the moment, I aim for compatibility with Ruby 1.8, 1.9, and JRuby 1.1.
15
+
16
+ == Dependencies
17
+
18
+ None outside of Ruby, though some optional gems add additional features. See
19
+ below on "Adapters" and "Collection pluralization".
20
+
21
+ == Installation instructions
22
+
23
+ gem install xml-object
24
+
25
+ Or from Github's gem server:
26
+
27
+ gem install jordi-xml-object --source http://gems.github.com
28
+
29
+ Both are the same, and are loaded the same way:
30
+
31
+ require 'xml-object'
32
+
33
+ == Example usage
34
+
35
+ <recipe name="bread" prep_time="5 mins" cook_time="3 hours">
36
+ <title>Basic bread</title>
37
+ <ingredient amount="8" unit="dL">Flour</ingredient>
38
+ <ingredient amount="10" unit="grams">Yeast</ingredient>
39
+ <ingredient amount="4" unit="dL" state="warm">Water</ingredient>
40
+ <ingredient amount="1" unit="teaspoon">Salt</ingredient>
41
+ <instructions easy="yes" hard="false">
42
+ <step>Mix all ingredients together.</step>
43
+ <step>Knead thoroughly.</step>
44
+ <step>Cover with a cloth, and leave for one hour in warm room.</step>
45
+ <step>Knead again.</step>
46
+ <step>Place in a bread baking tin.</step>
47
+ <step>Cover with a cloth, and leave for one hour in warm room.</step>
48
+ <step>Bake in the oven at 180(degrees)C for 30 minutes.</step>
49
+ </instructions>
50
+ </recipe>
51
+
52
+ require 'xml-object'
53
+ recipe = XMLObject.new io_with_recipe_xml_shown_above
54
+
55
+ recipe.name => "bread"
56
+ recipe.title => "Basic bread"
57
+
58
+ recipe.ingredients.is_a?(Array) => true
59
+ recipe.ingredients.first.amount => "8" # Not a Fixnum. Too hard. :(
60
+
61
+ recipe.instructions.easy? => true
62
+
63
+ recipe.instructions.first.upcase => "MIX ALL INGREDIENTS TOGETHER."
64
+ recipe.instructions.steps.size => 7
65
+
66
+ == Motivation
67
+
68
+ XML is an *extensible* markup language. It is extensible because it is meant
69
+ to define markup languages for *any* type of document, so new tags are needed
70
+ depending on the problem domain.
71
+
72
+ Sometimes, however, XML ends up being used to solve a much simpler problem:
73
+ the issue of passing a data-structure over the network, and/or between two
74
+ different languages. Tools like +JSON+ or +YAML+ are a much better fit for
75
+ this kind of job, but one doesn't always have that luxury.
76
+
77
+ == Caveats
78
+
79
+ The dot notation is used as follows. For the given file:
80
+
81
+ <outer id="root" name="foo">
82
+ <name>Outer Element</name>
83
+ </outer>
84
+
85
+ +outer.name+ is the +name+ *element*. Child elements are always looked up
86
+ first, then attributes. To access the attribute in the case of ambiguity, use
87
+ outer[:attr => 'name'].
88
+
89
+ +outer.id+ is really Object#id, because all of the object methods are
90
+ preserved (this is on purpose). To access the attribute +id+, use
91
+ outer[:attr => 'id'], or outer['id'] since there's no element/attribute
92
+ ambiguity.
93
+
94
+ == Features & Problems
95
+
96
+ === Adapters
97
+
98
+ XMLObject supports different adapters to do the actual XML parsing. It ships
99
+ with +REXML+, +Hpricot+, +JREXML+, and +LibXML+ adapters. By default, the
100
+ +REXML+ adapter is used.
101
+
102
+ To use a different adapter than the +REXML+ default:
103
+
104
+ require 'xml-object' # Require XMLObject first
105
+ require 'xml-object/adapters/hpricot' # (Under MRI or JRuby)
106
+ require 'xml-object/adapters/libxml' # (Under MRI only)
107
+ require 'xml-object/adapters/jrexml' # (Under Jruby only)
108
+
109
+ === Collection auto-folding
110
+
111
+ Similar to XmlSimple, XMLObject folds same named elements at the same level.
112
+ For example:
113
+
114
+ <student>
115
+ <name>Bob</name>
116
+ <course>Math</course>
117
+ <course>Biology</course>
118
+ </student>
119
+
120
+ student = XMLObject.new(xml_file)
121
+
122
+ student.course.is_a? Array => true
123
+ student.course.first == 'Math' => true
124
+ student.course.last == 'Biology => true
125
+
126
+ === Collection pluralization
127
+
128
+ With the same file from the +Collection auto-folding+ section above, you also
129
+ get this:
130
+
131
+ student.courses.first == student.course.first => true
132
+
133
+ Note that the pluralization algorithm is just tacking an 's' at the end of
134
+ the singular, unless +ActiveSupport+ is installed, in which case you get
135
+ irregular plurals, as well as the ability to teach the +Inflector+ about
136
+ new ones.
137
+
138
+ === Collection proxy
139
+
140
+ Sometimes, collections are expressed with a container element in XML:
141
+
142
+ <student>
143
+ <name>Bob</name>
144
+ <courses>
145
+ <course>Math</course>
146
+ <course>Biology</course>
147
+ </courses>
148
+ </student>
149
+
150
+ In this case, since the container element +courses+ has no text element of
151
+ its own, and it only has elements of one name under it, it delegates all
152
+ methods it doesn't contain to the collection below, so you get:
153
+
154
+ student.courses.collect { |c| c.downcase.to_sym } => [:math, :biology]
155
+
156
+ === Question mark notation
157
+
158
+ Strings that look like booleans are "booleanized" if called by their question
159
+ mark names (such as +enabled?+)
160
+
161
+ === Recursive
162
+
163
+ The design of the adapters assumes parsing of the objects recursively. Deep
164
+ files are bound to throw +SystemStackError+, but for the kinds of files I
165
+ need to read, things are working fine so far. In any case, stream parsing is
166
+ on the TODO list.
167
+
168
+ === Incomplete
169
+
170
+ It most likely doesn't work with a ton of features of complex XML files. I'll
171
+ always try to accomodate those, as long as they don't make the basic usage
172
+ more complex. As usual, patches welcome.
173
+
174
+ == Legal
175
+
176
+ Copyright (c) 2008 Jordi Bunster, released under the MIT license
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * Refactor so as to not do things recursively
@@ -0,0 +1,74 @@
1
+ * 0.9.7 (2008-10-20):
2
+ - LibXML adapter added
3
+ - JREXML "adapter" added, as well as JREXML support during tests and
4
+ benchmark tasks, but only when running under JRuby
5
+ - Backwards incompatible change: Elements with no text and multiple CDATA
6
+ values no longer report their value as their first CDATA element. They
7
+ report all of their CDATA nodes joined together, a-la LibXML
8
+
9
+ * 0.9.6 (2008-10-19):
10
+ - Ruby 1.9 compatibility
11
+ - XMLObject no longer requires ActiveSupport. Note that proper array
12
+ pluralization still depends on ActiveSupport's Inflector, so if it's not
13
+ there, we just pluralize things by tacking 's' at the end.
14
+ - XMLObject no longer tries to load Hpricot by default (too cumbersome
15
+ to test). Use "require 'xml-object/adapters/hpricot'" to enable the
16
+ Hpricot adapter.
17
+ - Backwards incompatible change. For the following XML:
18
+
19
+ <foo>
20
+ <bar>
21
+ <bez>
22
+ </foo>
23
+
24
+ <foos>Yipes!</foos>
25
+
26
+ before:
27
+
28
+ xml.foos => # The <foo> "array"
29
+
30
+ now:
31
+
32
+ xml.foos => # The <foos> element
33
+
34
+ It proved too expensive to support that edge case, and for no good
35
+ reason.
36
+
37
+ * 0.9.5 (2008-10-15):
38
+ - Project renamed to XMLObject, to match project name at Rubyforge.
39
+ The other names were taken. :(
40
+
41
+ * 0.9.0 (2008-10-15):
42
+ - Added support for plug-able adapters
43
+ - Backported REXML code as an adapter, added Hpricot adapter
44
+ - Performance: XMLStruct now decorates objects lazily
45
+ - Performance: XMLStruct uses the Hpricot adapter if possible, otherwise
46
+ REXML as a fallback
47
+ - API Change: XMLStruct.new is mostly delegated to the adapter, and both
48
+ included adapters behave the same: a String is considered to be
49
+ XML data, anything else is probed for #read and then #to_s
50
+
51
+ * 0.2.1 (2008-10-13):
52
+ - Fixed a bug where attributes with dashes would crash the party
53
+
54
+ * 0.2.0 (2008-10-13):
55
+ - Broke backwards compatibility
56
+ - XMLStruct.new now returns decorated String or Array objects, so that
57
+ access to elements, attributes, and "collection" values is consistent
58
+ - While Strings are no longer auto-typecast to float or int, they now
59
+ have, whenever possible, a question-mark form, which attemps to
60
+ returns booleans for strings like "Yes" and "false"
61
+ - XMLStruct.new can now take a filename or a file object
62
+ - Added more tests
63
+
64
+ * 0.1.3 (2008-10-10):
65
+ - Switched tests to use test/spec
66
+ - Added XMLStruct#to_obj to return the corresponding Ruby object value
67
+ - Added XMLStruct#to_raw_xml to return the REXML object
68
+ - Added documentation on auto-typecasting behaviour caveat
69
+
70
+ * 0.1.2 (2008-10-10):
71
+ - Documentation
72
+
73
+ * 0.1.1 (2008-10-10):
74
+ - First "release" ;)
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'xml-object'))
@@ -0,0 +1,79 @@
1
+ begin; require 'rubygems'; rescue Exception, StandardError; nil; end
2
+ begin; require 'activesupport'; rescue Exception, StandardError; nil; end
3
+
4
+ $:.unshift File.join(File.dirname(__FILE__), 'xml-object')
5
+
6
+ require 'core_ext'
7
+ require 'adapters'
8
+ require 'adapters/rexml'
9
+ require 'array_notation'
10
+ require 'blankish_slate'
11
+ require 'collection_proxy'
12
+ require 'element'
13
+ require 'method_missing_dispatchers'
14
+ require 'string'
15
+
16
+ module XMLObject
17
+ # Returns a String or Array object representing the given XML, decorated
18
+ # with methods to access attributes and/or child elements.
19
+ def self.new(duck)
20
+ case duck
21
+ when adapter::Element then new_decorated_obj(duck)
22
+ when Array then duck.map { |d| new_decorated_obj(d) }
23
+ else new adapter.new(duck)
24
+ end
25
+ end
26
+
27
+ private ##################################################################
28
+
29
+ # Takes any Element object, and converts it recursively into
30
+ # the corresponding tree of decorated objects.
31
+ def self.new_decorated_obj(xml) # :nodoc:
32
+ obj = if xml.value.blank? &&
33
+ xml.children.collect { |e| e.name }.uniq.size == 1
34
+
35
+ CollectionProxy.new new(xml.children)
36
+ else
37
+ # Teach our string to behave like and XML Element
38
+ xml.value.extend String, Element
39
+ end
40
+
41
+ obj.instance_variable_set :@__adapted_element, xml
42
+
43
+ xml.children.each { |child| add_child(obj, child.name, new(child)) }
44
+ xml.attributes.each { |name, value| add_attribute(obj, name, value) }
45
+
46
+ # Let's teach our object some new tricks:
47
+ obj.extend ArrayNotation, MethodMissingDispatchers
48
+ end
49
+
50
+ # Decorates the given object 'obj' with a method 'name' that returns the
51
+ # given 'element'. If 'name' is already taken, takes care of the array
52
+ # folding behaviour.
53
+ def self.add_child(obj, name, element) # :nodoc:
54
+ key = name.to_sym
55
+ children = obj.instance_variable_get :@__children
56
+
57
+ children[key] = if children[key]
58
+
59
+ children[key] = [ children[key] ] unless children[key].is_a? Array
60
+ children[key] << element
61
+ else
62
+ element
63
+ end
64
+
65
+ obj.instance_variable_set :@__children, children
66
+ element
67
+ end
68
+
69
+ # Decorates the given object 'obj' with a method 'name' that returns the
70
+ # given 'attr_value'.
71
+ def self.add_attribute(obj, name, attr_value) # :nodoc:
72
+
73
+ attributes = obj.instance_variable_get :@__attributes
74
+ attributes[(key = name.to_sym)] = attr_value.squish.extend String
75
+
76
+ obj.instance_variable_set :@__attributes, attributes
77
+ attr_value
78
+ end
79
+ end
@@ -0,0 +1,30 @@
1
+ module XMLObject # :nodoc:
2
+ module Adapters # :nodoc:
3
+ module Base # :nodoc:
4
+ class Element # :nodoc:
5
+ attr_accessor :raw, :name, :value, :attributes, :children # :nodoc:
6
+
7
+ def initialize(*args)
8
+
9
+ @children = @element_nodes.map { |node| self.class.new(node) }
10
+
11
+ @value = case
12
+ when (not text_value.blank?) then text_value
13
+ when (not cdata_value.blank?) then cdata_value
14
+ else ''
15
+ end
16
+ end
17
+
18
+ private ###########################################################
19
+
20
+ def text_value
21
+ @text_nodes.reject { |n| n.blank? }.join
22
+ end
23
+
24
+ def cdata_value
25
+ @cdata_nodes.join
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ require 'rubygems'
2
+ require 'hpricot'
3
+
4
+ module XMLObject::Adapters::Hpricot
5
+
6
+ # Can take a String of XML data, or anything that responds to
7
+ # either +read+ or +to_s+.
8
+ def self.new(duck)
9
+ case
10
+ when duck.is_a?(::Hpricot::Elem) then Element.new(duck)
11
+ when duck.is_a?(::String) then new(::Hpricot::XML(duck).root)
12
+ when duck.respond_to?(:read) then new(duck.read)
13
+ when duck.respond_to?(:to_s) then new(duck.to_s)
14
+ else raise "Don't know how to deal with '#{duck.class}' object"
15
+ end
16
+ end
17
+
18
+ private ##################################################################
19
+
20
+ class Element < XMLObject::Adapters::Base::Element # :nodoc:
21
+ def initialize(xml)
22
+ @raw, @name, @attributes = xml, xml.name, xml.attributes
23
+
24
+ @element_nodes = xml.children.select { |c| c.elem? }
25
+
26
+ @text_nodes = xml.children.select do |c|
27
+ c.text? && !c.is_a?(::Hpricot::CData)
28
+ end.map { |c| c.to_s }
29
+
30
+ @cdata_nodes = xml.children.select do |c|
31
+ c.is_a? ::Hpricot::CData
32
+ end.map { |c| c.to_s }
33
+
34
+ super
35
+ end
36
+ end
37
+ end
38
+
39
+ def XMLObject.adapter # :nodoc:
40
+ XMLObject::Adapters::Hpricot
41
+ end
@@ -0,0 +1,10 @@
1
+ require 'jrexml'
2
+ require File.join(File.dirname(__FILE__), 'rexml')
3
+
4
+ module XMLObject::Adapters
5
+ JREXML = REXML
6
+ end
7
+
8
+ def XMLObject.adapter # :nodoc:
9
+ XMLObject::Adapters::JREXML
10
+ end
@@ -0,0 +1,38 @@
1
+ require 'libxml'
2
+
3
+ module XMLObject::Adapters::LibXML
4
+
5
+ # Can take a String of XML data, or anything that responds to
6
+ # either +read+ or +to_s+.
7
+ def self.new(duck)
8
+ case
9
+ when duck.is_a?(::LibXML::XML::Node) then Element.new(duck)
10
+ when duck.is_a?(::String)
11
+ then new(::LibXML::XML::Parser.string(duck).parse.root)
12
+ when duck.respond_to?(:read) then new(duck.read)
13
+ when duck.respond_to?(:to_s) then new(duck.to_s)
14
+ else raise "Don't know how to deal with '#{duck.class}' object"
15
+ end
16
+ end
17
+
18
+ private ##################################################################
19
+
20
+ class Element < XMLObject::Adapters::Base::Element # :nodoc:
21
+ def initialize(xml)
22
+ @raw, @name, @attributes = xml, xml.name, xml.attributes.to_h
23
+
24
+ @element_nodes = xml.children.select { |c| c.element? }
25
+
26
+ @text_nodes = xml.children.select { |c| c.text? }.map { |c| c.to_s }
27
+ @cdata_nodes = xml.children.select { |c| c.cdata? }.map do |c|
28
+ c.to_s.chomp(']]>').sub('<![CDATA[', '')
29
+ end
30
+
31
+ super
32
+ end
33
+ end
34
+ end
35
+
36
+ def XMLObject.adapter # :nodoc:
37
+ XMLObject::Adapters::LibXML
38
+ end
@@ -0,0 +1,40 @@
1
+ require 'rexml/document'
2
+
3
+ module XMLObject::Adapters::REXML
4
+
5
+ # Can take a String of XML data, or anything that responds to
6
+ # either +read+ or +to_s+.
7
+ def self.new(duck)
8
+ case
9
+ when duck.is_a?(::REXML::Element) then Element.new(duck)
10
+ when duck.is_a?(::String) then new(::REXML::Document.new(duck).root)
11
+ when duck.respond_to?(:read) then new(duck.read)
12
+ when duck.respond_to?(:to_s) then new(duck.to_s)
13
+ else raise "Don't know how to deal with '#{duck.class}' object"
14
+ end
15
+ end
16
+
17
+ private ##################################################################
18
+
19
+ class Element < XMLObject::Adapters::Base::Element # :nodoc:
20
+ def initialize(xml)
21
+ @raw, @name, @attributes = xml, xml.name, xml.attributes
22
+
23
+ @element_nodes = xml.elements
24
+
25
+ @text_nodes = xml.children.select do |child|
26
+ child.class == ::REXML::Text
27
+ end.collect { |child| child.to_s }
28
+
29
+ @cdata_nodes = xml.children.select do |child|
30
+ child.class == ::REXML::CData
31
+ end.collect { |child| child.to_s }
32
+
33
+ super
34
+ end
35
+ end
36
+ end
37
+
38
+ def XMLObject.adapter # :nodoc:
39
+ XMLObject::Adapters::REXML
40
+ end
@@ -0,0 +1,44 @@
1
+ module XMLObject::ArrayNotation
2
+ # Array-bracket (+[]+) notation access to elements and attributes. Use
3
+ # when the element or attribute you need to reach is not reachable via dot
4
+ # notation (because it's not a valid method name, or because the method
5
+ # exists, such as +id+ or +class+).
6
+ #
7
+ # It also supports a hash key, which is used to reach attributes named
8
+ # the same as elements in the same depth level (which otherwise go first)
9
+ #
10
+ # All of this is a lot easier to explain by example:
11
+ #
12
+ # <article id="main_article" author="j-random">
13
+ # <author>J. Random Hacker</author>
14
+ # </article>
15
+ #
16
+ # article.id => 9314390 # Object#id
17
+ # article[:id] => "main_article" # id attribute
18
+ # article[:author] => "J. Random Hacker" # <author> element
19
+ # article[:attr => 'author'] => "j-random" # author attribute
20
+ #
21
+ # Valid keys for the hash notation in the example above are +:attr+,
22
+ # +:attribute+, +:child+, and +:element+.
23
+ def [](name)
24
+ return @__target[name] if @__target && name.is_a?(Numeric)
25
+
26
+ unless name.is_a? Hash
27
+ key = name.to_s.to_sym
28
+
29
+ return @__children[key] if @__children.has_key?(key)
30
+ return @__attributes[key] if @__attributes.has_key?(key)
31
+ end
32
+
33
+ raise 'one and only one key allowed' if name.size != 1
34
+
35
+ case (param = name.keys.first.to_sym)
36
+ when :element then @__children[name.values.first.to_sym]
37
+ when :child then @__children[name.values.first.to_sym]
38
+ when :attr then @__attributes[name.values.first.to_sym]
39
+ when :attribute then @__attributes[name.values.first.to_sym]
40
+ else raise %{ Invalid key :#{param.to_s}.
41
+ Use one of :element, :child, :attr, or :attribute }.squish!
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,10 @@
1
+ class XMLObject::BlankishSlate # :nodoc:
2
+
3
+ instance_methods.each do |m|
4
+ undef_method m unless m =~ /^__/ ||
5
+ m =~ /respond_to/ ||
6
+ m =~ /extend/ ||
7
+ m =~ /object_id/ ||
8
+ m =~ /^instance_/
9
+ end
10
+ end
@@ -0,0 +1,14 @@
1
+ class XMLObject::CollectionProxy < XMLObject::BlankishSlate # :nodoc:
2
+ def initialize(target)
3
+ @__children, @__attributes, @__target = {}, {}, target
4
+ end
5
+
6
+ private ##################################################################
7
+
8
+ def method_missing(m, *a, &b) # :nodoc:
9
+ dp = __question_dispatch(m, *a, &b)
10
+ dp = __dot_notation_dispatch(m, *a, &b) if dp.nil?
11
+ dp = @__target.__send__(m, *a, &b) if @__target.respond_to?(m) && dp.nil?
12
+ dp
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ require 'core_ext/hash'
2
+ require 'core_ext/nil_class'
3
+ require 'core_ext/string'
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def blank?
3
+ size.zero?
4
+ end unless {}.respond_to? :blank?
5
+ end
@@ -0,0 +1,5 @@
1
+ class NilClass
2
+ def blank?
3
+ true
4
+ end unless nil.respond_to? :blank?
5
+ end
@@ -0,0 +1,19 @@
1
+ class String
2
+ def blank?
3
+ self !~ /\S/
4
+ end unless ''.respond_to? :blank?
5
+
6
+ def squish
7
+ dup.squish!
8
+ end unless ''.respond_to? :squish
9
+
10
+ def squish!
11
+ strip!
12
+ gsub! /\s+/, ' '
13
+ self
14
+ end unless ''.respond_to? :squish!
15
+
16
+ def singularize
17
+ self.chomp 's'
18
+ end unless ''.respond_to? :singularize
19
+ end
@@ -0,0 +1,21 @@
1
+ module XMLObject::Element
2
+ def self.extended(obj) # :nodoc:
3
+ obj.instance_variable_set :@__children, {}
4
+ obj.instance_variable_set :@__attributes, {}
5
+ obj
6
+ end
7
+
8
+ # The raw, unadapted XML object. Whatever this is, it really depends on
9
+ # the current_adapter.
10
+ def raw_xml
11
+ @__adapted_element.raw if @__adapted_element
12
+ end
13
+
14
+ private ##################################################################
15
+
16
+ def method_missing(m, *a, &b) # :nodoc:
17
+ dp = __question_dispatch(m, *a, &b)
18
+ dp = __dot_notation_dispatch(m, *a, &b) if dp.nil?
19
+ dp
20
+ end
21
+ end
@@ -0,0 +1,43 @@
1
+ module XMLObject::MethodMissingDispatchers # :nodoc:
2
+
3
+ private ##################################################################
4
+
5
+ def __question_dispatch(meth, *args, &block)
6
+ return unless meth.to_s.match(/\?$/) && args.empty? && block.nil?
7
+
8
+ method_sans_question = meth.to_s.chomp('?').to_sym
9
+
10
+ if boolish = __send__(method_sans_question)
11
+ bool = case
12
+ when %w[ true yes t y ].include?(boolish.downcase) then true
13
+ when %w[ false no f n ].include?(boolish.downcase) then false
14
+ else nil
15
+ end
16
+
17
+ unless bool.nil? # Fun, eh?
18
+ instance_eval %{ def #{meth}; #{bool ? 'true' : 'false'}; end }
19
+ end
20
+
21
+ bool
22
+ end
23
+ end
24
+
25
+ def __dot_notation_dispatch(meth, *args, &block)
26
+ return unless args.empty? && block.nil?
27
+
28
+ if @__children.has_key?(meth)
29
+ instance_eval %{ def #{meth}; @__children[%s|#{meth}|]; end }
30
+ @__children[meth]
31
+
32
+ elsif @__attributes.has_key?(meth)
33
+ instance_eval %{ def #{meth}; @__attributes[%s|#{meth}|]; end }
34
+ @__attributes[meth]
35
+
36
+ elsif @__children.has_key?(singular = meth.to_s.singularize.to_sym) &&
37
+ @__children[singular].is_a?(Array)
38
+
39
+ instance_eval %{ def #{meth}; @__children[%s|#{singular}|]; end }
40
+ @__children[singular]
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,24 @@
1
+ module XMLObject::String
2
+
3
+ # Attempts to detect wether this String is really an integer or float,
4
+ # and returns accordingly. If not, just returns the string.
5
+ def rb
6
+ result = case
7
+ when (self !~ /\S/) then ''
8
+ when match(/[a-zA-Z]/) then ::String.new(self)
9
+ when match(/^[+-]?\d+$/) then self.to_i
10
+ when match(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/) then self.to_f
11
+ else ::String.new(self)
12
+ end
13
+
14
+ @__rb ||= result
15
+ end
16
+
17
+ # A decorated String is blank when it has a blank value, no child
18
+ # elements, and no attributes. For example:
19
+ #
20
+ # <blank_element></blank_element>
21
+ def blank?
22
+ (self !~ /\S/) && @__children.blank? && @__attributes.blank?
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.rubyforge_project = 'xml-object'
3
+
4
+ gem.name = 'xml-object'
5
+ gem.version = '0.9.7'
6
+ gem.date = '2008-10-20'
7
+ gem.author = 'Jordi Bunster'
8
+ gem.email = 'jordi@bunster.org'
9
+ gem.homepage = 'http://github.com/jordi/xml-object'
10
+
11
+ gem.summary = "The Rubyista's way to do quick XML sit-ups"
12
+ gem.description = %{ XMLObject is a library for reading (not writing) XML.
13
+ It is particularly suited for cases where one is dealing with small
14
+ documents of a known structure. While not devoid of caveats, it does
15
+ have a very pleasant, idiomatic Ruby syntax. }.strip!.gsub! /\s+/, ' '
16
+
17
+ gem.files = %w[
18
+ MIT-LICENSE
19
+ README.rdoc
20
+ TODO
21
+ WHATSNEW
22
+ lib
23
+ lib/jordi-xml-object.rb
24
+ lib/xml-object
25
+ lib/xml-object/adapters
26
+ lib/xml-object/adapters/hpricot.rb
27
+ lib/xml-object/adapters/jrexml.rb
28
+ lib/xml-object/adapters/libxml.rb
29
+ lib/xml-object/adapters/rexml.rb
30
+ lib/xml-object/adapters.rb
31
+ lib/xml-object/array_notation.rb
32
+ lib/xml-object/blankish_slate.rb
33
+ lib/xml-object/collection_proxy.rb
34
+ lib/xml-object/core_ext
35
+ lib/xml-object/core_ext/hash.rb
36
+ lib/xml-object/core_ext/nil_class.rb
37
+ lib/xml-object/core_ext/string.rb
38
+ lib/xml-object/core_ext.rb
39
+ lib/xml-object/element.rb
40
+ lib/xml-object/method_missing_dispatchers.rb
41
+ lib/xml-object/string.rb
42
+ lib/xml-object.rb
43
+ xml-object.gemspec
44
+ ]
45
+
46
+ gem.has_rdoc = !!(gem.extra_rdoc_files = %w[ README.rdoc ])
47
+ gem.rdoc_options << '--title' << 'XMLObject' <<
48
+ '--main' << 'README.rdoc' <<
49
+ '--inline-source'
50
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xml-object
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.7
5
+ platform: ruby
6
+ authors:
7
+ - Jordi Bunster
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-10-20 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: XMLObject is a library for reading (not writing) XML. It is particularly suited for cases where one is dealing with small documents of a known structure. While not devoid of caveats, it does have a very pleasant, idiomatic Ruby syntax.
17
+ email: jordi@bunster.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - MIT-LICENSE
26
+ - README.rdoc
27
+ - TODO
28
+ - WHATSNEW
29
+ - lib
30
+ - lib/jordi-xml-object.rb
31
+ - lib/xml-object
32
+ - lib/xml-object/adapters
33
+ - lib/xml-object/adapters/hpricot.rb
34
+ - lib/xml-object/adapters/jrexml.rb
35
+ - lib/xml-object/adapters/libxml.rb
36
+ - lib/xml-object/adapters/rexml.rb
37
+ - lib/xml-object/adapters.rb
38
+ - lib/xml-object/array_notation.rb
39
+ - lib/xml-object/blankish_slate.rb
40
+ - lib/xml-object/collection_proxy.rb
41
+ - lib/xml-object/core_ext
42
+ - lib/xml-object/core_ext/hash.rb
43
+ - lib/xml-object/core_ext/nil_class.rb
44
+ - lib/xml-object/core_ext/string.rb
45
+ - lib/xml-object/core_ext.rb
46
+ - lib/xml-object/element.rb
47
+ - lib/xml-object/method_missing_dispatchers.rb
48
+ - lib/xml-object/string.rb
49
+ - lib/xml-object.rb
50
+ - xml-object.gemspec
51
+ has_rdoc: true
52
+ homepage: http://github.com/jordi/xml-object
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --title
56
+ - XMLObject
57
+ - --main
58
+ - README.rdoc
59
+ - --inline-source
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: xml-object
77
+ rubygems_version: 1.3.0
78
+ signing_key:
79
+ specification_version: 2
80
+ summary: The Rubyista's way to do quick XML sit-ups
81
+ test_files: []
82
+