microformats 0.1 → 0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -1,4 +1,6 @@
1
- = MICROFORMATS
1
+ = Microformats
2
+
3
+ <b>STILL IN DEVELOPMENT, IT SHOULD WORK, BUT USE AT YOUR OWN RISK!</b>
2
4
 
3
5
  <em>Created by Chris Powers, September 11, 2010</em>
4
6
 
@@ -11,7 +13,7 @@ the new HTML5 Microdata standard.
11
13
 
12
14
  By using microformats, you are opening your data up to Google
13
15
  and other consumers for easy and intelligent consumption. In
14
- the future, Google plans on consuming microdata and making it
16
+ the future, Google plans on making consumed microdata
15
17
  directly searchable, which yields all sorts of new potential
16
18
  for relevant results.
17
19
 
@@ -21,27 +23,33 @@ Install the Microformats gem as usual:
21
23
 
22
24
  gem install microformats
23
25
 
24
- == Usage: Vcards
26
+ == Getting Started
27
+
28
+ To use Microformats, first include the Microformats::Helper
29
+ mixin into your view layer, like this in Rails:
30
+
31
+ # in app/helpers/application_helper.rb
32
+ module Application Helper
33
+ include Microformats::Helpers
34
+ end
35
+
36
+ == Usage: vCards and Addresses
25
37
 
26
38
  You can easily markup a person and/or organization using the
27
- 'vcard' helper method. This will use both the hCard Microformat
28
- and the http://www.data-vocabulary.org/Person/ microdata.
39
+ <tt>vcard</tt> helper method. This will use both the hCard Microformat
40
+ and the http://www.data-vocabulary.org/Person microdata.
29
41
 
30
- <strong>PLEASE NOTE:</strong> These two microdata standards do
42
+ <b>PLEASE NOTE:</b> These two microdata standards do
31
43
  not support the same fields. For example, hCard gives a person
32
44
  telephone numbers and email addresses. The Person microdata only
33
- gives organizations a single telephone number and has no knowledge
34
- of emails. It does, however, have a photo field, which is missing
35
- from hCard.
45
+ gives organizations a single telephone number and has no support
46
+ for email.
36
47
 
37
- To use it, first include the Microformats::Helper mixin into your view
38
- layer, like this in Rails:
48
+ vCards can embed addresses using the Microformats::Vcard#address
49
+ method, which gives you a block to run with a new
50
+ Microformats::Address object.
39
51
 
40
- module Application Helper
41
- include Microformats::Helpers
42
- end
43
-
44
- Then, you can do something like this in your view (using ERB here):
52
+ EXAMPLE (using ERB):
45
53
 
46
54
  <% vcard do |card| %>
47
55
  <%= card.photo "/images/me.jpg", :size => '200x300' %>
@@ -52,7 +60,7 @@ Then, you can do something like this in your view (using ERB here):
52
60
  <%= card.email "me@mydomain.com", :type => 'Home' %>
53
61
 
54
62
  I work at <%= card.company "Acme Co." %>
55
- <% vcard_address(:type => 'Work') do |adr| %>
63
+ <% card.address(:type => 'Work') do |adr| %>
56
64
  <%= adr.street "123 Main" %>
57
65
  <%= adr.city "Chicago" %>, <%= adr.state 'IL' %> <%= adr.zip '60010' %>
58
66
  <% end %>
@@ -62,16 +70,16 @@ Then, you can do something like this in your view (using ERB here):
62
70
  This will output the following markup:
63
71
 
64
72
  <div class='vcard' itemscope='itemscope' itemtype='http://data-vocabulary.org/Person'>
65
- <img itemprop='photo' src='/images/me.jpg' width='200' height='300' />
73
+ <img class='photo' itemprop='photo' src='/images/me.jpg' width='200' height='300' />
66
74
  <span class='fn' itemprop='name'>John Doe</span>
67
75
  <a class='url' href='http://mydomain.com' itemprop='url'>Visit my Site</a>
68
- <span class='tel'><span class='type'>Home</span> 999.888.7766</span>
69
- <span class='tel'><span class='type'>Work</span> 111.222.3344</span>
70
- <span class='email'><span class='type'>Home</span> me@mydomain.com</span>
76
+ <span class='tel'><span class='type'><span class='value-title' title='Home'></span></span>999.888.7766</span>
77
+ <span class='tel'><span class='type'><span class='value-title' title='Work'></span></span> 111.222.3344</span>
78
+ <a class='email' href='mailto:me@mydomain.com'><span class='value-title' title='Home'></span>me@mydomain.com</span>
71
79
 
72
80
  I work at <span class='org' itemprop='affiliation'>Acme Co.</span>
73
81
  <div class='adr' itemscope='itemscope' itemtype='http://data-vocabulary.org/Address'>
74
- <span class='type'>Work</span>
82
+ <span class='type'><span class='value-title' title='Work'></span></span>
75
83
  <span class='street-address' itemprop='street-address'>123 Main</span>
76
84
  <span class='locality' itemprop='locality'>Chicago</span>, <span class='region' itemprop='region'>IL</span> <span class='postal-code' itemprop='postal-code'>60010</span>
77
85
  </div>
@@ -81,7 +89,7 @@ This will output the following markup:
81
89
  While these helper methods default to using <span> tags
82
90
  (and <a> tags as appropriate), you can easily customize
83
91
  the tag used for any given piece of microdata by using the
84
- <tt>:tag</tt> options:
92
+ <tt>:tag</tt> option:
85
93
 
86
94
  <%= card.name "John Doe", :tag => :h1 %>
87
95
 
@@ -93,3 +101,47 @@ currently on, so this is a quick way to do this in Rails:
93
101
 
94
102
  <%= card.download_link request.request_uri %>
95
103
 
104
+ == Usage: Calendars and Events
105
+
106
+ Calendars with many events can be represented using the
107
+ Microformats::Calendar and Microformats::Event classes.
108
+ This employs the hCal and hEvent microformats along with the
109
+ http://www.data-vocabulary.org/Event microdata.
110
+
111
+ <b>NOTE:</b> An Event can use a nested vCard to represent
112
+ its location information.
113
+
114
+ EXAMPLE:
115
+
116
+ <% vcalendar :id => "my_calendar" do |cal| %>
117
+ <h1>Upcoming Events</h1>
118
+ <% cal.event do |event| %>
119
+ <h2><%= event.url(event.name("Happy Hour"), :href => "http://meetup.com/happyhour") %></h2>
120
+ <%= event.photo "/images/happy_hour.jpg", :size => "250x150" %>
121
+ <%= event.description "Come hang out with us for half price drinks at lots of fun!" %>
122
+ <span class='time_range'>
123
+ From <%= event.starts_at "October 30, 2010 at 7:30PM" %> -
124
+ <%= event.ends_at "October 30, 2010 at 10:00PM", :text => "10:00PM" %>
125
+ </span>
126
+ <% event.location do |card| %>
127
+ <%= card.url(card.company("Frank's Bar", :is_company => true), :href => "http://franksbar.com") %>
128
+ <% card.address do |adr| %>
129
+ <%= adr.street "784 N Main St" %><br />
130
+ <%= adr.city "Chicago" %>, <%= adr.state "IL" %> <%= adr.zip "60016" %>
131
+ <% end %>
132
+ <% end %>
133
+ <% end %>
134
+ <!-- More events could be added... -->
135
+ <% end %>
136
+
137
+ == Resources
138
+
139
+ * http://www.data-vocabulary.org
140
+ * http://microformats.org
141
+
142
+ == Care to Help?
143
+
144
+ There are still a lot of standards that need to be implemented into
145
+ this library, including but not limited to: Events, Products, Reviews,
146
+ Organizations. I will continue to work on these, but I'd be happy to
147
+ accept pull requests!
data/Rakefile CHANGED
@@ -1,6 +1,6 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
- # require 'rake/rdoctask'
3
+ require 'rake/rdoctask'
4
4
  # require 'rake/gempackagetask'
5
5
  # require 'rcov/rcovtask'
6
6
  # require 'date'
@@ -12,4 +12,16 @@ task :default => :spec
12
12
 
13
13
  Spec::Rake::SpecTask.new do |t|
14
14
  t.warning = true
15
- end
15
+ end
16
+
17
+ namespace :doc do
18
+ desc "Generate documentation for the gem."
19
+ Rake::RDocTask.new("gem") { |rdoc|
20
+ rdoc.rdoc_dir = 'doc'
21
+ rdoc.title = "Microformats"
22
+ rdoc.options << '--line-numbers' << '--inline-source'
23
+ rdoc.options << '--charset' << 'utf-8'
24
+ rdoc.rdoc_files.include('README.rdoc')
25
+ rdoc.rdoc_files.include('lib/**/*.rb')
26
+ }
27
+ end
data/lib/address.rb CHANGED
@@ -1,32 +1,97 @@
1
1
  class Microformats::Address
2
+ include Microformats::FormattingHelpers
3
+
4
+ def initialize(template)
5
+ @template = template
6
+ @default_tag = :span
7
+ end
8
+
9
+ # You can directly initialize and run this class, but it's easier
10
+ # to use the Microformats::Helpers#vaddress helper method or the
11
+ # Microformats::Vcard#address method.
12
+ #
13
+ # OPTIONS:
14
+ # * :type - A string that specifies the type of address('home', 'work', etc)
15
+ # * :tag - The HTML wrapper element (defaults to :div)
16
+ # * Any other passed options will be treated as HTML attributes.
17
+ #
18
+ def run(opts = {}, &block)
19
+ type = opts[:type] ? self.type(opts.delete(:type)) : nil
20
+ opts[:class] = combine_class_names('adr', opts[:class])
21
+ opts[:itemscope] = 'itemscope'
22
+ opts[:itemtype] = 'http://data-vocabulary.org/Address'
23
+ opts[:tag] ||= :div
24
+ concat_tag(opts) do
25
+ concat type if type
26
+ block.call(self)
27
+ end
28
+ end
29
+
30
+ # Outputs markup for the type of address ('work', 'home', etc.)
31
+ # There will be no visible text unless provided with the :text option.
32
+ #
33
+ # <em>NOTE: You get this for free with the :type option of
34
+ # Microformats::Helpers#vaddress, Microformats::Vcard#address and #run methods.</em>
35
+ #
36
+ # OPTIONS
37
+ # * :text - String, the text you want displayed as a text node (default is '')
38
+ # * Any other passed options will be treated as HTML attributes.
39
+ #
40
+ def type(str, opts = {})
41
+ inner = content_tag('', :class => 'value-title', :title => str)
42
+ text = opts.delete(:text) || ''
43
+ content_tag(inner + text, merge_html_attrs({:class => 'type'}, opts))
44
+ end
45
+
46
+ # Outputs the passed string as a street address.
47
+ #
48
+ # OPTIONS
49
+ # * :tag - The HTML wrapper element (defaults to :span)
50
+ # * Any other passed options will be treated as HTML attributes.
51
+ #
2
52
  def street(str, opts = {})
3
- content_tag(opts[:tag] || :span, str, :class => 'street-address', :itemprop => 'street-address')
53
+ content_tag(str, merge_html_attrs({:class => 'street-address', :itemprop => 'street-address'}, opts))
4
54
  end
5
55
 
56
+ # Outputs the passed string as a city.
57
+ #
58
+ # OPTIONS
59
+ # * :tag - The HTML wrapper element (defaults to :span)
60
+ # * Any other passed options will be treated as HTML attributes.
61
+ #
6
62
  def city(str, opts = {})
7
- content_tag(opts[:tag] || :span, str, :class => 'locality', :itemprop => 'locality')
63
+ content_tag(str, merge_html_attrs({:class => 'locality', :itemprop => 'locality'}, opts))
8
64
  end
9
65
 
66
+ # Outputs the passed string as a state.
67
+ #
68
+ # OPTIONS
69
+ # * :tag - The HTML wrapper element (defaults to :span)
70
+ # * Any other passed options will be treated as HTML attributes.
71
+ #
10
72
  def state(str, opts = {})
11
- content_tag(opts[:tag] || :span, str, :class => 'region', :itemprop => 'region')
73
+ content_tag(str, merge_html_attrs({:class => 'region', :itemprop => 'region'}, opts))
12
74
  end
13
75
 
76
+ # Outputs the passed string as a postal code.
77
+ #
78
+ # OPTIONS
79
+ # * :tag - The HTML wrapper element (defaults to :span)
80
+ # * Any other passed options will be treated as HTML attributes.
81
+ #
14
82
  def zip(str, opts = {})
15
- content_tag(opts[:tag] || :span, str, :class => 'postal-code', :itemprop => 'postal-code')
83
+ content_tag(str, merge_html_attrs({:class => 'postal-code', :itemprop => 'postal-code'}, opts))
16
84
  end
17
85
  alias_method :postal_code, :zip
18
86
 
87
+ # Outputs the passed string as a country.
88
+ #
89
+ # OPTIONS
90
+ # * :tag - The HTML wrapper element (defaults to :span)
91
+ # * Any other passed options will be treated as HTML attributes.
92
+ #
19
93
  def country(str, opts = {})
20
- content_tag(opts[:tag] || :span, str, :class => 'country-name', :itemprop => 'country-name')
94
+ content_tag(str, merge_html_attrs({:class => 'country-name', :itemprop => 'country-name'}, opts))
21
95
  end
22
96
 
23
- def content_tag(tag, content, opts={})
24
- attrs = opts.inject([]) do |out, tuple|
25
- k,v = tuple
26
- out << "#{k}='#{v}'"
27
- end
28
- attr_string = attrs.sort.join(' ')
29
- open_tag = attr_string == '' ? tag : "#{tag} #{attr_string}"
30
- "<#{open_tag}>#{content}</#{tag}>"
31
- end
32
97
  end
data/lib/calendar.rb ADDED
@@ -0,0 +1,40 @@
1
+ class Microformats::Calendar
2
+ include Microformats::FormattingHelpers
3
+
4
+ def initialize(template)
5
+ @template = template
6
+ @default_tag = :span
7
+ end
8
+
9
+ # You can directly initialize and run this class, but it's easier
10
+ # to use the Microformats::Helpers#vcalendar helper method.
11
+ #
12
+ # OPTIONS:
13
+ # * :tag - The HTML wrapper element (defaults to :div)
14
+ # * Any other passed options will be treated as HTML attributes.
15
+ #
16
+ def run(opts = {}, &block)
17
+ opts[:class] = combine_class_names('vcalendar', opts[:class])
18
+ opts[:tag] ||= :div
19
+ concat_tag(opts) do
20
+ block.call(self)
21
+ end
22
+ end
23
+
24
+ # Creates a vEvent with the given options and a block.
25
+ #
26
+ # OPTIONS:
27
+ # * :tag - The HTML wrapper element (defaults to :div)
28
+ # * Any other passed options will be treated as HTML attributes.
29
+ #
30
+ # EXAMPLE:
31
+ # <% calendar.event :id => 'my_event' do |event| %>
32
+ # This event is called <%= event.name "Cool Event" %>.
33
+ # <% end %>
34
+ #
35
+ def event(opts = {}, &block)
36
+ ev = Microformats::Event.new(@template)
37
+ opts[:class] = combine_class_names('vevent', opts[:class])
38
+ ev.run(opts, &block)
39
+ end
40
+ end
data/lib/event.rb ADDED
@@ -0,0 +1,156 @@
1
+ class Microformats::Event
2
+ include Microformats::FormattingHelpers
3
+
4
+ def initialize(template)
5
+ @template = template
6
+ @default_tag = :span
7
+ end
8
+
9
+ # You can directly initialize and run this class, but it's easier
10
+ # to use the Microformats::Helpers#vevent helper method or the
11
+ # Microformats::Calendar#event method.
12
+ #
13
+ # OPTIONS:
14
+ # * :tag - The HTML wrapper element (defaults to :div)
15
+ # * Any other passed options will be treated as HTML attributes.
16
+ #
17
+ def run(opts = {}, &block)
18
+ opts[:class] = combine_class_names('vevent', opts[:class])
19
+ opts[:itemscope] = 'itemscope'
20
+ opts[:itemtype] = 'http://data-vocabulary.org/Event'
21
+ opts[:tag] ||= :div
22
+ concat_tag(opts) do
23
+ block.call(self)
24
+ end
25
+ end
26
+
27
+ # Marks up an event's name.
28
+ #
29
+ # OPTIONS:
30
+ # * :tag - The HTML wrapper element (defaults to :span)
31
+ # * Any other passed options will be treated as HTML attributes.
32
+ #
33
+ def name(str, opts={})
34
+ content_tag(str, merge_html_attrs({:itemprop => 'summary', :class => 'summary'}, opts))
35
+ end
36
+
37
+ # Marks up the event's URL. By default, it will output an <a> tag using
38
+ # the passed in string as both the href and the text. If the :href option
39
+ # is passed, then the string argument is treated as text.
40
+ #
41
+ # OPTIONS:
42
+ # * :href - If passed, the string argument will be treated as the text node.
43
+ # * :tag - The HTML wrapper element (defaults to :span)
44
+ # * Any other passed options will be treated as HTML attributes.
45
+ #
46
+ # EXAMPLES:
47
+ # event.url('http://google.com') #=> <a class='url' href='http://google.com' itemprop='url'>http://google.com</a>
48
+ # event.url('Google', :href => 'http://google.com') #=> <a class='url' href='http://google.com' itemprop='url'>Google</a>
49
+ # event.url('http://google.com', :tag => :span) #=> <span class='url' itemprop='url'>http://google.com</span>
50
+ #
51
+ def url(str, opts = {})
52
+ if opts[:href]
53
+ content_tag(str, merge_html_attrs({:tag => :a, :class => 'url', :itemprop => 'url'}, opts))
54
+ elsif opts[:tag]
55
+ content_tag(str, merge_html_attrs({:class => 'url', :itemprop => 'url'}, opts))
56
+ else
57
+ content_tag(str, merge_html_attrs({:tag => :a, :class => 'url', :href => str, :itemprop => 'url'}, opts))
58
+ end
59
+ end
60
+
61
+ # Marks up the event photo as an <img> tag. Takes the image URL as the first argument.
62
+ #
63
+ # OPTIONS
64
+ # * :size - Pass a string with WIDTHxHEIGHT like "200x100" in lieu of the :width and :height options.
65
+ # * Any other passed options will be treated as HTML attributes.
66
+ #
67
+ def photo(str, opts = {})
68
+ if size = opts.delete(:size)
69
+ opts[:width], opts[:height] = size.split('x')
70
+ end
71
+ content_tag(nil, merge_html_attrs({:tag => :img, :class => 'photo', :itemprop => 'photo', :src => str}, opts))
72
+ end
73
+
74
+ # Marks up an event's description.
75
+ #
76
+ # OPTIONS:
77
+ # * :tag - The HTML wrapper element (defaults to :span)
78
+ # * Any other passed options will be treated as HTML attributes.
79
+ #
80
+ def description(str, opts={})
81
+ content_tag(str, merge_html_attrs({:itemprop => 'description', :class => 'description'}, opts))
82
+ end
83
+
84
+ # Marks up the event's start time/date. Accepts either a Time object
85
+ # or a time String as the first argument. By default, the text node
86
+ # will be the date and time output like "Oct 20, 2010 at 7:30PM", but
87
+ # this can be overridden by the :text option.
88
+ #
89
+ # OPTIONS:
90
+ # * :text - String, will be displayed as the text node.
91
+ # * Any other passed options will be treated as HTML attributes.
92
+ #
93
+ def starts_at(time_or_str, opts={})
94
+ if time_or_str.is_a?(String)
95
+ time = Time.parse(time_or_str)
96
+ encoded_time = encode_time(time)
97
+ humanized_time = opts.delete(:text) || time_or_str
98
+ else
99
+ encoded_time = encode_time(time_or_str)
100
+ humanized_time = opts.delete(:text) || humanize_time(time_or_str)
101
+ end
102
+ inner_span = content_tag('', :class => 'value-title', :title => encoded_time)
103
+ content_tag(inner_span + humanized_time, merge_html_attrs({:tag => :time, :itemprop => 'startDate', :class => 'dtstart', :datetime => encoded_time}, opts))
104
+ end
105
+
106
+ # Marks up the event's end time/date. Accepts either a Time object
107
+ # or a time String as the first argument. By default, the text node
108
+ # will be the date and time output like "Oct 20, 2010 at 7:30PM", but
109
+ # this can be overridden by the :text option.
110
+ #
111
+ # OPTIONS:
112
+ # * :text - String, will be displayed as the text node.
113
+ # * Any other passed options will be treated as HTML attributes.
114
+ #
115
+ def ends_at(time_or_str, opts={})
116
+ if time_or_str.is_a?(String)
117
+ time = Time.parse(time_or_str)
118
+ encoded_time = encode_time(time)
119
+ humanized_time = opts.delete(:text) || time_or_str
120
+ else
121
+ encoded_time = encode_time(time_or_str)
122
+ humanized_time = opts.delete(:text) || humanize_time(time_or_str)
123
+ end
124
+ inner_span = content_tag('', :class => 'value-title', :title => encoded_time)
125
+ content_tag(inner_span + humanized_time, merge_html_attrs({:tag => :time, :itemprop => 'endDate', :class => 'dtend', :datetime => encoded_time}, opts))
126
+ end
127
+
128
+ # Marks up an event's category name.
129
+ #
130
+ # OPTIONS:
131
+ # * :tag - The HTML wrapper element (defaults to :span)
132
+ # * Any other passed options will be treated as HTML attributes.
133
+ #
134
+ def category(str, opts = {})
135
+ content_tag(str, merge_html_attrs({:itemprop => 'eventType', :class => 'category'}, opts))
136
+ end
137
+
138
+ # Creates a vCard with the given options and a block to represent
139
+ # the event's location.
140
+ #
141
+ # OPTIONS:
142
+ # * :tag - The HTML wrapper element (defaults to :div)
143
+ # * Any other passed options will be treated as HTML attributes.
144
+ #
145
+ # EXAMPLE:
146
+ # <% event.location :id => 'my_location' do |card| %>
147
+ # We will be meeting at the <%= card.company "Obtiva" %> office.
148
+ # <% end %>
149
+ #
150
+ def location(opts = {}, &block)
151
+ card = Microformats::Vcard.new(@template)
152
+ opts[:class] = combine_class_names('location', opts[:class])
153
+ card.run(opts, &block)
154
+ end
155
+
156
+ end
@@ -0,0 +1,57 @@
1
+ # These are all internal methods used for formatting, no need
2
+ # to use any of them explicitly.
3
+ #
4
+ module Microformats::FormattingHelpers # :nodoc:
5
+ def content_tag(content, opts={}) # :nodoc:
6
+ tag = opts.delete(:tag) || @default_tag
7
+ attrs = opts.inject([]) do |out, tuple|
8
+ k,v = tuple
9
+ out << "#{k}='#{v}'" if v
10
+ out
11
+ end
12
+ attr_string = attrs.sort.join(' ')
13
+ open_tag = attr_string == '' ? tag : "#{tag} #{attr_string}"
14
+ if [:img].include?(tag)
15
+ "<#{open_tag} />"
16
+ else
17
+ "<#{open_tag}>#{content}</#{tag}>"
18
+ end
19
+ end
20
+
21
+ def concat_tag(opts={}) # :nodoc:
22
+ tag = opts.delete(:tag) || @default_tag
23
+ attrs = opts.inject([]) do |out, tuple|
24
+ k,v = tuple
25
+ out << "#{k}='#{v}'"
26
+ end
27
+ attr_string = attrs.sort.join(' ')
28
+ open_tag = attr_string == '' ? tag : "#{tag} #{attr_string}"
29
+ concat "<#{open_tag}>\n"
30
+ yield
31
+ concat "</#{tag}>\n"
32
+ end
33
+
34
+ def merge_html_attrs(base_attrs, overriding_attrs) # :nodoc:
35
+ classes = combine_class_names(base_attrs.delete(:class), overriding_attrs.delete(:class))
36
+ attrs = base_attrs.merge(overriding_attrs)
37
+ attrs[:class] = classes unless classes == ''
38
+ attrs
39
+ end
40
+
41
+ def concat(str) # :nodoc:
42
+ @template.concat(str)
43
+ end
44
+
45
+ def encode_time(t) # :nodoc:
46
+ t.strftime("%Y-%m-%dT%H:%M%z").gsub(/00$/, ":00")
47
+ end
48
+
49
+ def humanize_time(t) # :nodoc:
50
+ t.strftime("%b %d, %Y at %I:%M%p").gsub(/\s0/, ' ')
51
+ end
52
+
53
+ def combine_class_names(*classes) # :nodoc:
54
+ str = classes.flatten.compact.sort.join(' ').gsub(/\s+/, ' ')
55
+ (str =~ /\w/) ? str : nil
56
+ end
57
+ end
data/lib/helpers.rb CHANGED
@@ -1,21 +1,74 @@
1
+ # Include this file into your view layer. For example, in Rails:
2
+ #
3
+ # module ApplicationHelper
4
+ # include Microformats::Helpers
5
+ # end
6
+ #
1
7
  module Microformats::Helpers
2
- # FIXME: Figure out how to make this non-Rails specific
3
- # i.e. not rely on using concat
4
- def vcard(&block)
5
- concat "<div class='vcard' itemscope='itemscope' itemtype='http://data-vocabulary.org/Person'>\n"
6
- block.call(Microformats::Vcard.new)
7
- concat "</div>\n"
8
+ # Creates a vCard with the given options and a block.
9
+ #
10
+ # OPTIONS:
11
+ # * :tag - The HTML wrapper element (defaults to :div)
12
+ # * Any other passed options will be treated as HTML attributes.
13
+ #
14
+ # EXAMPLE:
15
+ # <% vcard :id => 'my_vcard' do |card| %>
16
+ # Hello, my name is <%= card.name "Chris" %>!
17
+ # <% end %>
18
+ #
19
+ def vcard(opts = {}, &block)
20
+ card = Microformats::Vcard.new(self)
21
+ card.run(opts, &block)
8
22
  end
9
23
 
10
- # FIXME: Figure out how to make this non-Rails specific
11
- # i.e. not rely on using concat
12
- def vcard_address(opts = {}, &block)
13
- address = Microformats::Address.new
14
- type = opts[:type] ? address.content_tag(:span, opts[:type], :class => 'type') : nil
15
- concat "<div class='adr' itemscope='itemscope' itemtype='http://data-vocabulary.org/Address'>\n"
16
- concat type if type
17
- block.call(address)
18
- concat "</div>\n"
24
+ # Creates a vAddress with the given options and a block.
25
+ #
26
+ # OPTIONS:
27
+ # * :type - A string that specifies the type of address('home', 'work', etc)
28
+ # * :tag - The HTML wrapper element (defaults to :div)
29
+ # * Any other passed options will be treated as HTML attributes.
30
+ #
31
+ # EXAMPLE:
32
+ # <% vaddress :type => 'work', :id => 'my_adr' do |adr| %>
33
+ # I live at <%= adr.street "123 Main St" %>.
34
+ # <% end %>
35
+ #
36
+ def vaddress(opts = {}, &block)
37
+ address = Microformats::Address.new(self)
38
+ address.run(opts, &block)
19
39
  end
20
40
 
41
+ # Creates a vEvent with the given options and a block.
42
+ #
43
+ # OPTIONS:
44
+ # * :tag - The HTML wrapper element (defaults to :div)
45
+ # * Any other passed options will be treated as HTML attributes.
46
+ #
47
+ # EXAMPLE:
48
+ # <% vevent :id => 'my_event' do |event| %>
49
+ # This event is called <%= event.name "Cool Event" %>.
50
+ # <% end %>
51
+ #
52
+ def vevent(opts = {}, &block)
53
+ event = Microformats::Event.new(self)
54
+ event.run(opts, &block)
55
+ end
56
+
57
+ # Creates a vCalendar with the given options and a block.
58
+ #
59
+ # OPTIONS:
60
+ # * :tag - The HTML wrapper element (defaults to :div)
61
+ # * Any other passed options will be treated as HTML attributes.
62
+ #
63
+ # EXAMPLE:
64
+ # <% vcalendar :id => 'my_cal' do |cal| %>
65
+ # <% cal.event :id => 'my_event' do |event| %>
66
+ # This event is called <%= event.name "Cool Event" %>.
67
+ # <% end %>
68
+ # <% end %>
69
+ #
70
+ def vcalendar(opts = {}, &block)
71
+ cal = Microformats::Calendar.new(self)
72
+ cal.run(opts, &block)
73
+ end
21
74
  end
data/lib/microformats.rb CHANGED
@@ -2,6 +2,11 @@ module Microformats
2
2
 
3
3
  end
4
4
 
5
+ require 'time'
6
+
7
+ require 'formatting_helpers'
5
8
  require 'vcard'
6
9
  require 'address'
10
+ require 'calendar'
11
+ require 'event'
7
12
  require 'helpers'
data/lib/vcard.rb CHANGED
@@ -1,70 +1,177 @@
1
1
  class Microformats::Vcard
2
- def initialize
2
+ include Microformats::FormattingHelpers
3
+
4
+ # You can directly initialize and runthis class, but it's easier
5
+ # to use the Microformats::Helpers#vcard helper method.
6
+ def initialize(template)
7
+ @template = template
3
8
  @default_tag = :span
4
9
  end
5
10
 
11
+ # You can directly initialize and runthis class, but it's easier
12
+ # to use the Microformats::Helpers#vcard helper method.
13
+ #
14
+ # OPTIONS:
15
+ # * :tag - The HTML wrapper element (defaults to :div)
16
+ # * Any other passed options will be treated as HTML attributes.
17
+ #
18
+ def run(opts = {}, &block)
19
+ opts[:class] = combine_class_names('vcard', opts[:class])
20
+ opts[:itemscope] = 'itemscope'
21
+ opts[:itemtype] = 'http://data-vocabulary.org/Person'
22
+ opts[:tag] ||= :div
23
+ concat_tag(opts) do
24
+ block.call(self)
25
+ end
26
+ end
27
+
28
+ # Marks up a person's name.
29
+ #
30
+ # OPTIONS:
31
+ # * :tag - The HTML wrapper element (defaults to :span)
32
+ # * Any other passed options will be treated as HTML attributes.
33
+ #
6
34
  def name(str, opts = {})
7
- content_tag(opts[:tag] || :span, str, :class => 'fn', :itemprop => 'name')
35
+ content_tag(str, merge_html_attrs({:class => 'fn', :itemprop => 'name'}, opts))
8
36
  end
9
37
 
38
+ # Marks up a company name. If this vCard represents a company
39
+ # rather than an individual person that works at a company, set
40
+ # the :is_company option to true.
41
+ #
42
+ # OPTIONS:
43
+ # * :is_company - Boolean, true if this is a company vCard (defaults to false)
44
+ # * :tag - The HTML wrapper element (defaults to :span)
45
+ # * Any other passed options will be treated as HTML attributes.
46
+ #
10
47
  def company(str, opts = {})
11
- content_tag(opts[:tag] || :span, str, :class => 'org', :itemprop => 'affiliation')
48
+ classes = opts.delete(:is_company) ? 'fn org' : 'org'
49
+ content_tag(str, merge_html_attrs({:class => classes, :itemprop => 'affiliation'}, opts))
12
50
  end
13
51
  alias_method :organization, :company
14
52
 
53
+ # Marks up the person's URL. By default, it will output an <a> tag using
54
+ # the passed in string as both the href and the text. If the :href option
55
+ # is passed, then the string argument is treated as text.
56
+ #
57
+ # OPTIONS:
58
+ # * :href - If passed, the string argument will be treated as the text node.
59
+ # * :tag - The HTML wrapper element (defaults to :span)
60
+ # * Any other passed options will be treated as HTML attributes.
61
+ #
62
+ # EXAMPLES:
63
+ # card.url('http://google.com') #=> <a class='url' href='http://google.com' itemprop='url'>http://google.com</a>
64
+ # card.url('Google', :href => 'http://google.com') #=> <a class='url' href='http://google.com' itemprop='url'>Google</a>
65
+ # card.url('http://google.com', :tag => :span) #=> <span class='url' itemprop='url'>http://google.com</span>
66
+ #
15
67
  def url(str, opts = {})
16
68
  if opts[:href]
17
- content_tag(:a, str, :href => opts[:href], :class => 'url', :itemprop => 'url')
69
+ content_tag(str, merge_html_attrs({:tag => :a, :class => 'url', :itemprop => 'url'}, opts))
18
70
  elsif opts[:tag]
19
- content_tag(opts[:tag], str, :class => 'url', :itemprop => 'url')
71
+ content_tag(str, merge_html_attrs({:class => 'url', :itemprop => 'url'}, opts))
20
72
  else
21
- content_tag(:a, str, :class => 'url', :href => str, :itemprop => 'url')
73
+ content_tag(str, merge_html_attrs({:tag => :a, :class => 'url', :href => str, :itemprop => 'url'}, opts))
22
74
  end
23
75
  end
24
76
 
77
+ # Marks up the vCard photo as an <img> tag. Takes the image URL as the first argument.
78
+ #
79
+ # OPTIONS
80
+ # * :size - Pass a string with WIDTHxHEIGHT like "200x100" in lieu of the :width and :height options.
81
+ # * Any other passed options will be treated as HTML attributes.
82
+ #
25
83
  def photo(str, opts = {})
26
- image_tag(str, opts.merge(:itemprop => 'photo'))
84
+ if size = opts.delete(:size)
85
+ opts[:width], opts[:height] = size.split('x')
86
+ end
87
+ content_tag(nil, merge_html_attrs({:tag => :img, :class => 'photo', :itemprop => 'photo', :src => str}, opts))
27
88
  end
28
89
 
90
+ # Marks up a phone number, takes the phone number as a string.
91
+ #
92
+ # OPTIONS
93
+ # * :type - A string that specifies the type of phone number ('home', 'work', etc)
94
+ # * :tag - The HTML wrapper element (defaults to :span)
95
+ # * Any other passed options will be treated as HTML attributes.
96
+ #
29
97
  def phone(str, opts = {})
30
- type = opts[:type] ? content_tag(:span, opts[:type], :class => 'type') + ' ' : ''
31
- content_tag(opts[:tag] || :span, type + str, :class => 'tel')
98
+ type = if opts[:type].to_s != ''
99
+ type_inner_span = content_tag('', :class => 'value-title', :title => opts.delete(:type))
100
+ content_tag(type_inner_span, :class => 'type')
101
+ else
102
+ ''
103
+ end
104
+ content_tag(type + str, merge_html_attrs({:class => 'tel'}, opts))
32
105
  end
33
106
 
107
+ # Marks up an email address, takes the email as a string.
108
+ #
109
+ # OPTIONS
110
+ # * :type - A string that specifies the type of phone number ('home', 'work', etc)
111
+ # * :tag - The HTML wrapper element (defaults to :a)
112
+ # * Any other passed options will be treated as HTML attributes.
113
+ #
34
114
  def email(str, opts = {})
35
- type = opts[:type] ? content_tag(:span, opts[:type], :class => 'type') + ' ' : ''
115
+ opts[:tag] ||= :a
116
+ type = if opts[:type].to_s != ''
117
+ type_inner_span = content_tag('', :class => 'value-title', :title => opts.delete(:type))
118
+ content_tag(type_inner_span, :class => 'type')
119
+ else
120
+ ''
121
+ end
36
122
  if opts[:tag] == :a
37
- content_tag(:a, type + str, :class => 'email', :href => "mailto:#{str}")
123
+ content_tag(type + str, merge_html_attrs({:class => 'email', :href => "mailto:#{str}"}, opts))
38
124
  else
39
- content_tag(opts[:tag] || :span, type + str, :class => 'email')
125
+ content_tag(type + str, merge_html_attrs({:class => 'email'}, opts))
40
126
  end
41
127
  end
42
128
 
129
+ # Accepts latitude and longitude as arguments. It will only output a
130
+ # visible text node if you provide the :text option.
131
+ #
132
+ # OPTIONS
133
+ # * :text - String, the text will be be displayed inside the 'geo' wrapper
134
+ #
135
+ def coordinates(lat, lng, opts = {})
136
+ lat_meta = content_tag('', :tag => :meta, :itemprop => 'latitude', :content => lat)
137
+ lng_meta = content_tag('', :tag => :meta, :itemprop => 'longitude', :content => lng)
138
+ lat_span = content_tag(content_tag('', :class => 'value-title', :title => lat), :class => 'latitude')
139
+ lng_span = content_tag(content_tag('', :class => 'value-title', :title => lng), :class => 'longitude')
140
+ text = opts[:text] || ''
141
+ content_tag(lat_meta + lng_meta + lat_span + lng_span + text, :class => 'geo', :itemprop => 'geo', :itemscope => 'itemscope', :itemtype => 'http://data-vocabulary.org/Geo')
142
+ end
143
+
144
+ # Outputs a link to h2vx.com that will let the user download the vcard
145
+ # at the passed URL.
146
+ #
147
+ # OPTIONS
148
+ # * :text - The link text (default is "Download vCard")
149
+ # * Any other passed options will be treated as HTML attributes.
150
+ #
151
+ # EXAMPLE
152
+ # <%# In Rails, request.request_uri returns the URL of this page %>
153
+ # <%= card.download_link request.request_uri %>
154
+ #
43
155
  def download_link(url, opts = {})
44
156
  str = opts.delete(:text) || "Download vCard"
45
157
  new_url = "http://h2vx.com/vcf/" + url.gsub("http://", '')
46
- content_tag(:a, str, :href => new_url, :type => 'text/directory')
47
- end
48
-
49
- def content_tag(tag, content, opts={})
50
- attrs = opts.inject([]) do |out, tuple|
51
- k,v = tuple
52
- out << "#{k}='#{v}'"
53
- end
54
- attr_string = attrs.sort.join(' ')
55
- open_tag = attr_string == '' ? tag : "#{tag} #{attr_string}"
56
- if [:img].include?(tag)
57
- "<#{open_tag} />"
58
- else
59
- "<#{open_tag}>#{content}</#{tag}>"
60
- end
158
+ content_tag(str, merge_html_attrs({:tag => :a, :href => new_url, :type => 'text/directory'}, opts))
61
159
  end
62
160
 
63
- def image_tag(src, opts={})
64
- if size = opts.delete(:size)
65
- opts[:width], opts[:height] = size.split('x')
66
- end
67
- opts[:src] = src
68
- content_tag(:img, nil, opts)
161
+ # Opens a new block for a nested vAddress.
162
+ #
163
+ # OPTIONS:
164
+ # * :type - A string that specifies the type of address('home', 'work', etc)
165
+ # * :tag - The HTML wrapper element (defaults to :div)
166
+ # * Any other passed options will be treated as HTML attributes.
167
+ #
168
+ # EXAMPLE:
169
+ # <% card.address :type => 'work', :id => 'my_adr' do |adr| %>
170
+ # I live at <%= adr.street "123 Main St" %>.
171
+ # <% end %>
172
+ #
173
+ def address(opts = {}, &block)
174
+ adr = Microformats::Address.new(@template)
175
+ adr.run(opts, &block)
69
176
  end
70
177
  end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: microformats
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 13
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 1
9
- version: "0.1"
8
+ - 3
9
+ version: "0.3"
10
10
  platform: ruby
11
11
  authors:
12
12
  - Chris Powers
@@ -31,14 +31,13 @@ files:
31
31
  - CHANGELOG.rdoc
32
32
  - LICENSE
33
33
  - Rakefile
34
- - lib/microformats.rb
35
34
  - lib/address.rb
35
+ - lib/calendar.rb
36
+ - lib/event.rb
37
+ - lib/formatting_helpers.rb
36
38
  - lib/helpers.rb
39
+ - lib/microformats.rb
37
40
  - lib/vcard.rb
38
- - spec/address_spec.rb
39
- - spec/helpers_spec.rb
40
- - spec/spec_helper.rb
41
- - spec/vcard_spec.rb
42
41
  has_rdoc: true
43
42
  homepage: http://github.com/chrisjpowers/microformats
44
43
  licenses: []
data/spec/address_spec.rb DELETED
@@ -1,57 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Microformats::Address do
4
- before(:each) do
5
- @address = Microformats::Address.new
6
- end
7
-
8
- describe "street" do
9
- it "should wrap the string with street-address" do
10
- @address.street("123 Main").should == "<span class='street-address' itemprop='street-address'>123 Main</span>"
11
- end
12
-
13
- it "should use the given tag" do
14
- @address.street("123 Main", :tag => :strong).should == "<strong class='street-address' itemprop='street-address'>123 Main</strong>"
15
- end
16
- end
17
-
18
- describe "city" do
19
- it "should wrap the string with locality" do
20
- @address.city("Chicago").should == "<span class='locality' itemprop='locality'>Chicago</span>"
21
- end
22
-
23
- it "should use the given tag" do
24
- @address.city("Chicago", :tag => :strong).should == "<strong class='locality' itemprop='locality'>Chicago</strong>"
25
- end
26
- end
27
-
28
- describe "state" do
29
- it "should wrap the string with region" do
30
- @address.state("IL").should == "<span class='region' itemprop='region'>IL</span>"
31
- end
32
-
33
- it "should use the given tag" do
34
- @address.state("IL", :tag => :strong).should == "<strong class='region' itemprop='region'>IL</strong>"
35
- end
36
- end
37
-
38
- describe "zip" do
39
- it "should wrap the string with postal-code" do
40
- @address.zip("60085").should == "<span class='postal-code' itemprop='postal-code'>60085</span>"
41
- end
42
-
43
- it "should use the given tag" do
44
- @address.zip("60085", :tag => :strong).should == "<strong class='postal-code' itemprop='postal-code'>60085</strong>"
45
- end
46
- end
47
-
48
- describe "country" do
49
- it "should wrap the string with country-name" do
50
- @address.country("USA").should == "<span class='country-name' itemprop='country-name'>USA</span>"
51
- end
52
-
53
- it "should use the given tag" do
54
- @address.country("USA", :tag => :strong).should == "<strong class='country-name' itemprop='country-name'>USA</strong>"
55
- end
56
- end
57
- end
data/spec/helpers_spec.rb DELETED
@@ -1,52 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Microformats::Helpers do
4
- class MyTester
5
- include Microformats::Helpers
6
-
7
- def concat(str)
8
- @output ||= ''
9
- @output << str
10
- end
11
-
12
- def output
13
- @output
14
- end
15
- end
16
-
17
- before(:each) do
18
- @tester = MyTester.new
19
- end
20
-
21
- describe "vcard" do
22
- it "should wrap a block in a vcard div" do
23
- @tester.should_receive(:do_something)
24
- @tester.vcard do
25
- @tester.do_something
26
- end
27
- @tester.output.should == "<div class='vcard' itemscope='itemscope' itemtype='http://data-vocabulary.org/Person'>\n</div>\n"
28
- end
29
- end
30
-
31
- describe "vcard_address" do
32
- context "with type" do
33
- it "should wrap the block in an adr div and output the type" do
34
- @tester.should_receive(:do_something)
35
- @tester.vcard_address :type => 'Work' do
36
- @tester.do_something
37
- end
38
- @tester.output.should == "<div class='adr' itemscope='itemscope' itemtype='http://data-vocabulary.org/Address'>\n<span class='type'>Work</span></div>\n"
39
- end
40
- end
41
-
42
- context "without type" do
43
- it "should wrap the block in an adr div" do
44
- @tester.should_receive(:do_something)
45
- @tester.vcard_address do
46
- @tester.do_something
47
- end
48
- @tester.output.should == "<div class='adr' itemscope='itemscope' itemtype='http://data-vocabulary.org/Address'>\n</div>\n"
49
- end
50
- end
51
- end
52
- end
data/spec/spec_helper.rb DELETED
@@ -1 +0,0 @@
1
- require File.join(File.dirname(__FILE__), '..', 'lib', 'microformats.rb')
data/spec/vcard_spec.rb DELETED
@@ -1,110 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Microformats::Vcard do
4
- before(:each) do
5
- @vcard = Microformats::Vcard.new
6
- end
7
-
8
- describe "name" do
9
- it "should wrap a string with fn class, default to span" do
10
- @vcard.name("John Doe").should == "<span class='fn' itemprop='name'>John Doe</span>"
11
- end
12
-
13
- it "should use the given tag" do
14
- @vcard.name("John Doe", :tag => :strong).should == "<strong class='fn' itemprop='name'>John Doe</strong>"
15
- end
16
- end
17
-
18
- describe "company" do
19
- it "should wrap a string with org class, default to span" do
20
- @vcard.company("Acme Co.").should == "<span class='org' itemprop='affiliation'>Acme Co.</span>"
21
- end
22
-
23
- it "should use the given tag" do
24
- @vcard.company("Acme Co.", :tag => :strong).should == "<strong class='org' itemprop='affiliation'>Acme Co.</strong>"
25
- end
26
- end
27
-
28
- describe "url" do
29
- it "should default to a tag with url class, using the URL for text and href" do
30
- @vcard.url("http://google.com").should == "<a class='url' href='http://google.com' itemprop='url'>http://google.com</a>"
31
- end
32
-
33
- it "should use given href" do
34
- @vcard.url('Google', :href => "http://google.com").should == "<a class='url' href='http://google.com' itemprop='url'>Google</a>"
35
- end
36
-
37
- it "should use given tag" do
38
- @vcard.url('http://google.com', :tag => :strong).should == "<strong class='url' itemprop='url'>http://google.com</strong>"
39
- end
40
- end
41
-
42
- describe "photo" do
43
- it "should create an image tag using the passed string as the src, adding itemprop photo" do
44
- @vcard.photo("/images/me.png").should == "<img itemprop='photo' src='/images/me.png' />"
45
- end
46
-
47
- it "should use :size option to set width and height" do
48
- @vcard.photo("/images/me.png", :size => "200x100").should == "<img height='100' itemprop='photo' src='/images/me.png' width='200' />"
49
- end
50
-
51
- it "should pass through options" do
52
- @vcard.photo("/images/me.png", :height => 100, :width => 200).should == "<img height='100' itemprop='photo' src='/images/me.png' width='200' />"
53
- end
54
-
55
- end
56
-
57
- describe "phone" do
58
- it "should wrap string with a tel class" do
59
- @vcard.phone('123.456.7890').should == "<span class='tel'>123.456.7890</span>"
60
- end
61
-
62
- it "should add a type span if given" do
63
- out = @vcard.phone('123.456.7890', :type => 'Work')
64
- out.should == "<span class='tel'><span class='type'>Work</span> 123.456.7890</span>"
65
- end
66
-
67
- it "should use the given tag" do
68
- out = @vcard.phone('123.456.7890', :type => 'Work', :tag => :strong)
69
- out.should == "<strong class='tel'><span class='type'>Work</span> 123.456.7890</strong>"
70
- end
71
- end
72
-
73
- describe "email" do
74
- it "should wrap string with a email class" do
75
- @vcard.email('john@doe.com').should == "<span class='email'>john@doe.com</span>"
76
- end
77
-
78
- it "should add a type span if given" do
79
- out = @vcard.email('john@doe.com', :type => 'Work')
80
- out.should == "<span class='email'><span class='type'>Work</span> john@doe.com</span>"
81
- end
82
-
83
- it "should use the given tag" do
84
- out = @vcard.email('john@doe.com', :type => 'Work', :tag => :strong)
85
- out.should == "<strong class='email'><span class='type'>Work</span> john@doe.com</strong>"
86
- end
87
-
88
- it "should use mailto if using an 'a' tag" do
89
- out = @vcard.email('john@doe.com', :type => 'Work', :tag => :a)
90
- out.should == "<a class='email' href='mailto:john@doe.com'><span class='type'>Work</span> john@doe.com</a>"
91
- end
92
- end
93
-
94
- describe "download_link" do
95
- it "should output a link to h2vx.com using the passed url" do
96
- out = @vcard.download_link('mydomain.com/page')
97
- out.should == "<a href='http://h2vx.com/vcf/mydomain.com/page' type='text/directory'>Download vCard</a>"
98
- end
99
-
100
- it "should strip the protocol from a passed url" do
101
- out = @vcard.download_link('http://mydomain.com/page')
102
- out.should == "<a href='http://h2vx.com/vcf/mydomain.com/page' type='text/directory'>Download vCard</a>"
103
- end
104
-
105
- it "should use :text option as text node if present" do
106
- out = @vcard.download_link('mydomain.com/page', :text => "Download Me Now")
107
- out.should == "<a href='http://h2vx.com/vcf/mydomain.com/page' type='text/directory'>Download Me Now</a>"
108
- end
109
- end
110
- end