Permalink
Browse files

* lib/hpricot/elements.rb: added block syntax to `attr`, new methods…

… `remove_attr` and `remove_class`. Got rid of `set`, making it an alias for `attr` which does much more.

git-svn-id: https://code.whytheluckystiff.net/svn/hpricot/trunk@150 9e82565c-fa0a-db11-9fa0-00132028b6dc
  • Loading branch information...
1 parent 594a0be commit 98411be96c6da178977a2a44d03382dcc5c25c6e why committed Jun 5, 2007
Showing with 81 additions and 18 deletions.
  1. +4 −1 README
  2. +60 −17 lib/hpricot/elements.rb
  3. +17 −0 test/test_alter.rb
View
5 README
@@ -257,7 +257,7 @@ So, let's go beyond just trying to fix the hierarchy. The
What measures does <tt>:xhtml_strict</tt> take?
- 1. Shift elements into their proper containers just like <tt>:fixup_tags</tt>.
+ 1. Shift elements into their proper containers just like :fixup_tags.
2. Remove unknown elements.
3. Remove unknown attributes.
4. Remove illegal content.
@@ -270,6 +270,9 @@ on the standard mode. The main difference is that :xml mode won't try to output
tags which are friendlier for browsers. For example, if an opening and closing
<tt>br</tt> tag is found, XML mode won't try to turn that into an empty element.
+XML mode also doesn't downcase the tags and attributes for you. So pay attention
+to case, friends.
+
The primary way to use Hpricot's XML mode is to call the Hpricot.XML method:
doc = open("http://redhanded.hobix.com/index.xml") do |f|
View
@@ -172,10 +172,36 @@ def wrap(str = nil, &blk)
end
end
- def attr key, value = nil
- if value
+ # Gets and sets attributes on all matched elements.
+ #
+ # Pass in a +key+ on its own and this method will return the string value
+ # assigned to that attribute for the first elements. Or +nil+ if the
+ # attribute isn't found.
+ #
+ # doc.search("a").attr("href")
+ # #=> "http://hacketyhack.net/"
+ #
+ # Or, pass in a +key+ and +value+. This will set an attribute for all
+ # matched elements.
+ #
+ # doc.search("p").attr("class", "basic")
+ #
+ # You may also use a Hash to set a series of attributes:
+ #
+ # (doc/"a").attr(:class => "basic", :href => "http://hackety.org/")
+ #
+ # Lastly, a block can be used to rewrite an attribute based on the element
+ # it belongs to. The block will pass in an element. Return from the block
+ # the new value of the attribute.
+ #
+ # records.attr("href") { |e| e['href'] + "#top" }
+ #
+ # This example adds a <tt>#top</tt> anchor to each link.
+ #
+ def attr key, value = nil, &blk
+ if value or blk
each do |el|
- el.set_attribute(key, value)
+ el.set_attribute(key, value || blk[el])
end
return self
end
@@ -186,7 +212,13 @@ def attr key, value = nil
return self[0].get_attribute(key)
end
end
+ alias_method :set, :attr
+ # Adds the class to all matched elements.
+ #
+ # (doc/"p").add_class("bacon")
+ #
+ # Now all paragraphs will have class="bacon".
def add_class class_name
each do |el|
next unless el.respond_to? :get_attribute
@@ -196,26 +228,37 @@ def add_class class_name
self
end
- # Sets an attribute for all elements in this list. You may use
- # a simple pair (<em>attribute name</em>, <em>attribute value</em>):
+ # Remove an attribute from each of the matched elements.
#
- # doc.search('p').set(:class, 'outline')
+ # (doc/"input").remove_attr("disabled")
#
- # Or, use a hash of pairs:
+ def remove_attr name
+ each do |el|
+ next unless el.respond_to? :remove_attribute
+ el.remove_attribute(name)
+ end
+ self
+ end
+
+ # Removes a class from all matched elements.
#
- # doc.search('div#sidebar').set(:class => 'outline', :id => 'topbar')
+ # (doc/"span").remove_class("lightgrey")
#
- def set(k, v = nil)
- case k
- when Hash
- each do |node|
- k.each { |a,b| node.set_attribute(a, b) }
- end
- else
- each do |node|
- node.set_attribute(k, v)
+ # Or, to remove all classes:
+ #
+ # (doc/"span").remove_class
+ #
+ def remove_class name = nil
+ each do |el|
+ next unless el.respond_to? :get_attribute
+ if name
+ classes = el.get_attribute('class').to_s.split(" ")
+ el.set_attribute('class', (classes - [name]).uniq.join(" "))
+ else
+ el.remove_attribute("class")
end
end
+ self
end
ATTR_RE = %r!\[ *(?:(@)([\w\(\)-]+)|([\w\(\)-]+\(\))) *([~\!\|\*$\^=]*) *'?"?([^\]'"]*)'?"? *\]!i
View
@@ -37,10 +37,27 @@ def test_add_class
def test_change_attributes
all_ps = (@basic/"p").attr("title", "Some Title")
all_as = (@basic/"a").attr("href", "http://my_new_href.com")
+ all_lb = (@basic/"link").attr("href") { |e| e.name }
assert_changed(@basic, "p", all_ps) {|p| p.attributes["title"] == "Some Title"}
assert_changed(@basic, "a", all_as) {|a| a.attributes["href"] == "http://my_new_href.com"}
+ assert_changed(@basic, "link", all_lb) {|a| a.attributes["href"] == "link" }
end
+ def test_remove_attr
+ all_rl = (@basic/"link").remove_attr("href")
+ assert_changed(@basic, "link", all_rl) { |link| link['href'].nil? }
+ end
+
+ def test_remove_class
+ all_c1 = (@basic/"p[@class*='last']").remove_class("last")
+ assert_changed(@basic, "p[@class*='last']", all_c1) { |p| p['class'] == 'final' }
+ end
+
+ def test_remove_all_classes
+ all_c2 = (@basic/"p[@class]").remove_class
+ assert_changed(@basic, "p[@class]", all_c2) { |p| p['class'].nil? }
+ end
+
def assert_changed original, selector, set, &block
assert set.all?(&block)
assert Hpricot(original.to_html).search(selector).all?(&block)

0 comments on commit 98411be

Please sign in to comment.