2010-05-01 68 views
4

我正在構建一個Rails站點,其中包括允許用戶構建自己的配方庫。食譜是手動輸入或通過鏈接到另一個網站(想想epicuriouscooks.com等)。我正在編寫腳本,這些腳本會從用戶鏈接中獲取這些站點的配方,到目前爲止(儘管存在法律問題),但這部分不會給我帶來任何麻煩。Rails架構問題

但是,我不確定在哪裏把我正在寫這些刮板腳本的代碼。我的第一個想法是把它放在食譜模型中,但它似乎有點過於參與到那裏去;圖書館或幫手會更合適嗎?

另外,正如我所提到的,我正在爲不同的食物網站建立幾種不同的刮刀。在我看來,優雅的方法是定義一個接口(或抽象基類),該接口確定一組用於構建給定鏈接的配方對象的方法,但我不確定最佳方法是什麼這裏也是。我如何構建這些面向對象的關係,代碼應該放在哪裏?

回答

2

你有這方面的明顯的兩面。首先是你將如何存儲食譜,這將是模型。顯然,模型不會欺騙其他網站,因爲他們有一個單一的責任:存儲有效​​的數據。您的控制器將啓動刮取和存儲過程,但不應該包含刮碼(儘管他們會調用它)。

在Ruby中,我們不會去抽象類或接口 - 它是鴨子類型的,所以您的scrapers實現一個已知方法或一組方法就足夠了 - 您的scraping引擎應該都是類似的,特別是他們公開的方法的條款。

你會把你的刮刀 - 這是蹩腳的答案 - 無論你想要的地方。 lib是好的,但如果你想製作一個插件可能不是一個壞主意。看到我的問題here - 由着名的Rails-guy Yehuda Katz提供了一個令人驚歎的答案 - 但是總的來說:沒有正確的答案。雖然有一些錯誤的。

0

通常,實際上不屬於MVC設計的實用程序類會被放入lib文件夾中。我也看到有人把它們放到models文件夾中,但lib確實是「正確」的地方。

然後,您可以根據需要在控制器內創建配方刮刀實例,將數據提供給模型。

0

並非應用程序/模型中的所有內容都必須是ActiveRecord模型。由於它們直接與應用程序的業務邏輯相關,因此它們屬於應用程序目錄,而不屬於lib目錄。他們也不是控制者,觀點或助手(幫助者可以單獨幫助觀點和看法)。所以,他們屬於應用程序/模型。我會確保命名空間,僅用於組織目的到應用程序/模型/刮板或類似的東西。

0

我會設置一個rake任務來刮取網站並創建新的rake任務。一旦這是工作,我會使用後臺處理器或cron作業來運行rake任務。

0

我會在lib中創建一個名爲scrapers的文件夾。然後在該文件夾中爲每個刮刀創建一個文件。調用這些epicurious,cooks等等。然後你可以定義一個基本刮板類,其中包含了所有刮板通用的共享方法。與下列相似

庫/鏟運機/底座。RB

class Scrapers::base 
    def shared_1() 
    end 
    def shared_2() 
    end 
    def must_implement1 
    raise NotImplemented 
    end 
    def must_implement2 
    raise NotImplemented 
    end 
end 

的lib /刮/ epicurious.rb

Class Epicurious < Base 
    def must_implement1 
    end 
    def must_implement2 
    end 
end 

然後使用Scrapers::Epicurious.new或調用一個類的方法你的控制器中調用內Scrapers::Base調用基於傳遞的參數相關的實施相關類別。

1

Scrape引擎應該是一個獨立的插件或寶石插件。對於骯髒和快速,你可以把它放在lib中。無論如何,這是慣例。它可能應該實現一個工廠類實例化不同類型取決於URL刮削器,因此對於客戶端使用,這將是簡單的:

Scraper.scrape(url) 

而且,如果這是一個長期運行的任務,你可能想考慮使用復職或延期工作將任務轉移到單獨的流程。

1

嘗試專注於在將它移動到gem/plugin之前先讓它工作。 另外,忘記接口/抽象類 - 只寫代碼做的事情。 你的模型應該知道的唯一事情是如果這是遠程配方,以及什麼是網址。 你可以把所有的刮碼放在app/scrapers中。下面是一個例子實施綱要:

class RecipePage 
    def new(url) 
    @url = url 
    @parser = get_parser 
    end 

    def get_attributes 
    raise "trying to scrape unknown site" unless @parser   
    @parser.recipe_attributes(get_html) 
    end 

    private  
    def get_html 
    #this uses your favorite http library to get html from the @url 
    end 

    def get_parser(url) 
    #this matches url to your class, ie returns domain camelized, or nil if you are not handling particular site yet 
    return EpicurusComParser 
    end 
end 

class EpicurusComParser 

    def self.recipe_attributes(html) 
    # this does the hard job of querying html and moving 
    # all the code to get title, text, image of recipe and return hash 
    { 
     :title => "recipe title", 
     :text => "recipe text", 
     :image => "recipe_image_url", 
    } 
    end 
end 
在模型

然後

class Recipe 
    after_create :scrape_recipe, :if => :recipe_url 

    private 

    def scrape_recipe 
    # do that in background - ie in DelayedJob 
    recipe_page = RecipePage.new(self.recipe_url) 
    self.update_attributes(recipe_page.get_attributes.merge(:scraped => true)) 
    end 
end 

然後你就可以創造更多的解析器,即CookComParser等