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 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
@@ -1,5 +1,5 @@
1
- %w[rubygems hpricot microformat/string open-uri ostruct timeout].each { |f| require f }
2
- require_gem 'hpricot', '>= 0.4.59'
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 === target ? target : {}
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 = find_occurances(doc)
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[one many].include? method.to_s
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[one many].include? method.to_s
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 === element ? [element.map { |k,v| { k => v } }].flatten : [element])
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 find_occurances(doc)
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 === attribute
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 && Hash === attribute[key]
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 Array === hash[key] && hash[key].size == 1
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.search("[@rel=bookmark]").first rescue nil
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 Class === target
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
- else element.innerHTML
186
- end.strip.strip_html.coerce
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 === value ? OpenStruct.new(value) : value
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(method, *args, &block) unless method == :properties || @properties.include?(method.to_s)
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
@@ -0,0 +1,5 @@
1
+ class Array
2
+ def first_or_self
3
+ size > 1 ? self : first
4
+ end
5
+ end
@@ -4,29 +4,27 @@ class Microformat
4
4
  class Simple < String
5
5
  extend Microformat::Base
6
6
 
7
- class << self
8
- def find_first(doc)
9
- find_every(doc).first
10
- end
7
+ def self.find_first(doc)
8
+ find_every(doc).first
9
+ end
11
10
 
12
- def find_occurances(doc)
13
- @from ? doc/from_as_xpath : super(doc)
14
- end
11
+ def self.find_occurences(doc)
12
+ @from ? doc/from_as_xpath : super(doc)
13
+ end
15
14
 
16
- def build_class(tag)
17
- new(tag.innerText)
18
- end
15
+ def self.build_class(tag)
16
+ new(tag.innerText)
17
+ end
19
18
 
20
- def from(options)
21
- @from ||= []
22
- options.each do |tag, value|
23
- @from << "@#{tag}=#{value}"
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
- def from_as_xpath
28
- "[#{@from.to_a * "|"}]"
29
- end
26
+ def self.from_as_xpath
27
+ "[#{@from.to_a * "|"}]"
30
28
  end
31
29
  end
32
30
  end
@@ -10,7 +10,7 @@ class String
10
10
  attempt = coercions.next
11
11
  break if !attempt.nil?
12
12
  end
13
- %w[@coercions @generator].each { |i| remove_instance_variable i }
13
+ %w(@coercions @generator).each { |i| remove_instance_variable i }
14
14
  attempt.nil? ? self : attempt
15
15
  end
16
16
 
@@ -1,3 +1,3 @@
1
1
  $:.unshift File.join(File.dirname(__FILE__), "lib"), File.join(File.dirname(__FILE__))
2
2
 
3
- %w[hentry hreview hcalendar rel_tag hcard xoxo].each { |format| require "mofo/#{format}" }
3
+ Dir['mofo/*.rb'].each { |format| require "mofo/#{format}" }
@@ -0,0 +1,6 @@
1
+ class Adr < Microformat
2
+ one :post_office_box, :extended_address, :street_address,
3
+ :locality, :region, :postal_code, :country_name, :value
4
+
5
+ many :type
6
+ end
@@ -0,0 +1,3 @@
1
+ class Geo < Microformat
2
+ one :latitude, :longitude
3
+ end
@@ -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, :location, :status, :summary, :uid,
9
- :last_modified, :url => :url
11
+ :duration, :status, :summary, :uid, :last_modified,
12
+ :url => :url, :location => [ HCard, Adr, Geo, String ]
10
13
 
11
14
  many :category
12
15
  end
@@ -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
@@ -5,7 +5,7 @@ require 'mofo/rel_tag'
5
5
 
6
6
  class HEntry < Microformat
7
7
  one :entry_title!, :entry_summary, :updated, :published,
8
- :author => HCard
8
+ :author => HCard, :tags => RelTag
9
9
 
10
10
  many :entry_content
11
11
  end
@@ -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
@@ -0,0 +1,6 @@
1
+ # => http://microformats.org/wiki/rel-design-pattern
2
+ require 'microformat/simple'
3
+
4
+ class RelBookmark < Microformat::Simple
5
+ from :rel => :bookmark
6
+ end
@@ -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