xmlparsable 1.1

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.md ADDED
@@ -0,0 +1,80 @@
1
+ # XmlParsable
2
+
3
+ XmlParsable is a simple means to declare how an XML grammar maps to Ruby objects. It does not
4
+ perform validation of the input, and it is only able to describe fairly simple structures. It
5
+ relies on LibXML::XML::SaxParser.
6
+
7
+ 1. Declare XML grammar
8
+
9
+ require "xmlparsable"
10
+
11
+ # Needed only for using the "collection" declaration or
12
+ # implement your own String#pluralize, String#singularize
13
+ require "active_support/core_ext"
14
+
15
+ class AddressBook
16
+ include XmlParsable
17
+
18
+ class Contact
19
+ include XmlParsable
20
+ element :givenname
21
+ element :familyname
22
+ element :phone
23
+ element :email
24
+ element :spouse, Contact
25
+ collection :children, Contact
26
+ element :birthdate, DateElement
27
+ element :updated, TimeElement
28
+ end
29
+
30
+ collection :contact, Contact
31
+ end
32
+
33
+ 2. Parse input XML from string, IO, or File
34
+
35
+ book = AddressBook.parse(<<-XML)
36
+ <contacts>
37
+ <contact>
38
+ <familyname>Kim</familyname>
39
+ <givenname>Jong-il</givenname>
40
+ <birthdate>1942-02-16</birthdate>
41
+ <updated>2010-01-01T12:45:30</updated>
42
+ <children>
43
+ <child><givenname>Sul-song</givenname></child>
44
+ <child><givenname>Jong-chul</givenname></child>
45
+ <child><givenname>Jong-un</givenname></child>
46
+ <child>
47
+ <givenname>Jong-nam</givenname>
48
+ <spouse>
49
+ <givenname>Jong-hui</givenname>
50
+ <familyname>Shin</familyname>
51
+ </spouse>
52
+ </child>
53
+ </children>
54
+ </contact>
55
+
56
+ <contact>
57
+ <givenname>Vladimir</givenname>
58
+ <familyname>Putin</familyname>
59
+ <email>vlad@gmail.com</email>
60
+ <spouse>
61
+ <givenname>Lyudmila</givenname>
62
+ <familyname>Putina</familyname>
63
+ </spouse>
64
+ </contact>
65
+ </contacts>
66
+ XML
67
+
68
+ 3. Iterate parsed data
69
+
70
+ kim, vlad = book.contacts
71
+ kim.givenname #=> "Kim"
72
+ kim.birthdate #=> #<Date: 4860813/2,0,2299161>
73
+ kim.updated #=> Fri Jan 01 12:45:30 2010
74
+
75
+ sulsong, jongchul, jongun, jongnam = kim.children
76
+ jongnam.spouse.givenname #=> "Jong-hui"
77
+ jongnam.spouse.familyname #=> "Shin"
78
+
79
+ vlad.email #=> "vlad@gmail.com"
80
+ vlad.spouse #=> #<AddressBook::Contact @givenname="Lyudmila" @familyname="Putina">
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ begin
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.verbose = false
6
+ t.pattern = "spec/examples/**/*.example"
7
+ t.rspec_opts = ["--color", "--format=p"]
8
+ end
9
+ rescue LoadError
10
+ require "spec/rake/spectask"
11
+
12
+ Spec::Rake::SpecTask.new do |t|
13
+ t.pattern = "spec/examples/**/*.example"
14
+ t.spec_opts << "--color"
15
+ t.spec_opts << "--format=p"
16
+ end
17
+ end
18
+
19
+ task :default => :spec
@@ -0,0 +1,21 @@
1
+ module XmlParsable
2
+ module Elements
3
+ class AbstractElement
4
+ def self.parsable
5
+ self
6
+ end
7
+
8
+ def open(name, attributes)
9
+ end
10
+
11
+ def read(text)
12
+ end
13
+
14
+ def comment(text)
15
+ end
16
+
17
+ def finalize
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,25 @@
1
+ require "date"
2
+
3
+ module XmlParsable
4
+ module Elements
5
+ #
6
+ # Parses content into a ruby Date value
7
+ #
8
+ class DateElement < AbstractElement
9
+ attr_reader :name
10
+
11
+ def initialize(name, attributes, parent, *arguments)
12
+ @name, @parent, @string = name, parent, ""
13
+ end
14
+
15
+ def read(text)
16
+ @string << text
17
+ end
18
+
19
+ def finalize
20
+ stripped = @string.strip
21
+ @parent.closed(self, Date.parse(stripped)) if stripped != ""
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
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 closed(element, value)
22
+ @elements << value
23
+ end
24
+
25
+ def finalize
26
+ @parent.closed(self, @elements)
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,48 @@
1
+ module XmlParsable
2
+ module Elements
3
+ class RecordElement < AbstractElement
4
+ attr_reader :name, :attributes, :parent, :target
5
+
6
+ def initialize(name, attributes, parent, target, parsers, *arguments)
7
+ @name = name
8
+ @attributes = attributes
9
+ @parent = parent
10
+ @target = target
11
+ @parsers = parsers
12
+ @elements = Hash.new
13
+ end
14
+
15
+ def open(name, attributes)
16
+ parser, *arguments = @parsers[name]
17
+ if parser
18
+ parser.new(name, attributes, self, *arguments)
19
+ end
20
+ end
21
+
22
+ def closed(element, value)
23
+ parser, *arguments = @parsers[element.name]
24
+ if arguments.include?(:repeated)
25
+ (@elements[element.name] ||= []) << value
26
+ else
27
+ @elements[element.name] = value
28
+ end
29
+ end
30
+
31
+ def finalize
32
+ @elements.each{|name, value| @target.instance_variable_set("@#{name}", value) }
33
+ @parent.closed(self, @target) if @parent
34
+ end
35
+
36
+ class Proxy
37
+ def initialize(base, parsers)
38
+ @base, @parsers = base, parsers
39
+ end
40
+
41
+ def new(name, attributes, parent, *arguments)
42
+ RecordElement.new(name, attributes, parent, @base.new, @parsers)
43
+ end
44
+ end
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ module XmlParsable
2
+ module Elements
3
+ #
4
+ # Parses content into a ruby String value. This discards comments and children
5
+ # elements so "abc <!-- foo --> def <b>whatever</b>" is read as "abc def"
6
+ #
7
+ class StringElement < AbstractElement
8
+ attr_reader :name
9
+
10
+ def initialize(name, attributes, parent, *arguments)
11
+ @name, @parent, @string = name, parent, ""
12
+ end
13
+
14
+ def read(text)
15
+ @string << text
16
+ end
17
+
18
+ def finalize
19
+ @parent.closed(self, @string)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,34 @@
1
+ module XmlParsable
2
+ module Elements
3
+ #
4
+ # Parses content into a ruby String value. Discards comments and flattens children
5
+ # elements so "abc def <b>what<!-- foo -->ever</b>" is read as "abc def whatever"
6
+ #
7
+ class TextElement < XmlElement::Node
8
+ attr_reader :name, :attributes, :parent
9
+
10
+ def initialize(name, attributes, parent, *arguments)
11
+ @name, @attributes, @parent, @text = name, attributes, parent, ""
12
+ end
13
+
14
+ def open(name, attributes)
15
+ TextElement.new(name, attributes, self)
16
+ end
17
+
18
+ def read(text)
19
+ @text << text
20
+ end
21
+
22
+ def comment(text)
23
+ end
24
+
25
+ def closed(element, value)
26
+ @text << value
27
+ end
28
+
29
+ def finalize
30
+ @parent.closed(self, @text)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ require "time"
2
+
3
+ module XmlParsable
4
+ module Elements
5
+ #
6
+ # Parses content into a ruby Time value
7
+ #
8
+ class TimeElement < AbstractElement
9
+ attr_reader :name
10
+
11
+ def initialize(name, attributes, parent, *arguments)
12
+ @name, @parent, @string = name, parent, ""
13
+ end
14
+
15
+ def read(text)
16
+ @string << text
17
+ end
18
+
19
+ def finalize
20
+ stripped = @string.strip
21
+ @parent.closed(self, Time.parse(stripped)) if stripped != ""
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,79 @@
1
+ module XmlParsable
2
+ module Elements
3
+ #
4
+ # Parses content into a generic tree structure, consisting of three
5
+ # classes -- Node, Text, and Comment.
6
+ #
7
+ class XmlElement
8
+
9
+ def self.parsable
10
+ Node
11
+ end
12
+
13
+ class Node < AbstractElement
14
+ attr_reader :name, :attributes, :parent, :children
15
+
16
+ def initialize(name, attributes, parent, *arguments)
17
+ @parent, @name, @attributes, @children = parent, name, attributes, Array.new
18
+ end
19
+
20
+ def open(name, attributes)
21
+ Node.new(name, attributes, self)
22
+ end
23
+
24
+ def read(text)
25
+ @children << Text.new(text, self)
26
+ end
27
+
28
+ def comment(text)
29
+ @children << Comment.new(text, self)
30
+ end
31
+
32
+ def closed(element, value)
33
+ @children << value
34
+ end
35
+
36
+ def finalize
37
+ @parent.closed(self, self)
38
+ end
39
+
40
+ def to_s(buffer = "")
41
+ buffer << "<#{@name}"
42
+ @attributes.each{|name, value| buffer << %[ #{name}='#{value}'] }
43
+
44
+ if @children.empty?
45
+ buffer << "/>"
46
+ else
47
+ buffer << ">"
48
+ @children.each{|child| child.to_s(buffer) }
49
+ buffer << "</#{@name}>"
50
+ end
51
+ end
52
+ end
53
+
54
+ class Text
55
+ attr_reader :text, :parent
56
+
57
+ def initialize(text, parent)
58
+ @text, @parent = text, parent
59
+ end
60
+
61
+ def to_s(buffer = "")
62
+ buffer << @text
63
+ end
64
+ end
65
+
66
+ class Comment
67
+ attr_reader :text, :parent
68
+
69
+ def initialize(text, parent)
70
+ @text, @parent = text, parent
71
+ end
72
+
73
+ def to_s(buffer = "")
74
+ buffer << "<!--" << @text << "-->"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,12 @@
1
+ module XmlParsable
2
+ module Elements
3
+ autoload :AbstractElement, "xmlparsable/elements/abstract"
4
+ autoload :DateElement, "xmlparsable/elements/date"
5
+ autoload :ListElement, "xmlparsable/elements/list"
6
+ autoload :RecordElement, "xmlparsable/elements/record"
7
+ autoload :StringElement, "xmlparsable/elements/string"
8
+ autoload :TextElement, "xmlparsable/elements/text"
9
+ autoload :TimeElement, "xmlparsable/elements/time"
10
+ autoload :XmlElement, "xmlparsable/elements/xml"
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module XmlParsable
2
+ class Parser
3
+ attr_reader :documents
4
+
5
+ def initialize(constructed = nil, &constructor)
6
+ @documents = Array.new
7
+ @constructor = constructor || lambda { constructed }
8
+ end
9
+
10
+ def on_start_document
11
+ @stack = [@constructor.call]
12
+ end
13
+
14
+ def on_end_document
15
+ @documents << @stack.shift.finalize
16
+ end
17
+
18
+ def on_characters(text)
19
+ @stack.first.read(text)
20
+ end
21
+
22
+ def on_comment(comment)
23
+ @stack.first.comment(comment)
24
+ end
25
+
26
+ def on_start_element_ns(name, attributes, prefix, uri, namespaces)
27
+ @stack.unshift(@stack.first.open(name, attributes) || StubElement)
28
+ end
29
+
30
+ def on_end_element_ns(name, prefix, uri)
31
+ @stack.shift.finalize
32
+ end
33
+
34
+ module StubElement
35
+ def self.open(name, attributes) self end
36
+ def self.close(element, value) end
37
+ def self.comment(text) end
38
+ def self.read(text) end
39
+ def self.finalize; end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,74 @@
1
+ require "libxml"
2
+
3
+ module XmlParsable
4
+
5
+ autoload :Parser, "xmlparsable/parser"
6
+ autoload :Elements, "xmlparsable/elements"
7
+
8
+ def self.parser(input)
9
+ case input
10
+ when String then LibXML::XML::SaxParser.string(input)
11
+ when File then LibXML::XML::SaxParser.file(input)
12
+ when IO then LibXML::XML::SaxParser.io(input)
13
+ else raise ArgumentError, "argument must be a String, File, or IO"
14
+ end
15
+ end
16
+
17
+ def self.included(base)
18
+ base.extend(ClassMethods)
19
+ base.__send__(:include, Elements)
20
+ end
21
+
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
+
28
+ (@parsers ||= {})[name.to_s] = [element.parsable, *arguments]
29
+
30
+ define_method(name) { instance_variable_get("@#{name}") }
31
+ end
32
+
33
+ def repeated(name, *arguments)
34
+ element ||= arguments.pop.delete(:class) if arguments.last.is_a?(Hash)
35
+ element ||= arguments.shift if arguments.first.is_a?(Class)
36
+ element ||= Elements::StringElement
37
+
38
+ (@parsers ||= {})[name.to_s.singularize] = [element.parsable, *arguments.push(:repeated)]
39
+
40
+ define_method(name) { instance_variable_get("@#{name.to_s.singularize}") }
41
+ end
42
+
43
+ def collection(name, *arguments)
44
+ item ||= arguments.pop.delete(:class) if arguments.last.is_a?(Hash)
45
+ item ||= arguments.shift if arguments.first.is_a?(Class)
46
+ item ||= Elements::StringElement
47
+
48
+ if arguments.empty?
49
+ single = name.to_s.singularize
50
+ plural = name.to_s.pluralize
51
+ else
52
+ plural = name
53
+ single = arguments.shift
54
+ end
55
+
56
+ element(plural, Elements::ListElement, single, item)
57
+ end
58
+
59
+ def parse(input)
60
+ root = XmlParsable::Elements::RecordElement.new(nil, nil, nil, new, @parsers)
61
+ stack = XmlParsable::Parser.new(root)
62
+ parser = XmlParsable.parser(input)
63
+
64
+ parser.callbacks = stack
65
+ parser.parse
66
+ root.target
67
+ end
68
+
69
+ def parsable
70
+ Elements::RecordElement::Proxy.new(self, @parsers)
71
+ end
72
+ end
73
+
74
+ end
@@ -0,0 +1,229 @@
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 :record, self
10
+ element :string, StringElement
11
+ element :text, TextElement
12
+ element :time, TimeElement
13
+ element :xml, XmlElement
14
+
15
+ collection :kids, self
16
+ collection :list, :item
17
+ end
18
+
19
+ describe "date" do
20
+ it "parses date into a ruby Date" do
21
+ Record.parse("<date>#{Date.today}</date>").date.should == Date.today
22
+ end
23
+
24
+ it "parses missing date as nil" do
25
+ Record.parse("<date><!-- nope --></date>").date.should == nil
26
+ end
27
+
28
+ it "ignores children" do
29
+ Record.parse("<date><x>2010-01-01</x></date>").date.should == nil
30
+ end
31
+
32
+ it "ignores comments" do
33
+ Record.parse("<date>#{Date.today} <!-- that's today --></date>").date.should == Date.today
34
+ end
35
+
36
+ it "ignores whitespace" do
37
+ Record.parse("<date>\n #{Date.today}\n </date>").date.should == Date.today
38
+ end
39
+ end
40
+
41
+ describe "list of strings" do
42
+ it "parses empty list into an empty Array" do
43
+ Record.parse("<list/>").list.should == []
44
+ end
45
+
46
+ it "parses non-empty list into an Array" do
47
+ Record.parse("<list><item>a</item><item>b</item></list>").list.should == ["a", "b"]
48
+ end
49
+
50
+ it "ignores non-matching children" do
51
+ Record.parse("<list><fail/><item/><boat/><item/><item/></list>").list.should == ["", "", ""]
52
+ end
53
+
54
+ it "ignores comments" do
55
+ Record.parse("<list><!-- <item>x</item> --><item><!-- y --></item></list>").list.should == [""]
56
+ end
57
+
58
+ it "ignores whitespace between children" do
59
+ Record.parse(<<-XML).list.should == ["a", "b", "c"]
60
+ <list>
61
+ <item>a</item>
62
+ <item>b</item>
63
+ <item>c</item>
64
+ </list>
65
+ XML
66
+ end
67
+ end
68
+
69
+ describe "list of records" do
70
+ it "parses empty list into an empty Array" do
71
+ Record.parse("<kids/>").kids.should == []
72
+ end
73
+
74
+ it "parses non-empty list into an array" do
75
+ x = Record.parse(<<-XML)
76
+ <kids>
77
+ <kid><text>first</text></kid>
78
+ <kid><text>second</text></kid>
79
+ <kid><text>third</text></kid>
80
+ </kids>
81
+ XML
82
+
83
+ x.kids.should have(3).items
84
+ x.kids[0].text.should == "first"
85
+ x.kids[1].text.should == "second"
86
+ x.kids[2].text.should == "third"
87
+ end
88
+ end
89
+
90
+ describe "string" do
91
+ it "parses empty string" do
92
+ Record.parse("<string></string>").string.should == ""
93
+ end
94
+
95
+ it "parses non-empty string" do
96
+ Record.parse("<string>lo and behold</string>").string.should == "lo and behold"
97
+ end
98
+
99
+ it "ignores comments" do
100
+ Record.parse("<string>hi<!-- secret message --></string>").string.should == "hi"
101
+ end
102
+
103
+ it "ignores children" do
104
+ Record.parse("<string><b>bold</b>, <i>italic</i>, plain</string>").string.should == ", , plain"
105
+ end
106
+
107
+ it "does not ignore whitespace" do
108
+ Record.parse("<string> foob ar</string>").string.should == " foob ar"
109
+ end
110
+ end
111
+
112
+ describe "text" do
113
+ it "parses text" do
114
+ Record.parse("<text>lo and behold</text>").text.should == "lo and behold"
115
+ end
116
+
117
+ it "ignores comments" do
118
+ Record.parse("<text>lo <!--and--> behold</text>").text.should == "lo behold"
119
+ end
120
+
121
+ it "strips away markup" do
122
+ Record.parse("<text>lo <span class='x'><i>and</i> behold</span></text>").text.should == "lo and behold"
123
+ end
124
+
125
+ it "does not ignore whitespace" do
126
+ Record.parse("<text> foob ar</text>").text.should == " foob ar"
127
+ end
128
+ end
129
+
130
+ describe "time" do
131
+ it "parses time" do
132
+ time = Time.now
133
+ Record.parse("<time>#{time}</time>").time.to_i.should == time.to_i
134
+ end
135
+
136
+ it "ignores comments" do
137
+ Record.parse("<time><!-- #{Time.now} --></time>").time.should be_nil
138
+ end
139
+
140
+ it "ignores children" do
141
+ Record.parse("<time><b>#{Time.now}</b></time>").time.should be_nil
142
+ end
143
+
144
+ it "ignores whitespace" do
145
+ time = Time.now
146
+ Record.parse("<time> #{time} </time>").time.to_i.should == time.to_i
147
+ end
148
+ end
149
+
150
+ describe "record" do
151
+ it "parses date" do
152
+ x = Record.parse("<record><date>#{Date.today}</date></record>")
153
+ x.date.should be_nil
154
+ x.record.date.should == Date.today
155
+ end
156
+
157
+ it "parses list" do
158
+ x = Record.parse("<record><list/></record>")
159
+ x.list.should be_nil
160
+ x.record.list.should == []
161
+ end
162
+
163
+ it "parses string" do
164
+ x = Record.parse("<record><string>abc</string></record>")
165
+ x.string.should be_nil
166
+ x.record.string.should == "abc"
167
+ end
168
+
169
+ it "parses text" do
170
+ x = Record.parse("<record><text><b>big</b><i>little</i></text></record>")
171
+ x.text.should be_nil
172
+ x.record.text.should == "biglittle"
173
+ end
174
+
175
+ it "parses time" do
176
+ time = Time.now
177
+ Record.parse("<record><time>#{time}</time></record>").record.time.to_i.should == time.to_i
178
+ end
179
+
180
+ it "parses multiple elements" do
181
+ time = Time.now
182
+ x = Record.parse <<-XML
183
+ <record>
184
+ <date>#{Date.today}</date>
185
+ <list>
186
+ <item>a</item>
187
+ <item>b</item>
188
+ <item>c</item>
189
+ </list>
190
+ <string>xyz</string>
191
+ <text>
192
+ <ul>
193
+ <li>vim</li>
194
+ <li>emacs</li>
195
+ <li>ctags</li>
196
+ <li>lint</li>
197
+ <li>gdb</li>
198
+ </ul>
199
+ </text>
200
+ <time>#{time}</time>
201
+ </record>
202
+ XML
203
+
204
+ x.record.date.should == Date.today
205
+ x.record.list.should == ["a","b","c"]
206
+ x.record.string.should == "xyz"
207
+ x.record.text.strip.should == <<-DOC.strip
208
+ vim
209
+ emacs
210
+ ctags
211
+ lint
212
+ gdb
213
+ DOC
214
+
215
+ x.record.time.to_i.should == time.to_i
216
+ end
217
+
218
+ it "parses recursive records" do
219
+ x = Record.parse("<record><record><record><record></record></record></record></record>")
220
+ x.should be_a(Record)
221
+ x.record.should be_a(Record)
222
+ x.record.record.should be_a(Record)
223
+ x.record.record.record.should be_a(Record)
224
+ x.record.record.record.record.should be_a(Record)
225
+ x.record.record.record.record.record.should be_nil
226
+ end
227
+ end
228
+
229
+ end
@@ -0,0 +1,13 @@
1
+ begin
2
+ require "rspec"
3
+ rescue LoadError
4
+ require "spec"
5
+ end
6
+
7
+ spec = File.dirname(__FILE__)
8
+
9
+ $:.unshift(spec)
10
+ $:.unshift(File.dirname(spec) + "/lib")
11
+
12
+ require "xmlparsable"
13
+ require "active_support/core_ext"
metadata ADDED
@@ -0,0 +1,60 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: xmlparsable
3
+ version: !ruby/object:Gem::Version
4
+ version: '1.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kyle Putnam
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-14 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Simple declarative XML parsing
15
+ email: putnam.kyle@gmail.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - README.md
21
+ - Rakefile
22
+ - lib/xmlparsable/elements/abstract.rb
23
+ - lib/xmlparsable/elements/date.rb
24
+ - lib/xmlparsable/elements/list.rb
25
+ - lib/xmlparsable/elements/record.rb
26
+ - lib/xmlparsable/elements/string.rb
27
+ - lib/xmlparsable/elements/text.rb
28
+ - lib/xmlparsable/elements/time.rb
29
+ - lib/xmlparsable/elements/xml.rb
30
+ - lib/xmlparsable/elements.rb
31
+ - lib/xmlparsable/parser.rb
32
+ - lib/xmlparsable.rb
33
+ - spec/examples/record.example
34
+ - spec/spec_helper.rb
35
+ homepage: https://github.com/kputnam/xmlparsable
36
+ licenses: []
37
+ post_install_message:
38
+ rdoc_options: []
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ none: false
49
+ requirements:
50
+ - - ! '>='
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ requirements: []
54
+ rubyforge_project:
55
+ rubygems_version: 1.8.24
56
+ signing_key:
57
+ specification_version: 3
58
+ summary: Simple declarative XML parsing
59
+ test_files:
60
+ - spec/examples/record.example