Skip to content

Commit

Permalink
enable the chaining of selector aliases
Browse files Browse the repository at this point in the history
  • Loading branch information
nakajima committed Nov 21, 2008
1 parent d7b1789 commit ec66f3d
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 28 deletions.
22 changes: 19 additions & 3 deletions lib/elementor/element_set.rb
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
module Elementor
class ElementSet < Array
attr_accessor :result, :selector

def with_text(str)
ElementSet.new(select { |item| item.text.include?(str) })
replace(select { |item|
item.text.include?(str)
}) ; self
end

def with_attrs(options={})
ElementSet.new(select { |item|
replace(select { |item|
options.all? { |key, value| item[key.to_s] == value }
})
}) ; self
end

def inspect
map(&:text).inspect
end

def method_missing(sym, *args, &block)
result.try(sym, doc, *args) || super
end

def respond_to?(sym)
result.respond_to?(sym) || super
end

def doc
result.doc.search(selector)
end
end
end
9 changes: 8 additions & 1 deletion lib/elementor/result.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,19 @@ def method_missing(sym, *args, &block)
def define_elements!
element_names.each do |name, selector|
meta_def(name) do |*filters|
set = ElementSet.new(doc.search(selector).to_ary)
set = ElementSet.new scope(filters).search(selector)
set.result = self
set.selector = selector
filters.empty? ? set : filters.inject(set) { |result, fn| fn[result] }
end
end
end

def scope(filters)
scope = filters.first.is_a?(Proc) ? nil : filters.shift
scope || doc
end

def element_names
@element_names ||= { }
end
Expand Down
90 changes: 66 additions & 24 deletions spec/elementor_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,16 @@
head { title("This is the title") }
body {
h1("A header")
div(:id => "tag-cloud") {
div(:class => "tag-cloud") {
a(:href => '#foo', :class => 'even') { text "Foo" }
a(:href => '#bar') { text "Bar" }
a(:href => '#fizz', :class => 'even') { text "Fizz" }
a(:href => '#buzz') { text "Buzz" }
}
div(:id => "user-links") {
h1("User Link Section")
div(:class => "tag-cloud") {
a(:href => '#fizz', :class => 'even') { text "Fizz" }
a(:href => '#buzz') { text "Buzz" }
}
}
}
}
Expand All @@ -26,11 +31,11 @@

describe "the html" do
it "has tags" do
Nokogiri(body).search('#tag-cloud a').should have(4).nodes
Nokogiri(body).search('.tag-cloud a').should have(4).nodes
end

it "has a header" do
Nokogiri(body).search('h1').should have(1).nodes
it "has a headers" do
Nokogiri(body).search('h1').should have(2).nodes
end
end

Expand All @@ -52,8 +57,9 @@

proc {
@result = elements(:from => :whoops!) do |tag|
tag.header "h1"
tag.tags "#tag-cloud a"
tag.headers "h1"
tag.tags ".tag-cloud a"
tag.user_links "#user-links"
end

@result.instance_variable_get("@this").doc
Expand All @@ -69,11 +75,12 @@

proc {
@result = elements(:from => :whoops!) do |tag|
tag.header "h1"
tag.tags "#tag-cloud a"
tag.headers "h1"
tag.tags ".tag-cloud a"
tag.user_links "#user-links"
end

result.should have(0).header
result.should have(0).headers
}.should raise_error(NoMethodError)
end
end
Expand All @@ -82,47 +89,82 @@
context "with elements defined" do
before(:each) do
@result = elements(:from => :body) do |tag|
tag.header "h1"
tag.tags "#tag-cloud a"
tag.headers "h1"
tag.tags ".tag-cloud a"
tag.user_links "#user-links"
end
end

it "can find tags" do
result.should have(1).header
result.should have(2).headers
result.should have(4).tags
end

it "can still use old Nokogiri traversal methods" do
result.search('#tag-cloud a').should have(4).nodes
result.search('h1').should have(1).nodes
result.search('.tag-cloud a').should have(4).nodes
result.search('h1').should have(2).nodes
end

it "creates more readable inspect" do
result.header.inspect.should == "[\"A header\"]"
result.headers.inspect.should == "[\"A header\", \"User Link Section\"]"
end

describe "chaining selectors" do
it "can chain selector aliases" do
result.user_links.headers.should have(1).node
result.user_links.tags.should have(2).nodes
end

it "works with with_text filter" do
result.user_links.tags.with_text('Fizz').should have(1).node
end

it "works with with_attrs filter" do
result.user_links.tags.with_attrs(:class => 'even').should have(1).node
end

describe "as an rspec matcher" do
it "works with no matches" do
result.tags.should have(0).user_links
end

it "works with 1 match" do
result.user_links.should have(1).headers
end

it "works with many matches" do
result.user_links.should have(2).tags
end

it "allows chaining" do
result.user_links.should have(1).tags.with_attrs(:class => "even").with_text('Fizz')
end
end
end

describe ":from option" do
it "determines which method should be called to get the markup" do
@result = elements(:from => :body) do |tag|
tag.header "h1"
tag.tags "#tag-cloud a"
tag.headers "h1"
tag.tags ".tag-cloud a"
tag.user_links "#user-links"
tag.bodies "body"
end

result.should have(1).header
result.should have(2).headers
result.should have(4).tags
end

it "works when the HTML isn't present until after the #elements call" do
mock(self).deferred_source.once.returns(HTML.dup)

@result = elements(:from => :deferred_source) do |tag|
tag.header "h1"
tag.tags "#tag-cloud a"
tag.headers "h1"
tag.tags ".tag-cloud a"
tag.bodies "body"
end

result.should have(1).header
result.should have(2).headers
result.should have(4).tags
end
end
Expand Down Expand Up @@ -195,7 +237,7 @@
result.should have(4).tags
result.parse!(%(<h1>Fred</h1>))
result.should have(0).tags
result.should have(1).header.with_text("Fred")
result.should have(1).headers.with_text("Fred")
end
end
end
Expand Down

0 comments on commit ec66f3d

Please sign in to comment.