Nokogiri XML导入饲料组织?

时间:2011-02-19 12:54:12

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

我构建了一个依赖于我目前使用Nokogiri解析的XML Feed的网站。虽然我的管理控制器中目前有所有代码,所以我可以通过URL /admin/import/实际调用导入。

我不禁想到这不属于控制器。有没有更好的方法来执行此操作,即将代码移动到独立的import.rb文件中,以便只能从控制台访问?如果是这样,我需要在/lib/目录中放置此文件?

以下是代码段:

class AdminController < ApplicationController

    def import
      f = File.open("#{Rails.root}/public/feed.xml")
      @doc = Nokogiri::XML(f)
      f.close

      ignore_list = [] # ignore list

      @doc.xpath("/*/product[not(name = following-sibling::product/name)]").each do |node|
        if !ignore_list.include? node.xpath("./programName").inner_text.strip
          Product.create(:name => clean_field(node.xpath("./name").inner_text).downcase, 
          :description => clean_field(node.xpath("./description").inner_text),
          :brand => Brand.find_or_create_by_name(clean_field_key(node.xpath("./brand").inner_text).downcase),         
          :merchant => Merchant.find_or_create_by_name(clean_field_key(node.xpath("./programName").inner_text).downcase),     
          :image => node.xpath("./imageUrl").inner_text.strip,
          :link => node.xpath("./productUrl").inner_text.strip,
          :category => Category.find_or_create_by_name(clean_field_key(node.xpath("./CategoryName").inner_text).downcase),
          :price => "£" + node.xpath("./price").inner_text.strip)
          print clean_field(node.xpath("./name").inner_text).downcase + "\n"       
        end
      end
    end
end

2 个答案:

答案 0 :(得分:4)

您的代码听起来像是作为Rails运行脚本运行时效果很好。它们是在您网站的正常Rails流程之外运行的脚本,但可以完全访问ActiveRecord设置,因此您可以轻松访问数据库。

我不认为Rails对文件的位置和所有其他文件一样严格,但是我会在'app'下创建一个名为'scripts'的子目录并将其放在那里。保持整洁的目录结构对维护是一件好事。

您没有说您是在运行Rails 3还是以前的版本。如果您正在运行Rails 3,请在Rails应用程序的命令行中键入rails runner -h以获取更多信息。

有些人认为脚本应该使用rake运行,我同意 IF 他们正在操纵文件和文件夹,并对应用程序运行的rails-space进行一般维护。如果你'重新执行作为数据库管理的一部分的定期任务,或者,在您的情况下,检索用于支持您的应用程序的内容,我认为它应该是一个“跑步者”任务。

您可以构建功能,这样您仍然可以触发代码通过URL运行,但我认为有可能滥用它,特别是如果您可以覆盖所需数据或用重复/冗余数据填充数据库。我认为通过操作系统发起的cron定期运行任务会更好,只是为了保持良好的间隔,或者只是手动运行。如果您通过URL访问保持可用,我建议您使用密码来避免滥用。

最后,作为一直在这么做的人,我建议您在代码中使用一些结构和对齐方式:

Product.create(
  :name        => clean_field(node.xpath("./name").inner_text).downcase,
  :description => clean_field(node.xpath("./description").inner_text),
  :brand       => Brand.find_or_create_by_name(clean_field_key(node.xpath("./brand").inner_text).downcase),
  :merchant    => Merchant.find_or_create_by_name(clean_field_key(node.xpath("./programName").inner_text).downcase),
  :image       => node.xpath("./imageUrl").inner_text.strip,
  :link        => node.xpath("./productUrl").inner_text.strip,
  :category    => Category.find_or_create_by_name(clean_field_key(node.xpath("./CategoryName").inner_text).downcase),
  :price       => "£" + node.xpath("./price").inner_text.strip
)

简单的对齐可以帮助您维护代码,或者帮助保持最终维护代码的人员的理智。我可能会看起来像:

Product.create(
  :name        => clean_field( node.xpath( "./name"        ).inner_text ).downcase,
  :description => clean_field( node.xpath( "./description" ).inner_text ),

  :brand       => Brand.find_or_create_by_name(    clean_field_key( node.xpath( "./brand"       ).inner_text ).downcase ),
  :merchant    => Merchant.find_or_create_by_name( clean_field_key( node.xpath( "./programName" ).inner_text ).downcase ),

  :image       => node.xpath( "./imageUrl"   ).inner_text.strip,
  :link        => node.xpath( "./productUrl" ).inner_text.strip,

  :category    => Category.find_or_create_by_name( clean_field_key( node.xpath( "./CategoryName" ).inner_text ).downcase ),

  :price       => "£" + node.xpath( "./price" ).inner_text.strip
)

但那只是我。我喜欢有更多的空格,特别是当有嵌套方法时,我喜欢在常用/类似函数之间进行一些垂直对齐。我发现它可以更轻松地扫描代码并查看任何差异,这有助于您在调试或查找特定内容时。再一次,这只是我的偏好,但这是我多年来用很多不同语言编写代码时学到的东西。

答案 1 :(得分:3)

我的一些应用程序中有类似的功能。我通常把这个逻辑放在一个类中,例如Importer。这样我就可以从控制台使用它,并制作一个访问控制的控制器动作,让其他人从网上使用它。你把这个类放在哪里并不是非常重要,只要它在应用程序的加载路径中。我倾向于将我的应用程序/模型与/ lib放在一起,这样我就不需要在进行更改时重新加载应用程序。