mofo 0.1.2 → 0.2
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/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
|