jordi-xml_struct 0.1.3 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +9 -25
- data/TODO +0 -1
- data/WHATSNEW +10 -0
- data/lib/xml_struct.rb +60 -122
- data/lib/xml_struct/blankish_slate.rb +9 -0
- data/lib/xml_struct/collection_proxy.rb +10 -0
- data/lib/xml_struct/common_behaviours.rb +53 -0
- data/lib/xml_struct/string.rb +31 -0
- data/test/samples/lorem.xml +1 -1
- data/test/samples/recipe.xml +16 -0
- data/test/test_helper.rb +2 -1
- data/test/xml_struct_test.rb +72 -22
- metadata +8 -3
- data/xml_struct.gemspec +0 -69
data/README.markdown
CHANGED
@@ -4,13 +4,12 @@
|
|
4
4
|
|
5
5
|
XML Struct attempts to make accessing small, well-formed XML structures
|
6
6
|
convenient, by using dot notation (`foo.bar`) to represent both attributes
|
7
|
-
and child elements whenever possible
|
8
|
-
onto native objects (`Fixnum`, `Float`, etc).
|
7
|
+
and child elements whenever possible.
|
9
8
|
|
10
9
|
XML parsing libraries (in general) have interfaces that are useful when
|
11
10
|
one is using XML for its intended purpose, but cumbersome when one always
|
12
11
|
sends the same XML structure, and always process all of it in the same
|
13
|
-
way. This one
|
12
|
+
way. This one aims to be a bit different.
|
14
13
|
|
15
14
|
## Example usage
|
16
15
|
|
@@ -35,10 +34,10 @@ way. This one is a bit different.
|
|
35
34
|
recipe = XMLStruct.new io_with_recipe_xml_shown_above
|
36
35
|
|
37
36
|
recipe.name => "bread"
|
38
|
-
recipe.title
|
37
|
+
recipe.title => "Basic bread"
|
39
38
|
|
40
39
|
recipe.ingredients.is_a?(Array) => true
|
41
|
-
recipe.ingredients.first.amount => 8
|
40
|
+
recipe.ingredients.first.amount => "8" # Not a Fixnum. Too hard. :(
|
42
41
|
|
43
42
|
recipe.instructions.easy? => true
|
44
43
|
|
@@ -101,9 +100,9 @@ level. For example:
|
|
101
100
|
With the same file from the `Collection auto-folding` section above, you
|
102
101
|
also get this (courtesy of `ActiveSupport`'s `Inflector`):
|
103
102
|
|
104
|
-
student.courses == student.course => true
|
103
|
+
student.courses.first == student.course.first => true
|
105
104
|
|
106
|
-
### Collection proxy
|
105
|
+
### Collection proxy
|
107
106
|
|
108
107
|
Sometimes, collections are expressed with a container element in XML:
|
109
108
|
|
@@ -121,25 +120,10 @@ all methods it doesn't contain to the collection below, so you get:
|
|
121
120
|
|
122
121
|
student.courses.collect { |c| c.downcase.to_sym } => [:math, :biology]
|
123
122
|
|
124
|
-
###
|
123
|
+
### Question mark notation
|
125
124
|
|
126
|
-
|
127
|
-
|
128
|
-
promoted, but only if called by their question mark names (such as
|
129
|
-
`enabled?`.)
|
130
|
-
|
131
|
-
At first, elements appear to do the same, but they don't, really. (I'd like
|
132
|
-
to, but one can't define methods on, say `5`, or `-1.2`.) So in the case of
|
133
|
-
the following XML:
|
134
|
-
|
135
|
-
<thing><name>Foo</name></thing>
|
136
|
-
|
137
|
-
While `thing.name == 'Foo'` is `true`, `thing.name => <XMLStruct ...>`. The
|
138
|
-
consequence is that you can call `String` methods on `thing.name` (such as
|
139
|
-
`upcase`, or `==`), but if you assign it to a new variable, you will not get
|
140
|
-
a `String` object. To do that, call `thing.name.to_obj`, which will return
|
141
|
-
you the auto type-casted (see above) version of `thing.name`, in this case
|
142
|
-
`"Foo"`.
|
125
|
+
Strings that look like booleans are "booleanized" if called by their
|
126
|
+
question mark names (such as `enabled?`)
|
143
127
|
|
144
128
|
### Slow
|
145
129
|
|
data/TODO
CHANGED
data/WHATSNEW
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
* 0.2.0 (2008-10-13):
|
2
|
+
- Broke backwards compatibility
|
3
|
+
- XMLRecord.new now returns decorated String or Array objects, so that
|
4
|
+
access to elements, attributes, and "collection" values is consistent
|
5
|
+
- While Strings are no longer auto-typecast to float or int, they now
|
6
|
+
have, whenever possible, a question-mark form, which attemps to
|
7
|
+
returns booleans for strings like "Yes" and "false"
|
8
|
+
- XMLRecord.new can now take a filename or a file object
|
9
|
+
- Added more tests
|
10
|
+
|
1
11
|
* 0.1.3 (2008-10-10):
|
2
12
|
- Switched tests to use test/spec
|
3
13
|
- Added XMLStruct#to_obj to return the corresponding Ruby object value
|
data/lib/xml_struct.rb
CHANGED
@@ -2,156 +2,94 @@ require 'rubygems'
|
|
2
2
|
require 'activesupport'
|
3
3
|
require 'rexml/document'
|
4
4
|
|
5
|
-
|
5
|
+
module XMLStruct; end
|
6
6
|
|
7
|
-
|
7
|
+
require File.join(File.dirname(__FILE__), 'xml_struct', 'blankish_slate')
|
8
|
+
require File.join(File.dirname(__FILE__), 'xml_struct', 'collection_proxy')
|
9
|
+
require File.join(File.dirname(__FILE__), 'xml_struct', 'string')
|
10
|
+
require File.join(File.dirname(__FILE__), 'xml_struct', 'common_behaviours')
|
8
11
|
|
9
|
-
|
12
|
+
module XMLStruct
|
13
|
+
|
14
|
+
# Returns a String or Array object representing the given XML, decorated
|
15
|
+
# with methods to access attributes and/or child elements.
|
10
16
|
def self.new(duck)
|
11
|
-
|
17
|
+
case duck
|
18
|
+
when ::String : return new(File.open(duck))
|
19
|
+
when ::IO : return new(REXML::Document.new(duck).root)
|
20
|
+
when REXML::Element : return new_decorated_obj(duck)
|
21
|
+
when REXML::Elements : return duck.map { |dee| new_decorated_obj(dee) }
|
22
|
+
else raise "Don't know how to start from '#{duck.class}' object."
|
23
|
+
end
|
12
24
|
end
|
13
25
|
|
14
26
|
# Takes any REXML::Element object, and converts it recursively into
|
15
|
-
# the corresponding tree of
|
16
|
-
def
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
@raw.each_element { |el| __set_child el.name, self.class.new(el) }
|
21
|
-
@raw.attributes.each { |n, v| __set_attribute n, v }
|
22
|
-
end
|
23
|
-
|
24
|
-
# Returns the raw REXML::Element object used to build this XMLStruct
|
25
|
-
def to_raw_xml
|
26
|
-
@raw
|
27
|
-
end
|
28
|
-
|
29
|
-
# Returns the Ruby value to which we pass missing methods
|
30
|
-
def to_obj
|
31
|
-
@value
|
32
|
-
end
|
33
|
-
|
34
|
-
# An XMLStruct is blank when it has a blank value, no child elements,
|
35
|
-
# and no attributes. For example:
|
36
|
-
#
|
37
|
-
# <blank_element></blank_element>
|
38
|
-
def blank?
|
39
|
-
to_obj.blank? && @children.blank? && @attributes.blank?
|
40
|
-
end
|
41
|
-
|
42
|
-
# XMLStruct objects are compared according to their values
|
43
|
-
def <=>(other)
|
44
|
-
to_obj <=> other
|
45
|
-
end
|
46
|
-
|
47
|
-
# Array-notation access to elements and attributes. It comes handy when
|
48
|
-
# the element or attribute you need to reach is not reachable via dot
|
49
|
-
# notation (because it's not a valid method name, or because the method
|
50
|
-
# exists, such as 'id' or 'class').
|
51
|
-
#
|
52
|
-
# It also supports hash keys, which are useful to reach attributes named
|
53
|
-
# the same as elements in the same level (which are otherwise prioritized)
|
54
|
-
#
|
55
|
-
# All of this is a lot easier to exampling by example:
|
56
|
-
#
|
57
|
-
# <article id="main_article" author="j-random">
|
58
|
-
# <author>J. Random Hacker</author>
|
59
|
-
# </article>
|
60
|
-
#
|
61
|
-
# article.id => 9314390 # Object#id gets called
|
62
|
-
# article[:id] => "main_article" # id attribute
|
63
|
-
# article[:author] => <XMLStruct ...> # <author> element
|
64
|
-
# article[:attr => 'author'] => "j-random" # author attribute
|
65
|
-
#
|
66
|
-
# Valid keys for the hash notation in the example above are :attr,
|
67
|
-
# :attribute, :child, and :element.
|
68
|
-
def [](name)
|
69
|
-
unless name.is_a? Hash
|
70
|
-
return @children[name.to_sym] if @children[name.to_sym]
|
71
|
-
return @attributes[name.to_sym] if @attributes[name.to_sym]
|
72
|
-
end
|
73
|
-
|
74
|
-
raise 'one and only one key allowed' if name.size != 1
|
27
|
+
# the corresponding tree of decorated objects.
|
28
|
+
def self.new_decorated_obj(xml)
|
29
|
+
obj = if xml.text.blank? &&
|
30
|
+
xml.elements.map { |e| e.name }.uniq.size == 1
|
75
31
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
32
|
+
CollectionProxy.new new(xml.elements)
|
33
|
+
else
|
34
|
+
case
|
35
|
+
when (not xml.text.blank?) : xml.text.to_s
|
36
|
+
when (xml.cdatas.size >= 1) : xml.cdatas.first.to_s
|
37
|
+
else ''
|
38
|
+
end.extend String
|
83
39
|
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def method_missing(method, *args, &block) # :nodoc:
|
87
|
-
|
88
|
-
if method.to_s.match(/\?$/) && args.empty? && block.nil?
|
89
|
-
boolish = send(method.to_s.chomp('?').to_sym).to_s
|
90
40
|
|
91
|
-
|
41
|
+
obj.instance_variable_set :@__raw_xml, xml
|
92
42
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
single_child.send method, *args, &block
|
97
|
-
else
|
98
|
-
to_obj.send method, *args, &block
|
99
|
-
end
|
100
|
-
end
|
43
|
+
xml.each_element { |child| add_child(obj, child.name, new(child)) }
|
44
|
+
xml.attributes.each { |name, value| add_attribute(obj, name, value) }
|
101
45
|
|
102
|
-
|
103
|
-
%{ #<#{self.class.to_s} value=#{to_obj.inspect} (#{to_obj.class.to_s})
|
104
|
-
attributes=[#{@attributes.keys.map(&:to_s).join(', ') }]
|
105
|
-
children=[#{@children.keys.map(&:to_s).join(', ') }]> }.squish
|
46
|
+
obj.extend CommonBehaviours
|
106
47
|
end
|
107
48
|
|
108
|
-
private
|
49
|
+
private ##################################################################
|
109
50
|
|
110
|
-
|
111
|
-
|
51
|
+
# Decorates the given object 'obj' with a method 'name' that returns the
|
52
|
+
# given 'element'. If 'name' is already taken, takes care of the array
|
53
|
+
# folding behaviour.
|
54
|
+
def self.add_child(obj, name, element)
|
55
|
+
key = name.to_sym
|
56
|
+
children = obj.instance_variable_get :@__children
|
112
57
|
|
113
|
-
|
58
|
+
children[key] = if children[key]
|
114
59
|
|
115
|
-
unless respond_to?((plural_key = key.to_s.pluralize).to_sym)
|
116
|
-
instance_eval %{
|
60
|
+
unless obj.respond_to?((plural_key = key.to_s.pluralize).to_sym)
|
61
|
+
obj.instance_eval %{
|
62
|
+
def #{plural_key}; @__children[:#{key.to_s}]; end }
|
117
63
|
end
|
118
64
|
|
119
|
-
|
120
|
-
|
65
|
+
children[key] = [ children[key] ] unless children[key].is_a? Array
|
66
|
+
children[key] << element
|
121
67
|
else
|
122
|
-
unless respond_to? key
|
123
|
-
instance_eval %{
|
68
|
+
unless obj.respond_to? key
|
69
|
+
obj.instance_eval %{
|
70
|
+
def #{key.to_s}; @__children[:#{key.to_s}]; end }
|
124
71
|
end
|
125
72
|
|
126
73
|
element
|
127
74
|
end
|
128
|
-
end
|
129
75
|
|
130
|
-
|
131
|
-
|
132
|
-
@attributes[(key = name.to_sym)] = obj.is_a?(String) ? obj.squish : obj
|
133
|
-
|
134
|
-
unless respond_to? key
|
135
|
-
instance_eval %{ def #{key.to_s}; @attributes[:#{key.to_s}]; end }
|
136
|
-
end
|
76
|
+
obj.instance_variable_set :@__children, children
|
77
|
+
element
|
137
78
|
end
|
138
79
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
else (raw.cdatas.first.to_s rescue '')
|
143
|
-
end
|
80
|
+
# Decorates the given object 'obj' with a method 'name' that returns the
|
81
|
+
# given 'attr_value'.
|
82
|
+
def self.add_attribute(obj, name, attr_value) # :nodoc:
|
144
83
|
|
145
|
-
|
146
|
-
|
84
|
+
attributes = obj.instance_variable_get :@__attributes
|
85
|
+
attributes[(key = name.to_sym)] = attr_value.squish.extend String
|
147
86
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
when str.match(/[a-zA-Z]/) : str
|
152
|
-
when str.match(/^[+-]?\d+$/) : str.to_i
|
153
|
-
when str.match(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/) : str.to_f
|
154
|
-
else str
|
87
|
+
unless obj.respond_to? key
|
88
|
+
obj.instance_eval %{
|
89
|
+
def #{key.to_s}; @__attributes[:#{key.to_s}]; end }
|
155
90
|
end
|
91
|
+
|
92
|
+
obj.instance_variable_set :@__attributes, attributes
|
93
|
+
attr_value
|
156
94
|
end
|
157
95
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class XMLStruct::CollectionProxy < XMLStruct::BlankishSlate
|
2
|
+
def initialize(target)
|
3
|
+
@__children, @__attributes, @__target = {}, {}, target
|
4
|
+
end
|
5
|
+
|
6
|
+
def method_missing(m, *a, &b) # :nodoc:
|
7
|
+
answer = __question_answer(m, *a, &b)
|
8
|
+
answer.nil? ? (@__target.__send__(m, *a, &b) if @__target) : answer
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module XMLStruct::CommonBehaviours
|
2
|
+
|
3
|
+
# Detect an existing method being called in question form:
|
4
|
+
def __question_answer(method, *args, &block)
|
5
|
+
if method.to_s.match(/\?$/) && args.empty? && block.nil?
|
6
|
+
boolish = __send__(method.to_s.chomp('?').to_sym).to_s
|
7
|
+
|
8
|
+
return true if %w[ true yes t y ].include? boolish.downcase
|
9
|
+
return false if %w[ false no f n ].include? boolish.downcase
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Array-notation access to elements and attributes. It comes handy when
|
14
|
+
# the element or attribute you need to reach is not reachable via dot
|
15
|
+
# notation (because it's not a valid method name, or because the method
|
16
|
+
# exists, such as 'id' or 'class').
|
17
|
+
#
|
18
|
+
# It also supports hash keys, which are useful to reach attributes named
|
19
|
+
# the same as elements in the same level (which otherwise go first)
|
20
|
+
#
|
21
|
+
# All of this is a lot easier to exampling by example:
|
22
|
+
#
|
23
|
+
# <article id="main_article" author="j-random">
|
24
|
+
# <author>J. Random Hacker</author>
|
25
|
+
# </article>
|
26
|
+
#
|
27
|
+
# article.id => 9314390 # Object#id gets called
|
28
|
+
# article[:id] => "main_article" # id attribute
|
29
|
+
# article[:author] => <XMLStruct ...> # <author> element
|
30
|
+
# article[:attr => 'author'] => "j-random" # author attribute
|
31
|
+
#
|
32
|
+
# Valid keys for the hash notation in the example above are :attr,
|
33
|
+
# :attribute, :child, and :element.
|
34
|
+
def [](name)
|
35
|
+
return @__target[name] if @__target && name.is_a?(Numeric)
|
36
|
+
|
37
|
+
unless name.is_a? Hash
|
38
|
+
return @__children[name.to_sym] if @__children[name.to_sym]
|
39
|
+
return @__attributes[name.to_sym] if @__attributes[name.to_sym]
|
40
|
+
end
|
41
|
+
|
42
|
+
raise 'one and only one key allowed' if name.size != 1
|
43
|
+
|
44
|
+
case (param = name.keys.first.to_sym)
|
45
|
+
when :element : @__children[name.values.first.to_sym]
|
46
|
+
when :child : @__children[name.values.first.to_sym]
|
47
|
+
when :attr : @__attributes[name.values.first.to_sym]
|
48
|
+
when :attribute : @__attributes[name.values.first.to_sym]
|
49
|
+
else raise %{ Invalid key :#{param.to_s}.
|
50
|
+
Use one of :element, :child, :attr, or :attribute }.squish!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module XMLStruct::String
|
2
|
+
def self.extended(obj) # :nodoc:
|
3
|
+
obj.instance_variable_set :@__children, {}
|
4
|
+
obj.instance_variable_set :@__attributes, {}
|
5
|
+
obj
|
6
|
+
end
|
7
|
+
|
8
|
+
# Attempts to detect wether this String is really an integer or float,
|
9
|
+
# and returns accordingly. If not, just returns the string.
|
10
|
+
def rb
|
11
|
+
@__rb ||= case
|
12
|
+
when (self !~ /\S/) : nil
|
13
|
+
when match(/[a-zA-Z]/) : ::String.new(self)
|
14
|
+
when match(/^[+-]?\d+$/) : self.to_i
|
15
|
+
when match(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/) : self.to_f
|
16
|
+
else ::String.new(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# A decorated String is blank when it has a blank value, no child
|
21
|
+
# elements, and no attributes. For example:
|
22
|
+
#
|
23
|
+
# <blank_element></blank_element>
|
24
|
+
def blank?
|
25
|
+
(self !~ /\S/) && @__children.blank? && @__attributes.blank?
|
26
|
+
end
|
27
|
+
|
28
|
+
def method_missing(m, *a, &b) # :nodoc:
|
29
|
+
__question_answer(m, *a, &b)
|
30
|
+
end
|
31
|
+
end
|
data/test/samples/lorem.xml
CHANGED
@@ -0,0 +1,16 @@
|
|
1
|
+
<recipe name="bread" prep_time="5 mins" cook_time="3 hours">
|
2
|
+
<title>Basic bread</title>
|
3
|
+
<ingredient amount="8" unit="dL">Flour</ingredient>
|
4
|
+
<ingredient amount="10" unit="grams">Yeast</ingredient>
|
5
|
+
<ingredient amount="4" unit="dL" state="warm">Water</ingredient>
|
6
|
+
<ingredient amount="1" unit="teaspoon">Salt</ingredient>
|
7
|
+
<instructions easy="yes" hard="false">
|
8
|
+
<step>Mix all ingredients together.</step>
|
9
|
+
<step>Knead thoroughly.</step>
|
10
|
+
<step>Cover with a cloth, and leave for one hour in warm room.</step>
|
11
|
+
<step>Knead again.</step>
|
12
|
+
<step>Place in a bread baking tin.</step>
|
13
|
+
<step>Cover with a cloth, and leave for one hour in warm room.</step>
|
14
|
+
<step>Bake in the oven at 180(degrees)C for 30 minutes.</step>
|
15
|
+
</instructions>
|
16
|
+
</recipe>
|
data/test/test_helper.rb
CHANGED
@@ -23,7 +23,8 @@ def xml_file(name_symbol)
|
|
23
23
|
end
|
24
24
|
|
25
25
|
require 'digest/md5'
|
26
|
-
{ :lorem
|
26
|
+
{ :lorem => '9062c0f294383435d5b04ce6d67b6d61',
|
27
|
+
:recipe => '6087ab42049273d123d473093b04ab12' }.each do |file_key, md5|
|
27
28
|
|
28
29
|
unless Digest::MD5.hexdigest(xml_file(file_key).read) == md5
|
29
30
|
raise "Sample test file #{file_key.to_s}.xml doesn't match expected MD5"
|
data/test/xml_struct_test.rb
CHANGED
@@ -1,5 +1,50 @@
|
|
1
1
|
require File.join(File.dirname(__FILE__), 'test_helper')
|
2
2
|
|
3
|
+
describe 'The XML Struct module' do
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@filename = File.join(File.dirname(__FILE__), 'samples', 'recipe.xml')
|
7
|
+
@file = File.open @filename
|
8
|
+
@recipe = XMLStruct.new xml_file(:recipe)
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'should know how to run "new" with a filename given' do
|
12
|
+
XMLStruct.new(@filename).name.should == @recipe.name
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should know how to run "new" with a file given' do
|
16
|
+
XMLStruct.new(@file).name.should == @recipe.name
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should raise an exception when given something else to "new"' do
|
20
|
+
should.raise(RuntimeError) { XMLStruct.new(8) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe 'README Recipe' do
|
25
|
+
|
26
|
+
def setup
|
27
|
+
@recipe = XMLStruct.new xml_file(:recipe)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should have name and title as "bread" and "Basic bread"' do
|
31
|
+
@recipe.name.should == "bread"
|
32
|
+
@recipe.title.should == "Basic bread"
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'should treat "recipe.ingredients" as an Array' do
|
36
|
+
@recipe.ingredients.is_a?(Array).should.be true
|
37
|
+
@recipe.ingredients.first.amount.to_i.should == 8
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should have 7 easy instructions' do
|
41
|
+
@recipe.instructions.easy?.should.be true
|
42
|
+
@recipe.instructions.steps.size.should == 7
|
43
|
+
@recipe.instructions.first.upcase.should ==
|
44
|
+
"MIX ALL INGREDIENTS TOGETHER."
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
3
48
|
describe 'XML Struct' do
|
4
49
|
|
5
50
|
include RubyProf::Test if defined? RubyProf::Test
|
@@ -8,18 +53,18 @@ describe 'XML Struct' do
|
|
8
53
|
@lorem = XMLStruct.new xml_file(:lorem)
|
9
54
|
end
|
10
55
|
|
11
|
-
it 'should be an XMLStruct' do
|
12
|
-
@lorem.should.be.an.instance_of
|
56
|
+
it 'should be an instance of XMLStruct::String' do
|
57
|
+
@lorem.should.be.an.instance_of ::String
|
13
58
|
end
|
14
59
|
|
15
|
-
it '
|
60
|
+
it 'be blank if devoid of children, attributes and value' do
|
16
61
|
@lorem.ipsum.should.be.blank
|
17
62
|
end
|
18
63
|
|
19
|
-
it '
|
20
|
-
@lorem.dolor.
|
21
|
-
|
22
|
-
|
64
|
+
it 'not be blank when value, children, or attributes are present' do
|
65
|
+
[ @lorem.dolor, @lorem.sit, @lorem.ut ].each do |xr|
|
66
|
+
xr.should.not.be.blank
|
67
|
+
end
|
23
68
|
end
|
24
69
|
|
25
70
|
it 'should allow access to attributes named like invalid methods' do
|
@@ -27,11 +72,11 @@ describe 'XML Struct' do
|
|
27
72
|
end
|
28
73
|
|
29
74
|
it 'should allow access to elements named like invalid methods' do
|
30
|
-
'
|
75
|
+
@lorem['_minim'].should == 'veniam'
|
31
76
|
end
|
32
77
|
|
33
78
|
it 'should provide unambiguous access to elements named like attributes' do
|
34
|
-
|
79
|
+
@lorem.sed[:element => 'do'].should == 'eiusmod elementus'
|
35
80
|
end
|
36
81
|
|
37
82
|
it 'should provide unambiguous access to attributes named like elements' do
|
@@ -39,18 +84,18 @@ describe 'XML Struct' do
|
|
39
84
|
end
|
40
85
|
|
41
86
|
it 'should return elements first when using dot notation' do
|
42
|
-
@lorem.sed
|
87
|
+
@lorem.sed.do.should == @lorem.sed[:element => 'do']
|
43
88
|
end
|
44
89
|
|
45
|
-
it 'should return elements first when using
|
90
|
+
it 'should return elements first when using [] with string key' do
|
46
91
|
@lorem.sed['do'].should == @lorem.sed[:element => 'do']
|
47
92
|
end
|
48
93
|
|
49
|
-
it 'should return elements first when using
|
94
|
+
it 'should return elements first when using [] with symbol key' do
|
50
95
|
@lorem.sed[:do].should == @lorem.sed[:element => 'do']
|
51
96
|
end
|
52
97
|
|
53
|
-
it 'should raise exception when unkown keys are used in
|
98
|
+
it 'should raise exception when unkown keys are used in [{}] mode' do
|
54
99
|
should.raise(RuntimeError) { @lorem[:foo => 'bar'] }
|
55
100
|
end
|
56
101
|
|
@@ -68,16 +113,21 @@ describe 'XML Struct' do
|
|
68
113
|
|
69
114
|
it 'should convert integer-looking attribute strings to integers' do
|
70
115
|
@lorem.consecteturs.each do |c|
|
71
|
-
c['id'].is_a?(Numeric).should.be true
|
116
|
+
c['id'].rb.is_a?(Numeric).should.be true
|
72
117
|
end
|
73
118
|
end
|
74
119
|
|
75
120
|
it 'should convert float-looking attribute strings to floats' do
|
76
121
|
@lorem.consecteturs.each do |c|
|
77
|
-
c.capacity.is_a?(Float).should.be true
|
122
|
+
c.capacity.rb.is_a?(Float).should.be true
|
78
123
|
end
|
79
124
|
end
|
80
125
|
|
126
|
+
it 'should not convert strings with more than numbers to Fixnum' do
|
127
|
+
@lorem.sed.do.price.rb.should == @lorem.sed.do.price
|
128
|
+
@lorem.sed.do.price.rb.should.not == 8
|
129
|
+
end
|
130
|
+
|
81
131
|
it 'should convert bool-looking attribute strings to bools when asked' do
|
82
132
|
@lorem.consecteturs.each { |c| c.enabled?.should == !!(c.enabled?) }
|
83
133
|
end
|
@@ -88,31 +138,31 @@ describe 'XML Struct' do
|
|
88
138
|
end
|
89
139
|
|
90
140
|
it 'should pass forth methods to single array child when empty valued' do
|
91
|
-
@lorem.cupidatats
|
141
|
+
@lorem.cupidatats[0].should == @lorem.cupidatats.cupidatat[0]
|
92
142
|
end
|
93
143
|
|
94
144
|
it 'should not pass methods to single array child if not empty valued' do
|
95
|
-
|
145
|
+
should.raise(RuntimeError) { @lorem.voluptate[0] }
|
96
146
|
end
|
97
147
|
|
98
148
|
it 'should be valued as its text when text first and CDATA exist' do
|
99
|
-
|
149
|
+
@lorem.ullamco.should == 'Laboris'
|
100
150
|
end
|
101
151
|
|
102
152
|
it 'should have the value of its first CDATA when multiple exist' do
|
103
|
-
|
153
|
+
@lorem.deserunt.should == 'mollit'
|
104
154
|
end
|
105
155
|
|
106
156
|
it 'should squish whitespace in string attribute values' do
|
107
|
-
|
157
|
+
@lorem.irure.metadata.should == 'dolor'
|
108
158
|
end
|
109
159
|
|
110
160
|
it 'should not squish whitespace in string element values' do
|
111
|
-
" \n\t\t\treprehenderit "
|
161
|
+
@lorem.irure.should == " \n\t\t\treprehenderit "
|
112
162
|
end
|
113
163
|
|
114
164
|
it 'should not squish whitespace in CDATA values' do
|
115
|
-
"\t foo\n"
|
165
|
+
@lorem.should == "\t foo\n"
|
116
166
|
end
|
117
167
|
|
118
168
|
it 'should have a working inspect function' do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jordi-xml_struct
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jordi Bunster
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2008-10-
|
12
|
+
date: 2008-10-13 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -37,8 +37,12 @@ files:
|
|
37
37
|
- WHATSNEW
|
38
38
|
- lib
|
39
39
|
- lib/jordi-xml_struct.rb
|
40
|
+
- lib/xml_struct
|
41
|
+
- lib/xml_struct/blankish_slate.rb
|
42
|
+
- lib/xml_struct/collection_proxy.rb
|
43
|
+
- lib/xml_struct/common_behaviours.rb
|
44
|
+
- lib/xml_struct/string.rb
|
40
45
|
- lib/xml_struct.rb
|
41
|
-
- xml_struct.gemspec
|
42
46
|
has_rdoc: true
|
43
47
|
homepage: http://github.com/jordi/xml_struct
|
44
48
|
post_install_message:
|
@@ -70,6 +74,7 @@ test_files:
|
|
70
74
|
- test
|
71
75
|
- test/samples
|
72
76
|
- test/samples/lorem.xml
|
77
|
+
- test/samples/recipe.xml
|
73
78
|
- test/test_helper.rb
|
74
79
|
- test/vendor
|
75
80
|
- test/vendor/test-spec
|
data/xml_struct.gemspec
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
Gem::Specification.new do |s|
|
2
|
-
s.name = 'xml_struct'
|
3
|
-
s.version = '0.1.3'
|
4
|
-
s.date = '2008-10-10'
|
5
|
-
|
6
|
-
s.author = 'Jordi Bunster'
|
7
|
-
s.email = 'jordi@bunster.org'
|
8
|
-
s.homepage = 'http://github.com/jordi/xml_struct'
|
9
|
-
|
10
|
-
s.add_dependency 'activesupport', '>= 2.1.1'
|
11
|
-
|
12
|
-
s.summary = "The Rubyista's way to do quick XML sit-ups"
|
13
|
-
s.description = %{ This is a library for reading (not writing) XML. It is
|
14
|
-
particularly suited for cases where one is dealing with
|
15
|
-
small documents of a known structure.
|
16
|
-
|
17
|
-
It is slow and not devoid of caveats, but has a very
|
18
|
-
pleasant, Ruby-like syntax. }.strip!.gsub! /\s+/, ' '
|
19
|
-
|
20
|
-
s.test_files = %w[ test
|
21
|
-
test/samples
|
22
|
-
test/samples/lorem.xml
|
23
|
-
test/test_helper.rb
|
24
|
-
test/vendor
|
25
|
-
test/vendor/test-spec
|
26
|
-
test/vendor/test-spec/bin
|
27
|
-
test/vendor/test-spec/bin/specrb
|
28
|
-
test/vendor/test-spec/examples
|
29
|
-
test/vendor/test-spec/examples/stack.rb
|
30
|
-
test/vendor/test-spec/examples/stack_spec.rb
|
31
|
-
test/vendor/test-spec/lib
|
32
|
-
test/vendor/test-spec/lib/test
|
33
|
-
test/vendor/test-spec/lib/test/spec
|
34
|
-
test/vendor/test-spec/lib/test/spec/dox.rb
|
35
|
-
test/vendor/test-spec/lib/test/spec/rdox.rb
|
36
|
-
test/vendor/test-spec/lib/test/spec/should-output.rb
|
37
|
-
test/vendor/test-spec/lib/test/spec/version.rb
|
38
|
-
test/vendor/test-spec/lib/test/spec.rb
|
39
|
-
test/vendor/test-spec/Rakefile
|
40
|
-
test/vendor/test-spec/README
|
41
|
-
test/vendor/test-spec/ROADMAP
|
42
|
-
test/vendor/test-spec/SPECS
|
43
|
-
test/vendor/test-spec/test
|
44
|
-
test/vendor/test-spec/test/spec_dox.rb
|
45
|
-
test/vendor/test-spec/test/spec_flexmock.rb
|
46
|
-
test/vendor/test-spec/test/spec_mocha.rb
|
47
|
-
test/vendor/test-spec/test/spec_nestedcontexts.rb
|
48
|
-
test/vendor/test-spec/test/spec_new_style.rb
|
49
|
-
test/vendor/test-spec/test/spec_should-output.rb
|
50
|
-
test/vendor/test-spec/test/spec_testspec.rb
|
51
|
-
test/vendor/test-spec/test/spec_testspec_order.rb
|
52
|
-
test/vendor/test-spec/test/test_testunit.rb
|
53
|
-
test/vendor/test-spec/TODO
|
54
|
-
test/xml_struct_test.rb ]
|
55
|
-
|
56
|
-
s.files = %w[ MIT-LICENSE
|
57
|
-
README.markdown
|
58
|
-
Rakefile
|
59
|
-
TODO
|
60
|
-
WHATSNEW
|
61
|
-
lib
|
62
|
-
lib/jordi-xml_struct.rb
|
63
|
-
lib/xml_struct.rb
|
64
|
-
xml_struct.gemspec ]
|
65
|
-
|
66
|
-
s.has_rdoc = true
|
67
|
-
s.extra_rdoc_files = %w[ README.markdown ]
|
68
|
-
s.rdoc_options = %w[ --main README.markdown ]
|
69
|
-
end
|