mofo 0.1.2 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +8 -0
- data/README +16 -6
- data/lib/microformat.rb +59 -20
- data/lib/microformat/array.rb +5 -0
- data/lib/microformat/simple.rb +16 -18
- data/lib/microformat/string.rb +1 -1
- data/lib/mofo.rb +1 -1
- data/lib/mofo/adr.rb +6 -0
- data/lib/mofo/geo.rb +3 -0
- data/lib/mofo/hcalendar.rb +5 -2
- data/lib/mofo/hcard.rb +7 -12
- data/lib/mofo/hentry.rb +1 -1
- data/lib/mofo/hresume.rb +17 -0
- data/lib/mofo/rel_bookmark.rb +6 -0
- data/lib/mofo/xfn.rb +66 -0
- data/lib/mofo/xoxo.rb +37 -39
- data/test/ext_test.rb +44 -0
- data/test/fixtures/event_addr.html +26 -0
- data/test/fixtures/hresume.html +409 -0
- data/test/fixtures/include_pattern_single_attribute.html +246 -0
- data/test/fixtures/upcoming_single.html +479 -0
- data/test/fixtures/xfn.html +200 -0
- data/test/format_test.rb +1 -1
- data/test/hatom_test.rb +5 -6
- data/test/hcalendar_test.rb +33 -0
- data/test/hcard_test.rb +132 -0
- data/test/hresume_test.rb +23 -0
- data/test/hreview_test.rb +36 -0
- data/test/include_pattern_test.rb +27 -0
- data/test/reltag_test.rb +40 -0
- data/test/test_helper.rb +9 -2
- data/test/xfn_test.rb +76 -0
- data/test/xoxo_test.rb +1 -1
- metadata +31 -4
data/CHANGELOG
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
= 0.2
|
2
|
+
- Minor refactoring
|
3
|
+
- Added Geo and Adr [Andrew Turner]
|
4
|
+
- Added XFN [Steve Ivy, Christian Carter]
|
5
|
+
- Added hResume [Grant Rodgers]
|
6
|
+
- Added include-pattern support
|
7
|
+
- occurances => occurences
|
8
|
+
|
1
9
|
= 0.1.2
|
2
10
|
- Upgraded to test/spec 0.3
|
3
11
|
- Fixed so tests run as Rails plugin
|
data/README
CHANGED
@@ -145,17 +145,15 @@
|
|
145
145
|
- hCalendar [ upcoming.org ]
|
146
146
|
- hReview [ cork'd reviews ]
|
147
147
|
- hEntry [ err the blog posts ]
|
148
|
+
- hResume [ linkedin.com ]
|
148
149
|
- xoxo [ chowhound.com ]
|
150
|
+
- geo [ upcoming.org ]
|
151
|
+
- adr [ upcoming.org ]
|
152
|
+
- xfn [ linkedin.com ]
|
149
153
|
|
150
154
|
patterns:
|
151
155
|
- rel-tag
|
152
156
|
- rel-bookmark
|
153
|
-
|
154
|
-
Here are the microformats we want soon soon soon:
|
155
|
-
- geo
|
156
|
-
- hResume
|
157
|
-
|
158
|
-
patterns:
|
159
157
|
- include-pattern
|
160
158
|
|
161
159
|
= Ruby on Rails
|
@@ -185,3 +183,15 @@ them in a session.
|
|
185
183
|
=> http://rubyforge.org/projects/scrapi/
|
186
184
|
>> uformats
|
187
185
|
=> http://rubyforge.org/projects/uformats
|
186
|
+
|
187
|
+
= Contributors
|
188
|
+
|
189
|
+
>> Steve Ivy
|
190
|
+
>> Olle Jonsson
|
191
|
+
>> Christian Carter
|
192
|
+
>> Grant Rodgers
|
193
|
+
|
194
|
+
= Author
|
195
|
+
|
196
|
+
>> Chris Wanstrath
|
197
|
+
=> chris[at]ozmm[dot]org
|
data/lib/microformat.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
%w
|
2
|
-
|
1
|
+
%w(rubygems hpricot microformat/string microformat/array open-uri ostruct timeout).each { |f| require f }
|
2
|
+
gem 'hpricot', '>= 0.4.59'
|
3
3
|
|
4
4
|
class Microformat
|
5
5
|
module Base
|
@@ -8,12 +8,12 @@ class Microformat
|
|
8
8
|
#
|
9
9
|
def find(*args)
|
10
10
|
target, @options = args
|
11
|
-
@options ||= Hash
|
11
|
+
@options ||= target.is_a?(Hash) ? target : {}
|
12
12
|
[:first, :all].each { |key| target = @options[key] if @options[key] }
|
13
13
|
|
14
|
-
doc = build_doc(@options[:text] ? @options : target)
|
14
|
+
@doc = build_doc(@options[:text] ? @options : target)
|
15
15
|
|
16
|
-
microformats =
|
16
|
+
microformats = find_occurences(@doc)
|
17
17
|
raise MicroformatNotFound if @options[:strict] && microformats.empty?
|
18
18
|
return @options[:first] ? nil : [] if microformats.empty?
|
19
19
|
|
@@ -29,6 +29,22 @@ class Microformat
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
+
names_and_keys = proc do |attributes|
|
33
|
+
attributes.map do |att|
|
34
|
+
att.respond_to?(:keys) ? att.keys.first : att
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
define_method :attributes do
|
39
|
+
names_and_keys[@attributes[:many] + @attributes[:one]]
|
40
|
+
end
|
41
|
+
|
42
|
+
%w(many one).each do |type|
|
43
|
+
define_method("#{type}s") do
|
44
|
+
names_and_keys[@attributes[type.intern]]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
32
48
|
protected
|
33
49
|
##
|
34
50
|
# DSL Related
|
@@ -41,7 +57,7 @@ class Microformat
|
|
41
57
|
def collector
|
42
58
|
collector = Hash.new([])
|
43
59
|
def collector.method_missing(method, *classes)
|
44
|
-
super unless %w
|
60
|
+
super unless %w(one many).include? method.to_s
|
45
61
|
self[method] += Microformat.send(:break_out_hashes, classes)
|
46
62
|
end
|
47
63
|
collector
|
@@ -52,7 +68,7 @@ class Microformat
|
|
52
68
|
end
|
53
69
|
|
54
70
|
def method_missing(method, *args, &block)
|
55
|
-
super unless %w
|
71
|
+
super unless %w(one many).include? method.to_s
|
56
72
|
(collected = collector).instance_eval(&block) if block_given?
|
57
73
|
classes = block_given? ? [args.first => collected] : break_out_hashes(args)
|
58
74
|
@attributes[method] += classes
|
@@ -60,7 +76,7 @@ class Microformat
|
|
60
76
|
|
61
77
|
def break_out_hashes(array)
|
62
78
|
array.inject([]) do |memo, element|
|
63
|
-
memo + (Hash
|
79
|
+
memo + (element.is_a?(Hash) ? [element.map { |k,v| { k => v } }].flatten : [element])
|
64
80
|
end
|
65
81
|
end
|
66
82
|
|
@@ -96,7 +112,7 @@ class Microformat
|
|
96
112
|
end
|
97
113
|
end
|
98
114
|
|
99
|
-
def
|
115
|
+
def find_occurences(doc)
|
100
116
|
doc/".#{@container}"
|
101
117
|
end
|
102
118
|
|
@@ -116,7 +132,7 @@ class Microformat
|
|
116
132
|
|
117
133
|
[:one, :many].each do |name|
|
118
134
|
attributes[name].each do |attribute|
|
119
|
-
is_hash = Hash
|
135
|
+
is_hash = attribute.is_a? Hash
|
120
136
|
key = is_hash ? attribute.keys.first : attribute
|
121
137
|
|
122
138
|
# rel="bookmark" pattern
|
@@ -133,7 +149,7 @@ class Microformat
|
|
133
149
|
raise InvalidMicroformat if found.empty? && key.to_s =~ /!/
|
134
150
|
next if found.empty?
|
135
151
|
|
136
|
-
if is_hash &&
|
152
|
+
if is_hash && attribute[key].is_a?(Hash)
|
137
153
|
built_hash = build_hash(found, attribute[key])
|
138
154
|
key = key.no_bang
|
139
155
|
if built_hash.size.zero? && found.size.nonzero?
|
@@ -152,15 +168,31 @@ class Microformat
|
|
152
168
|
hash[key] = parse_element(found.first, target)
|
153
169
|
end
|
154
170
|
end
|
155
|
-
hash[key] = hash[key].first if
|
171
|
+
hash[key] = hash[key].first if hash[key].is_a?(Array) && hash[key].size == 1
|
156
172
|
end
|
157
173
|
end
|
158
174
|
|
159
|
-
hash
|
175
|
+
hash.merge extract_includes(doc)
|
176
|
+
end
|
177
|
+
|
178
|
+
def extract_includes(doc)
|
179
|
+
@includes ||= {}
|
180
|
+
|
181
|
+
doc.search(".include").inject({}) do |hash, element|
|
182
|
+
target = element.attributes['data'] || element.attributes['href']
|
183
|
+
|
184
|
+
return @includes[target] if @includes[target]
|
185
|
+
|
186
|
+
unless (includes = @doc/target).empty?
|
187
|
+
hash.merge @includes[target] = build_hash(includes)
|
188
|
+
else
|
189
|
+
hash
|
190
|
+
end
|
191
|
+
end
|
160
192
|
end
|
161
193
|
|
162
194
|
def extract_bookmark(doc)
|
163
|
-
bookmark = doc.
|
195
|
+
bookmark = doc.at("[@rel=bookmark]") rescue nil
|
164
196
|
bookmark.attributes['href'] if bookmark.respond_to? :attributes
|
165
197
|
end
|
166
198
|
|
@@ -176,24 +208,31 @@ class Microformat
|
|
176
208
|
when 'a' then element['href']
|
177
209
|
when 'object' then element['value']
|
178
210
|
end
|
179
|
-
elsif
|
211
|
+
elsif target.is_a? Array
|
212
|
+
target.inject(nil) do |found, klass|
|
213
|
+
klass = klass.respond_to?(:find) ? klass : nil
|
214
|
+
|
215
|
+
found || parse_element(element, klass)
|
216
|
+
end
|
217
|
+
elsif target.is_a? Class
|
180
218
|
target.find(:first => element)
|
181
219
|
else
|
182
|
-
case element.name
|
220
|
+
value = case element.name
|
183
221
|
when 'abbr' then element['title']
|
184
222
|
when 'img' then element['alt']
|
185
|
-
|
186
|
-
|
223
|
+
end || ''
|
224
|
+
|
225
|
+
(value.empty? ? element.innerHTML : value).strip.strip_html.coerce
|
187
226
|
end
|
188
227
|
end
|
189
228
|
|
190
229
|
def prepare_value(value)
|
191
|
-
Hash
|
230
|
+
value.is_a?(Hash) ? OpenStruct.new(value) : value
|
192
231
|
end
|
193
232
|
end
|
194
233
|
|
195
234
|
def method_missing(method, *args, &block)
|
196
|
-
return super
|
235
|
+
return super unless method == :properties || @properties.include?(method.to_s)
|
197
236
|
self.class.class_eval { define_method(method) { instance_variable_get("@#{method}") } }
|
198
237
|
instance_variable_get("@#{method}")
|
199
238
|
end
|
data/lib/microformat/simple.rb
CHANGED
@@ -4,29 +4,27 @@ class Microformat
|
|
4
4
|
class Simple < String
|
5
5
|
extend Microformat::Base
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
end
|
7
|
+
def self.find_first(doc)
|
8
|
+
find_every(doc).first
|
9
|
+
end
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
11
|
+
def self.find_occurences(doc)
|
12
|
+
@from ? doc/from_as_xpath : super(doc)
|
13
|
+
end
|
15
14
|
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
def self.build_class(tag)
|
16
|
+
new(tag.innerText)
|
17
|
+
end
|
19
18
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
19
|
+
def self.from(options)
|
20
|
+
@from ||= []
|
21
|
+
options.each do |tag, value|
|
22
|
+
@from << "@#{tag}=#{value}"
|
25
23
|
end
|
24
|
+
end
|
26
25
|
|
27
|
-
|
28
|
-
|
29
|
-
end
|
26
|
+
def self.from_as_xpath
|
27
|
+
"[#{@from.to_a * "|"}]"
|
30
28
|
end
|
31
29
|
end
|
32
30
|
end
|
data/lib/microformat/string.rb
CHANGED
data/lib/mofo.rb
CHANGED
data/lib/mofo/adr.rb
ADDED
data/lib/mofo/geo.rb
ADDED
data/lib/mofo/hcalendar.rb
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# => http://microformats.org/wiki/hcalendar
|
2
2
|
require 'microformat'
|
3
|
+
require 'mofo/hcard'
|
4
|
+
require 'mofo/adr'
|
5
|
+
require 'mofo/geo'
|
3
6
|
|
4
7
|
class HCalendar < Microformat
|
5
8
|
container :vevent
|
6
9
|
|
7
10
|
one :class, :description, :dtend, :dtstamp, :dtstart,
|
8
|
-
:duration, :
|
9
|
-
:
|
11
|
+
:duration, :status, :summary, :uid, :last_modified,
|
12
|
+
:url => :url, :location => [ HCard, Adr, Geo, String ]
|
10
13
|
|
11
14
|
many :category
|
12
15
|
end
|
data/lib/mofo/hcard.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
1
|
# => http://microformats.org/wiki/hcard
|
2
2
|
require 'microformat'
|
3
|
+
require 'mofo/geo'
|
4
|
+
require 'mofo/adr'
|
3
5
|
|
4
6
|
class HCard < Microformat
|
5
7
|
container :vcard
|
6
8
|
|
7
|
-
one :fn, :bday, :tz, :sort_string, :uid, :class
|
9
|
+
one :fn, :bday, :tz, :sort_string, :uid, :class,
|
10
|
+
:geo => Geo
|
11
|
+
|
8
12
|
many :label, :sound, :title, :role, :key,
|
9
13
|
:mailer, :rev, :nickname, :category, :note,
|
10
|
-
:logo => :url, :url => :url, :photo => :url
|
14
|
+
:logo => :url, :url => :url, :photo => :url,
|
15
|
+
:adr => Adr
|
11
16
|
|
12
17
|
one :n do
|
13
18
|
one :family_name, :given_name, :additional_name
|
@@ -24,16 +29,6 @@ class HCard < Microformat
|
|
24
29
|
many :value
|
25
30
|
end
|
26
31
|
|
27
|
-
many :adr do
|
28
|
-
one :post_office_box, :extended_address, :street_address,
|
29
|
-
:locality, :region, :postal_code, :country_name, :value
|
30
|
-
many :type
|
31
|
-
end
|
32
|
-
|
33
|
-
one :geo do
|
34
|
-
one :latitude, :longitude
|
35
|
-
end
|
36
|
-
|
37
32
|
many :org do
|
38
33
|
one :organization_name, :organization_unit
|
39
34
|
end
|
data/lib/mofo/hentry.rb
CHANGED
data/lib/mofo/hresume.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# => http://microformats.org/wiki/hresume
|
2
|
+
require 'microformat'
|
3
|
+
require 'mofo/hcard'
|
4
|
+
require 'mofo/hcalendar'
|
5
|
+
require 'mofo/rel_tag'
|
6
|
+
|
7
|
+
class HResume < Microformat
|
8
|
+
container :hresume
|
9
|
+
|
10
|
+
one :summary, :contact => HCard
|
11
|
+
|
12
|
+
# TODO: linkedin uses a comma delimited skills list rather than the rel tags in the spec
|
13
|
+
many :skills
|
14
|
+
|
15
|
+
many :affiliation => HCard, :education => HCalendar,
|
16
|
+
:experience => HCalendar
|
17
|
+
end
|
data/lib/mofo/xfn.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
$:.unshift 'lib'
|
2
|
+
require 'microformat'
|
3
|
+
|
4
|
+
class XFN < Microformat
|
5
|
+
class Link < OpenStruct
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
def relation.has?(value)
|
9
|
+
is_a?(Array) ? include?(value) : self == value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_html
|
14
|
+
%[<a href="#{link}" rel="#{Array(relation) * ' '}">#{name}</a>]
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
link.inspect
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_s
|
22
|
+
link
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
attr_accessor :links
|
27
|
+
|
28
|
+
def self.find_occurences(doc)
|
29
|
+
@occurences ||= XFN.new(doc)
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
alias :find_first :find_occurences
|
34
|
+
alias :find_every :find_occurences
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(doc)
|
38
|
+
@links = doc.search("a[@rel]").map do |rl|
|
39
|
+
relation = rl[:rel].include?(' ') ? rl[:rel].split(' ') : rl[:rel]
|
40
|
+
Link.new(:name => rl.innerHTML, :link => rl[:href], :relation => relation)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def relations
|
45
|
+
@relations ||= @links.map { |l| l.relation }
|
46
|
+
end
|
47
|
+
|
48
|
+
def [](*rels)
|
49
|
+
@links.select do |link|
|
50
|
+
relation = link.relation
|
51
|
+
relation.respond_to?(:all?) && rels.all? { |rel| relation.include? rel }
|
52
|
+
end.first_or_self
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(method, *args, &block)
|
56
|
+
method = method.to_s
|
57
|
+
if (rels = method.split(/_and_/)).size > 1
|
58
|
+
self[*rels]
|
59
|
+
elsif @links.class.public_instance_methods.include? method
|
60
|
+
@links.send(method, *args, &block)
|
61
|
+
else
|
62
|
+
check = args.first == true ? :== : :has?
|
63
|
+
@links.select { |link| link.relation.send(check, method) }.first_or_self
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|