xoxo 1.0.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/HISTORY +14 -0
- data/LICENSE +61 -0
- data/MANIFEST +20 -0
- data/README +58 -0
- data/RELEASE +14 -0
- data/lib/xoxo.rb +241 -0
- data/meta/abstract +5 -0
- data/meta/authors +1 -0
- data/meta/created +1 -0
- data/meta/homepage +1 -0
- data/meta/license +1 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/released +1 -0
- data/meta/repository +1 -0
- data/meta/summary +1 -0
- data/meta/version +1 -0
- data/test/test_xoxo.rb +272 -0
- metadata +85 -0
data/HISTORY
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
= HISTORY
|
2
|
+
|
3
|
+
== 1.0.0 // 2009-07-06
|
4
|
+
|
5
|
+
This is essentially the XOXO library written Christian Neukirchen.
|
6
|
+
While the library has been distributed as part of Facets for some
|
7
|
+
time, there is no separate gem avaialbe for it. So this then is
|
8
|
+
that stand-alone release. It differs from the original only in
|
9
|
+
that it provides #to_xoxo.
|
10
|
+
|
11
|
+
* 1 Major Enhancement
|
12
|
+
|
13
|
+
* Happy Birthday!
|
14
|
+
|
data/LICENSE
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
= RUBY LICENSE
|
2
|
+
|
3
|
+
xoxo.rb is copyrighted free software by Christian Neukirchen <chneukirchen@gmail.com>.
|
4
|
+
You can redistribute it and/or modify it under either the terms of the GPL
|
5
|
+
(see COPYING file), or the conditions below:
|
6
|
+
|
7
|
+
1. You may make and give away verbatim copies of the source form of the
|
8
|
+
software without restriction, provided that you duplicate all of the
|
9
|
+
original copyright notices and associated disclaimers.
|
10
|
+
|
11
|
+
2. You may modify your copy of the software in any way, provided that
|
12
|
+
you do at least ONE of the following:
|
13
|
+
|
14
|
+
a) place your modifications in the Public Domain or otherwise
|
15
|
+
make them Freely Available, such as by posting said
|
16
|
+
modifications to Usenet or an equivalent medium, or by allowing
|
17
|
+
the author to include your modifications in the software.
|
18
|
+
|
19
|
+
b) use the modified software only within your corporation or
|
20
|
+
organization.
|
21
|
+
|
22
|
+
c) rename any non-standard executables so the names do not conflict
|
23
|
+
with standard executables, which must also be provided.
|
24
|
+
|
25
|
+
d) make other distribution arrangements with the author.
|
26
|
+
|
27
|
+
3. You may distribute the software in object code or executable
|
28
|
+
form, provided that you do at least ONE of the following:
|
29
|
+
|
30
|
+
a) distribute the executables and library files of the software,
|
31
|
+
together with instructions (in the manual page or equivalent)
|
32
|
+
on where to get the original distribution.
|
33
|
+
|
34
|
+
b) accompany the distribution with the machine-readable source of
|
35
|
+
the software.
|
36
|
+
|
37
|
+
c) give non-standard executables non-standard names, with
|
38
|
+
instructions on where to get the original software distribution.
|
39
|
+
|
40
|
+
d) make other distribution arrangements with the author.
|
41
|
+
|
42
|
+
4. You may modify and include the part of the software into any other
|
43
|
+
software (possibly commercial). But some files in the distribution
|
44
|
+
are not written by the author, so that they are not under this terms.
|
45
|
+
|
46
|
+
They are gc.c(partly), utils.c(partly), regex.[ch], st.[ch] and some
|
47
|
+
files under the ./missing directory. See each file for the copying
|
48
|
+
condition.
|
49
|
+
|
50
|
+
5. The scripts and library files supplied as input to or produced as
|
51
|
+
output from the software do not automatically fall under the
|
52
|
+
copyright of the software, but belong to whomever generated them,
|
53
|
+
and may be sold commercially, and may be aggregated with this
|
54
|
+
software.
|
55
|
+
|
56
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
57
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
58
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
59
|
+
PURPOSE.
|
60
|
+
|
61
|
+
|
data/MANIFEST
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
test
|
2
|
+
test/test_xoxo.rb
|
3
|
+
RELEASE
|
4
|
+
LICENSE
|
5
|
+
README
|
6
|
+
HISTORY
|
7
|
+
meta
|
8
|
+
meta/created
|
9
|
+
meta/repository
|
10
|
+
meta/homepage
|
11
|
+
meta/summary
|
12
|
+
meta/abstract
|
13
|
+
meta/package
|
14
|
+
meta/released
|
15
|
+
meta/version
|
16
|
+
meta/license
|
17
|
+
meta/authors
|
18
|
+
meta/project
|
19
|
+
lib
|
20
|
+
lib/xoxo.rb
|
data/README
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
= XOXO
|
2
|
+
|
3
|
+
* http://death.rubyforge.org
|
4
|
+
* http://death.rubyforge.org/xoxo
|
5
|
+
* http://chneukirchen.org/repos/xoxo-rb/
|
6
|
+
|
7
|
+
|
8
|
+
== DESCRIPTION
|
9
|
+
|
10
|
+
XOXO is a Ruby XOXO parser and generator. It provides
|
11
|
+
a Ruby API similar to Marshal and YAML (though more
|
12
|
+
specific) to load and dump XOXO[http://microformats.org/wiki/xoxo],
|
13
|
+
an simple, open outline format written in standard XHTML and
|
14
|
+
suitable for embedding in (X)HTML, Atom, RSS, and arbitrary XML.
|
15
|
+
|
16
|
+
|
17
|
+
== FEATURES/ISSUES
|
18
|
+
|
19
|
+
* Uses REXML's pull parser.
|
20
|
+
* Serialize any object via to_xoxo.
|
21
|
+
|
22
|
+
|
23
|
+
== RELEASE NOTES
|
24
|
+
|
25
|
+
Please see RELEASE file.
|
26
|
+
|
27
|
+
|
28
|
+
== SYNOPSIS
|
29
|
+
|
30
|
+
Simple way to generate XOXO.
|
31
|
+
|
32
|
+
obj.to_xoxo
|
33
|
+
|
34
|
+
|
35
|
+
== HOW TO INSTALL
|
36
|
+
|
37
|
+
To install with RubyGems simply open a console and type:
|
38
|
+
|
39
|
+
gem install xoxo
|
40
|
+
|
41
|
+
Local installation requires Setup.rb (gem install setup),
|
42
|
+
then download the tarball package and type:
|
43
|
+
|
44
|
+
tar -xvzf xoxo-1.0.0.tgz
|
45
|
+
cd xoxo-1.0.0
|
46
|
+
sudo setup.rb all
|
47
|
+
|
48
|
+
Windows users use 'ruby setup.rb all'.
|
49
|
+
|
50
|
+
|
51
|
+
== COPYING
|
52
|
+
|
53
|
+
Copyright (C) 2006 Christian Neukirchen
|
54
|
+
|
55
|
+
This program is ditributed unser the terms of the Ruby license.
|
56
|
+
|
57
|
+
See LICENSE file for details.
|
58
|
+
|
data/RELEASE
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
= RELEASE NOTES
|
2
|
+
|
3
|
+
This is essentially the XOXO library written Christian Neukirchen.
|
4
|
+
While the library has been distributed as part of Facets for some
|
5
|
+
time, there is no separate gem avaialbe for it. So this then is
|
6
|
+
that stand-alone release. It differs from the original only in
|
7
|
+
that it provides #to_xoxo.
|
8
|
+
|
9
|
+
### 1.0.0 // 2009-07-06
|
10
|
+
|
11
|
+
* 1 Major Enhancement
|
12
|
+
|
13
|
+
* Happy Birthday!
|
14
|
+
|
data/lib/xoxo.rb
ADDED
@@ -0,0 +1,241 @@
|
|
1
|
+
# xoxo.rb
|
2
|
+
#
|
3
|
+
# Copyright (C) 2006 Christian Neukirchen
|
4
|
+
#
|
5
|
+
# Ruby License
|
6
|
+
#
|
7
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
8
|
+
# software under the same terms as Ruby.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
11
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
12
|
+
# FOR A PARTICULAR PURPOSE.
|
13
|
+
#
|
14
|
+
# See original version at http://chneukirchen.org/repos/xoxo-rb/.
|
15
|
+
|
16
|
+
require 'rexml/parsers/pullparser'
|
17
|
+
|
18
|
+
# XOXO is a Ruby XOXO parser and generator. It provides
|
19
|
+
# a Ruby API similar to Marshal and YAML (though more
|
20
|
+
# specific) to load and dump XOXO[http://microformats.org/wiki/xoxo],
|
21
|
+
# an simple, open outline format written in standard XHTML and
|
22
|
+
# suitable for embedding in (X)HTML, Atom, RSS, and arbitrary XML.
|
23
|
+
#
|
24
|
+
module XOXO
|
25
|
+
# xoxo.rb version number
|
26
|
+
#VERSION = "1.0.0"
|
27
|
+
|
28
|
+
# Load and return a XOXO structure from the String, IO or StringIO or _xoxo_.
|
29
|
+
#
|
30
|
+
def self.load(xoxo)
|
31
|
+
structs = Parser.new(xoxo).parse.structs
|
32
|
+
while structs.kind_of?(Array) && structs.size == 1
|
33
|
+
structs = structs.first
|
34
|
+
end
|
35
|
+
structs
|
36
|
+
end
|
37
|
+
|
38
|
+
# Return a XOXO string corresponding to the Ruby object +struct+,
|
39
|
+
# translated to the following rules:
|
40
|
+
#
|
41
|
+
# * Arrays become ordered lists <tt><ol></tt>.
|
42
|
+
# * Hashes become definition lists <tt><dl></tt>, keys are
|
43
|
+
# stringified with +to_s+.
|
44
|
+
# * Everything else becomes stringified with +to_s+ and wrapped in
|
45
|
+
# appropriate list elements (<tt><li></tt> or <tt><dt></tt>/<tt><dd></tt>).
|
46
|
+
#
|
47
|
+
# Additionally, you can pass these options on the _options_ hash:
|
48
|
+
# <tt>:html_wrap</tt> => +true+:: Wrap the XOXO with a basic XHTML 1.0
|
49
|
+
# Transitional header.
|
50
|
+
# <tt>:css</tt> => _css_:: Reference _css_ as stylesheet for the
|
51
|
+
# wrapped XOXO document.
|
52
|
+
#
|
53
|
+
def self.dump(struct, options={})
|
54
|
+
struct = [struct] unless struct.kind_of? Array
|
55
|
+
|
56
|
+
if options[:html_wrap]
|
57
|
+
result = <<EOF.strip
|
58
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN
|
59
|
+
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
60
|
+
<html xmlns="http://www.w3.org/1999/xhtml"><head profile=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
61
|
+
EOF
|
62
|
+
if options[:css]
|
63
|
+
result << %Q[<style type="text/css" >@import "#{options[:css]}";</style>]
|
64
|
+
end
|
65
|
+
|
66
|
+
result << "</head><body>" << make_xoxo(struct, 'xoxo') << "</body></html>"
|
67
|
+
else
|
68
|
+
result = make_xoxo(struct, 'xoxo')
|
69
|
+
end
|
70
|
+
|
71
|
+
result
|
72
|
+
end
|
73
|
+
|
74
|
+
private
|
75
|
+
|
76
|
+
def self.make_xoxo(struct, class_name=nil)
|
77
|
+
s = ''
|
78
|
+
case struct
|
79
|
+
when Array
|
80
|
+
if class_name
|
81
|
+
s << %Q[<ol class="#{class_name}">]
|
82
|
+
else
|
83
|
+
s << "<ol>"
|
84
|
+
end
|
85
|
+
struct.each { |item|
|
86
|
+
s << "<li>" << make_xoxo(item) << "</li>"
|
87
|
+
}
|
88
|
+
s << "</ol>"
|
89
|
+
|
90
|
+
when Hash
|
91
|
+
d = struct.dup
|
92
|
+
if d.has_key? 'url'
|
93
|
+
s << '<a href="' << d['url'].to_s << '" '
|
94
|
+
text = d.fetch('text') { d.fetch('title', d['url']) }
|
95
|
+
%w[title rel type].each { |tag|
|
96
|
+
if d.has_key? tag
|
97
|
+
s << tag.to_s << '="' << make_xoxo(d.delete(tag)) << '" '
|
98
|
+
end
|
99
|
+
}
|
100
|
+
s << '>' << make_xoxo(text) << '</a>'
|
101
|
+
d.delete 'text'
|
102
|
+
d.delete 'url'
|
103
|
+
end
|
104
|
+
|
105
|
+
unless d.empty?
|
106
|
+
s << "<dl>"
|
107
|
+
d.each { |key, value|
|
108
|
+
s << "<dt>" << key.to_s << "</dt><dd>" << make_xoxo(value) << "</dd>"
|
109
|
+
}
|
110
|
+
s << "</dl>"
|
111
|
+
end
|
112
|
+
|
113
|
+
when String
|
114
|
+
s << struct
|
115
|
+
|
116
|
+
else
|
117
|
+
s << struct.to_s # too gentle?
|
118
|
+
end
|
119
|
+
|
120
|
+
s
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class XOXO::Parser # :nodoc:
|
125
|
+
CONTAINER_TAGS = %w{dl ol ul}
|
126
|
+
|
127
|
+
attr_reader :structs
|
128
|
+
|
129
|
+
def initialize(xoxo)
|
130
|
+
@parser = REXML::Parsers::PullParser.new(xoxo)
|
131
|
+
|
132
|
+
@textstack = ['']
|
133
|
+
@xostack = []
|
134
|
+
@structs = []
|
135
|
+
@tags = []
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse
|
139
|
+
while @parser.has_next?
|
140
|
+
res = @parser.pull
|
141
|
+
|
142
|
+
if res.start_element?
|
143
|
+
@tags << res[0]
|
144
|
+
|
145
|
+
case res[0]
|
146
|
+
when "a"
|
147
|
+
attrs = normalize_attrs res[1]
|
148
|
+
attrs['url'] = attrs['href']
|
149
|
+
attrs.delete 'href'
|
150
|
+
push attrs
|
151
|
+
@textstack << ''
|
152
|
+
|
153
|
+
when "dl"
|
154
|
+
push({})
|
155
|
+
|
156
|
+
when "ol", "ul"
|
157
|
+
push []
|
158
|
+
|
159
|
+
when "li", "dt", "dd"
|
160
|
+
@textstack << ''
|
161
|
+
|
162
|
+
end
|
163
|
+
elsif res.end_element?
|
164
|
+
@tags.pop
|
165
|
+
|
166
|
+
case res[0]
|
167
|
+
when "a"
|
168
|
+
val = @textstack.pop
|
169
|
+
unless val.empty?
|
170
|
+
val = '' if @xostack.last['title'] == val
|
171
|
+
val = '' if @xostack.last['url'] == val
|
172
|
+
@xostack.last['text'] = val unless val.empty?
|
173
|
+
end
|
174
|
+
@xostack.pop
|
175
|
+
|
176
|
+
when "dl", "ol", "ul"
|
177
|
+
@xostack.pop
|
178
|
+
|
179
|
+
when "li"
|
180
|
+
val = @textstack.pop
|
181
|
+
while @structs.last != @xostack.last
|
182
|
+
val = @structs.pop
|
183
|
+
@xostack.last << val
|
184
|
+
end
|
185
|
+
@xostack.last << val if val.kind_of? String
|
186
|
+
|
187
|
+
when "dt"
|
188
|
+
# skip
|
189
|
+
|
190
|
+
when "dd"
|
191
|
+
val = @textstack.pop
|
192
|
+
key = @textstack.pop
|
193
|
+
|
194
|
+
val = @structs.pop if @structs.last != @xostack.last
|
195
|
+
@xostack.last[key] = val
|
196
|
+
|
197
|
+
end
|
198
|
+
elsif res.text?
|
199
|
+
unless @tags.empty? || CONTAINER_TAGS.include?(@tags.last)
|
200
|
+
@textstack.last << res[0]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
self
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
def normalize_attrs(attrs)
|
211
|
+
attrs.keys.find_all { |k, v| k != k.downcase }.each { |k, v|
|
212
|
+
v = v.downcase if k == "rel" || k == "type"
|
213
|
+
attrs.delete k
|
214
|
+
attrs[k.downcase] = v
|
215
|
+
}
|
216
|
+
attrs
|
217
|
+
end
|
218
|
+
|
219
|
+
def push(struct)
|
220
|
+
if struct == {} && @structs.last.kind_of?(Hash) &&
|
221
|
+
@structs.last.has_key?('url') &&
|
222
|
+
@structs.last != @xostack.last
|
223
|
+
# put back the <a>-made one for extra def's
|
224
|
+
@xostack << @structs.last
|
225
|
+
else
|
226
|
+
@structs << struct
|
227
|
+
@xostack << @structs.last
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
class Object
|
234
|
+
|
235
|
+
# Dump object as XOXO.
|
236
|
+
|
237
|
+
def to_xoxo(*args)
|
238
|
+
XOXO.dump(self,*args)
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
data/meta/abstract
ADDED
@@ -0,0 +1,5 @@
|
|
1
|
+
XOXO is a Ruby XOXO parser and generator. It provides
|
2
|
+
a Ruby API similar to Marshal and YAML (though more
|
3
|
+
specific) to load and dump XOXO[http://microformats.org/wiki/xoxo],
|
4
|
+
an simple, open outline format written in standard XHTML and
|
5
|
+
suitable for embedding in (X)HTML, Atom, RSS, and arbitrary XML.
|
data/meta/authors
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Christian Neukirchen <chneukirchen@gmail.com>
|
data/meta/created
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2006-01-01
|
data/meta/homepage
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
http://death.rubyforge.org/
|
data/meta/license
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
Ruby
|
data/meta/package
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
xoxo
|
data/meta/project
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
death
|
data/meta/released
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2009-07-06
|
data/meta/repository
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
svn://rubyforge.org/var/svn/death/xoxo
|
data/meta/summary
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
XOXO Parser and Generator
|
data/meta/version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.0.0
|
data/test/test_xoxo.rb
ADDED
@@ -0,0 +1,272 @@
|
|
1
|
+
require 'xoxo'
|
2
|
+
require 'test/unit'
|
3
|
+
|
4
|
+
class TC_XOXO < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_simple_list
|
7
|
+
l = ['1', '2', '3']
|
8
|
+
html = XOXO.dump(l)
|
9
|
+
assert_equal '<ol class="xoxo"><li>1</li><li>2</li><li>3</li></ol>', html
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_nested_list
|
13
|
+
l = ['1', ['2', '3']]
|
14
|
+
assert_equal '<ol class="xoxo"><li>1</li><li><ol><li>2</li><li>3</li></ol></li></ol>', XOXO.dump(l)
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_hash
|
18
|
+
h = {'test' => '1', 'name' => 'Kevin'}
|
19
|
+
# Changed since Ruby sorts the hash differently.
|
20
|
+
assert_equal '<ol class="xoxo"><li><dl><dt>name</dt><dd>Kevin</dd><dt>test</dt><dd>1</dd></dl></li></ol>', XOXO.dump(h)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_single_item
|
24
|
+
l = 'test'
|
25
|
+
assert_equal '<ol class="xoxo"><li>test</li></ol>', XOXO.dump(l)
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_wrap_differs
|
29
|
+
l = 'test'
|
30
|
+
html = XOXO.dump(l)
|
31
|
+
html_wrap = XOXO.dump(l, :html_wrap => true)
|
32
|
+
assert_not_equal html, html_wrap
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_wrap_single_item
|
36
|
+
l = 'test'
|
37
|
+
html = XOXO.dump(l, :html_wrap => true)
|
38
|
+
assert_equal <<EOF.strip, html
|
39
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN
|
40
|
+
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
41
|
+
<html xmlns="http://www.w3.org/1999/xhtml"><head profile=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /></head><body><ol class="xoxo"><li>test</li></ol></body></html>
|
42
|
+
EOF
|
43
|
+
end
|
44
|
+
|
45
|
+
def test_wrap_item_with_css
|
46
|
+
l = 'test'
|
47
|
+
html = XOXO.dump(l, :html_wrap => true, :css => 'reaptest.css')
|
48
|
+
assert_equal <<EOF.strip, html
|
49
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN
|
50
|
+
http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
51
|
+
<html xmlns="http://www.w3.org/1999/xhtml"><head profile=""><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><style type="text/css" >@import "reaptest.css";</style></head><body><ol class="xoxo"><li>test</li></ol></body></html>
|
52
|
+
EOF
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_hash_roundtrip
|
56
|
+
h = {'test' => '1', 'name' => 'Kevin'}
|
57
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_hash_with_url_roundtrip
|
61
|
+
h = {'url' => 'http://example.com', 'name' => 'Kevin'}
|
62
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_nested_hash_roundtrip
|
66
|
+
h = {'test' => '1', 'inner' => {'name' => 'Kevin'}}
|
67
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_nested_hash_with_url_roundtrip
|
71
|
+
h = {'url' => 'http://example.com', 'inner' => {
|
72
|
+
'url' => 'http://slashdot.org', 'name' => 'Kevin'}}
|
73
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_list_round_trip
|
77
|
+
l = ['3', '2', '1']
|
78
|
+
assert_equal l, XOXO.load(XOXO.dump(l))
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_list_of_hashes_round_trip
|
82
|
+
l = ['3', {'a' => '2'}, {'b' => '1', 'c' => '4'}]
|
83
|
+
assert_equal l, XOXO.load(XOXO.dump(l))
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_list_of_lists_round_trip
|
87
|
+
l = ['3', ['a', '2'], ['b', ['1', ['c', '4']]]]
|
88
|
+
assert_equal l, XOXO.load(XOXO.dump(l))
|
89
|
+
end
|
90
|
+
|
91
|
+
def test_hashes_of_lists_roundtrip
|
92
|
+
h = {
|
93
|
+
'test' => ['1', '2'],
|
94
|
+
'name' => 'Kevin',
|
95
|
+
'nestlist' => ['a', ['b', 'c']],
|
96
|
+
'nestdict' => {'e' => '6', 'f' => '7'}
|
97
|
+
}
|
98
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_xoxo_junk_in_containers
|
102
|
+
h = XOXO.load '<ol>bad<li><dl>worse<dt>good</dt><dd>buy</dd> now</dl></li></ol>'
|
103
|
+
assert_equal({'good' => 'buy'}, h)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_xoxo_junk_in_elements
|
107
|
+
l = XOXO.load '<ol><li>bad<dl><dt>good</dt><dd>buy</dd></dl>worse</li><li>bag<ol><li>OK</li></ol>fish</li></ol>'
|
108
|
+
assert_equal([{'good' => 'buy'}, ['OK']], l)
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_xoxo_with_spaces_and_newlines
|
112
|
+
xoxo_sample = <<EOF.strip
|
113
|
+
<ol class='xoxo'>
|
114
|
+
<li>
|
115
|
+
<dl>
|
116
|
+
<dt>text</dt>
|
117
|
+
<dd>item 1</dd>
|
118
|
+
<dt>description</dt>
|
119
|
+
<dd> This item represents the main point we're trying to make.</dd>
|
120
|
+
<dt>url</dt>
|
121
|
+
<dd>http://example.com/more.xoxo</dd>
|
122
|
+
<dt>title</dt>
|
123
|
+
<dd>title of item 1</dd>
|
124
|
+
<dt>type</dt>
|
125
|
+
<dd>text/xml</dd>
|
126
|
+
<dt>rel</dt>
|
127
|
+
<dd>help</dd>
|
128
|
+
</dl>
|
129
|
+
</li>
|
130
|
+
</ol>
|
131
|
+
EOF
|
132
|
+
h = XOXO.load xoxo_sample
|
133
|
+
h2 = {
|
134
|
+
'text' => 'item 1',
|
135
|
+
'description' => " This item represents the main point we're trying to make.",
|
136
|
+
'url' => 'http://example.com/more.xoxo',
|
137
|
+
'title' => 'title of item 1',
|
138
|
+
'type' => 'text/xml',
|
139
|
+
'rel' => 'help'
|
140
|
+
}
|
141
|
+
assert_equal h2, XOXO.load(xoxo_sample)
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_special_attribute_decoding
|
145
|
+
xoxo_sample = <<EOF.strip
|
146
|
+
<ol class='xoxo'>
|
147
|
+
<li>
|
148
|
+
<dl>
|
149
|
+
<dt>text</dt>
|
150
|
+
<dd>item 1</dd>
|
151
|
+
<dt>url</dt>
|
152
|
+
<dd>http://example.com/more.xoxo</dd>
|
153
|
+
<dt>title</dt>
|
154
|
+
<dd>title of item 1</dd>
|
155
|
+
<dt>type</dt>
|
156
|
+
<dd>text/xml</dd>
|
157
|
+
<dt>rel</dt>
|
158
|
+
<dd>help</dd>
|
159
|
+
</dl>
|
160
|
+
</li>
|
161
|
+
</ol>
|
162
|
+
EOF
|
163
|
+
smart_xoxo_sample = <<EOF.strip
|
164
|
+
<ol class='xoxo'>
|
165
|
+
<li><a href="http://example.com/more.xoxo"
|
166
|
+
title="title of item 1"
|
167
|
+
type="text/xml"
|
168
|
+
rel="help">item 1</a>
|
169
|
+
<!-- note how the "text" property is simply the contents of the <a> element -->
|
170
|
+
</li>
|
171
|
+
</ol>
|
172
|
+
EOF
|
173
|
+
assert_equal XOXO.load(xoxo_sample), XOXO.load(smart_xoxo_sample)
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_special_attribute_and_dl_decoding
|
177
|
+
xoxo_sample = <<EOF.strip
|
178
|
+
<ol class="xoxo">
|
179
|
+
<li>
|
180
|
+
<dl>
|
181
|
+
<dt>text</dt>
|
182
|
+
<dd>item 1</dd>
|
183
|
+
<dt>description</dt>
|
184
|
+
<dd> This item represents the main point we're trying to make.</dd>
|
185
|
+
<dt>url</dt>
|
186
|
+
<dd>http://example.com/more.xoxo</dd>
|
187
|
+
<dt>title</dt>
|
188
|
+
<dd>title of item 1</dd>
|
189
|
+
<dt>type</dt>
|
190
|
+
<dd>text/xml</dd>
|
191
|
+
<dt>rel</dt>
|
192
|
+
<dd>help</dd>
|
193
|
+
</dl>
|
194
|
+
</li>
|
195
|
+
</ol>
|
196
|
+
EOF
|
197
|
+
smart_xoxo_sample = <<EOF.strip
|
198
|
+
<ol class="xoxo">
|
199
|
+
<li><a href="http://example.com/more.xoxo"
|
200
|
+
title="title of item 1"
|
201
|
+
type="text/xml"
|
202
|
+
rel="help">item 1</a>
|
203
|
+
<!-- note how the "text" property is simply the contents of the <a> element -->
|
204
|
+
<dl>
|
205
|
+
<dt>description</dt>
|
206
|
+
<dd> This item represents the main point we're trying to make.</dd>
|
207
|
+
</dl>
|
208
|
+
</li>
|
209
|
+
</ol>
|
210
|
+
EOF
|
211
|
+
assert_equal XOXO.load(xoxo_sample), XOXO.load(smart_xoxo_sample)
|
212
|
+
end
|
213
|
+
|
214
|
+
def test_special_attribute_encode
|
215
|
+
h = {
|
216
|
+
'url' => 'http://example.com/more.xoxo',
|
217
|
+
'title' => 'sample url',
|
218
|
+
'type' => "text/xml",
|
219
|
+
'rel' => 'help',
|
220
|
+
'text' => 'an example'
|
221
|
+
}
|
222
|
+
assert_equal '<ol class="xoxo"><li><a href="http://example.com/more.xoxo" title="sample url" rel="help" type="text/xml" >an example</a></li></ol>', XOXO.dump(h)
|
223
|
+
end
|
224
|
+
|
225
|
+
def test_special_attribute_roundtrip_full
|
226
|
+
h = {
|
227
|
+
'url' => 'http://example.com/more.xoxo',
|
228
|
+
'title' => 'sample url',
|
229
|
+
'type' => "text/xml",
|
230
|
+
'rel' => 'help',
|
231
|
+
'text' => 'an example'
|
232
|
+
}
|
233
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
234
|
+
end
|
235
|
+
|
236
|
+
def test_special_attribute_roundtrip_no_text
|
237
|
+
h = {
|
238
|
+
'url' => 'http://example.com/more.xoxo',
|
239
|
+
'title' => 'sample url',
|
240
|
+
'type' => "text/xml",
|
241
|
+
'rel' => 'help'
|
242
|
+
}
|
243
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_special_attribute_roundtrip_no_text_or_title
|
247
|
+
h = {'url' => 'http://example.com/more.xoxo'}
|
248
|
+
assert_equal h, XOXO.load(XOXO.dump(h))
|
249
|
+
end
|
250
|
+
|
251
|
+
def test_attention_roundtrip
|
252
|
+
kmattn = <<EOF.strip
|
253
|
+
<ol class="xoxo"><li><a href="http://www.boingboing.net/" title="Boing Boing Blog" >Boing Boing Blog</a><dl><dt>alturls</dt><dd><ol><li><a href="http://boingboing.net/rss.xml" >xmlurl</a></li></ol></dd><dt>description</dt><dd>Boing Boing Blog</dd></dl></li><li><a href="http://www.financialcryptography.com/" title="Financial Cryptography" >Financial Cryptography</a><dl><dt>alturls</dt><dd><ol><li><a href="http://www.financialcryptography.com/mt/index.rdf" >xmlurl</a></li></ol></dd><dt>description</dt><dd>Financial Cryptography</dd></dl></li><li><a href="http://hublog.hubmed.org/" title="HubLog" >HubLog</a><dl><dt>alturls</dt><dd><ol><li><a href="http://hublog.hubmed.org/index.xml" >xmlurl</a></li><li><a href="http://hublog.hubmed.org/foaf.rdf" >foafurl</a></li></ol></dd><dt>description</dt><dd>HubLog</dd></dl></li></ol>
|
254
|
+
EOF
|
255
|
+
assert_equal kmattn, XOXO.dump(XOXO.load(kmattn))
|
256
|
+
assert_equal XOXO.load(kmattn), XOXO.load(XOXO.dump(XOXO.load(kmattn)))
|
257
|
+
assert_equal XOXO.dump(XOXO.load(kmattn)),
|
258
|
+
XOXO.dump(XOXO.load(XOXO.dump(XOXO.load(kmattn))))
|
259
|
+
end
|
260
|
+
|
261
|
+
def test_unicode_roundtrip
|
262
|
+
unicode = "Tantek \xc3\x87elik and a snowman \xe2\x98\x83"
|
263
|
+
assert_equal unicode, XOXO.load(XOXO.dump(unicode))
|
264
|
+
end
|
265
|
+
|
266
|
+
# TBD: Implement proper encodings.
|
267
|
+
#
|
268
|
+
# def test_utf8_roundtrip
|
269
|
+
# end
|
270
|
+
# def test_windows1252_roundtrip
|
271
|
+
# end
|
272
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: xoxo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Neukirchen <chneukirchen@gmail.com>
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-06 00:00:00 -04:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: |-
|
17
|
+
XOXO is a Ruby XOXO parser and generator. It provides
|
18
|
+
a Ruby API similar to Marshal and YAML (though more
|
19
|
+
specific) to load and dump XOXO[http://microformats.org/wiki/xoxo],
|
20
|
+
an simple, open outline format written in standard XHTML and
|
21
|
+
suitable for embedding in (X)HTML, Atom, RSS, and arbitrary XML.
|
22
|
+
email: chneukirchen@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README
|
29
|
+
- MANIFEST
|
30
|
+
- RELEASE
|
31
|
+
- LICENSE
|
32
|
+
- HISTORY
|
33
|
+
files:
|
34
|
+
- test/test_xoxo.rb
|
35
|
+
- RELEASE
|
36
|
+
- LICENSE
|
37
|
+
- README
|
38
|
+
- HISTORY
|
39
|
+
- meta/created
|
40
|
+
- meta/repository
|
41
|
+
- meta/homepage
|
42
|
+
- meta/summary
|
43
|
+
- meta/abstract
|
44
|
+
- meta/package
|
45
|
+
- meta/released
|
46
|
+
- meta/version
|
47
|
+
- meta/license
|
48
|
+
- meta/authors
|
49
|
+
- meta/project
|
50
|
+
- lib/xoxo.rb
|
51
|
+
- MANIFEST
|
52
|
+
has_rdoc: true
|
53
|
+
homepage: http://death.rubyforge.org/
|
54
|
+
licenses: []
|
55
|
+
|
56
|
+
post_install_message:
|
57
|
+
rdoc_options:
|
58
|
+
- --inline-source
|
59
|
+
- --title
|
60
|
+
- xoxo api
|
61
|
+
- --main
|
62
|
+
- README
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: "0"
|
70
|
+
version:
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: "0"
|
76
|
+
version:
|
77
|
+
requirements: []
|
78
|
+
|
79
|
+
rubyforge_project: death
|
80
|
+
rubygems_version: 1.3.4
|
81
|
+
signing_key:
|
82
|
+
specification_version: 3
|
83
|
+
summary: XOXO is a Ruby XOXO parser and generator.
|
84
|
+
test_files:
|
85
|
+
- test/test_xoxo.rb
|