jordi-xml_struct 0.1.3 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|