mofo 0.2.2 → 0.2.3

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.
Files changed (64) hide show
  1. data/CHANGELOG +6 -0
  2. data/Manifest.txt +55 -0
  3. data/Rakefile +32 -0
  4. data/init.rb +2 -0
  5. data/lib/microformat.rb +39 -6
  6. data/lib/mofo/hentry.rb +4 -0
  7. data/site/index.html +382 -0
  8. data/site/mofo-logo.png +0 -0
  9. data/site/mootools.v1.00.js +2 -0
  10. data/site/style.css +159 -0
  11. data/test/base_url_test.rb +22 -0
  12. data/test/fixtures/corkd.html +1 -1
  13. data/test/hatom_test.rb +1 -1
  14. data/test/hreview_test.rb +4 -3
  15. data/test/include_pattern_test.rb +1 -1
  16. data/test/test_helper.rb +17 -6
  17. metadata +63 -115
  18. data/test/format_test.rb +0 -230
  19. data/vendor/testspec-0.3.0/ChangeLog +0 -177
  20. data/vendor/testspec-0.3.0/README +0 -289
  21. data/vendor/testspec-0.3.0/ROADMAP +0 -1
  22. data/vendor/testspec-0.3.0/Rakefile +0 -151
  23. data/vendor/testspec-0.3.0/SPECS +0 -108
  24. data/vendor/testspec-0.3.0/TODO +0 -2
  25. data/vendor/testspec-0.3.0/bin/specrb +0 -104
  26. data/vendor/testspec-0.3.0/doc/classes/Kernel.html +0 -140
  27. data/vendor/testspec-0.3.0/doc/classes/Object.html +0 -155
  28. data/vendor/testspec-0.3.0/doc/classes/Test/Spec.html +0 -128
  29. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/CustomShould.html +0 -236
  30. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/DefinitionError.html +0 -111
  31. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/Should.html +0 -884
  32. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/ShouldNot.html +0 -487
  33. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/TestCase.html +0 -220
  34. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/TestCase/ClassMethods.html +0 -318
  35. data/vendor/testspec-0.3.0/doc/classes/Test/Spec/TestCase/InstanceMethods.html +0 -195
  36. data/vendor/testspec-0.3.0/doc/classes/Test/Unit/UI/RDox/TestRunner.html +0 -222
  37. data/vendor/testspec-0.3.0/doc/classes/Test/Unit/UI/SpecDox/TestRunner.html +0 -476
  38. data/vendor/testspec-0.3.0/doc/created.rid +0 -1
  39. data/vendor/testspec-0.3.0/doc/files/README.html +0 -516
  40. data/vendor/testspec-0.3.0/doc/files/ROADMAP.html +0 -109
  41. data/vendor/testspec-0.3.0/doc/files/SPECS.html +0 -386
  42. data/vendor/testspec-0.3.0/doc/files/lib/test/spec/dox_rb.html +0 -108
  43. data/vendor/testspec-0.3.0/doc/files/lib/test/spec/rdox_rb.html +0 -108
  44. data/vendor/testspec-0.3.0/doc/files/lib/test/spec/should-output_rb.html +0 -115
  45. data/vendor/testspec-0.3.0/doc/files/lib/test/spec_rb.html +0 -123
  46. data/vendor/testspec-0.3.0/doc/fr_class_index.html +0 -38
  47. data/vendor/testspec-0.3.0/doc/fr_file_index.html +0 -33
  48. data/vendor/testspec-0.3.0/doc/fr_method_index.html +0 -102
  49. data/vendor/testspec-0.3.0/doc/index.html +0 -24
  50. data/vendor/testspec-0.3.0/doc/rdoc-style.css +0 -208
  51. data/vendor/testspec-0.3.0/examples/stack.rb +0 -38
  52. data/vendor/testspec-0.3.0/examples/stack_spec.rb +0 -119
  53. data/vendor/testspec-0.3.0/lib/test/spec.rb +0 -490
  54. data/vendor/testspec-0.3.0/lib/test/spec/dox.rb +0 -122
  55. data/vendor/testspec-0.3.0/lib/test/spec/rdox.rb +0 -25
  56. data/vendor/testspec-0.3.0/lib/test/spec/should-output.rb +0 -49
  57. data/vendor/testspec-0.3.0/test/spec_dox.rb +0 -39
  58. data/vendor/testspec-0.3.0/test/spec_flexmock.rb +0 -210
  59. data/vendor/testspec-0.3.0/test/spec_mocha.rb +0 -118
  60. data/vendor/testspec-0.3.0/test/spec_nestedcontexts.rb +0 -26
  61. data/vendor/testspec-0.3.0/test/spec_should-output.rb +0 -26
  62. data/vendor/testspec-0.3.0/test/spec_testspec.rb +0 -522
  63. data/vendor/testspec-0.3.0/test/spec_testspec_order.rb +0 -26
  64. data/vendor/testspec-0.3.0/test/test_testunit.rb +0 -21
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ = 0.2.3
2
+ - Moved to Echoe
3
+ - Added feature of figuring out the base URL for relative URLs
4
+ - Removed vendor/, now has test/spec 0.3 gem dependency
5
+ - Added after_find callbacks to run arbitrary code after a uformat is found
6
+
1
7
  = 0.2.2
2
8
  - Proper ISO8601 date handling and cleanup of String#coerce
3
9
 
data/Manifest.txt ADDED
@@ -0,0 +1,55 @@
1
+ ./CHANGELOG
2
+ ./init.rb
3
+ ./lib/microformat/array.rb
4
+ ./lib/microformat/simple.rb
5
+ ./lib/microformat/string.rb
6
+ ./lib/microformat/time.rb
7
+ ./lib/microformat.rb
8
+ ./lib/mofo/adr.rb
9
+ ./lib/mofo/geo.rb
10
+ ./lib/mofo/hcalendar.rb
11
+ ./lib/mofo/hcard.rb
12
+ ./lib/mofo/hentry.rb
13
+ ./lib/mofo/hfeed.rb
14
+ ./lib/mofo/hresume.rb
15
+ ./lib/mofo/hreview.rb
16
+ ./lib/mofo/rel_bookmark.rb
17
+ ./lib/mofo/rel_tag.rb
18
+ ./lib/mofo/xfn.rb
19
+ ./lib/mofo/xoxo.rb
20
+ ./lib/mofo.rb
21
+ ./LICENSE
22
+ ./Manifest.txt
23
+ ./Rakefile
24
+ ./README
25
+ ./site/index.html
26
+ ./site/mofo-logo.png
27
+ ./site/mootools.v1.00.js
28
+ ./site/style.css
29
+ ./test/base_url_test.rb
30
+ ./test/ext_test.rb
31
+ ./test/fixtures/bob.html
32
+ ./test/fixtures/chowhound.html
33
+ ./test/fixtures/corkd.html
34
+ ./test/fixtures/event_addr.html
35
+ ./test/fixtures/events.html
36
+ ./test/fixtures/fake.html
37
+ ./test/fixtures/fauxtank.html
38
+ ./test/fixtures/hatom.html
39
+ ./test/fixtures/hresume.html
40
+ ./test/fixtures/include_pattern_single_attribute.html
41
+ ./test/fixtures/simple.html
42
+ ./test/fixtures/stoneship.html
43
+ ./test/fixtures/upcoming.html
44
+ ./test/fixtures/upcoming_single.html
45
+ ./test/fixtures/xfn.html
46
+ ./test/hatom_test.rb
47
+ ./test/hcalendar_test.rb
48
+ ./test/hcard_test.rb
49
+ ./test/hresume_test.rb
50
+ ./test/hreview_test.rb
51
+ ./test/include_pattern_test.rb
52
+ ./test/reltag_test.rb
53
+ ./test/test_helper.rb
54
+ ./test/xfn_test.rb
55
+ ./test/xoxo_test.rb
data/Rakefile ADDED
@@ -0,0 +1,32 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ version = '0.2.3'
5
+ svn_repo = 'svn+ssh://chris@errtheblog.com/svn/projects/mofo'
6
+
7
+ begin
8
+ require 'echoe'
9
+
10
+ Echoe.new('mofo', version) do |p|
11
+ p.rubyforge_name = 'mofo'
12
+ p.summary = "mofo is a ruby microformat parser"
13
+ p.description = "mofo is a ruby microformat parser"
14
+ p.url = "http://mofo.rubyforge.org/"
15
+ p.author = 'Chris Wanstrath'
16
+ p.email = "chris@ozmm.org"
17
+ p.extra_deps << ['hpricot', '>=0.4.59']
18
+ p.test_globs = 'test/*_test.rb'
19
+ end
20
+
21
+ rescue LoadError => boom
22
+ puts "You are missing a dependency required for meta-operations on this gem."
23
+ puts "#{boom.to_s.capitalize}."
24
+ end
25
+
26
+ desc "Tag the current trunk with the current release version"
27
+ task :tag do
28
+ warn "WARNING: this will tag #{svn_repo}/trunk using the tag REL_#{version.gsub!('.','_')}"
29
+ warn "If you do not wish to continue, you have 5 seconds to cancel by pressing CTRL-C..."
30
+ 5.times { |i| print "#{5-i} "; $stdout.flush; sleep 1 }
31
+ system %[svn copy #{svn_repo}/trunk #{svn_repo}/tags/REL_#{version} -m "Tagging the #{version} release"]
32
+ end
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ # Rails Plugin
2
+ require 'mofo'
data/lib/microformat.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  %w(rubygems hpricot microformat/string microformat/array open-uri ostruct timeout).each { |f| require f }
2
- gem 'hpricot', '>= 0.4.59'
2
+ gem 'hpricot', '>=0.4.59'
3
3
 
4
4
  class Microformat
5
5
  module Base
@@ -10,6 +10,8 @@ class Microformat
10
10
  target, @options = args
11
11
  @options ||= target.is_a?(Hash) ? target : {}
12
12
  [:first, :all].each { |key| target = @options[key] if @options[key] }
13
+
14
+ extract_base_url! target, @options
13
15
 
14
16
  @doc = build_doc(@options[:text] ? @options : target)
15
17
 
@@ -49,9 +51,16 @@ class Microformat
49
51
  ##
50
52
  # DSL Related
51
53
  #
54
+ def after_find(&block)
55
+ @after_find_procs ||= Hash.new { |h,k| h[k] = [] }
56
+ @after_find_procs[name] << block if block_given?
57
+ @after_find_procs[name]
58
+ end
59
+ alias :after_finds :after_find
60
+
52
61
  def inherited(klass)
53
- klass.instance_variable_set("@container", klass.name.downcase)
54
- klass.instance_variable_set("@attributes", Hash.new([]))
62
+ klass.instance_variable_set(:@container, klass.name.downcase)
63
+ klass.instance_variable_set(:@attributes, Hash.new([]))
55
64
  end
56
65
 
57
66
  def collector
@@ -119,14 +128,31 @@ class Microformat
119
128
  def build_class(microformat)
120
129
  hash = build_hash(microformat)
121
130
  class_eval { attr_reader *(hash.keys << :properties) }
131
+
122
132
  klass = new
123
- klass.instance_variable_set("@properties", hash.keys.map { |i| i.to_s } )
133
+ klass.instance_variable_set(:@properties, hash.keys.map { |i| i.to_s } )
134
+
124
135
  hash.each do |key, value|
125
136
  klass.instance_variable_set("@#{key}", prepare_value(value) )
126
137
  end
138
+
139
+ after_find_callbacks! klass
140
+
127
141
  klass
128
142
  end
129
143
 
144
+ def after_find_callbacks!(object)
145
+ original_ivars = object.instance_variables.dup
146
+
147
+ after_finds.each do |block|
148
+ object.instance_eval &block
149
+ end
150
+
151
+ Array(object.instance_variables - original_ivars).each do |ivar|
152
+ object.properties << ivar.gsub('@','')
153
+ end
154
+ end
155
+
130
156
  def build_hash(doc, attributes = @attributes)
131
157
  hash = {}
132
158
 
@@ -203,11 +229,12 @@ class Microformat
203
229
 
204
230
  def parse_element(element, target = nil)
205
231
  if target == :url
206
- case element.name
232
+ url = case element.name
207
233
  when 'img' then element['src']
208
234
  when 'a' then element['href']
209
235
  when 'object' then element['value']
210
236
  end
237
+ url[/^http/] ? url : @base_url.to_s + url
211
238
  elsif target.is_a? Array
212
239
  target.inject(nil) do |found, klass|
213
240
  klass = klass.respond_to?(:find) ? klass : nil
@@ -215,7 +242,7 @@ class Microformat
215
242
  found || parse_element(element, klass)
216
243
  end
217
244
  elsif target.is_a? Class
218
- target.find(:first => element)
245
+ target.find({:first => element}.merge(@options))
219
246
  else
220
247
  value = case element.name
221
248
  when 'abbr' then element['title']
@@ -229,6 +256,12 @@ class Microformat
229
256
  def prepare_value(value)
230
257
  value.is_a?(Hash) ? OpenStruct.new(value) : value
231
258
  end
259
+
260
+ def extract_base_url!(target, options)
261
+ @base_url = nil
262
+ @base_url ||= options[:base] || options[:url]
263
+ @base_url ||= target[/^(http:\/\/[^\/]+)/] if target.respond_to?(:scan)
264
+ end
232
265
  end
233
266
 
234
267
  def method_missing(method, *args, &block)
data/lib/mofo/hentry.rb CHANGED
@@ -8,4 +8,8 @@ class HEntry < Microformat
8
8
  :author => HCard, :tags => RelTag
9
9
 
10
10
  many :entry_content
11
+
12
+ after_find do
13
+ @updated = @published unless @updated if @published
14
+ end
11
15
  end
data/site/index.html ADDED
@@ -0,0 +1,382 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
2
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml">
5
+ <head>
6
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
7
+
8
+ <title>mofo - a ruby microformat parser</title>
9
+ <link href="style.css" rel="stylesheet" type="text/css" />
10
+ <script type="text/javascript" src="mootools.v1.00.js"></script>
11
+ <script type="text/javascript">function sc(el) { new Fx.Scroll(window).toElement(el); }</script>
12
+ </head>
13
+
14
+ <body>
15
+ <div id="container">
16
+ <div id="header">
17
+ <img src="mofo-logo.png" alt="mofo!" />
18
+ <br /><hr />
19
+ </div>
20
+
21
+ <div id="left">
22
+ <h3>mofo</h3>
23
+
24
+ <ul class="xoxo">
25
+ <li><a href="#" onclick="sc('get_started')">Get Started</a></li>
26
+ <li><a href="#" onclick="sc('microwhozit')">Microwhozit?</a></li>
27
+ <li><a href="#" onclick="sc('find')">Mofo#find</a></li>
28
+ <li><a href="#" onclick="sc('supported')">Supported Microformats</a></li>
29
+ <li><a href="#" onclick="sc('rails')">Ruby on Rails</a></li>
30
+ <li><a href="#" onclick="sc('touch')">Get in Touch</a></li>
31
+ </ul>
32
+
33
+ <h3>Points of Interest</h3>
34
+
35
+ <ul class="xoxo">
36
+ <li><a href="http://errtheblog.com/post/37">Me and uFormats</a></li>
37
+ <li><a href="http://microformats.org">Microformats HQ</a></li>
38
+ <li><a href="http://microformatique.com">Microformatique</a></li>
39
+ <li><a href="http://labnotes.org">Assaf Arkin</a></li>
40
+ <li><a href="http://allinthehead.com">Drew McClellan</a></li>
41
+ <li><a href="http://tantek.com">Tantek Çelik</a></li>
42
+ <li><a href="http://theryanking.com/blog">Ryan King</a></li>
43
+ </ul>
44
+
45
+ <h3>Other Parsers</h3>
46
+
47
+ <ul class="xoxo">
48
+ <li><a href="http://rubyforge.org/projects/scrapi">Scrapi</a> [ruby]</li>
49
+ <li><a href="http://rubyforge.org/projects/uformats">uFormats</a> [ruby]</li>
50
+ <li><a href="http://allinthehead.com/hkit">hKit</a> [php]</li>
51
+ <li><a href="http://www.danwebb.net/2007/2/9/sumo-a-generic-microformats-parser-for-javascript">Sumo</a> [js]</li>
52
+ <li><a href="https://addons.mozilla.org/en-US/firefox/addon/4106">Operator</a> [firefox]</li>
53
+ </ul>
54
+ </div>
55
+
56
+ <div id="main">
57
+ <h3 id="get_started">Get Started Immediately</h3>
58
+ <br/>
59
+ <pre>
60
+ $ sudo gem install mofo -y
61
+ ... install mofo and hpricot dependency ...
62
+ $ irb -rubygems
63
+ &gt;&gt; require 'mofo'
64
+ =&gt; true
65
+ &gt;&gt; fireball = HCard.find 'http://flickr.com/people/gruber/'
66
+ =&gt; #&lt;HCard:0x6db898 ...&gt;
67
+ &gt;&gt; fireball.properties
68
+ =&gt; ["fn", "logo", "url", "n", "adr", "title", "nickname"]
69
+ &gt;&gt; fireball.nickname
70
+ =&gt; "gruber"
71
+ &gt;&gt; fireball.url
72
+ =&gt; "http://daringfireball.net/"
73
+ &gt;&gt; fireball.n.family_name
74
+ =&gt; "Gruber"
75
+ &gt;&gt; fireball.title
76
+ =&gt; "Raconteur"
77
+ &gt;&gt; fireball.adr.locality
78
+ =&gt; "Philadelphia"
79
+ &gt;&gt; fireball.logo
80
+ =&gt; "http://static.flickr.com/9/buddyicons/44621776@N00.jpg?1117572751"
81
+ </pre>
82
+
83
+ <h3 id="microwhozit">Microwhozit?</h3>
84
+
85
+ <p>
86
+ Microformats are tiny little markup definitions built on top of, usually,
87
+ HTML or XHTML.
88
+ </p>
89
+ <p>
90
+ You have a blog. You have recent posts on your blog's index page. You have
91
+ an Atom feed. You have recent posts on your blog's Atom feed. See where I'm
92
+ going with this?
93
+ </p>
94
+ <p>
95
+ The hAtom microformat (or uformat) can be embedded in your existing HTML by
96
+ setting CSS classes with semantic meaning inside of your posts. A class to signify
97
+ a post is contained within this div, a class to signify the contents of this
98
+ h3 are the post's title, a class to signify the contents of this span is the
99
+ blog post's author, etc.
100
+ </p>
101
+ <p>
102
+ You can then use a microformat parser (like, say, mofo) to extract this information
103
+ as you would from an Atom feed. Hell, you can even convert hAtom to Atom. It's an
104
+ insta-feed! No extra code required!
105
+ </p>
106
+ <p>
107
+ You're already doing the work, you see. Microformats are everywhere. We just need
108
+ to set them free.
109
+ </p>
110
+ <p>
111
+ Check it:</p>
112
+
113
+ <pre>
114
+ &lt;div class="post"&gt;
115
+ &lt;h3&gt;Megadeth Show Last Night&lt;/h3&gt;
116
+ &lt;span class="subtitle"&gt;Posted by Chris on June 4th&lt;/span&gt;
117
+ &lt;div class="content"&gt;Went to a show last night. Megadeth. It was alright.&lt;/div&gt;
118
+ &lt;/div&gt;
119
+ </pre>
120
+
121
+ <p>Right? Normal. Here's the same post marked up with hAtom:</p>
122
+
123
+ <pre>
124
+ &lt;div class="post <strong>hentry</strong>"&gt;
125
+ &lt;h3 class="<strong>entry-title</strong>"&gt;Megadeth Show Last Night&lt;/h3&gt;
126
+ &lt;span class="subtitle"&gt;Posted by &lt;span class="<strong>author vcard fn</strong>"&gt;Chris&lt;/span&gt; on
127
+ &lt;abbr class="<strong>updated</strong>" title="2006-06-04T10:32:10Z"&gt;June 4th&lt;/abbr&gt;&lt;/span&gt;
128
+ &lt;div class="content <strong>entry-content</strong>"&gt;
129
+ Went to a show last night. Megadeth. It was alright.
130
+ &lt;/div&gt;
131
+ &lt;/div&gt;
132
+ </pre>
133
+
134
+ <p>
135
+ All I did was add the hentry, entry-title, and entry-content classes to existing containers. Then I
136
+ went ahead and wrapped the date in an &lt;abbr&gt; tag giving it a title in the microformat-standard way. Finally
137
+ I put a div around Chris signifying it as the author field of the hEntry and making it a valid hCard by
138
+ including the vcard and fn classes. It's really not all that hard. Did I mess it up? Maybe, but I'm sure I got
139
+ close. And I didn't even use a reference. Practice.
140
+ </p>
141
+
142
+ <p>
143
+ How'd we parse this, tho?
144
+ </p>
145
+
146
+ <pre>
147
+ $ irb -rubygems
148
+ &gt;&gt; require 'mofo'
149
+ =&gt; true
150
+ &gt;&gt; post = HEntry.find 'http://milesofstyle.org/posts/351-megadeth-show-last-night'
151
+ =&gt; #&lt;HEntry:0x6db898 ... &gt;
152
+ &gt;&gt; post.entry_title
153
+ =&gt; "Megadeth Show Last Night"
154
+ &gt;&gt; post.properties
155
+ =&gt; ["entry_content", "updated", "author", "entry_title"]
156
+ &gt;&gt; post.updated
157
+ =&gt; Sun Jun 04 10:32:10 UTC 2006
158
+ &gt;&gt; post.updated.class
159
+ =&gt; Time
160
+ &gt;&gt; post.author
161
+ =&gt; #&lt;HCard:0x6e7b98 @properties=["fn"], @fn="Chris"&gt;
162
+ &gt;&gt; post.author.fn
163
+ =&gt; "Chris"
164
+ &gt;&gt; post.entry_content
165
+ =&gt; "Went to a show last night. Megadeth. It was alright."
166
+ </pre>
167
+ <p>
168
+ That's, like, stupid easy. If HEntry.find gets back more than one hEntry, you'll get an array.
169
+ </p>
170
+
171
+ <h3 id="find">Mofo#find</h3>
172
+
173
+ <p>Everything revolves around the #find method. Sound familiar? Yeah.</p>
174
+
175
+ <pre>
176
+ &gt;&gt; Microformat.find "http://valid-url.com"
177
+ &gt;&gt; Microformat.find "/path/to/existing/file"
178
+ &gt;&gt; Microformat.find :text =&gt; "microformat text"
179
+ </pre>
180
+
181
+ <p>
182
+ Also, #find can be told explicitly to find all (returning an array on failure) or only find
183
+ the first (returning nil on failure).
184
+ </p>
185
+
186
+ <pre>
187
+ &gt;&gt; Microformat.find :all =&gt; "/existing/file"
188
+ =&gt; [ array of microformat objects ]
189
+ &gt;&gt; Microformat.find :first =&gt; "/existing/file"
190
+ =&gt; microformat object
191
+ &gt;&gt; Microformat.find "/existing/file"
192
+ =&gt; either an array of objects or just one object
193
+ </pre>
194
+
195
+ <p>When parsing a string, use :all and :first go outside of :text.</p>
196
+
197
+ <pre>
198
+ &gt;&gt; Microformat.find :all =&gt; { :text =&gt; 'mfin text' }
199
+ </pre>
200
+
201
+ <p>
202
+ That's it.
203
+ </p>
204
+
205
+ <h3 id="supported">Supported Microformats</h3>
206
+
207
+ <p><strong>hCard</strong> - <a href="http://microformats.org/wiki/hcard">http://microformats.org/wiki/hcard</a></p>
208
+ <pre>
209
+ &gt;&gt; messina = HCard.find 'http://www.flickr.com/people/factoryjoe/'
210
+ =&gt; #&lt;HCard:0x125eb5c ...&gt;
211
+ &gt;&gt; messina.properties
212
+ =&gt; ["fn", "note", "logo", "url", "n", "adr", "title", "nickname"]
213
+ &gt;&gt; messina.title
214
+ =&gt; "Citizen Provocateur, Open Source Ambassador"
215
+ &gt;&gt; messina.logo
216
+ =&gt; "http://farm1.static.flickr.com/1/buddyicons/25419820@N00.jpg?1167346106"
217
+ &gt;&gt; messina.n
218
+ =&gt; #&lt;OpenStruct given_name="Chris", family_name="Messina"&gt;
219
+ &gt;&gt; messina.fn
220
+ =&gt; "Chris Messina"
221
+ &gt;&gt; messina.url
222
+ =&gt; "http://factoryjoe.com/blog"
223
+ &gt;&gt; messina.nickname
224
+ =&gt; "factoryjoe"
225
+ </pre>
226
+
227
+ <p><strong>hCalendar</strong> - <a href="http://microformats.org/wiki/hcalendar">http://microformats.org/wiki/hcalendar</a></p>
228
+ <pre>
229
+ &gt;&gt; events = HCalendar.find 'http://upcoming.org'
230
+ =&gt; [#&lt;HCalendar:0x131d304 ...&gt; ... ]
231
+ &gt;&gt; events.size
232
+ =&gt; 17
233
+ &gt;&gt; events.first.properties
234
+ =&gt; ["summary", "url", "location"]
235
+ &gt;&gt; events.first.location
236
+ =&gt; "Neumo&amp;#39;s, Seattle, WA"
237
+ &gt;&gt; events.first.summary
238
+ =&gt; "Ratatat + 120 Days"
239
+ </pre>
240
+
241
+ <p><strong>hReview</strong> - <a href="http://microformats.org/wiki/hreview">http://microformats.org/wiki/hreview</a></p>
242
+ <pre>
243
+ &gt;&gt; wine = HReview.find 'http://corkd.com/wine/view/1772'
244
+ =&gt; [#&lt;HReview:0x156c3f8 ...&gt; ...]
245
+ &gt;&gt; wine.size
246
+ =&gt; 7
247
+ &gt;&gt; wine.first.properties
248
+ =&gt; ["rating", "description", "item", "reviewer", "tags", "dtreviewed"]
249
+ &gt;&gt; wine.first.rating
250
+ =&gt; 3
251
+ &gt;&gt; wine.first.tags
252
+ =&gt; ["fresh", "lime", "pear"]
253
+ &gt;&gt; wine.first.dtreviewed
254
+ =&gt; Fri Jun 02 00:00:00 -0700 2006
255
+ </pre>
256
+
257
+ <p><strong>hEntry</strong> - <a href="http://microformats.org/wiki/hatom">http://microformats.org/wiki/hatom</a></p>
258
+ <pre>
259
+ &gt;&gt; post = HEntry.find 'http://errtheblog.com'
260
+ =&gt; #&lt;HEntry:0x169309c ...&gt;
261
+ &gt;&gt; post.properties
262
+ =&gt; ["published", "entry_title", "author", "entry_content", "bookmark", "tags"]
263
+ &gt;&gt; post.author.class
264
+ =&gt; HCard
265
+ &gt;&gt; post.author.fn
266
+ =&gt; "Chris"
267
+ &gt;&gt; post.published
268
+ =&gt; Mon Mar 26 09:21:00 UTC 2007
269
+ &gt;&gt; post.entry_content.length
270
+ =&gt; 4737
271
+ </pre>
272
+
273
+ <p><strong>hResume</strong> - <a href="http://microformats.org/wiki/hresume">http://microformats.org/wiki/hresume</a></p>
274
+ <pre>
275
+ &gt;&gt; crunch = HResume.find 'http://www.linkedin.com/in/michaelarrington'
276
+ =&gt; #&lt;HResume:0x129d370 ...&gt;
277
+ &gt;&gt; crunch.properties
278
+ =&gt; ["summary", "education", "experience", "contact"]
279
+ &gt;&gt; crunch.experience.first.class
280
+ =&gt; HCalendar
281
+ &gt;&gt; crunch.contact
282
+ =&gt; #&lt;HCard:0x36614 ...&gt;
283
+ &gt;&gt; crunch.contact.title
284
+ =&gt; "Editor - TechCrunch"
285
+ </pre>
286
+
287
+ <p><strong>XOXO</strong> - <a href="http://microformats.org/wiki/xoxo">http://microformats.org/wiki/xoxo</a></p>
288
+ <pre>
289
+ &gt;&gt; mofo = XOXO.find 'http://mofo.rubyforge.org', :class =&gt; true
290
+ =&gt; [["Get Started", "Microwhozit?", "Mofo#find", ...]
291
+ &gt;&gt; mofo.first
292
+ =&gt; ["Get Started", "Microwhozit?", "Mofo#find", "Supported Microformats", ...]
293
+ &gt;&gt; mofo[1]
294
+ =&gt; ["Me and uFormats", "Microformats HQ", "Microformatique", "Assaf Arkin", ...]
295
+ &gt;&gt; mofo[1].first
296
+ =&gt; "Me and uFormats"
297
+ &gt;&gt; mofo[1].first.class
298
+ =&gt; XOXO::Label
299
+ &gt;&gt; mofo[1].first.url
300
+ =&gt; "http://errtheblog.com/post/37"
301
+ </pre>
302
+
303
+ <p><strong>Geo</strong> - <a href="http://microformats.org/wiki/geo">http://microformats.org/wiki/geo</a></p>
304
+ <pre>
305
+ &gt;&gt; somewhere = Geo.find 'http://www.geograph.org.uk/photo/1234'
306
+ =&gt; #&lt;Geo:0x12337a4 ...&gt;
307
+ &gt;&gt; somewhere.latitude
308
+ =&gt; 54.05836
309
+ &gt;&gt; somewhere.longitude
310
+ =&gt; -2.14662
311
+ </pre>
312
+
313
+ <p><strong>Adr</strong> - <a href="http://microformats.org/wiki/adr">http://microformats.org/wiki/adr</a></p>
314
+ <pre>
315
+ ...coming soon...
316
+ </pre>
317
+
318
+ <p><strong>XFN</strong> - <a href="http://microformats.org/wiki/xfn">http://microformats.org/wiki/xfn</a></p>
319
+ <pre>
320
+ &gt;&gt; tons = XFN.find 'http://deliciouslymeta.com/projects/xfn/test_data.html'
321
+ =&gt; #&lt;XFN:0x157f200 ...&gt;
322
+ &gt;&gt; tons.first
323
+ =&gt; #&lt;XFN::Link name="friend - contact", relation="contact", link="#contact"&gt;
324
+ &gt;&gt; tons.me_and_parent
325
+ =&gt; #&lt;XFN::Link name="me + parent", relation=["me", "parent"], link="#parent"&gt;
326
+ &gt;&gt; tons.me_and_parent.name
327
+ =&gt; "me + parent
328
+ &gt;&gt; tons.neighbor
329
+ =&gt; [#&lt;XFN::Link ...&gt; ...]
330
+ &gt;&gt; tons.neighbor.size
331
+ =&gt; 5
332
+ &gt;&gt; tons.parent_and_kin.link
333
+ =&gt; "#parent"
334
+ </pre>
335
+
336
+
337
+ <h3 id="rails">Ruby on Rails</h3>
338
+
339
+ <p>
340
+ mofo doubles as a Rails plugin. Just drop it into vendor/plugins and you are good to go, with all the
341
+ available microformat parsers loaded into your application.
342
+
343
+ mofo classes are YAML and Marshal approved, meaning you can cache them with memcached (or DRb) or store
344
+ them in a session.
345
+ </p>
346
+
347
+ <p>
348
+ Install with <a href="http://www.rubyinside.com/advent2006/12-piston.html">Piston</a>:
349
+ </p>
350
+
351
+ <pre>
352
+ $ piston import svn://errtheblog.com/svn/mofo/trunk vendor/plugins/mofo
353
+ </pre>
354
+
355
+ <p>
356
+ Install with SVN:
357
+ </p>
358
+
359
+ <pre>
360
+ $ ./script/plugin install -x svn://errtheblog.com/svn/mofo/trunk
361
+ </pre>
362
+
363
+ <h3 id="touch">Get in Touch</h3>
364
+
365
+ <ul>
366
+ <li>Me: chris[at]ozmm[dot]org (chris wanstrath)</li>
367
+ <li>Trac: <a href="http://require.errtheblog.com/mofo/browser">http://require.errtheblog.com/mofo/browser</a></li>
368
+ <li>SVN: svn://errtheblog.com/svn/mofo/trunk</li>
369
+ </ul>
370
+ </div>
371
+
372
+ <div id="footer">
373
+ <hr />
374
+ <p class="left">| <a href="http://jigsaw.w3.org/css-validator/">CSS</a> | <a href="http://validator.w3.org/check?uri=referer">XHTML 1.1</a> |</p>
375
+ <p class="right">Designed by <a href="mailto:support@syndicateme.net">syndicateme.net</a>. Hosted by <a href="http://rubyforge.org">Rubyforge</a>. Birthed by <a href="http://errtheblog.com">Err</a>.</p>
376
+ <p>&nbsp;</p>
377
+ </div>
378
+ </div>
379
+ <script src="http://static.getclicky.com/4581.js" type="text/javascript"></script>
380
+ <noscript><p><img alt="Clicky" src="http://static.getclicky.com/4581ns.gif" /></p></noscript>
381
+ </body>
382
+ </html>