uformats 1.2.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/CHANGES +8 -0
- data/COPYING +4 -0
- data/README +36 -0
- data/lib/uformats.rb +158 -0
- data/lib/uformats/basic.rb +268 -0
- data/lib/uformats/hcalendar.rb +23 -0
- data/lib/uformats/hcard.rb +100 -0
- data/lib/uformats/hreview.rb +182 -0
- data/lib/uformats/pluralizer.rb +32 -0
- data/test/all.rb +4 -0
- data/test/basic_test.rb +441 -0
- data/test/hcalendar_test.rb +157 -0
- data/test/hcard_test.rb +438 -0
- data/test/hreview_test.rb +500 -0
- data/test/pluralizer_test.rb +24 -0
- data/test/test_helper.rb +13 -0
- data/test/uformats_test.rb +235 -0
- metadata +70 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'uformats/basic'
|
2
|
+
|
3
|
+
module Microformats
|
4
|
+
module HCalendar
|
5
|
+
class Event < BasicNestedFormat
|
6
|
+
|
7
|
+
identifier :vevent
|
8
|
+
|
9
|
+
structure(
|
10
|
+
:version => :string,
|
11
|
+
:url => :url,
|
12
|
+
:summary => :string,
|
13
|
+
:dtstart => :datetime,
|
14
|
+
:dtend => :datetime,
|
15
|
+
:dtstamp => :datetime,
|
16
|
+
:location => :string,
|
17
|
+
:description => :string,
|
18
|
+
:uid => :string
|
19
|
+
)
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'uformats/basic'
|
2
|
+
|
3
|
+
module Microformats
|
4
|
+
class HCard < BasicNestedFormat
|
5
|
+
|
6
|
+
identifier :vcard
|
7
|
+
|
8
|
+
structure(
|
9
|
+
:fn => :string,
|
10
|
+
:n => {
|
11
|
+
:family_name => :string,
|
12
|
+
:given_name => :string,
|
13
|
+
:additional_names => :string,
|
14
|
+
:honorific_prefix => [:string],
|
15
|
+
:honorific_suffix => [:string],
|
16
|
+
},
|
17
|
+
:nickname => [:string],
|
18
|
+
:sort_string => :string,
|
19
|
+
:url => [:url],
|
20
|
+
:email => [{
|
21
|
+
:type => [:string],
|
22
|
+
:value => :email,
|
23
|
+
}],
|
24
|
+
:tel => [{
|
25
|
+
:type => [:string],
|
26
|
+
:value => :value,
|
27
|
+
}],
|
28
|
+
:adr => [{
|
29
|
+
:post_office_box => :string,
|
30
|
+
:extended_address => :string,
|
31
|
+
:street_address => :string,
|
32
|
+
:locality => :string,
|
33
|
+
:region => :string,
|
34
|
+
:postal_code => :string,
|
35
|
+
:country_name => :string,
|
36
|
+
:type => [:string],
|
37
|
+
:value => :string,
|
38
|
+
}],
|
39
|
+
:label => :string,
|
40
|
+
:geo => {
|
41
|
+
:latitude => :float,
|
42
|
+
:longitude => :float,
|
43
|
+
},
|
44
|
+
:tz => :string,
|
45
|
+
:photo => :url,
|
46
|
+
:logo => :url,
|
47
|
+
:sound => :string,
|
48
|
+
:bday => :datetime,
|
49
|
+
:title => :string,
|
50
|
+
:role => :string,
|
51
|
+
:org => {
|
52
|
+
:organization_name => :string,
|
53
|
+
:organization_unit => :string,
|
54
|
+
},
|
55
|
+
:category => [:string],
|
56
|
+
:note => [:string],
|
57
|
+
:class => :string,
|
58
|
+
:key => :string,
|
59
|
+
:mailer => :string,
|
60
|
+
:uid => :string,
|
61
|
+
:rev => :string
|
62
|
+
)
|
63
|
+
|
64
|
+
def post_process!
|
65
|
+
n_optimize!
|
66
|
+
org_optimize!
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Perform implied 'N' optimization
|
71
|
+
# http://microformats.org/wiki/hcard#Implied_.22N.22_Optimization
|
72
|
+
#
|
73
|
+
def n_optimize!
|
74
|
+
@tree[:n] ||= MethodHash.new
|
75
|
+
if (!@tree[:n][:family_name] && !@tree[:n][:given_name] && @tree[:fn])
|
76
|
+
@tree[:n][:family_name], @tree[:n][:given_name] = case @tree[:fn]
|
77
|
+
when /^(\w+), (\w+)$/u
|
78
|
+
[$1, $2]
|
79
|
+
when /^(\w+) (\w)\.?$/u
|
80
|
+
[$1, $2]
|
81
|
+
when /^(\w+) (\w+)$/u
|
82
|
+
[$2, $1]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
#
|
88
|
+
# Perform org optimization (for when organization-name is implicit)
|
89
|
+
#
|
90
|
+
def org_optimize!
|
91
|
+
return if lookup(:org, :organization_name)
|
92
|
+
Microformats.each_element_by_class(source, :org) do |element|
|
93
|
+
@tree[:org] ||= MethodHash.new
|
94
|
+
@tree[:org][:organization_name] = process_as_string(element)
|
95
|
+
return
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'uformats/basic'
|
2
|
+
require 'uformats/hcard'
|
3
|
+
|
4
|
+
module Microformats
|
5
|
+
class HReview < BasicNestedFormat
|
6
|
+
|
7
|
+
structure(
|
8
|
+
:version => :string,
|
9
|
+
:summary => :string,
|
10
|
+
:type => :string,
|
11
|
+
:item => {
|
12
|
+
:fn => :string,
|
13
|
+
:url => [:url],
|
14
|
+
:photo => [:url],
|
15
|
+
:vcard => :hcard,
|
16
|
+
:vevent => :event,
|
17
|
+
},
|
18
|
+
:reviewer => {
|
19
|
+
:fn => :string,
|
20
|
+
:url => :url,
|
21
|
+
:email => :email,
|
22
|
+
:vcard => :hcard,
|
23
|
+
},
|
24
|
+
:dtreviewed => :datetime,
|
25
|
+
:rating => [:rating],
|
26
|
+
:worst => [:worst],
|
27
|
+
:best => [:best],
|
28
|
+
:description => :xhtml,
|
29
|
+
:permalink => :url
|
30
|
+
)
|
31
|
+
|
32
|
+
VALID_TYPES = %w[product business event person place website url]
|
33
|
+
|
34
|
+
def post_process!
|
35
|
+
fix_anonymous_reviewer!
|
36
|
+
fix_ratings!
|
37
|
+
end
|
38
|
+
|
39
|
+
#
|
40
|
+
# Returns true if this is a valid hReview
|
41
|
+
#
|
42
|
+
def valid?
|
43
|
+
unless (
|
44
|
+
lookup(:item, :fn) || lookup(:item, :url) ||
|
45
|
+
lookup(:item, :photo) || lookup(:item, :vcard)
|
46
|
+
)
|
47
|
+
@validation_message = 'missing item details'
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
if (
|
51
|
+
lookup(:type) &&
|
52
|
+
!VALID_TYPES.include?(lookup(:type))
|
53
|
+
)
|
54
|
+
@validation_message = 'invalid review type'
|
55
|
+
return false
|
56
|
+
end
|
57
|
+
if (
|
58
|
+
(lookup(:version) || 999).to_f > 0.2 &&
|
59
|
+
%w[person business].include?(lookup(:type)) &&
|
60
|
+
!lookup(:item, :vcard)
|
61
|
+
)
|
62
|
+
@validation_message = 'item must be vcard for person or business'
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
attr_accessor :validation_message
|
68
|
+
|
69
|
+
def license
|
70
|
+
return (licenses || []).first
|
71
|
+
end
|
72
|
+
|
73
|
+
def licenses
|
74
|
+
return @licenses ||= Microformats.rel_licenses(@source)
|
75
|
+
end
|
76
|
+
|
77
|
+
def process_as_hcard(element)
|
78
|
+
return HCard.new(element)
|
79
|
+
end
|
80
|
+
|
81
|
+
def process_as_event(element)
|
82
|
+
return HCalendar::Event.new(element)
|
83
|
+
end
|
84
|
+
|
85
|
+
def process_as_rating(element)
|
86
|
+
value = process_as_float(element)
|
87
|
+
tags = Microformats.rel_tags_above_element(element)
|
88
|
+
if !value && value_element = Microformats.first_element_by_class(element, :value)
|
89
|
+
value = process_as_float(value_element)
|
90
|
+
tags = Microformats.rel_tags_above_element(value_element)
|
91
|
+
end
|
92
|
+
return set_score(:score=, value, tags)
|
93
|
+
end
|
94
|
+
|
95
|
+
def process_as_best(element)
|
96
|
+
value = process_as_float(element)
|
97
|
+
tags = Microformats.rel_tags_above_element(element)
|
98
|
+
return set_score(:best=, value, tags)
|
99
|
+
end
|
100
|
+
|
101
|
+
def process_as_worst(element)
|
102
|
+
value = process_as_float(element)
|
103
|
+
tags = Microformats.rel_tags_above_element(element)
|
104
|
+
return set_score(:worst=, value, tags)
|
105
|
+
end
|
106
|
+
|
107
|
+
def set_score(sym, value, tags)
|
108
|
+
@ratings ||= {}
|
109
|
+
if tags
|
110
|
+
tags.each do |tag|
|
111
|
+
@ratings[tag] ||= Rating.new
|
112
|
+
@ratings[tag].__send__(sym, value)
|
113
|
+
end
|
114
|
+
else
|
115
|
+
@rating ||= Rating.new
|
116
|
+
@rating.__send__(sym, value)
|
117
|
+
end
|
118
|
+
return nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def fix_anonymous_reviewer!
|
122
|
+
return if lookup(:reviewer, :fn)
|
123
|
+
Microformats.each_element_by_class(@source, :reviewer) do |e|
|
124
|
+
if 'anonymous' == process_as_string(e).downcase
|
125
|
+
@tree[:reviewer][:fn] = 'anonymous'
|
126
|
+
return
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#
|
132
|
+
# Assign default values for ratings, and insert them into
|
133
|
+
# the tree
|
134
|
+
#
|
135
|
+
def fix_ratings!
|
136
|
+
if (@rating)
|
137
|
+
@rating.best ||= Rating::BEST
|
138
|
+
@rating.worst ||= Rating::WORST
|
139
|
+
default_best = @rating.best
|
140
|
+
default_worst = @rating.worst
|
141
|
+
end
|
142
|
+
default_best ||= Rating::BEST
|
143
|
+
default_worst ||= Rating::WORST
|
144
|
+
@ratings ||= {}
|
145
|
+
@ratings.each do |tag, rating|
|
146
|
+
rating.best ||= default_best
|
147
|
+
rating.worst ||= default_worst
|
148
|
+
end
|
149
|
+
@tree[:rating] = @rating
|
150
|
+
@tree[:ratings] = @ratings
|
151
|
+
end
|
152
|
+
|
153
|
+
class Rating
|
154
|
+
WORST = 1
|
155
|
+
BEST = 5
|
156
|
+
|
157
|
+
def initialize(score=nil)
|
158
|
+
@score = score
|
159
|
+
end
|
160
|
+
|
161
|
+
attr_reader :best, :worst, :score
|
162
|
+
|
163
|
+
def best=(b)
|
164
|
+
@best = b.to_i
|
165
|
+
end
|
166
|
+
|
167
|
+
def worst=(w)
|
168
|
+
@worst = w.to_i
|
169
|
+
end
|
170
|
+
|
171
|
+
def score=(s)
|
172
|
+
@score = s.to_f
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_f
|
176
|
+
return (@score - (@worst || WORST)).to_f / ((@best || BEST) - (@worst || WORST))
|
177
|
+
end
|
178
|
+
end # Rating
|
179
|
+
|
180
|
+
end # HReview
|
181
|
+
|
182
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Microformats
|
2
|
+
class Pluralizer
|
3
|
+
@@plurals = {}
|
4
|
+
|
5
|
+
PLURALS = [
|
6
|
+
[/([^aeo])y$/, '\1ies'],
|
7
|
+
[/(s+|ch|x)$/, '\1es'],
|
8
|
+
]
|
9
|
+
|
10
|
+
def self.pluralize(sym)
|
11
|
+
return @@plurals[sym] if @@plurals[sym]
|
12
|
+
string = sym.to_s
|
13
|
+
PLURALS.each do |match, replacement|
|
14
|
+
if string.match(match)
|
15
|
+
plural = string.sub(match, replacement).to_sym
|
16
|
+
@@plurals[sym] = plural
|
17
|
+
return plural
|
18
|
+
end
|
19
|
+
end
|
20
|
+
plural = (string + 's').to_sym
|
21
|
+
@@plurals[sym] = plural
|
22
|
+
return plural
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.singularized_lookup(hash, sym)
|
26
|
+
return hash[sym] if hash[sym]
|
27
|
+
plural = hash[Pluralizer.pluralize(sym)]
|
28
|
+
return plural[0] if plural.respond_to?(:[])
|
29
|
+
return nil
|
30
|
+
end
|
31
|
+
end # Pluralizer
|
32
|
+
end
|
data/test/all.rb
ADDED
data/test/basic_test.rb
ADDED
@@ -0,0 +1,441 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
require 'uformats/hcard'
|
3
|
+
|
4
|
+
class TestFormat < Microformats::BasicNestedFormat
|
5
|
+
structure(
|
6
|
+
:tv => {
|
7
|
+
:type => [:string],
|
8
|
+
:value => :value,
|
9
|
+
},
|
10
|
+
:te => {
|
11
|
+
:type => [:string],
|
12
|
+
:value => :email,
|
13
|
+
},
|
14
|
+
:string => :string,
|
15
|
+
:nested => {
|
16
|
+
:inner => :string,
|
17
|
+
},
|
18
|
+
:integer => :integer,
|
19
|
+
:float => :float,
|
20
|
+
:datetime => :datetime,
|
21
|
+
:url => :url,
|
22
|
+
:email => :email,
|
23
|
+
:xhtml => :xhtml
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
class TestFormatWithPostProcess < Microformats::BasicNestedFormat
|
28
|
+
|
29
|
+
structure(:string => :string)
|
30
|
+
|
31
|
+
def post_process!
|
32
|
+
@post = true
|
33
|
+
end
|
34
|
+
attr_reader :post
|
35
|
+
end
|
36
|
+
|
37
|
+
class BasicNestedFormatTest < Test::Unit::TestCase
|
38
|
+
|
39
|
+
def test_should_accept_rexml_document_as_source
|
40
|
+
assert_nothing_raised{
|
41
|
+
t = TestFormat.new(REXML::Document.new(%{
|
42
|
+
<x class="testformat">
|
43
|
+
</x>
|
44
|
+
}))
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_should_accept_text_as_source
|
49
|
+
assert_nothing_raised{
|
50
|
+
t = TestFormat.new(%{
|
51
|
+
<x class="testformat">
|
52
|
+
</x>
|
53
|
+
})
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
def test_should_call_post_process
|
58
|
+
t = TestFormatWithPostProcess.new('<x></x>')
|
59
|
+
assert(t.post)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_should_not_cause_error_if_post_process_not_defined
|
63
|
+
assert_nothing_raised{ TestFormat.new('<x></x>') }
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_should_expand_include_patterns_before_processing
|
67
|
+
before = %{
|
68
|
+
<div>
|
69
|
+
<span class="testformat">
|
70
|
+
<span class="string" id="master">Foo</span>
|
71
|
+
</span>
|
72
|
+
<span class="testformat">
|
73
|
+
<object data="#master" class="include" type="text/html"></object>
|
74
|
+
</span>
|
75
|
+
</div>
|
76
|
+
}
|
77
|
+
tfs = []
|
78
|
+
TestFormat.each(before) do |tf|
|
79
|
+
tfs << tf
|
80
|
+
end
|
81
|
+
assert_equal('Foo', tfs[0].string)
|
82
|
+
assert_equal('Foo', tfs[1].string)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_should_expand_relative_urls
|
86
|
+
t = TestFormat.new(%{
|
87
|
+
<x class="testformat">
|
88
|
+
<a class="url" href="/link">link</a>
|
89
|
+
</x>
|
90
|
+
}, 'http://www.example.com/')
|
91
|
+
assert_equal('http://www.example.com/link', t.url)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_should_return_nested_item_or_nil_via_lookup_method
|
95
|
+
t = TestFormat.new(%{
|
96
|
+
<x class="testformat">
|
97
|
+
<x class="tv">
|
98
|
+
<x class="type">a</x>
|
99
|
+
<x class="type">b</x>
|
100
|
+
<x class="value">c</x>
|
101
|
+
</x>
|
102
|
+
</x>
|
103
|
+
}, 'http://www.example.com/')
|
104
|
+
assert_equal('a', t.lookup(:tv, :type))
|
105
|
+
assert_equal('a', t.lookup(:tv, :types, 0))
|
106
|
+
assert_equal('b', t.lookup(:tv, :types, 1))
|
107
|
+
assert_equal('c', t.lookup(:tv, :value))
|
108
|
+
assert_nil(t.lookup(:foo, :bar))
|
109
|
+
end
|
110
|
+
|
111
|
+
def test_should_return_email_when_href
|
112
|
+
t = TestFormat.new(%{
|
113
|
+
<x class="testformat">
|
114
|
+
<a class="email" href="mailto:bob@example.com">email</a>
|
115
|
+
</x>
|
116
|
+
})
|
117
|
+
assert_equal('bob@example.com', t.email)
|
118
|
+
end
|
119
|
+
|
120
|
+
def test_should_return_email_when_text
|
121
|
+
t = TestFormat.new(%{
|
122
|
+
<x class="testformat">
|
123
|
+
<a class="email">bob@example.com</a>
|
124
|
+
</x>
|
125
|
+
})
|
126
|
+
assert_equal('bob@example.com', t.email)
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
def test_should_interpret_nested_values_when_xml_is_nested
|
131
|
+
t = TestFormat.new(%{
|
132
|
+
<x class="testformat">
|
133
|
+
<a><b class="nested"><c class="inner">foo</c>bar</b></a>
|
134
|
+
</x>
|
135
|
+
})
|
136
|
+
assert_equal('foo', t.nested.inner)
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_should_interpret_nested_values_when_xml_is_not_nested
|
140
|
+
t = TestFormat.new(%{
|
141
|
+
<x class="testformat">
|
142
|
+
<a><b class="nested inner">foo</b>bar</a>
|
143
|
+
</x>
|
144
|
+
})
|
145
|
+
assert_equal('foo', t.nested.inner)
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_should_return_nil_for_missing_href_in_url
|
149
|
+
t = TestFormat.new(%{
|
150
|
+
<x class="testformat">
|
151
|
+
<a class="url">
|
152
|
+
quux
|
153
|
+
</a>
|
154
|
+
</x>
|
155
|
+
})
|
156
|
+
assert_nil(t.url)
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_should_process_type_and_value_when_value_is_explicit
|
160
|
+
t = TestFormat.new(%{
|
161
|
+
<x class="testformat">
|
162
|
+
<a class="tv">
|
163
|
+
<b class="type">foo</b>
|
164
|
+
<b class="type">bar</b>
|
165
|
+
<b class="value">baz</b>
|
166
|
+
quux
|
167
|
+
</a>
|
168
|
+
</x>
|
169
|
+
})
|
170
|
+
assert_equal('foo', t.tv.type)
|
171
|
+
assert_equal(['foo', 'bar'], t.tv.types)
|
172
|
+
assert_equal('baz', t.tv.value)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_should_process_type_and_value_when_value_is_implicit
|
176
|
+
t = TestFormat.new(%{
|
177
|
+
<x class="testformat">
|
178
|
+
<a class="tv">
|
179
|
+
<b class="type">foo</b>
|
180
|
+
<b class="type">bar</b>
|
181
|
+
baz
|
182
|
+
</a>
|
183
|
+
</x>
|
184
|
+
})
|
185
|
+
assert_equal('foo', t.tv.type)
|
186
|
+
assert_equal(['foo', 'bar'], t.tv.types)
|
187
|
+
assert_equal('baz', t.tv.value)
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_should_process_type_and_email_when_value_is_explicit
|
191
|
+
t = TestFormat.new(%{
|
192
|
+
<x class="testformat">
|
193
|
+
<a class="te">
|
194
|
+
<b class="type">foo</b>
|
195
|
+
<b class="type">bar</b>
|
196
|
+
<b class="value" href="mailto:user@example.com">user@example.com</b>
|
197
|
+
quux
|
198
|
+
</a>
|
199
|
+
</x>
|
200
|
+
})
|
201
|
+
assert_equal('foo', t.te.type)
|
202
|
+
assert_equal(['foo', 'bar'], t.te.types)
|
203
|
+
assert_equal('user@example.com', t.te.value)
|
204
|
+
end
|
205
|
+
|
206
|
+
def test_should_process_type_and_email_when_value_is_implicit_href
|
207
|
+
t = TestFormat.new(%{
|
208
|
+
<x class="testformat">
|
209
|
+
<a class="te" href="mailto:user@example.com">
|
210
|
+
user@example.com
|
211
|
+
<b class="type">foo</b>
|
212
|
+
<b class="type">bar</b>
|
213
|
+
</a>
|
214
|
+
</x>
|
215
|
+
})
|
216
|
+
assert_equal('foo', t.te.type)
|
217
|
+
assert_equal(['foo', 'bar'], t.te.types)
|
218
|
+
assert_equal('user@example.com', t.te.value)
|
219
|
+
end
|
220
|
+
|
221
|
+
def test_should_process_type_and_email_when_value_is_implicit_text
|
222
|
+
t = TestFormat.new(%{
|
223
|
+
<x class="testformat">
|
224
|
+
<a class="te">
|
225
|
+
user@example.com
|
226
|
+
</a>
|
227
|
+
</x>
|
228
|
+
})
|
229
|
+
assert_equal('user@example.com', t.te.value)
|
230
|
+
end
|
231
|
+
|
232
|
+
def test_should_reassemble_marked_up_strings
|
233
|
+
t = TestFormat.new(%{
|
234
|
+
<x class="testformat">
|
235
|
+
<a class="string">
|
236
|
+
<h1>About</h1>
|
237
|
+
<p>This is <a href="/">some text</a>.</p>
|
238
|
+
<h2>Final</h2>
|
239
|
+
<p>This is the last paragraph.</p>
|
240
|
+
</a>
|
241
|
+
</x>
|
242
|
+
})
|
243
|
+
assert_equal('About This is some text. Final This is the last paragraph.', t.string)
|
244
|
+
end
|
245
|
+
|
246
|
+
def test_should_regurgitate_xhtml_verbatim
|
247
|
+
xhtml = %{
|
248
|
+
<h1>About</h1>
|
249
|
+
<p>This is <a href='/'>some text</a>.</p>
|
250
|
+
<h2>Final</h2>
|
251
|
+
<p>This is the last paragraph.</p>
|
252
|
+
}
|
253
|
+
t = TestFormat.new(%{
|
254
|
+
<x class="testformat">
|
255
|
+
<a class="xhtml">
|
256
|
+
#{xhtml}
|
257
|
+
</a>
|
258
|
+
</x>
|
259
|
+
})
|
260
|
+
assert_equal(xhtml.strip.gsub(/\s+/, ' '), t.xhtml.strip.gsub(/\s+/, ' '))
|
261
|
+
end
|
262
|
+
|
263
|
+
def test_should_use_title_attribute_when_string_is_abbr
|
264
|
+
t = TestFormat.new(%{
|
265
|
+
<x class="testformat">
|
266
|
+
<abbr class="string" title="foo">bar</abbr>
|
267
|
+
</x>
|
268
|
+
})
|
269
|
+
assert_equal('foo', t.string)
|
270
|
+
end
|
271
|
+
|
272
|
+
def test_should_use_alt_attribute_when_string_is_img
|
273
|
+
t = TestFormat.new(%{
|
274
|
+
<x class="testformat">
|
275
|
+
<img class="string" alt="foo" />
|
276
|
+
</x>
|
277
|
+
})
|
278
|
+
assert_equal('foo', t.string)
|
279
|
+
end
|
280
|
+
|
281
|
+
def test_should_use_href_for_url_when_available
|
282
|
+
t = TestFormat.new(%{
|
283
|
+
<x class="testformat">
|
284
|
+
<a class="url" href="http://example.com/foo">bar</a>
|
285
|
+
</x>
|
286
|
+
})
|
287
|
+
assert_equal('http://example.com/foo', t.url)
|
288
|
+
end
|
289
|
+
|
290
|
+
def test_should_use_src_for_url_when_available
|
291
|
+
t = TestFormat.new(%{
|
292
|
+
<x class="testformat">
|
293
|
+
<img class="url" src="http://example.com/foo" />
|
294
|
+
</x>
|
295
|
+
})
|
296
|
+
assert_equal('http://example.com/foo', t.url)
|
297
|
+
end
|
298
|
+
|
299
|
+
def test_should_find_all_matching_microformats_via_each_with_text
|
300
|
+
src = %{
|
301
|
+
<z>
|
302
|
+
<x class="testformat">
|
303
|
+
<a class="integer">1</a>
|
304
|
+
</x>
|
305
|
+
<x class="testformat">
|
306
|
+
<a class="integer">2</a>
|
307
|
+
</x>
|
308
|
+
</z>
|
309
|
+
}
|
310
|
+
ary = []
|
311
|
+
TestFormat.each(src) do |tf|
|
312
|
+
ary << tf
|
313
|
+
end
|
314
|
+
assert_equal(1, ary[0].integer)
|
315
|
+
assert_equal(2, ary[1].integer)
|
316
|
+
end
|
317
|
+
|
318
|
+
def test_should_find_all_matching_microformats_via_each_with_rexml
|
319
|
+
src = REXML::Document.new(%{
|
320
|
+
<z>
|
321
|
+
<x class="testformat">
|
322
|
+
<a class="integer">1</a>
|
323
|
+
</x>
|
324
|
+
<x class="testformat">
|
325
|
+
<a class="integer">2</a>
|
326
|
+
</x>
|
327
|
+
</z>
|
328
|
+
})
|
329
|
+
ary = []
|
330
|
+
TestFormat.each(src) do |tf|
|
331
|
+
ary << tf
|
332
|
+
end
|
333
|
+
assert_equal(1, ary[0].integer)
|
334
|
+
assert_equal(2, ary[1].integer)
|
335
|
+
end
|
336
|
+
|
337
|
+
|
338
|
+
def test_should_find_first_matching_microformats_via_first
|
339
|
+
src = %{
|
340
|
+
<z>
|
341
|
+
<x class="testformat">
|
342
|
+
<a class="integer">1</a>
|
343
|
+
</x>
|
344
|
+
<x class="testformat">
|
345
|
+
<a class="integer">2</a>
|
346
|
+
</x>
|
347
|
+
</z>
|
348
|
+
}
|
349
|
+
tf = TestFormat.first(src)
|
350
|
+
assert_equal(1, tf.integer)
|
351
|
+
end
|
352
|
+
|
353
|
+
|
354
|
+
def test_should_parse_integers
|
355
|
+
t = TestFormat.new(%{
|
356
|
+
<x class="testformat">
|
357
|
+
<a class="integer">09</a>
|
358
|
+
</x>
|
359
|
+
})
|
360
|
+
assert_equal(9, t.integer)
|
361
|
+
end
|
362
|
+
|
363
|
+
def test_should_parse_floats
|
364
|
+
t = TestFormat.new(%{
|
365
|
+
<x class="testformat">
|
366
|
+
<a class="float">3.14159</a>
|
367
|
+
</x>
|
368
|
+
})
|
369
|
+
assert_in_delta(3.14159, t.float, 0.01)
|
370
|
+
end
|
371
|
+
|
372
|
+
def test_should_parse_zulu_dates
|
373
|
+
t = TestFormat.new(%{
|
374
|
+
<x class="testformat">
|
375
|
+
<a class="datetime">20050102T030405Z</a>
|
376
|
+
</x>
|
377
|
+
})
|
378
|
+
assert_equal(2005, t.datetime.utc.year)
|
379
|
+
assert_equal(1, t.datetime.utc.month)
|
380
|
+
assert_equal(2, t.datetime.utc.day)
|
381
|
+
assert_equal(3, t.datetime.utc.hour)
|
382
|
+
assert_equal(4, t.datetime.utc.min)
|
383
|
+
assert_equal(5, t.datetime.utc.sec)
|
384
|
+
end
|
385
|
+
|
386
|
+
def test_should_parse_partial_dates
|
387
|
+
t = TestFormat.new(%{
|
388
|
+
<x class="testformat">
|
389
|
+
<a class="datetime">200501</a>
|
390
|
+
</x>
|
391
|
+
})
|
392
|
+
assert_equal(2005, t.datetime.utc.year)
|
393
|
+
assert_equal(1, t.datetime.utc.month)
|
394
|
+
end
|
395
|
+
|
396
|
+
def test_should_return_nil_for_invalid_date
|
397
|
+
t = TestFormat.new(%{
|
398
|
+
<x class="testformat">
|
399
|
+
<a class="datetime">Hello!</a>
|
400
|
+
</x>
|
401
|
+
})
|
402
|
+
assert_nil(t.datetime)
|
403
|
+
t = TestFormat.new(%{
|
404
|
+
<x class="testformat">
|
405
|
+
<a class="datetime">9999-88-88</a>
|
406
|
+
</x>
|
407
|
+
})
|
408
|
+
assert_nil(t.datetime)
|
409
|
+
end
|
410
|
+
|
411
|
+
def test_should_parse_time_zones_with_multiple_pluses
|
412
|
+
t = TestFormat.new(%{
|
413
|
+
<x class="testformat">
|
414
|
+
<a class="datetime">20010101T120000++0200</a>
|
415
|
+
</x>
|
416
|
+
})
|
417
|
+
assert_equal(10, t.datetime.utc.hour)
|
418
|
+
end
|
419
|
+
|
420
|
+
def test_should_parse_time_zones_with_multiple_minuses
|
421
|
+
t = TestFormat.new(%{
|
422
|
+
<x class="testformat">
|
423
|
+
<a class="datetime">20010101T120000--0200</a>
|
424
|
+
</x>
|
425
|
+
})
|
426
|
+
assert_equal(14, t.datetime.utc.hour)
|
427
|
+
end
|
428
|
+
|
429
|
+
|
430
|
+
def test_should_handle_email_when_encapsulated_by_unknown_element
|
431
|
+
t = TestFormat.new(%{
|
432
|
+
<x class="testformat">
|
433
|
+
<div class="te">
|
434
|
+
<a class="internet" href="mailto:info@example.com">info@example.com</a>
|
435
|
+
</div>
|
436
|
+
</x>
|
437
|
+
})
|
438
|
+
assert_equal('info@example.com', t.te.value)
|
439
|
+
end
|
440
|
+
|
441
|
+
end
|