xmlparsable 1.4.2 → 1.5.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/lib/xmlparsable/elements/array.rb +36 -0
- data/lib/xmlparsable/elements/record.rb +6 -3
- data/lib/xmlparsable/elements.rb +1 -1
- data/lib/xmlparsable.rb +38 -30
- data/spec/examples/accessors.example +89 -0
- data/spec/examples/elements/array.example +110 -0
- data/spec/examples/elements/date.example +44 -0
- data/spec/examples/elements/numeric.example +60 -0
- data/spec/examples/elements/record.example +84 -0
- data/spec/examples/elements/string.example +44 -0
- data/spec/examples/elements/text.example +38 -0
- data/spec/examples/elements/time.example +36 -0
- data/spec/examples/elements/xml.example +1 -0
- data/spec/examples/inheritance.example +100 -124
- metadata +20 -4
- data/lib/xmlparsable/elements/list.rb +0 -31
- data/spec/examples/record.example +0 -260
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module XmlParsable
|
|
2
|
+
module Elements
|
|
3
|
+
|
|
4
|
+
class ArrayElement < AbstractElement
|
|
5
|
+
attr_reader :name
|
|
6
|
+
|
|
7
|
+
class Array < ::Array
|
|
8
|
+
attr_reader :xmlattrs
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def initialize(name, attributes, parent, arguments)
|
|
12
|
+
@name, @parent, @elements = name, parent, Array.new
|
|
13
|
+
|
|
14
|
+
@item_name = arguments[:item_name].to_s
|
|
15
|
+
@item_type = arguments[:class]
|
|
16
|
+
@item_args = arguments[:item]
|
|
17
|
+
@elements.instance_variable_set(:@xmlattrs, attributes)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def open(name, attributes)
|
|
21
|
+
if name == @item_name
|
|
22
|
+
@item_type.parsable.new(name, attributes, self, @item_args)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def close(element, value)
|
|
27
|
+
@elements << value
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def finalize
|
|
31
|
+
@parent.close(self, @elements)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -10,10 +10,12 @@ module XmlParsable
|
|
|
10
10
|
@target = target
|
|
11
11
|
@parsers = parsers
|
|
12
12
|
@elements = Hash.new
|
|
13
|
+
@target.instance_variable_set(:@xmlattrs, @attributes)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
def open(name, attributes)
|
|
16
17
|
parser, *arguments = @parsers[name]
|
|
18
|
+
|
|
17
19
|
if parser
|
|
18
20
|
parser.new(name, attributes, self, *arguments)
|
|
19
21
|
end
|
|
@@ -21,15 +23,16 @@ module XmlParsable
|
|
|
21
23
|
|
|
22
24
|
def close(element, value)
|
|
23
25
|
parser, *arguments = @parsers[element.name]
|
|
26
|
+
|
|
24
27
|
if arguments.include?(:repeated)
|
|
25
|
-
(@elements[element.name] ||= []) << value
|
|
28
|
+
(@elements[element.name.to_sym] ||= []) << value
|
|
26
29
|
else
|
|
27
|
-
@elements[element.name] = value
|
|
30
|
+
@elements[element.name.to_sym] = value
|
|
28
31
|
end
|
|
29
32
|
end
|
|
30
33
|
|
|
31
34
|
def finalize
|
|
32
|
-
@
|
|
35
|
+
@target.instance_variable_set(:@xmlelems, @elements)
|
|
33
36
|
@parent.close(self, @target) if @parent
|
|
34
37
|
end
|
|
35
38
|
|
data/lib/xmlparsable/elements.rb
CHANGED
|
@@ -2,7 +2,7 @@ module XmlParsable
|
|
|
2
2
|
module Elements
|
|
3
3
|
autoload :AbstractElement, "xmlparsable/elements/abstract"
|
|
4
4
|
autoload :DateElement, "xmlparsable/elements/date"
|
|
5
|
-
autoload :
|
|
5
|
+
autoload :ArrayElement, "xmlparsable/elements/array"
|
|
6
6
|
autoload :NumericElement, "xmlparsable/elements/numeric"
|
|
7
7
|
autoload :RecordElement, "xmlparsable/elements/record"
|
|
8
8
|
autoload :StringElement, "xmlparsable/elements/string"
|
data/lib/xmlparsable.rb
CHANGED
|
@@ -20,50 +20,58 @@ module XmlParsable
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
module ClassMethods
|
|
23
|
-
def element(name, *arguments)
|
|
24
|
-
element ||= arguments.pop.delete(:class) if arguments.last.is_a?(Hash)
|
|
25
|
-
element ||= arguments.shift if arguments.first.is_a?(Class)
|
|
26
|
-
element ||= Elements::StringElement
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
24
|
+
# element :name, StringElement, {options...}
|
|
25
|
+
# element :name, {:class => StringElement, options...}
|
|
26
|
+
def element(elem_name, *extras)
|
|
27
|
+
arguments = extras.pop if extras.last.is_a?(Hash)
|
|
28
|
+
arguments ||= Hash.new
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def repeated(name, *arguments)
|
|
39
|
-
element ||= arguments.pop.delete(:class) if arguments.last.is_a?(Hash)
|
|
40
|
-
element ||= arguments.shift if arguments.first.is_a?(Class)
|
|
41
|
-
element ||= Elements::StringElement
|
|
30
|
+
elem_type = extras.shift if extras.first.is_a?(Class)
|
|
31
|
+
elem_type ||= arguments.delete(:class)
|
|
32
|
+
elem_type ||= Elements::StringElement
|
|
33
|
+
raise ArgumentError, "extra arguments" unless extras.empty?
|
|
42
34
|
|
|
43
35
|
# Look up the inheritance hierarchy to map element name to parser
|
|
44
36
|
parent = superclass < XmlParsable ?
|
|
45
37
|
superclass.instance_variable_get(:@parsers) : Hash.new
|
|
46
38
|
|
|
47
39
|
@parsers ||= Hash.new{|h,k| parent[k] }
|
|
48
|
-
@parsers[
|
|
40
|
+
@parsers[elem_name.to_s] = [elem_type.parsable, arguments]
|
|
41
|
+
|
|
42
|
+
# Define accessor method (or don't, see if I care!)
|
|
43
|
+
attr_name = arguments.fetch(:accessor, elem_name)
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
if attr_name and not method_defined?(attr_name)
|
|
46
|
+
define_method(attr_name) { instance_variable_get(:@xmlelems)[elem_name] }
|
|
47
|
+
end
|
|
51
48
|
end
|
|
52
49
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
# collection :hive, BeeElement, {:item_name => :bee, array-options..., :item => {item-options...}}
|
|
51
|
+
# collection :hive, :bee, BeeElement, {array-options..., :item => {item-options...}}
|
|
52
|
+
# collection :hive, {array-options..., :item => {:class => BeeElement, item-options...}}
|
|
53
|
+
def collection(elem_name, *extras)
|
|
54
|
+
arguments = extras.pop if extras.last.is_a?(Hash)
|
|
55
|
+
arguments ||= Hash.new
|
|
56
|
+
|
|
57
|
+
arguments[:item] ||= Hash.new
|
|
58
|
+
arguments[:item_name] ||= extras.shift unless extras.first.is_a?(Class)
|
|
59
|
+
arguments[:item_name] ||= elem_name.to_s.singularize
|
|
60
|
+
arguments[:class] ||= extras.shift if extras.first.is_a?(Class)
|
|
61
|
+
arguments[:class] ||= Elements::StringElement
|
|
62
|
+
raise ArgumentError, "extra arguments" unless extras.empty?
|
|
63
|
+
|
|
64
|
+
element(elem_name, Elements::ArrayElement, arguments)
|
|
65
|
+
end
|
|
57
66
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
67
|
+
def repeated(elem_name, *extras)
|
|
68
|
+
if extras.last.is_a?(Hash)
|
|
69
|
+
extras.last[:repeated] = true
|
|
70
|
+
element(elem_name, *extras)
|
|
61
71
|
else
|
|
62
|
-
|
|
63
|
-
|
|
72
|
+
extras.push(Hash[:repeated => true])
|
|
73
|
+
element(elem_name, *extras)
|
|
64
74
|
end
|
|
65
|
-
|
|
66
|
-
element(plural, Elements::ListElement, single, item)
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
def parse(input)
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable, "accessors" do
|
|
4
|
+
|
|
5
|
+
let(:input) do
|
|
6
|
+
<<-XML
|
|
7
|
+
<root lang="en" charset="utf-8">
|
|
8
|
+
<size>500</size>
|
|
9
|
+
<wise>none</wise>
|
|
10
|
+
<secret>Ovaltine</secret>
|
|
11
|
+
</root>
|
|
12
|
+
XML
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context "default" do
|
|
16
|
+
let(:parser) do
|
|
17
|
+
Class.new do
|
|
18
|
+
class Inner
|
|
19
|
+
attr_reader :xmlattrs
|
|
20
|
+
attr_reader :xmlelems
|
|
21
|
+
|
|
22
|
+
include XmlParsable
|
|
23
|
+
element :size
|
|
24
|
+
element :wise
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
include XmlParsable
|
|
28
|
+
element :root, Inner
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
it "stores attributes in `xmlattrs`" do
|
|
33
|
+
xml = parser.parse(input)
|
|
34
|
+
xml.root.xmlattrs.should == Hash["lang" => "en", "charset" => "utf-8"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "stores elements in `xmlelems`" do
|
|
38
|
+
xml = parser.parse(input)
|
|
39
|
+
xml.root.xmlelems[:size] = "500"
|
|
40
|
+
xml.root.xmlelems[:wise] = "none"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "creates accessors for elements" do
|
|
44
|
+
xml = parser.parse(input)
|
|
45
|
+
xml.root.xmlelems[:size].should eql(xml.root.size)
|
|
46
|
+
xml.root.xmlelems[:wise].should eql(xml.root.wise)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
context "overridden" do
|
|
51
|
+
let(:parser) do
|
|
52
|
+
Class.new do
|
|
53
|
+
class Inner
|
|
54
|
+
attr_reader :xmlelems
|
|
55
|
+
|
|
56
|
+
def size
|
|
57
|
+
"junk junk"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
include XmlParsable
|
|
61
|
+
element :size
|
|
62
|
+
element :wise, accessor: :wisdom
|
|
63
|
+
element :secret, accessor: false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
include XmlParsable
|
|
67
|
+
element :root, Inner
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it "stores elements in `xmlelems`" do
|
|
72
|
+
xml = parser.parse(input)
|
|
73
|
+
xml.root.xmlelems[:size] == "500"
|
|
74
|
+
xml.root.xmlelems[:wise] == "none"
|
|
75
|
+
xml.root.xmlelems[:secret] == "Ovaltine"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
it "doesn't create accessors for hidden elements" do
|
|
79
|
+
xml = parser.parse(input)
|
|
80
|
+
xml.root.should_not respond_to(:secret)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "creates accessors for renamed elements" do
|
|
84
|
+
xml = parser.parse(input)
|
|
85
|
+
xml.root.xmlelems[:wise].should eql(xml.root.wisdom)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::ArrayElement do
|
|
4
|
+
|
|
5
|
+
describe "declaration" do
|
|
6
|
+
|
|
7
|
+
it "fully specified" do
|
|
8
|
+
parser = Class.new do
|
|
9
|
+
sheep = Class.new do
|
|
10
|
+
include XmlParsable
|
|
11
|
+
element :name
|
|
12
|
+
element :bahh
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
include XmlParsable
|
|
16
|
+
collection :flock, :sheep, sheep
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
xml = parser.parse(<<-XML)
|
|
20
|
+
<flock>
|
|
21
|
+
<sheep><bahh>H*</bahh></sheep>
|
|
22
|
+
<sheep><bahh>HH</bahh></sheep>
|
|
23
|
+
<sheep><bahh>*H</bahh></sheep>
|
|
24
|
+
</flock>
|
|
25
|
+
XML
|
|
26
|
+
|
|
27
|
+
xml.flock[0].bahh.should == "H*"
|
|
28
|
+
xml.flock[1].bahh.should == "HH"
|
|
29
|
+
xml.flock[2].bahh.should == "*H"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
specify "default child type is StringElement" do
|
|
33
|
+
parser = Class.new do
|
|
34
|
+
include XmlParsable
|
|
35
|
+
collection :contacts, :name
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
xml = parser.parse("<contacts><name>Green</name><name>White</name></contacts>")
|
|
39
|
+
xml.contacts.should == %w(Green White)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
specify "default child name is singular collection name" do
|
|
43
|
+
parser = Class.new do
|
|
44
|
+
include XmlParsable
|
|
45
|
+
collection :bees, XmlParsable::Elements::NumericElement
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
xml = parser.parse("<bees><bee>1000</bee><bee>1001</bee></bees>")
|
|
49
|
+
xml.bees.should == [1000, 1001]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
describe "parser" do
|
|
54
|
+
let(:parser) do
|
|
55
|
+
Class.new do
|
|
56
|
+
include XmlParsable
|
|
57
|
+
collection :list, :item
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "parses empty list into an empty Array" do
|
|
62
|
+
input = "<list/>"
|
|
63
|
+
expected = []
|
|
64
|
+
|
|
65
|
+
parser.parse(input).list.should == expected
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "parses attributes" do
|
|
69
|
+
input = '<list thang="yarn" milk="yawn"/>'
|
|
70
|
+
expected = Hash["thang" => "yarn", "milk" => "yawn"]
|
|
71
|
+
|
|
72
|
+
parser.parse(input).list.xmlattrs.should == expected
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "parses non-empty list into an Array" do
|
|
76
|
+
input = "<list><item>a</item><item>b</item></list>"
|
|
77
|
+
expected = ["a", "b"]
|
|
78
|
+
|
|
79
|
+
parser.parse(input).list.should == expected
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
it "ignores non-matching children" do
|
|
83
|
+
input = "<list><fail/><item/><boat/><item/><item/></list>"
|
|
84
|
+
expected = ["", "", ""]
|
|
85
|
+
|
|
86
|
+
parser.parse(input).list.should == expected
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
it "ignores comments" do
|
|
90
|
+
input = "<list><!-- <item>x</item> --><item><!-- y --></item></list>"
|
|
91
|
+
expected = [""]
|
|
92
|
+
|
|
93
|
+
parser.parse(input).list.should == expected
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "ignores whitespace between children" do
|
|
97
|
+
input = <<-XML
|
|
98
|
+
<list>
|
|
99
|
+
<item>a</item>
|
|
100
|
+
<item>b</item>
|
|
101
|
+
<item>c</item>
|
|
102
|
+
</list>
|
|
103
|
+
XML
|
|
104
|
+
|
|
105
|
+
expected = ["a", "b", "c"]
|
|
106
|
+
parser.parse(input).list.should == expected
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::DateElement do
|
|
4
|
+
|
|
5
|
+
describe "parser" do
|
|
6
|
+
let(:parser) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include XmlParsable
|
|
9
|
+
element :date, XmlParsable::Elements::DateElement
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "parses date into a ruby Date" do
|
|
14
|
+
expected = Date.today
|
|
15
|
+
input = "<date>#{expected}</date>"
|
|
16
|
+
parser.parse(input).date.should == expected
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "parses missing date as nil" do
|
|
20
|
+
expected = nil
|
|
21
|
+
input = "<date><!-- nope --></date>"
|
|
22
|
+
parser.parse(input).date.should == expected
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "ignores children" do
|
|
26
|
+
expected = nil
|
|
27
|
+
input = "<date><x>2010-01-01</x></date>"
|
|
28
|
+
parser.parse(input).date.should == expected
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "ignores comments" do
|
|
32
|
+
expected = Date.today
|
|
33
|
+
input = "<date>#{expected} <!-- that's today --></date>"
|
|
34
|
+
parser.parse(input).date.should == expected
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "ignores whitespace" do
|
|
38
|
+
expected = Date.today
|
|
39
|
+
input = "<date>\n #{Date.today}\n </date>"
|
|
40
|
+
parser.parse(input).date.should == expected
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::StringElement do
|
|
4
|
+
|
|
5
|
+
describe "parser" do
|
|
6
|
+
let(:parser) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include XmlParsable
|
|
9
|
+
element :number, XmlParsable::Elements::NumericElement
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "parses integers" do
|
|
14
|
+
expected = rand(1_000)
|
|
15
|
+
input = "<number>#{expected}</number>"
|
|
16
|
+
parser.parse(input).number.should == expected
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "parses hexadecimal" do
|
|
20
|
+
expected = rand(1_000)
|
|
21
|
+
input = "<number>0x#{expected.to_s(16)}</number>"
|
|
22
|
+
parser.parse(input).number.should == expected
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "parser octal" do
|
|
26
|
+
expected = rand(1_000)
|
|
27
|
+
input = "<number>0#{expected.to_s(8)}</number>"
|
|
28
|
+
parser.parse(input).number.should == expected
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "parses binary" do
|
|
32
|
+
expected = rand(1_000)
|
|
33
|
+
input = "<number>0b#{expected.to_s(2)}</number>"
|
|
34
|
+
parser.parse(input).number.should == expected
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "parses rationals" do
|
|
38
|
+
expected = rand(1_000) + rand
|
|
39
|
+
input = "<number>#{expected}</number>"
|
|
40
|
+
parser.parse(input).number.should == BigDecimal(expected.to_s)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "ignores comments" do
|
|
44
|
+
input = "<time><!-- 65432 --></time>"
|
|
45
|
+
parser.parse(input).number.should be_nil
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "ignores children" do
|
|
49
|
+
input = "<time><b>#{rand}</b></time>"
|
|
50
|
+
parser.parse(input).number.should be_nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
it "ignores whitespace" do
|
|
54
|
+
expected = rand(1_000)
|
|
55
|
+
input = "<number> #{expected} </number>"
|
|
56
|
+
parser.parse(input).number.should == expected
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::RecordElement do
|
|
4
|
+
|
|
5
|
+
let(:parser) do
|
|
6
|
+
Class.new do
|
|
7
|
+
include XmlParsable
|
|
8
|
+
|
|
9
|
+
element :date, XmlParsable::Elements::DateElement
|
|
10
|
+
element :string, XmlParsable::Elements::StringElement
|
|
11
|
+
element :text, XmlParsable::Elements::TextElement
|
|
12
|
+
element :time, XmlParsable::Elements::TimeElement
|
|
13
|
+
element :xml, XmlParsable::Elements::XmlElement
|
|
14
|
+
element :number, XmlParsable::Elements::NumericElement
|
|
15
|
+
element :record, self
|
|
16
|
+
|
|
17
|
+
collection :kids, self
|
|
18
|
+
collection :list, :item
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
describe "parser" do
|
|
23
|
+
it "parses multiple elements" do
|
|
24
|
+
time = Time.now
|
|
25
|
+
date = Date.today
|
|
26
|
+
|
|
27
|
+
xml = parser.parse <<-XML
|
|
28
|
+
<record>
|
|
29
|
+
<date>#{date}</date>
|
|
30
|
+
<time>#{time}</time>
|
|
31
|
+
<list>
|
|
32
|
+
<item>a</item>
|
|
33
|
+
<item>b</item>
|
|
34
|
+
<item>c</item>
|
|
35
|
+
</list>
|
|
36
|
+
<string>xyz</string>
|
|
37
|
+
<number>888</number>
|
|
38
|
+
<text>
|
|
39
|
+
<ul>
|
|
40
|
+
<li>vim</li>
|
|
41
|
+
<li>emacs</li>
|
|
42
|
+
<li>ctags</li>
|
|
43
|
+
<li>lint</li>
|
|
44
|
+
<li>gdb</li>
|
|
45
|
+
</ul>
|
|
46
|
+
</text>
|
|
47
|
+
</record>
|
|
48
|
+
XML
|
|
49
|
+
|
|
50
|
+
xml.record.list.should == ["a","b","c"]
|
|
51
|
+
xml.record.string.should == "xyz"
|
|
52
|
+
xml.record.number.should == 888
|
|
53
|
+
xml.record.date.should == date
|
|
54
|
+
xml.record.time.to_i.should == time.to_i
|
|
55
|
+
xml.record.text.strip.should == <<-DOC.strip
|
|
56
|
+
vim
|
|
57
|
+
emacs
|
|
58
|
+
ctags
|
|
59
|
+
lint
|
|
60
|
+
gdb
|
|
61
|
+
DOC
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it "parses recursive records" do
|
|
65
|
+
xml = parser.parse(<<-XML)
|
|
66
|
+
<record>
|
|
67
|
+
<string>root</string>
|
|
68
|
+
<record>
|
|
69
|
+
<string>child</string>
|
|
70
|
+
<record>
|
|
71
|
+
<string>grand child</string>
|
|
72
|
+
</record>
|
|
73
|
+
</record>
|
|
74
|
+
</record>
|
|
75
|
+
XML
|
|
76
|
+
|
|
77
|
+
xml.string.should be_nil
|
|
78
|
+
xml.record.string.should == "root"
|
|
79
|
+
xml.record.record.string.should == "child"
|
|
80
|
+
xml.record.record.record.string.should == "grand child"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::StringElement do
|
|
4
|
+
|
|
5
|
+
describe "parser" do
|
|
6
|
+
let(:parser) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include XmlParsable
|
|
9
|
+
element :string, XmlParsable::Elements::StringElement
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "parses empty string" do
|
|
14
|
+
expected = ""
|
|
15
|
+
input = "<string></string>"
|
|
16
|
+
parser.parse(input).string.should == expected
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "parses non-empty string" do
|
|
20
|
+
expected = "lo and behold"
|
|
21
|
+
input = "<string>lo and behold</string>"
|
|
22
|
+
parser.parse(input).string.should == expected
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "ignores comments" do
|
|
26
|
+
expected = "hi"
|
|
27
|
+
input = "<string>hi<!-- secret message --></string>"
|
|
28
|
+
parser.parse(input).string.should == expected
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "ignores children" do
|
|
32
|
+
expected = ", , plain"
|
|
33
|
+
input = "<string><b>bold</b>, <i>italic</i>, plain</string>"
|
|
34
|
+
parser.parse(input).string.should == expected
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it "does not ignore whitespace" do
|
|
38
|
+
expected = " foob ar"
|
|
39
|
+
input = "<string> foob ar</string>"
|
|
40
|
+
parser.parse(input).string.should == expected
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::StringElement do
|
|
4
|
+
|
|
5
|
+
describe "parser" do
|
|
6
|
+
let(:parser) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include XmlParsable
|
|
9
|
+
element :text, XmlParsable::Elements::TextElement
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "parses text" do
|
|
14
|
+
expected = "lo and behold"
|
|
15
|
+
input = "<text>lo and behold</text>"
|
|
16
|
+
parser.parse(input).text.should == expected
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "ignores comments" do
|
|
20
|
+
expected = "lo behold"
|
|
21
|
+
input = "<text>lo <!--and--> behold</text>"
|
|
22
|
+
parser.parse(input).text.should == expected
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it "strips away markup" do
|
|
26
|
+
expected = "lo and behold"
|
|
27
|
+
input = "<text>lo <span class='x'><i>and</i> behold</span></text>"
|
|
28
|
+
parser.parse(input).text.should == expected
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it "does not ignore whitespace" do
|
|
32
|
+
expected = " foob ar"
|
|
33
|
+
input = "<text> foob ar</text>"
|
|
34
|
+
parser.parse(input).text.should == expected
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
|
|
3
|
+
describe XmlParsable::Elements::StringElement do
|
|
4
|
+
|
|
5
|
+
describe "parser" do
|
|
6
|
+
let(:parser) do
|
|
7
|
+
Class.new do
|
|
8
|
+
include XmlParsable
|
|
9
|
+
element :time, XmlParsable::Elements::TimeElement
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "parses time" do
|
|
14
|
+
expected = Time.now
|
|
15
|
+
input = "<time>#{expected}</time>"
|
|
16
|
+
parser.parse(input).time.to_i.should == expected.to_i
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "ignores comments" do
|
|
20
|
+
input = "<time><!-- #{Time.now} --></time>"
|
|
21
|
+
parser.parse(input).time.should be_nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "ignores children" do
|
|
25
|
+
input = "<time><b>#{Time.now}</b></time>"
|
|
26
|
+
parser.parse(input).time.should be_nil
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "ignores whitespace" do
|
|
30
|
+
expected = Time.now
|
|
31
|
+
input = "<time> #{expected} </time>"
|
|
32
|
+
parser.parse(input).time.to_i.should == expected.to_i
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "spec_helper"
|
|
@@ -1,154 +1,130 @@
|
|
|
1
1
|
require "spec_helper"
|
|
2
2
|
|
|
3
|
-
describe XmlParsable
|
|
3
|
+
describe XmlParsable, "inheritance" do
|
|
4
|
+
|
|
5
|
+
let(:input) do
|
|
6
|
+
<<-XML
|
|
7
|
+
<root>
|
|
8
|
+
<!-- This is parsed as a Date, Integer, or String -->
|
|
9
|
+
<id>20120531</id>
|
|
10
|
+
<inherited>11235</inherited>
|
|
11
|
+
|
|
12
|
+
<a>some a-data</a>
|
|
13
|
+
<b>some b-data</b>
|
|
14
|
+
<c>
|
|
15
|
+
<id>IDENTIFIER</id>
|
|
16
|
+
<inherited></inherited>
|
|
17
|
+
</c>
|
|
18
|
+
</root>
|
|
19
|
+
XML
|
|
20
|
+
end
|
|
4
21
|
|
|
5
|
-
|
|
6
|
-
|
|
22
|
+
let(:common) do
|
|
23
|
+
Class.new do
|
|
24
|
+
attr_reader :xmlelems
|
|
7
25
|
|
|
8
|
-
class Record
|
|
9
26
|
include XmlParsable
|
|
10
|
-
element :id
|
|
11
|
-
element :
|
|
27
|
+
element :id
|
|
28
|
+
element :inherited, XmlParsable::Elements::NumericElement
|
|
12
29
|
end
|
|
13
|
-
|
|
14
|
-
element :record, Record
|
|
15
30
|
end
|
|
16
31
|
|
|
17
|
-
|
|
18
|
-
|
|
32
|
+
let(:base) do
|
|
33
|
+
root = common
|
|
34
|
+
Class.new do
|
|
35
|
+
include XmlParsable
|
|
36
|
+
element :root, root
|
|
37
|
+
end
|
|
38
|
+
end
|
|
19
39
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
40
|
+
# Override the parent's id element
|
|
41
|
+
let(:a) do
|
|
42
|
+
parent = common
|
|
43
|
+
Class.new do
|
|
44
|
+
inner = Class.new(parent) do
|
|
45
|
+
element :id, XmlParsable::Elements::TimeElement
|
|
46
|
+
element :a
|
|
47
|
+
end
|
|
48
|
+
include XmlParsable
|
|
49
|
+
element :root, inner
|
|
23
50
|
end
|
|
51
|
+
end
|
|
24
52
|
|
|
25
|
-
|
|
53
|
+
# Override the parent's id element
|
|
54
|
+
let(:b) do
|
|
55
|
+
parent = common
|
|
56
|
+
Class.new do
|
|
57
|
+
inner = Class.new(parent) do
|
|
58
|
+
element :id, XmlParsable::Elements::NumericElement
|
|
59
|
+
element :b
|
|
60
|
+
end
|
|
61
|
+
include XmlParsable
|
|
62
|
+
element :root, inner
|
|
63
|
+
end
|
|
26
64
|
end
|
|
27
65
|
|
|
28
|
-
|
|
29
|
-
|
|
66
|
+
let(:c) do
|
|
67
|
+
parent = common
|
|
30
68
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
69
|
+
Class.new do
|
|
70
|
+
inner = Class.new(parent) do
|
|
71
|
+
element :c, self
|
|
72
|
+
end
|
|
34
73
|
|
|
35
|
-
|
|
74
|
+
include XmlParsable
|
|
75
|
+
element :root, inner
|
|
76
|
+
end
|
|
36
77
|
end
|
|
37
78
|
|
|
38
|
-
|
|
39
|
-
|
|
79
|
+
describe "base parser" do
|
|
80
|
+
it "is separate from derived parsers" do
|
|
81
|
+
xml = base.parse(input)
|
|
82
|
+
xml.root.id.should == "20120531"
|
|
83
|
+
xml.root.inherited.should == 11235
|
|
40
84
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
85
|
+
xml.root.xmlelems.should_not have_key(:a)
|
|
86
|
+
xml.root.xmlelems.should_not have_key(:b)
|
|
87
|
+
xml.root.xmlelems.should_not have_key(:c)
|
|
44
88
|
|
|
45
|
-
|
|
89
|
+
xml.root.should_not respond_to(:a)
|
|
90
|
+
xml.root.should_not respond_to(:b)
|
|
91
|
+
xml.root.should_not respond_to(:c)
|
|
92
|
+
end
|
|
46
93
|
end
|
|
47
94
|
|
|
48
|
-
describe "
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
specify { should_not respond_to(:parent) }
|
|
54
|
-
|
|
55
|
-
it "parses attributes" do
|
|
56
|
-
data = BaseRecord.parse(<<-XML.strip)
|
|
57
|
-
<record>
|
|
58
|
-
<id>0xCAFE</id>
|
|
59
|
-
<size>0xFE</size>
|
|
60
|
-
</record>
|
|
61
|
-
XML
|
|
62
|
-
|
|
63
|
-
data.record.should be_a(BaseRecord::Record)
|
|
64
|
-
data.record.id.should == "0xCAFE"
|
|
65
|
-
data.record.size.should == 0xFE
|
|
95
|
+
describe "derived parsers" do
|
|
96
|
+
it "inherit elements from base parser" do
|
|
97
|
+
a.parse(input).root.inherited.should == 11235
|
|
98
|
+
b.parse(input).root.inherited.should == 11235
|
|
99
|
+
c.parse(input).root.inherited.should == 11235
|
|
66
100
|
end
|
|
67
|
-
end
|
|
68
101
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
it "parses attributes" do
|
|
77
|
-
data = FirstRecord.parse(<<-XML.strip)
|
|
78
|
-
<record>
|
|
79
|
-
<id>20120530T154500</id>
|
|
80
|
-
<size>0xCA7</size>
|
|
81
|
-
<name>Sabji</name>
|
|
82
|
-
</record>
|
|
83
|
-
XML
|
|
84
|
-
|
|
85
|
-
data.record.should be_a(FirstRecord::Record)
|
|
86
|
-
data.record.id.should == Time.local(2012, 5, 30, 15, 45)
|
|
87
|
-
data.record.size.should == 0xCA7
|
|
88
|
-
data.record.name.should == "Sabji"
|
|
102
|
+
it "can specify additional elements" do
|
|
103
|
+
a.parse(input).root.a.should == "some a-data"
|
|
104
|
+
b.parse(input).root.b.should == "some b-data"
|
|
105
|
+
|
|
106
|
+
c.parse(input).root.c.should_not be_nil
|
|
107
|
+
c.parse(input).root.c.id.should == "IDENTIFIER"
|
|
108
|
+
c.parse(input).root.c.inherited.should be_nil
|
|
89
109
|
end
|
|
90
|
-
end
|
|
91
110
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
specify { should respond_to(:size) }
|
|
96
|
-
specify { should_not respond_to(:name) }
|
|
97
|
-
specify { should_not respond_to(:parent) }
|
|
98
|
-
|
|
99
|
-
it "parses attributes" do
|
|
100
|
-
data = SecondRecord.parse(<<-XML)
|
|
101
|
-
<record>
|
|
102
|
-
<id>0.314</id>
|
|
103
|
-
<size>3.014</size>
|
|
104
|
-
</record>
|
|
105
|
-
XML
|
|
106
|
-
|
|
107
|
-
data.record.should be_a(SecondRecord::Record)
|
|
108
|
-
data.record.id.should == 0.314
|
|
109
|
-
data.record.size.should == 3.014
|
|
111
|
+
it "can override an inherited element" do
|
|
112
|
+
a.parse(input).root.id.should be_a(Time)
|
|
113
|
+
b.parse(input).root.id.should be_a(Integer)
|
|
110
114
|
end
|
|
111
|
-
end
|
|
112
115
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
<parent>
|
|
126
|
-
<id>X039-P99-THX1138</id>
|
|
127
|
-
<size>112358</size>
|
|
128
|
-
<parent>
|
|
129
|
-
<id>X039-P99</id>
|
|
130
|
-
<size>11235</size>
|
|
131
|
-
<parent>
|
|
132
|
-
<id>X039</id>
|
|
133
|
-
<size>1123</size>
|
|
134
|
-
</parent>
|
|
135
|
-
</parent>
|
|
136
|
-
</parent>
|
|
137
|
-
</record>
|
|
138
|
-
XML
|
|
139
|
-
|
|
140
|
-
data.record.should be_a(ThirdRecord::Record)
|
|
141
|
-
data.record.id.should == "X039-P99-THX1138-U"
|
|
142
|
-
data.record.size.should == 11235813
|
|
143
|
-
|
|
144
|
-
data.record.parent.id.should == "X039-P99-THX1138"
|
|
145
|
-
data.record.parent.size.should == 112358
|
|
146
|
-
|
|
147
|
-
data.record.parent.parent.id.should == "X039-P99"
|
|
148
|
-
data.record.parent.parent.size.should == 11235
|
|
149
|
-
|
|
150
|
-
data.record.parent.parent.parent.id.should == "X039"
|
|
151
|
-
data.record.parent.parent.parent.size.should == 1123
|
|
116
|
+
specify "are independent from one another" do
|
|
117
|
+
xml = a.parse(input).root
|
|
118
|
+
xml.should_not respond_to(:b)
|
|
119
|
+
xml.should_not respond_to(:c)
|
|
120
|
+
|
|
121
|
+
xml = b.parse(input).root
|
|
122
|
+
xml.should_not respond_to(:a)
|
|
123
|
+
xml.should_not respond_to(:c)
|
|
124
|
+
|
|
125
|
+
xml = c.parse(input).root
|
|
126
|
+
xml.should_not respond_to(:a)
|
|
127
|
+
xml.should_not respond_to(:b)
|
|
152
128
|
end
|
|
153
129
|
end
|
|
154
130
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: xmlparsable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.5.0
|
|
5
5
|
prerelease:
|
|
6
6
|
platform: ruby
|
|
7
7
|
authors:
|
|
@@ -36,8 +36,8 @@ files:
|
|
|
36
36
|
- README.md
|
|
37
37
|
- Rakefile
|
|
38
38
|
- lib/xmlparsable/elements/abstract.rb
|
|
39
|
+
- lib/xmlparsable/elements/array.rb
|
|
39
40
|
- lib/xmlparsable/elements/date.rb
|
|
40
|
-
- lib/xmlparsable/elements/list.rb
|
|
41
41
|
- lib/xmlparsable/elements/numeric.rb
|
|
42
42
|
- lib/xmlparsable/elements/record.rb
|
|
43
43
|
- lib/xmlparsable/elements/string.rb
|
|
@@ -47,8 +47,16 @@ files:
|
|
|
47
47
|
- lib/xmlparsable/elements.rb
|
|
48
48
|
- lib/xmlparsable/parser.rb
|
|
49
49
|
- lib/xmlparsable.rb
|
|
50
|
+
- spec/examples/accessors.example
|
|
51
|
+
- spec/examples/elements/array.example
|
|
52
|
+
- spec/examples/elements/date.example
|
|
53
|
+
- spec/examples/elements/numeric.example
|
|
54
|
+
- spec/examples/elements/record.example
|
|
55
|
+
- spec/examples/elements/string.example
|
|
56
|
+
- spec/examples/elements/text.example
|
|
57
|
+
- spec/examples/elements/time.example
|
|
58
|
+
- spec/examples/elements/xml.example
|
|
50
59
|
- spec/examples/inheritance.example
|
|
51
|
-
- spec/examples/record.example
|
|
52
60
|
- spec/spec_helper.rb
|
|
53
61
|
homepage: https://github.com/kputnam/xmlparsable
|
|
54
62
|
licenses: []
|
|
@@ -75,5 +83,13 @@ signing_key:
|
|
|
75
83
|
specification_version: 3
|
|
76
84
|
summary: Simple declarative XML parsing
|
|
77
85
|
test_files:
|
|
86
|
+
- spec/examples/accessors.example
|
|
87
|
+
- spec/examples/elements/array.example
|
|
88
|
+
- spec/examples/elements/date.example
|
|
89
|
+
- spec/examples/elements/numeric.example
|
|
90
|
+
- spec/examples/elements/record.example
|
|
91
|
+
- spec/examples/elements/string.example
|
|
92
|
+
- spec/examples/elements/text.example
|
|
93
|
+
- spec/examples/elements/time.example
|
|
94
|
+
- spec/examples/elements/xml.example
|
|
78
95
|
- spec/examples/inheritance.example
|
|
79
|
-
- spec/examples/record.example
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
module XmlParsable
|
|
2
|
-
module Elements
|
|
3
|
-
|
|
4
|
-
class ListElement < AbstractElement
|
|
5
|
-
attr_reader :name
|
|
6
|
-
|
|
7
|
-
def initialize(name, attributes, parent, *arguments)
|
|
8
|
-
@name, @parent, @elements = name, parent, []
|
|
9
|
-
|
|
10
|
-
@cname = arguments.shift.to_s
|
|
11
|
-
@cparser = arguments.shift
|
|
12
|
-
@cargs = arguments
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def open(name, attributes)
|
|
16
|
-
if name == @cname
|
|
17
|
-
@cparser.parsable.new(name, attributes, self, *@cargs)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def close(element, value)
|
|
22
|
-
@elements << value
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def finalize
|
|
26
|
-
@parent.close(self, @elements)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
end
|
|
@@ -1,260 +0,0 @@
|
|
|
1
|
-
require "spec_helper"
|
|
2
|
-
|
|
3
|
-
describe XmlParsable::Elements::RecordElement do
|
|
4
|
-
|
|
5
|
-
class Record
|
|
6
|
-
include XmlParsable
|
|
7
|
-
|
|
8
|
-
element :date, DateElement
|
|
9
|
-
element :string, StringElement
|
|
10
|
-
element :text, TextElement
|
|
11
|
-
element :time, TimeElement
|
|
12
|
-
element :xml, XmlElement
|
|
13
|
-
element :number, NumericElement
|
|
14
|
-
element :record, self
|
|
15
|
-
|
|
16
|
-
collection :kids, self
|
|
17
|
-
collection :list, :item
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
describe "date" do
|
|
21
|
-
it "parses date into a ruby Date" do
|
|
22
|
-
Record.parse("<date>#{Date.today}</date>").date.should == Date.today
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
it "parses missing date as nil" do
|
|
26
|
-
Record.parse("<date><!-- nope --></date>").date.should == nil
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
it "ignores children" do
|
|
30
|
-
Record.parse("<date><x>2010-01-01</x></date>").date.should == nil
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
it "ignores comments" do
|
|
34
|
-
Record.parse("<date>#{Date.today} <!-- that's today --></date>").date.should == Date.today
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
it "ignores whitespace" do
|
|
38
|
-
Record.parse("<date>\n #{Date.today}\n </date>").date.should == Date.today
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
describe "list of strings" do
|
|
43
|
-
it "parses empty list into an empty Array" do
|
|
44
|
-
Record.parse("<list/>").list.should == []
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
it "parses non-empty list into an Array" do
|
|
48
|
-
Record.parse("<list><item>a</item><item>b</item></list>").list.should == ["a", "b"]
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
it "ignores non-matching children" do
|
|
52
|
-
Record.parse("<list><fail/><item/><boat/><item/><item/></list>").list.should == ["", "", ""]
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
it "ignores comments" do
|
|
56
|
-
Record.parse("<list><!-- <item>x</item> --><item><!-- y --></item></list>").list.should == [""]
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
it "ignores whitespace between children" do
|
|
60
|
-
Record.parse(<<-XML).list.should == ["a", "b", "c"]
|
|
61
|
-
<list>
|
|
62
|
-
<item>a</item>
|
|
63
|
-
<item>b</item>
|
|
64
|
-
<item>c</item>
|
|
65
|
-
</list>
|
|
66
|
-
XML
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
describe "list of records" do
|
|
71
|
-
it "parses empty list into an empty Array" do
|
|
72
|
-
Record.parse("<kids/>").kids.should == []
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
it "parses non-empty list into an array" do
|
|
76
|
-
x = Record.parse(<<-XML)
|
|
77
|
-
<kids>
|
|
78
|
-
<kid><text>first</text></kid>
|
|
79
|
-
<kid><text>second</text></kid>
|
|
80
|
-
<kid><text>third</text></kid>
|
|
81
|
-
</kids>
|
|
82
|
-
XML
|
|
83
|
-
|
|
84
|
-
x.kids.should have(3).items
|
|
85
|
-
x.kids[0].text.should == "first"
|
|
86
|
-
x.kids[1].text.should == "second"
|
|
87
|
-
x.kids[2].text.should == "third"
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
describe "string" do
|
|
92
|
-
it "parses empty string" do
|
|
93
|
-
Record.parse("<string></string>").string.should == ""
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
it "parses non-empty string" do
|
|
97
|
-
Record.parse("<string>lo and behold</string>").string.should == "lo and behold"
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
it "ignores comments" do
|
|
101
|
-
Record.parse("<string>hi<!-- secret message --></string>").string.should == "hi"
|
|
102
|
-
end
|
|
103
|
-
|
|
104
|
-
it "ignores children" do
|
|
105
|
-
Record.parse("<string><b>bold</b>, <i>italic</i>, plain</string>").string.should == ", , plain"
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
it "does not ignore whitespace" do
|
|
109
|
-
Record.parse("<string> foob ar</string>").string.should == " foob ar"
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
describe "text" do
|
|
114
|
-
it "parses text" do
|
|
115
|
-
Record.parse("<text>lo and behold</text>").text.should == "lo and behold"
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
it "ignores comments" do
|
|
119
|
-
Record.parse("<text>lo <!--and--> behold</text>").text.should == "lo behold"
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it "strips away markup" do
|
|
123
|
-
Record.parse("<text>lo <span class='x'><i>and</i> behold</span></text>").text.should == "lo and behold"
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
it "does not ignore whitespace" do
|
|
127
|
-
Record.parse("<text> foob ar</text>").text.should == " foob ar"
|
|
128
|
-
end
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
describe "time" do
|
|
132
|
-
it "parses time" do
|
|
133
|
-
time = Time.now
|
|
134
|
-
Record.parse("<time>#{time}</time>").time.to_i.should == time.to_i
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
it "ignores comments" do
|
|
138
|
-
Record.parse("<time><!-- #{Time.now} --></time>").time.should be_nil
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it "ignores children" do
|
|
142
|
-
Record.parse("<time><b>#{Time.now}</b></time>").time.should be_nil
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it "ignores whitespace" do
|
|
146
|
-
time = Time.now
|
|
147
|
-
Record.parse("<time> #{time} </time>").time.to_i.should == time.to_i
|
|
148
|
-
end
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
describe "number" do
|
|
152
|
-
it "parses integers" do
|
|
153
|
-
number = rand(1_000)
|
|
154
|
-
Record.parse("<number>#{number}</number>").number.should == number
|
|
155
|
-
end
|
|
156
|
-
|
|
157
|
-
it "parses rationals" do
|
|
158
|
-
number = rand(1_000) + rand
|
|
159
|
-
Record.parse("<number>#{number}</number>").number.should == BigDecimal(number.to_s)
|
|
160
|
-
end
|
|
161
|
-
|
|
162
|
-
it "ignores comments" do
|
|
163
|
-
Record.parse("<time><!-- 65432 --></time>").number.should be_nil
|
|
164
|
-
end
|
|
165
|
-
|
|
166
|
-
it "ignores children" do
|
|
167
|
-
Record.parse("<time><b>#{rand}</b></time>").number.should be_nil
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
it "ignores whitespace" do
|
|
171
|
-
number = rand(1_000)
|
|
172
|
-
Record.parse("<number> #{number} </number>").number.should == number
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
describe "record" do
|
|
177
|
-
it "parses date" do
|
|
178
|
-
x = Record.parse("<record><date>#{Date.today}</date></record>")
|
|
179
|
-
x.date.should be_nil
|
|
180
|
-
x.record.date.should == Date.today
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
it "parses list" do
|
|
184
|
-
x = Record.parse("<record><list/></record>")
|
|
185
|
-
x.list.should be_nil
|
|
186
|
-
x.record.list.should == []
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
it "parses string" do
|
|
190
|
-
x = Record.parse("<record><string>abc</string></record>")
|
|
191
|
-
x.string.should be_nil
|
|
192
|
-
x.record.string.should == "abc"
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
it "parses text" do
|
|
196
|
-
x = Record.parse("<record><text><b>big</b><i>little</i></text></record>")
|
|
197
|
-
x.text.should be_nil
|
|
198
|
-
x.record.text.should == "biglittle"
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
it "parses time" do
|
|
202
|
-
time = Time.now
|
|
203
|
-
Record.parse("<record><time>#{time}</time></record>").record.time.to_i.should == time.to_i
|
|
204
|
-
end
|
|
205
|
-
|
|
206
|
-
it "parses number" do
|
|
207
|
-
number = rand(1_000) + rand
|
|
208
|
-
Record.parse("<record><number>#{number}</number></record>").record.number.should == BigDecimal(number.to_s)
|
|
209
|
-
end
|
|
210
|
-
|
|
211
|
-
it "parses multiple elements" do
|
|
212
|
-
time = Time.now
|
|
213
|
-
x = Record.parse <<-XML
|
|
214
|
-
<record>
|
|
215
|
-
<date>#{Date.today}</date>
|
|
216
|
-
<list>
|
|
217
|
-
<item>a</item>
|
|
218
|
-
<item>b</item>
|
|
219
|
-
<item>c</item>
|
|
220
|
-
</list>
|
|
221
|
-
<string>xyz</string>
|
|
222
|
-
<text>
|
|
223
|
-
<ul>
|
|
224
|
-
<li>vim</li>
|
|
225
|
-
<li>emacs</li>
|
|
226
|
-
<li>ctags</li>
|
|
227
|
-
<li>lint</li>
|
|
228
|
-
<li>gdb</li>
|
|
229
|
-
</ul>
|
|
230
|
-
</text>
|
|
231
|
-
<time>#{time}</time>
|
|
232
|
-
</record>
|
|
233
|
-
XML
|
|
234
|
-
|
|
235
|
-
x.record.date.should == Date.today
|
|
236
|
-
x.record.list.should == ["a","b","c"]
|
|
237
|
-
x.record.string.should == "xyz"
|
|
238
|
-
x.record.text.strip.should == <<-DOC.strip
|
|
239
|
-
vim
|
|
240
|
-
emacs
|
|
241
|
-
ctags
|
|
242
|
-
lint
|
|
243
|
-
gdb
|
|
244
|
-
DOC
|
|
245
|
-
|
|
246
|
-
x.record.time.to_i.should == time.to_i
|
|
247
|
-
end
|
|
248
|
-
|
|
249
|
-
it "parses recursive records" do
|
|
250
|
-
x = Record.parse("<record><record><record><record></record></record></record></record>")
|
|
251
|
-
x.should be_a(Record)
|
|
252
|
-
x.record.should be_a(Record)
|
|
253
|
-
x.record.record.should be_a(Record)
|
|
254
|
-
x.record.record.record.should be_a(Record)
|
|
255
|
-
x.record.record.record.record.should be_a(Record)
|
|
256
|
-
x.record.record.record.record.record.should be_nil
|
|
257
|
-
end
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
end
|