2013-01-13 76 views
8

我正在用Paperclip爲Ruby on Rails編寫一些圖像上傳代碼,並且我有一個工作解決方案,但它非常黑客,所以我非常感謝關於如何更好地實現它。我有一個'Asset'類,其中包含關於上傳圖像的信息,包括Paperclip附件和封裝尺寸信息的'Generator'類。每個「項目」有多個資產和發電機;所有資產應根據每個發電機指定的尺寸進行調整;因此每個項目都有一定的規模,其所有資產都應該有。Ruby on Rails - 回形針和動態參數

發生器型號:

class Generator < ActiveRecord::Base 
    attr_accessible :height, :width 

    belongs_to :project 

    def sym 
    "#{self.width}x#{self.height}".to_sym 
    end 
end 

資產模型:

class Asset < ActiveRecord::Base 
    attr_accessible :filename, 
    :image # etc. 
    attr_accessor :generators 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.styles } 

    belongs_to :project 

    # this is utterly horrendous 
    def styles 
    s = {} 
    if @generators == nil 
     @generators = self.project.generators 
    end 

    @generators.each do |g| 
     s[g.sym] = "#{g.width}x#{g.height}" 
    end 
    s 
    end 
end 

資產控制器創建方法:

def create 
    @project = Project.find(params[:project_id]) 
    @asset = Asset.new 
    @asset.generators = @project.generators 
    @asset.update_attributes(params[:asset]) 
    @asset.project = @project 
    @asset.uploaded_by = current_user 

    respond_to do |format| 
     if @asset.save_(current_user) 
     @project.last_asset = @asset 
     @project.save 

     format.html { redirect_to project_asset_url(@asset.project, @asset), notice: 'Asset was successfully created.' } 
     format.json { render json: @asset, status: :created, location: @asset } 
     else 
     format.html { render action: "new" } 
     format.json { render json: @asset.errors, status: :unprocessable_entity } 
     end 
    end 
    end 

我遇到的問題是雞 - 蛋的問題:新創建的資產不知道使用哪個生成器(大小規格),直到它被實例化爲止LY。我嘗試過使用@project.assets.build,但在資產獲取其項目關聯集並在我之前刪除之前,Paperclip代碼仍然被執行。

'if @generators == nil'hack是這樣的更新方法將工作,而不會在控制器中進一步黑客攻擊。

總而言之,感覺非常糟糕。任何人都可以建議如何以更明智的方式寫這個,或者甚至是採取這種方式?

在此先感謝! :)

+0

這個問題,它的答案,幫助了很多建造相同的功能在我的應用程序。感謝大家! :) – Gediminas

回答

15

我碰到一個項目試圖使用基於多態關係相關模型的動態樣式項目相同的回形針雞/蛋問題。我已將我的解決方案改編爲現有的代碼。說明如下:

class Asset < ActiveRecord::Base 
    attr_accessible :image, :deferred_image 
    attr_writer :deferred_image 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.styles } 

    belongs_to :project 

    after_save :assign_deferred_image 

    def styles 
    project.generators.each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" } 
    end 

    private 
    def assign_deferred_image 
    if @deferred_image 
     self.image = @deferred_image 
     @deferred_image = nil 
     save! 
    end 
    end 
end 

基本上得到解決回形針試圖檢索動態風格的項目相關信息已經被傳播之前,可以將所有的image屬性分配給非回形針屬性(問題在這種情況下,我已將它命名爲deferred_image)。 after_save掛鉤將@deferred_image的值分配給self.image,該值將啓動所有Paperclip爵士樂。

你的控制器變爲:

# AssetsController 
def create 
    @project = Project.find(params[:project_id]) 
    @asset = @project.assets.build(params[:asset]) 
    @asset.uploaded_by = current_user 

    respond_to do |format| 
    # all this is unrelated and can stay the same 
    end 
end 

和視圖:

<%= form_for @asset do |f| %> 
    <%# other asset attributes %> 
    <%= f.label :deferred_upload %> 
    <%= f.file_field :deferred_upload %> 
    <%= f.submit %> 
<% end %> 

該解決方案還允許使用accepts_nested_attributesProject模型assets關係(這是我目前如何使用它 - 作爲創建/編輯項目的一部分上傳資產)。

也有一些缺點,以這種方式(例如,驗證回形針image相對於Asset實例的有效性得到棘手),但它是我能想出短猴子的修補回形針不知何故推遲執行最佳style方法,直到關聯信息被填充之後。

我會密切關注這個問題,看看有沒有人有更好的解決方案來解決這個問題!


最起碼,如果你選擇使用相同的解決方案,以保持,您可以進行以下文體改善您的Asset#styles方法:

def styles 
    (@generators || project.generators).each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" } 
end 

是否完全一樣的事情和您現有的方法,但更簡潔。

+0

嘿,那裏,謝謝你的回答! :) – Dave

5

雖然我非常喜歡Cade的解決方案,但只是一個建議。看起來'風格'屬於一個項目......那麼你爲什麼不在那裏計算髮電機呢?

例如:

class Asset < ActiveRecord::Base 
    attr_accessible :filename, 
    :image # etc. 
    attr_accessor :generators 

    has_attached_file :image, 
    :styles => lambda { |a| a.instance.project.styles } 
end 


class Project < ActiveRecord::Base 
    .... 

    def styles 
    @generators ||= self.generators.inject {} do |hash, g| 
     hash[g.sym] = "#{g.width}x#{g.height}" 
    end 
    end 
end 

編輯:試着改變你的控制器(假設該項目擁有衆多資產):

def create 
    @project = Project.find(params[:project_id]) 
    @asset = @project.assets.new 
    @asset.generators = @project.generators 
    @asset.update_attributes(params[:asset]) 
    @asset.uploaded_by = current_user 
end 
+0

雖然找到實際樣式方法的問題很有趣,但這不適用於新記錄。在樣式lambda中,'a.instance.project'在保存記錄之前是'nil',所以你會在'nil'上得到'NoMethodError'。 – Cade

+0

我編輯了我的解決方案...我確定該實例必須指向@asset變量?如果你像我一樣改變控制器,那麼它應該已經在一個隱藏的實例變量中使用回形針來使用回形針... –

+0

Rails在項目模型,樣式操作上拍攝了一個錯誤。說:'語法錯誤,意想不到的keyword_do_block,期待keyword_end' – Gediminas

2

我剛剛解決了類似的問題,我有。 在我的「styles」lambda中,我根據「category」屬性的值返回不同的樣式。但問題是Image.new(attrs)和image.update_attributes(attrs)沒有以可預測的順序設置屬性,因此我無法保證image.category在我的樣式lambda之前會有一個值叫做。我的解決辦法是重寫屬性=()在我的圖片模型如下:

class Image 
    ... 
    has_attached_file :image, :styles => my_lambda, ... 
    ... 
    def attributes=(new_attributes, guard_protected_attributes = true) 
    return unless new_attributes.is_a?(Hash) 
    if new_attributes.key?("image") 
     only_attached_file = { 
     "image" => new_attributes["image"] 
     } 
     without_attached_file = new_attributes 
     without_attached_file.delete("image") 
     # set the non-paperclip attributes first 
     super(without_attached_file, guard_protected_attributes) 
     # set the paperclip attribute(s) after 
     super(only_attached_file, guard_protected_attributes) 
    else 
     super(new_attributes, guard_protected_attributes) 
    end 
    end 
    ... 
end 

這確保了紙夾屬性的其他屬性設置後,從而可在使用它們:式拉姆達。

這顯然不會幫助回形針屬性是「手動」設置的情況。但在這些情況下,您可以通過指定明智的訂單來幫助自己。在我來說,我可以這樣寫:

image = Image.new 
image.category = "some category" 
image.image = File.open("/somefile") # styles lambda can use the "category" attribute 
image.save! 

(回形針2.7.4,Rails 3中,紅寶石1.8.7)

相關問題