Возникли проблемы с Nokogiri и Rails - PullRequest
1 голос
/ 06 мая 2011

Занимался этим некоторое время. Если я скажу Категория просто создать, все работает нормально. Если я скажу это 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 Ответ

3 голосов
/ 06 мая 2011

Резюме

Вместо этих строк:

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 показывать вам только значение, но это не значит, что это на самом деле строка.)

Как показано выше, вам нужно использовать метод text (или value или content) на узлах Attr, чтобы получить строковое значение, которое вы действительно хотели:

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

В качестве альтернативы (и более просто) используйте метод Element#[] для непосредственного извлечения значения атрибута элемента:

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