shit works
This commit is contained in:
parent
9260774403
commit
ce2ea993a8
4 changed files with 364 additions and 7 deletions
6
Rakefile
6
Rakefile
|
@ -4,9 +4,9 @@ require 'rubygems'
|
||||||
require 'hoe'
|
require 'hoe'
|
||||||
|
|
||||||
Hoe.spec 'microformats2' do
|
Hoe.spec 'microformats2' do
|
||||||
# developer('FIX', 'FIX@example.com')
|
developer('Shane Becker', 'veganstraightedge@gmail.com')
|
||||||
|
extra_deps << ['nokogiri', ">= 0"]
|
||||||
# self.rubyforge_name = 'microformats2x' # if different than 'microformats2'
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
# vim: syntax=ruby
|
# vim: syntax=ruby
|
||||||
|
|
|
@ -1,3 +1,163 @@
|
||||||
class Microformats2
|
require 'nokogiri'
|
||||||
VERSION = '1.0.0'
|
require 'time'
|
||||||
|
require 'date'
|
||||||
|
|
||||||
|
module Microformats2
|
||||||
|
VERSION = "1.0.0"
|
||||||
|
|
||||||
|
def self.parse(html)
|
||||||
|
raise LoadError unless html.is_a?(String)
|
||||||
|
doc = Nokogiri::HTML(html)
|
||||||
|
microformats = Hash.new{|hash, key| hash[key] = Array.new}
|
||||||
|
doc.css("*[class^=h-]").each do |microformat|
|
||||||
|
constant_name = classify(microformat.attribute("class").to_s.gsub("-","_"))
|
||||||
|
|
||||||
|
if Object.const_defined?(constant_name)
|
||||||
|
klass = Object.const_get(constant_name)
|
||||||
|
else
|
||||||
|
klass = Class.new
|
||||||
|
Object.const_set constant_name, klass
|
||||||
|
end
|
||||||
|
|
||||||
|
obj = klass.new
|
||||||
|
|
||||||
|
# Add any properties to the object
|
||||||
|
self.add_properties(microformat, obj)
|
||||||
|
self.add_urls(microformat, obj)
|
||||||
|
self.add_dates(microformat, obj)
|
||||||
|
self.add_times(microformat, obj)
|
||||||
|
#letters = %w(p u d n e i t)
|
||||||
|
|
||||||
|
microformats[constant_name.downcase.to_sym] << obj
|
||||||
|
end
|
||||||
|
|
||||||
|
return microformats
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_properties(mf, obj)
|
||||||
|
%w(p n e i).each do |letter|
|
||||||
|
mf.css("*[class|=#{letter}]").each do |property|
|
||||||
|
property.attribute("class").to_s.split.each do |css_class|
|
||||||
|
if css_class =~ /^[pnei]/
|
||||||
|
css_class = css_class[2..-1].gsub("-","_")
|
||||||
|
method_name = css_class.gsub("-","_")
|
||||||
|
value = property.text.strip_whitespace
|
||||||
|
|
||||||
|
obj.class.class_eval { attr_accessor method_name }
|
||||||
|
|
||||||
|
if cur = obj.send(method_name)
|
||||||
|
if cur.kind_of? Array
|
||||||
|
cur << value
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", [cur, value])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_urls(mf, obj)
|
||||||
|
mf.css("*[class*=u-]").each do |property|
|
||||||
|
property.attribute("class").to_s.split.each do |css_class|
|
||||||
|
if css_class =~ /^u/
|
||||||
|
css_class = css_class[2..-1].gsub("-","_")
|
||||||
|
method_name = css_class.gsub("-","_")
|
||||||
|
value = property.attribute("href").to_s
|
||||||
|
|
||||||
|
obj.class.class_eval { attr_accessor method_name }
|
||||||
|
|
||||||
|
if cur = obj.send(method_name)
|
||||||
|
if cur.kind_of? Array
|
||||||
|
cur << value
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", [cur, value])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_dates(mf, obj)
|
||||||
|
mf.css("*[class*=d-]").each do |property|
|
||||||
|
property.attribute("class").to_s.split.each do |css_class|
|
||||||
|
if css_class =~ /^d/
|
||||||
|
css_class = css_class[2..-1].gsub("-","_")
|
||||||
|
method_name = css_class.gsub("-","_")
|
||||||
|
value = DateTime.parse((property.attribute("title") || property.text).to_s)
|
||||||
|
|
||||||
|
obj.class.class_eval { attr_accessor method_name }
|
||||||
|
|
||||||
|
if cur = obj.send(method_name)
|
||||||
|
if cur.kind_of? Array
|
||||||
|
cur << value
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", [cur, value])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.add_times(mf, obj)
|
||||||
|
mf.css("*[class*=t-]").each do |property|
|
||||||
|
property.attribute("class").to_s.split.each do |css_class|
|
||||||
|
if css_class =~ /^t/
|
||||||
|
css_class = css_class[2..-1].gsub("-","_")
|
||||||
|
method_name = css_class.gsub("-","_")
|
||||||
|
value = Time.parse((property.attribute("title") || property.text).to_s)
|
||||||
|
|
||||||
|
obj.class.class_eval { attr_accessor method_name }
|
||||||
|
|
||||||
|
if cur = obj.send(method_name)
|
||||||
|
if cur.kind_of? Array
|
||||||
|
cur << value
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", [cur, value])
|
||||||
|
end
|
||||||
|
else
|
||||||
|
obj.send("#{method_name}=", value)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class LoadError < StandardError; end
|
||||||
|
|
||||||
|
# Thank you Rails Developers for your unitentional contribution to this project
|
||||||
|
# File activesupport/lib/active_support/inflector/inflections.rb, line 206
|
||||||
|
def self.classify(str)
|
||||||
|
# strip out any leading schema name
|
||||||
|
camelize(singularize(str.to_s.sub(/.*\./, '')))
|
||||||
|
end
|
||||||
|
|
||||||
|
# File activesupport/lib/active_support/inflector/inflections.rb, line 148
|
||||||
|
def self.singularize(word)
|
||||||
|
result = word.to_s.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
# File activesupport/lib/active_support/inflector/methods.rb, line 28
|
||||||
|
def self.camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
||||||
|
if first_letter_in_uppercase
|
||||||
|
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
||||||
|
else
|
||||||
|
lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class String
|
||||||
|
def strip_whitespace
|
||||||
|
self.gsub(/\n+/, " ").gsub(/\s+/, " ").strip
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
30
test/hcard.html
Normal file
30
test/hcard.html
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://iamshane.com">
|
||||||
|
<span class="p-given-name">Shane</span>
|
||||||
|
<abbr class="p-additional-name">B</abbr>
|
||||||
|
<span class="p-family-name">Becker</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h1 class="h-mitch">
|
||||||
|
<a class="p-snap" href="http://iamshane.com">
|
||||||
|
<span class="p-krackle">Breakfast</span>
|
||||||
|
<span class="p-pop">Cereal</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -2,7 +2,174 @@ require "test/unit"
|
||||||
require "microformats2"
|
require "microformats2"
|
||||||
|
|
||||||
class TestMicroformats2 < Test::Unit::TestCase
|
class TestMicroformats2 < Test::Unit::TestCase
|
||||||
def test_sanity
|
def test_acceptence_of_string
|
||||||
flunk "write tests or I will kneecap you"
|
assert_nothing_raised Microformats2::LoadError do
|
||||||
|
Microformats2.parse("A String")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_throw_exception_on_non_string_params
|
||||||
|
assert_raise Microformats2::LoadError do
|
||||||
|
Microformats2.parse(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_returns_array_of_microformat_objects
|
||||||
|
result = Microformats2.parse("A String")
|
||||||
|
assert_equal Array, result.class
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_only_parse_microformats
|
||||||
|
result = Microformats2.parse("<html><body><p>Something</p></body></html>")
|
||||||
|
assert_equal 0, result.size
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_extracts_hcard_from_html
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
assert_equal HCard, result.first.class
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_constructs_properties_from_hcard
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
mycard = result.first
|
||||||
|
|
||||||
|
assert_equal "Chris", mycard.given_name
|
||||||
|
assert_equal "R.", mycard.additional_name
|
||||||
|
assert_equal "Messina", mycard.family_name
|
||||||
|
assert_equal "Chris R. Messina", mycard.fn
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_constructs_dates
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="d-bday">1979-09-18</span>
|
||||||
|
<span class="d-epoch" title="1970-01-01">EPOCH!</span>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
mycard = result.first
|
||||||
|
|
||||||
|
assert_equal DateTime.parse("1979-09-18"), mycard.bday
|
||||||
|
assert_equal DateTime.parse("1970-01-01"), mycard.epoch
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_constructs_times
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<span class="t-start">09:30</span>
|
||||||
|
<span class="t-end" title="6:00">Leaving time</span>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
mycard = result.first
|
||||||
|
|
||||||
|
assert_equal Time.parse("09:30"), mycard.start
|
||||||
|
assert_equal Time.parse("06:00"), mycard.end
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_ignores_pattern_matches_not_at_the_beginning_of_class
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<span class="p-n-x">Chris</span>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
mycard = result.first
|
||||||
|
|
||||||
|
assert_equal "Chris", mycard.n_x
|
||||||
|
assert mycard.n_x.is_a?(String)
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_constructs_urls_from_hcard
|
||||||
|
hcard = <<-END
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Simple hCard</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<body>
|
||||||
|
<h1 class="h-card">
|
||||||
|
<a class="p-fn u-url" href="http://factoryjoe.com/">
|
||||||
|
<span class="p-given-name">Chris</span>
|
||||||
|
<abbr class="p-additional-name">R.</abbr>
|
||||||
|
<span class="p-family-name">Messina</span>
|
||||||
|
</a>
|
||||||
|
</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
END
|
||||||
|
result = Microformats2.parse(hcard)
|
||||||
|
mycard = result.first
|
||||||
|
assert_equal "http://factoryjoe.com/", mycard.url
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue