与Nokogiri和Rails有困难

时间:2011-05-06 17:01:19

标签: ruby ruby-on-rails-3 nokogiri

已经有一段时间了。如果我告诉Category只是创建,一切正常。如果我告诉它find_or_create我会收到错误。

这些工作:

puts topic.at_xpath("@topicid")
puts topic.at_xpath("@topicname")

Category.create!(:topic_id => topic.at_xpath("@topicid"), :name => topic.at_xpath("@topicname"))

但这些不是:

Category.find_by_name(topic.at_xpath("@topicname"))

Category.find_or_create_by_topic_id_and_name(topic.at_xpath("@topicid"), topic.at_xpath("@topicname"))

我在哪里弄乱?

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

错误是:

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>'

1 个答案:

答案 0 :(得分:3)

摘要

而不是这些行:

id = topic.at_xpath("@topicid")
name = topic.at_xpath("@topicname")

请改用:

id   = topic['topicid']
name = topic['topicname']

说明

让我们看一个简单的测试用例:

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"

从上面可以看出,通过XPath选择属性实际上会为您提供一个Attr节点,该节点与该属性的字符串值不同。 (使用puts会导致Attr的to_s方法仅向您显示值,但这并不意味着它实际上是一个字符串。)

如上所示,您需要在Attr节点上使用text方法(或valuecontent)来获取您真正想要的字符串值:

id   = topic.at_xpath("@topicid").text
name = topic.at_xpath("@topicname").text

或者(更简单地)使用Element#[]方法直接从元素中获取属性的值:

id   = topic['topicid']
name = topic['topicname']