microformat 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.rvmrc +12 -0
- data/.travis.yml +3 -0
- data/README.md +56 -3
- data/Rakefile +11 -0
- data/lib/microformat/attribute.rb +41 -0
- data/lib/microformat/attribute_definition.rb +23 -0
- data/lib/microformat/attribute_map.rb +40 -0
- data/lib/microformat/card.rb +34 -0
- data/lib/microformat/collection.rb +26 -0
- data/lib/microformat/format.rb +48 -0
- data/lib/microformat/parser.rb +48 -0
- data/lib/microformat/review.rb +19 -0
- data/lib/microformat/selectors.rb +45 -0
- data/lib/microformat/version.rb +1 -1
- data/lib/microformat.rb +7 -2
- data/microformat.gemspec +22 -13
- data/spec/microformat/attribute_definition_spec.rb +16 -0
- data/spec/microformat/attribute_spec.rb +23 -0
- data/spec/microformat/collection_spec.rb +31 -0
- data/spec/microformat/format_spec.rb +46 -0
- data/spec/microformat/parser_spec.rb +83 -0
- data/spec/microformat/selectors_spec.rb +92 -0
- data/spec/microformat_spec.rb +17 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/support/env.rb +8 -0
- metadata +98 -5
data/.rvmrc
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env bash
|
2
|
+
|
3
|
+
if ! rvm list | grep -q ruby-1.9.3-p194 ; then
|
4
|
+
rvm install 1.9.3-p194
|
5
|
+
fi
|
6
|
+
|
7
|
+
rvm 1.9.3-p194@platformq_microformat --create
|
8
|
+
|
9
|
+
if ! gem list | grep -q bundler ; then
|
10
|
+
gem install --no-ri --no-rdoc bundler
|
11
|
+
bundle install --without production
|
12
|
+
fi
|
data/.travis.yml
ADDED
data/README.md
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
# Microformat
|
2
2
|
|
3
|
-
|
3
|
+
Reads Microformats from HTML documents
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
|
-
|
9
|
+
```ruby
|
10
|
+
gem "microformat"
|
11
|
+
```
|
10
12
|
|
11
13
|
And then execute:
|
12
14
|
|
@@ -18,7 +20,58 @@ Or install it yourself as:
|
|
18
20
|
|
19
21
|
## Usage
|
20
22
|
|
21
|
-
|
23
|
+
You can parse a document (or partial document), providing the HTML as a string:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
html = "<html>...</html>"
|
27
|
+
Microformat.parse(html) # => Microformat::Collection
|
28
|
+
```
|
29
|
+
|
30
|
+
Or provide a Nokogiri element:
|
31
|
+
|
32
|
+
```ruby
|
33
|
+
require "nokogiri"
|
34
|
+
|
35
|
+
html = "<html>...</html>"
|
36
|
+
doc = Nokogiri::HTML(html)
|
37
|
+
element = doc.css(".hreview")
|
38
|
+
Microformat.parse(element)
|
39
|
+
# => Microformat::Collection
|
40
|
+
```
|
41
|
+
|
42
|
+
Collections act as standard Ruby arrays, however they provide additional filtering methods:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
collection = Microformat.parse(element)
|
46
|
+
# => Microformat::Collection (of all microformat objects)
|
47
|
+
collection.filter(Microformat::Review, Microformat::ReviewAggregate)
|
48
|
+
# => Microformat::Collection (of only hreviews and hreview-aggregates)
|
49
|
+
```
|
50
|
+
|
51
|
+
You can also improve performance by passing a set of formats to the initial parse:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
formats = [Microformat::Review, Microformat::Card]
|
55
|
+
Microformat.parse(element, filter: formats)
|
56
|
+
# => Microformat::Collection (of only hreviews and hcards)
|
57
|
+
```
|
58
|
+
|
59
|
+
You can specify a limit (should you only want the first or a few objects)
|
60
|
+
|
61
|
+
```ruby
|
62
|
+
Microformat.parse(element, limit: 3)
|
63
|
+
# => Microformat::Collection (of max size 3)
|
64
|
+
```
|
65
|
+
|
66
|
+
You can also parse a Nokogiri element to return an object of the first found exact Microformat:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
html = "<html>...</html>"
|
70
|
+
doc = Nokogiri::HTML(html)
|
71
|
+
element = doc.css(".hcard")
|
72
|
+
Microformat::Card.parse(element)
|
73
|
+
# => Microformat::Card instance
|
74
|
+
```
|
22
75
|
|
23
76
|
## Contributing
|
24
77
|
|
data/Rakefile
CHANGED
@@ -1,2 +1,13 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
require "bundler/gem_tasks"
|
3
|
+
|
4
|
+
require "rspec/core/rake_task"
|
5
|
+
|
6
|
+
desc "Runs all the specs"
|
7
|
+
task default: %w(spec)
|
8
|
+
|
9
|
+
desc "Run specs"
|
10
|
+
RSpec::Core::RakeTask.new do |t|
|
11
|
+
t.pattern = "./spec/**/*_spec.rb" # don't need this, it's default.
|
12
|
+
# Put spec opts in a file named .rspec in root
|
13
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "microformat/attribute_map"
|
2
|
+
|
3
|
+
module Microformat
|
4
|
+
class Attribute
|
5
|
+
attr_reader :name, :options, :attributes
|
6
|
+
|
7
|
+
def initialize(name, options = {}, &block)
|
8
|
+
@name = name
|
9
|
+
@options = options
|
10
|
+
@attributes ||= {}
|
11
|
+
if options[:format]
|
12
|
+
@attributes.merge!(options[:format].attribute_definition.attributes)
|
13
|
+
end
|
14
|
+
block.yield(self) if block_given?
|
15
|
+
end
|
16
|
+
|
17
|
+
def attribute(name, options = {}, &block)
|
18
|
+
Attribute.new(name, options, &block).tap do |attribute|
|
19
|
+
@attributes.merge!(name.to_sym => attribute)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def read_from(document)
|
24
|
+
selector = options[:selector] || ".#{@name}"
|
25
|
+
sub_document = document.css(selector).first
|
26
|
+
value = read_value_from(sub_document)
|
27
|
+
AttributeMap.new(self, sub_document, { value: value })
|
28
|
+
end
|
29
|
+
|
30
|
+
def read_value_from(document)
|
31
|
+
Array(options[:attribute] || "text").each do |attr|
|
32
|
+
if attr == "text"
|
33
|
+
return document.text
|
34
|
+
elsif document[attr]
|
35
|
+
return document[attr]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "microformat/attribute"
|
2
|
+
require "microformat/attribute_map"
|
3
|
+
|
4
|
+
module Microformat
|
5
|
+
class AttributeDefinition
|
6
|
+
attr_reader :attributes
|
7
|
+
|
8
|
+
def initialize(&block)
|
9
|
+
@attributes ||= {}
|
10
|
+
block.yield(self)
|
11
|
+
end
|
12
|
+
|
13
|
+
def attribute(name, options = {}, &block)
|
14
|
+
Attribute.new(name, options, &block).tap do |attribute|
|
15
|
+
@attributes.merge!(name.to_sym => attribute)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def map_to(document)
|
20
|
+
AttributeMap.new(self, document)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Microformat
|
2
|
+
class AttributeMap
|
3
|
+
attr_reader :definition, :document
|
4
|
+
|
5
|
+
def initialize(definition, document, values = {})
|
6
|
+
@definition = definition
|
7
|
+
@document = document
|
8
|
+
@values = values
|
9
|
+
@attribute_cache = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def respond_to_missing?(name, public_only = false)
|
13
|
+
values_responds_to?(name) ||
|
14
|
+
definition_responds_to?(name)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(name, *args, &block)
|
18
|
+
if values_responds_to?(name)
|
19
|
+
@values.fetch(name.to_sym)
|
20
|
+
elsif @attribute_cache.has_key?(name)
|
21
|
+
@attribute_cache[name]
|
22
|
+
elsif definition_responds_to?(name)
|
23
|
+
attribute = @definition.attributes.fetch(name)
|
24
|
+
value = attribute.read_from(document)
|
25
|
+
@attribute_cache[name] = value
|
26
|
+
else
|
27
|
+
super(name)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def values_responds_to?(name)
|
33
|
+
@values.has_key?(name.to_sym)
|
34
|
+
end
|
35
|
+
|
36
|
+
def definition_responds_to?(name)
|
37
|
+
@definition.attributes.has_key?(name.to_sym)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "microformat/format"
|
2
|
+
|
3
|
+
module Microformat
|
4
|
+
class Card < Format
|
5
|
+
selector ".vcard"
|
6
|
+
|
7
|
+
attribute_definition do |root|
|
8
|
+
root.attribute(:fn, required: true)
|
9
|
+
root.attribute(:n) do |n|
|
10
|
+
n.attribute(:"honorific-prefix")
|
11
|
+
n.attribute(:"given-name")
|
12
|
+
n.attribute(:"additional-name")
|
13
|
+
n.attribute(:"family-name")
|
14
|
+
n.attribute(:"honorific-suffix")
|
15
|
+
end
|
16
|
+
root.attribute(:nickname)
|
17
|
+
root.attribute(:org)
|
18
|
+
root.attribute(:photo, cast: :url, attribute: ["href", "src"])
|
19
|
+
root.attribute(:url, cast: :url, attribute: "href")
|
20
|
+
root.attribute(:email, cast: :email)
|
21
|
+
root.attribute(:tel, cast: :tel)
|
22
|
+
root.attribute(:adr) do |adr|
|
23
|
+
adr.attribute(:"street-address")
|
24
|
+
adr.attribute(:locality)
|
25
|
+
adr.attribute(:region)
|
26
|
+
adr.attribute(:"postal-code")
|
27
|
+
adr.attribute(:"country-name")
|
28
|
+
end
|
29
|
+
root.attribute(:"bday")
|
30
|
+
root.attribute(:"category")
|
31
|
+
root.attribute(:"note")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require "microformat/selectors"
|
2
|
+
|
3
|
+
module Microformat
|
4
|
+
class Collection < ::Array
|
5
|
+
def <<(element)
|
6
|
+
# load the first matching format
|
7
|
+
format = Selectors.class_matching(element)
|
8
|
+
# use the microformat to parse the element
|
9
|
+
parsed = format.parse(element)
|
10
|
+
# add the parsed object to the array
|
11
|
+
super(parsed)
|
12
|
+
end
|
13
|
+
|
14
|
+
def filter(*args)
|
15
|
+
classes = Array(args).flatten
|
16
|
+
# find the applicable classes
|
17
|
+
filtered = select do |object|
|
18
|
+
Array(classes).include?(object.class)
|
19
|
+
end
|
20
|
+
# create a new collection from them
|
21
|
+
self.class.new.tap do |collection|
|
22
|
+
collection.replace(filtered)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "microformat/attribute_definition"
|
2
|
+
require "microformat/selectors"
|
3
|
+
|
4
|
+
module Microformat
|
5
|
+
class Format
|
6
|
+
def self.selector(selector = nil)
|
7
|
+
define_selector(selector) if selector
|
8
|
+
@selector
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.attribute_definition(&block)
|
12
|
+
@attribute_definition = AttributeDefinition.new(&block) if block_given?
|
13
|
+
@attribute_definition
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.parse(document)
|
17
|
+
new(document)
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :document
|
21
|
+
|
22
|
+
def initialize(document)
|
23
|
+
@document = document
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_missing?(name, public_only = false)
|
27
|
+
attribute_map.respond_to?(name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def method_missing(name)
|
31
|
+
if attribute_map.respond_to?(name)
|
32
|
+
attribute_map.send(name)
|
33
|
+
else
|
34
|
+
super(name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def attribute_map
|
40
|
+
@attribute_map ||= self.class.attribute_definition.map_to(document)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.define_selector(selector)
|
44
|
+
Selectors.define(selector, self)
|
45
|
+
@selector = selector
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require "nokogiri"
|
2
|
+
require "microformat/collection"
|
3
|
+
require "microformat/selectors"
|
4
|
+
|
5
|
+
module Microformat
|
6
|
+
class Parser
|
7
|
+
def self.parse(doc, options = {})
|
8
|
+
# ensure the document is parsed
|
9
|
+
unless doc.respond_to?(:css)
|
10
|
+
doc = Nokogiri::HTML(doc)
|
11
|
+
end
|
12
|
+
# return the collection
|
13
|
+
new(doc, options).collection
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :doc, :options
|
17
|
+
|
18
|
+
def initialize(doc, options)
|
19
|
+
@doc = doc
|
20
|
+
@options = options
|
21
|
+
parse(elements)
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse(elements)
|
25
|
+
i = 0; while i < [limit, elements.size].min do
|
26
|
+
collection << elements[i]
|
27
|
+
i += 1
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def limit
|
32
|
+
@limit ||= options[:limit] || Float::INFINITY
|
33
|
+
end
|
34
|
+
|
35
|
+
def selectors
|
36
|
+
@selectors ||= Selectors.filter(options[:filter])
|
37
|
+
end
|
38
|
+
|
39
|
+
def collection
|
40
|
+
@collection ||= Collection.new
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
def elements
|
45
|
+
@elements ||= doc.css(selectors.join(", "))
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "microformat/format"
|
2
|
+
require "microformat/card"
|
3
|
+
|
4
|
+
module Microformat
|
5
|
+
class Review < Format
|
6
|
+
selector ".hreview"
|
7
|
+
|
8
|
+
attribute_definition do |root|
|
9
|
+
root.attribute(:summary)
|
10
|
+
root.attribute(:type)
|
11
|
+
root.attribute(:item, format: Card)
|
12
|
+
root.attribute(:reviewer, format: Card)
|
13
|
+
root.attribute(:rating, cast: :decimal)
|
14
|
+
root.attribute(:description)
|
15
|
+
root.attribute(:tags, multiple: true, selector: "[rel='tag']")
|
16
|
+
root.attribute(:permalink, selector: "[rel='bookmark']", attribute: "href")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "singleton"
|
2
|
+
|
3
|
+
module Microformat
|
4
|
+
class Selectors < Hash
|
5
|
+
include Singleton
|
6
|
+
|
7
|
+
def self.class_matching(element)
|
8
|
+
# load the CSS classes of the element
|
9
|
+
css_classes = element["class"].split(/\s+/).map do |c|
|
10
|
+
".#{c}"
|
11
|
+
end
|
12
|
+
# loop through the classes
|
13
|
+
Array(css_classes).each do |css_class|
|
14
|
+
instance.each do |selector, klass|
|
15
|
+
if selector == css_class
|
16
|
+
return klass
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
raise RuntimeError, "No class is defined for any of the selectors: #{css_classes.join(" / ")}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.define(selector, klass)
|
24
|
+
if instance.has_key?(selector) && instance.fetch(selector) != klass
|
25
|
+
raise ArgumentError, "#{instance.fetch(selector).name} has already implemented the selector '#{selector}'"
|
26
|
+
else
|
27
|
+
instance.merge!(selector => klass)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.filter(klasses = nil)
|
32
|
+
if klasses
|
33
|
+
instance.filter(Array(klasses))
|
34
|
+
else
|
35
|
+
instance.keys
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def filter(klasses)
|
40
|
+
select do |selector, klass|
|
41
|
+
Array(klasses).include?(klass)
|
42
|
+
end.keys
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/microformat/version.rb
CHANGED
data/lib/microformat.rb
CHANGED
data/microformat.gemspec
CHANGED
@@ -1,17 +1,26 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
4
|
|
4
|
-
|
5
|
-
gem.authors = ["Ryan Townsend"]
|
6
|
-
gem.email = ["ryan@ryantownsend.co.uk"]
|
7
|
-
gem.description = %q{Reads Microformats from HTML documents}
|
8
|
-
gem.summary = gem.description
|
9
|
-
gem.homepage = "https://github.com/platformq/microformat"
|
5
|
+
require "microformat/version"
|
10
6
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.authors = ["Ryan Townsend"]
|
9
|
+
s.email = ["ryan@ryantownsend.co.uk"]
|
10
|
+
s.description = %q{Reads Microformats from HTML documents}
|
11
|
+
s.summary = s.description
|
12
|
+
s.homepage = "https://github.com/platformq/microformat"
|
13
|
+
|
14
|
+
s.files = `git ls-files`.split($\)
|
15
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
17
|
+
s.name = "microformat"
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.version = Microformat::VERSION
|
20
|
+
|
21
|
+
# gems
|
22
|
+
s.add_dependency "nokogiri"
|
23
|
+
s.add_development_dependency "rake"
|
24
|
+
s.add_development_dependency "rspec"
|
25
|
+
s.add_development_dependency "simplecov"
|
17
26
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Microformat::AttributeDefinition do
|
4
|
+
subject { Microformat::AttributeDefinition }
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
it "should accept a block of nested attributes" do
|
8
|
+
map = subject.new do |map|
|
9
|
+
map.attribute(:one)
|
10
|
+
map.attribute(:two)
|
11
|
+
end
|
12
|
+
|
13
|
+
expect(map.attributes.size).to eq(2)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Microformat::Attribute do
|
4
|
+
subject { Microformat::Attribute }
|
5
|
+
|
6
|
+
describe "#initialize" do
|
7
|
+
it "should accept a block of nested attributes" do
|
8
|
+
attribute = subject.new(:name) do |attr|
|
9
|
+
attr.attribute(:one)
|
10
|
+
attr.attribute(:two)
|
11
|
+
attr.attribute(:three) do |three|
|
12
|
+
three.attribute(:four)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
expect(attribute.attributes.size).to eq(3)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should set the name" do
|
20
|
+
expect(subject.new(:fn).name).to eq(:fn)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Microformat::Collection do
|
4
|
+
subject do
|
5
|
+
Microformat::Collection.new
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#<<" do
|
9
|
+
it "should parse the element to the collection" do
|
10
|
+
selector = ".selector"
|
11
|
+
source = %Q(<html><body>
|
12
|
+
<div class="#{selector.gsub(/^\./, "")}"></div>
|
13
|
+
</body></html>)
|
14
|
+
element = Nokogiri::HTML(source).css(selector).first
|
15
|
+
parsed = mock("parsed_object")
|
16
|
+
format = mock("format")
|
17
|
+
format.should_receive(:parse).and_return(parsed)
|
18
|
+
Microformat::Selectors.define(selector, format)
|
19
|
+
subject << element
|
20
|
+
expect(subject).to include parsed
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#filter" do
|
25
|
+
it "should filter the collection down to the specific classes" do
|
26
|
+
objects = [Object.new, Hash.new, Array.new]
|
27
|
+
subject.replace(objects)
|
28
|
+
expect(subject.filter(Hash)).to eq [objects[1]]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
module Microformat
|
4
|
+
class TestFormat < Microformat::Format; end
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Microformat::Format do
|
8
|
+
after(:each) do
|
9
|
+
Microformat::Selectors.instance.replace({})
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "::selector" do
|
13
|
+
describe "getting the value" do
|
14
|
+
it "should return the value" do
|
15
|
+
Microformat::TestFormat.selector ".review"
|
16
|
+
expect(Microformat::TestFormat.selector).to eq ".review"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "setting the value" do
|
21
|
+
it "should define the selector" do
|
22
|
+
Microformat::TestFormat.selector ".review2"
|
23
|
+
expect(Microformat::Selectors.instance[".review2"]).to eq Microformat::TestFormat
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "::attribute_definition" do
|
29
|
+
describe "setting the value" do
|
30
|
+
it "should accept a block and set the map correctly" do
|
31
|
+
map = Microformat::TestFormat.attribute_definition do |attr|
|
32
|
+
attr.attribute :name
|
33
|
+
end
|
34
|
+
expect(map.attributes.values.first.name).to eq :name
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "::parse" do
|
40
|
+
it "should return and instance of the format using the given document" do
|
41
|
+
html = %Q(<html><body>Something</body></html>)
|
42
|
+
doc = Nokogiri::HTML(html)
|
43
|
+
expect(Microformat::TestFormat.parse(doc).document).to eq doc
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
describe Microformat::Parser do
|
5
|
+
describe "::parse" do
|
6
|
+
subject do
|
7
|
+
Microformat::Parser.parse(doc)
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
Microformat::Selectors.instance.replace({})
|
12
|
+
[
|
13
|
+
Microformat::Card,
|
14
|
+
Microformat::Review
|
15
|
+
].each { |klass| klass.selector(klass.selector) }
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:html) do
|
19
|
+
%Q(<html><body></body></html>)
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:review_html) do
|
23
|
+
%Q(<html><body>
|
24
|
+
<div class="hreview">
|
25
|
+
<div class="summary">It's good</div>
|
26
|
+
<div class="reviewer">
|
27
|
+
<p class="fn">Dave</p>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</body></html>)
|
31
|
+
end
|
32
|
+
|
33
|
+
context "given a HTML string" do
|
34
|
+
let(:doc) { html }
|
35
|
+
|
36
|
+
it "should return an instance of Microformat::Collection" do
|
37
|
+
expect(subject).to be_kind_of Microformat::Collection
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context "given a HTML document" do
|
42
|
+
let(:doc) { Nokogiri::HTML(html) }
|
43
|
+
|
44
|
+
it "should return an instance of Microformat::Collection" do
|
45
|
+
expect(subject).to be_kind_of Microformat::Collection
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "given a HTML element" do
|
50
|
+
let(:doc) { Nokogiri::HTML(html).css("body") }
|
51
|
+
|
52
|
+
it "should return an instance of Microformat::Collection" do
|
53
|
+
expect(subject).to be_kind_of Microformat::Collection
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "given a HTML document with a hReview" do
|
58
|
+
let(:doc) { Nokogiri::HTML(review_html) }
|
59
|
+
|
60
|
+
it "should return a collection with one object" do
|
61
|
+
expect(subject.size).to eq 1
|
62
|
+
end
|
63
|
+
|
64
|
+
describe "the returned Microformat object" do
|
65
|
+
subject do
|
66
|
+
Microformat::Parser.parse(doc).first
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should be a Microformat::Review" do
|
70
|
+
expect(subject).to be_kind_of(Microformat::Review)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should return the summary" do
|
74
|
+
expect(subject.summary.value).to eq "It's good"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should return the reviewer's full name" do
|
78
|
+
expect(subject.reviewer.fn.value).to eq "Dave"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Microformat::Selectors do
|
4
|
+
let(:selectors) { Hash.new }
|
5
|
+
|
6
|
+
subject do
|
7
|
+
Microformat::Selectors.instance
|
8
|
+
end
|
9
|
+
|
10
|
+
before(:each) do
|
11
|
+
selectors.each do |sel,klass|
|
12
|
+
Microformat::Selectors.define(sel, klass)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
after(:each) do
|
17
|
+
Microformat::Selectors.instance.replace({})
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "::instance" do
|
21
|
+
it "should always return the same object" do
|
22
|
+
expect(subject.class.instance.object_id).to eq subject.class.instance.object_id
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "::define" do
|
27
|
+
context "when the selector is already defined" do
|
28
|
+
it "should raise an exception" do
|
29
|
+
subject.class.define(".hello", Hash)
|
30
|
+
expect {
|
31
|
+
subject.class.define(".hello", Object)
|
32
|
+
}.to raise_error(ArgumentError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when redefining the selector" do
|
37
|
+
it "should not raise an exception" do
|
38
|
+
subject.class.define(".hello", Hash)
|
39
|
+
expect {
|
40
|
+
subject.class.define(".hello", Hash)
|
41
|
+
}.not_to raise_error(ArgumentError)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
context "when the selector is not defined" do
|
46
|
+
it "should add the definition to the hash" do
|
47
|
+
subject.class.define(".hello", Hash)
|
48
|
+
expect(subject[".hello"]).to eq Hash
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "::class_matching" do
|
54
|
+
let(:selectors) do
|
55
|
+
{
|
56
|
+
".review" => Object,
|
57
|
+
".card" => Hash
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should return the first class that matches the given selectors" do
|
62
|
+
doc = %Q(<html><body>
|
63
|
+
<div class="card review"></div>
|
64
|
+
</body></html>)
|
65
|
+
element = Nokogiri::HTML(doc).css(".card").first
|
66
|
+
expect(subject.class.class_matching(element)).to eq Hash
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with no matching selectors" do
|
70
|
+
it "should raise an exception" do
|
71
|
+
doc = %Q(<html><body>
|
72
|
+
<div class="non"></div>
|
73
|
+
</body></html>)
|
74
|
+
element = Nokogiri::HTML(doc).css(".non").first
|
75
|
+
expect {
|
76
|
+
subject.class.class_matching(element)
|
77
|
+
}.to raise_error(RuntimeError)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "::filter" do
|
83
|
+
let(:selectors) do
|
84
|
+
{ ".review" => Object }
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should return an array of selectors that match the given classes" do
|
88
|
+
expect(subject.class.filter(Object)).to eq [".review"]
|
89
|
+
expect(subject.class.filter(Hash)).to eq []
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Microformat do
|
4
|
+
subject { Microformat }
|
5
|
+
|
6
|
+
describe "::parse" do
|
7
|
+
let(:args) { ["one", 2, :three] }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
Microformat::Parser.should_receive(:parse).with(*args).and_return(true)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should pass args onto the parser" do
|
14
|
+
expect(subject.parse(*args)).to be_true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/support/env.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: microformat
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,72 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-10-
|
13
|
-
dependencies:
|
12
|
+
date: 2012-10-22 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: rspec
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: simplecov
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
14
78
|
description: Reads Microformats from HTML documents
|
15
79
|
email:
|
16
80
|
- ryan@ryantownsend.co.uk
|
@@ -19,13 +83,33 @@ extensions: []
|
|
19
83
|
extra_rdoc_files: []
|
20
84
|
files:
|
21
85
|
- .gitignore
|
86
|
+
- .rvmrc
|
87
|
+
- .travis.yml
|
22
88
|
- Gemfile
|
23
89
|
- LICENSE
|
24
90
|
- README.md
|
25
91
|
- Rakefile
|
26
92
|
- lib/microformat.rb
|
93
|
+
- lib/microformat/attribute.rb
|
94
|
+
- lib/microformat/attribute_definition.rb
|
95
|
+
- lib/microformat/attribute_map.rb
|
96
|
+
- lib/microformat/card.rb
|
97
|
+
- lib/microformat/collection.rb
|
98
|
+
- lib/microformat/format.rb
|
99
|
+
- lib/microformat/parser.rb
|
100
|
+
- lib/microformat/review.rb
|
101
|
+
- lib/microformat/selectors.rb
|
27
102
|
- lib/microformat/version.rb
|
28
103
|
- microformat.gemspec
|
104
|
+
- spec/microformat/attribute_definition_spec.rb
|
105
|
+
- spec/microformat/attribute_spec.rb
|
106
|
+
- spec/microformat/collection_spec.rb
|
107
|
+
- spec/microformat/format_spec.rb
|
108
|
+
- spec/microformat/parser_spec.rb
|
109
|
+
- spec/microformat/selectors_spec.rb
|
110
|
+
- spec/microformat_spec.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
- spec/support/env.rb
|
29
113
|
homepage: https://github.com/platformq/microformat
|
30
114
|
licenses: []
|
31
115
|
post_install_message:
|
@@ -46,8 +130,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
130
|
version: '0'
|
47
131
|
requirements: []
|
48
132
|
rubyforge_project:
|
49
|
-
rubygems_version: 1.8.
|
133
|
+
rubygems_version: 1.8.23
|
50
134
|
signing_key:
|
51
135
|
specification_version: 3
|
52
136
|
summary: Reads Microformats from HTML documents
|
53
|
-
test_files:
|
137
|
+
test_files:
|
138
|
+
- spec/microformat/attribute_definition_spec.rb
|
139
|
+
- spec/microformat/attribute_spec.rb
|
140
|
+
- spec/microformat/collection_spec.rb
|
141
|
+
- spec/microformat/format_spec.rb
|
142
|
+
- spec/microformat/parser_spec.rb
|
143
|
+
- spec/microformat/selectors_spec.rb
|
144
|
+
- spec/microformat_spec.rb
|
145
|
+
- spec/spec_helper.rb
|
146
|
+
- spec/support/env.rb
|