Been at this for a while. If i tell Category to just create, everything works fine. If I tell it to find_or_create I get errors.
These work:
puts topic.at_xpath("@topicid")
puts topic.at_xpath("@topicname")
and
Category.create!(:topic_id => topic.at_xpath("@topicid"), :name => topic.at_xpath("@topicname"))
But these don't:
Category.find_by_name(topic.at_xpath("@topicname"))
or
Category.find_or_create_by_topic_id_and_name(topic.at_xpath("@topicid"), topic.at_xpath("@topicname"))
Where am I messing up?
class FeedEntry < ActiveRecord::Base
require 'nokogiri'
require 'open-uri'
has_many :category_feeds
has_many :categories, :through => :category_feeds
accepts_nested_attributes_for :categories, :allow_destroy => true, :reject_if => proc { |obj| obj.blank? }
def self.nokogiri_get_feed
url = "http://some_feed.com/atom_feed"
doc = Nokogiri::HTML(open(url))
doc.remove_namespaces!
doc.search('feed entry').each do |item|
unless exists?开发者_运维问答 :guid => item.css('id').text
create!(:name => item.css('title').text, :summary => item.css('title').text, :url => item.at_css("link")[:href], :published_at => item.css('updated').text, :guid => item.css('id').text)
item.xpath('content').each do |i|
i.css('topic').each do |topic|
id = topic.at_xpath("@topicid")
name = topic.at_xpath("@topicname")
update_attributes!(:categories=>[Category.find_or_create_by_topic_id_and_name(id, name)])
end
end
end
end
end
end
errors are:
ruby-1.9.2-p180 :001 > FeedEntry.nokogiri_get_feed
TypeError: Cannot visit Nokogiri::XML::Attr
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:21:in `rescue in visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:260:in `visit_Arel_Nodes_Equality'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:120:in `visit_Arel_Nodes_Grouping'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `block in visit_Arel_Nodes_SelectCore'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `map'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:91:in `visit_Arel_Nodes_SelectCore'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `block in visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `map'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:77:in `visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/sqlite.rb:7:in `visit_Arel_Nodes_SelectStatement'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:15:in `visit'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/visitor.rb:5:in `accept'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/arel-2.0.9/lib/arel/visitors/to_sql.rb:19:in `block in accept' ... 11 levels...
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:35:in `block (2 levels) in nokogiri_get_feed'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:239:in `block in each'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `upto'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:33:in `block in nokogiri_get_feed'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:239:in `block in each'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `upto'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/nokogiri-1.4.4/lib/nokogiri/xml/node_set.rb:238:in `each'
from /Users/pca/projects/cdapp/cdrails/app/models/feed_entry.rb:30:in `nokogiri_get_feed'
from (irb):1
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands/console.rb:44:in `start'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands/console.rb:8:in `start'
from /Users/pca/.rvm/gems/ruby-1.9.2-p180/gems/railties-3.0.3/lib/rails/commands.rb:23:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'
Summary
Instead of these lines:
id = topic.at_xpath("@topicid")
name = topic.at_xpath("@topicname")
Use these instead:
id = topic['topicid']
name = topic['topicname']
Explanation
Let's look at a simple test case:
require 'nokogiri'
xml = Nokogiri::XML("<root foo='bar' />")
foo = xml.root.at_xpath('@foo')
puts foo
#=> bar
p foo
#=> #<Nokogiri::XML::Attr:0x15c1d64 name="foo" value="bar">
p foo.text
#=> "bar"
p xml.root['foo']
#=> "bar"
As you can see from the above, selecting an attribute via XPath actually gives you an Attr
node, which is not the same as the string value of that attribute. (Using puts
causes the to_s
method of the Attr to show you only the value, but that doesn't mean that it's actually a string.)
As shown above, you need to use the text
method (or value
or content
) on the Attr nodes to get the string value back that you really wanted:
id = topic.at_xpath("@topicid").text
name = topic.at_xpath("@topicname").text
Alternatively (and more simply) use the Element#[]
method to fetch the value of an attribute off of an element directly:
id = topic['topicid']
name = topic['topicname']
精彩评论