Ruby on Rails - 回形针和动态参数 [英] Ruby on Rails - Paperclip and dynamic parameters
问题描述
我正在使用 Paperclip 为 Ruby on Rails 编写一些图像上传代码,我有一个可行的解决方案,但它非常笨拙,因此我非常感谢有关如何更好地实现它的建议.我有一个包含有关上传图像的信息的资产"类,包括回形针附件,以及一个封装尺寸信息的生成器"类.每个项目"都有多个资产和生成器;应根据每个生成器指定的大小调整所有资产的大小;因此,每个项目都有其所有资产应具有的特定大小集.
I'm writing some image upload code for Ruby on Rails with Paperclip, and I've got a working solution but it's very hacky so I'd really appreciate advice on how to better implement it. I have an 'Asset' class containing information about the uploaded images including the Paperclip attachment, and a 'Generator' class that encapsulates sizing information. Each 'Project' has multiple assets and generators; all Assets should be resized according to the sizes specified by each generator; each Project therefore has a certain set of sizes that all of its assets should have.
发电机型号:
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
我遇到的问题是鸡蛋问题:新创建的资产在正确实例化之前不知道要使用哪个生成器(大小规格).我尝试使用 @project.assets.build,但是在 Asset 设置其项目关联之前仍然执行 Paperclip 代码并且将其清除.
The problem I am having is a chicken-egg issue: the newly created Asset doesn't know which generators (size specifications) to use until after it's been instantiated properly. I tried using @project.assets.build, but then the Paperclip code is still executed before the Asset gets its project association set and nils out on me.
'if @generators == nil' hack 是为了使 update 方法无需进一步修改控制器即可工作.
The 'if @generators == nil' hack is so the update method will work without further hacking in the controller.
总而言之,感觉很糟糕.任何人都可以建议如何以更明智的方式编写此内容,或者甚至可以针对此类事情采取一种方法吗?
All in all it feels pretty bad. Can anyone suggest how to write this in a more sensible way, or even an approach to take for this kind of thing?
提前致谢!:)
推荐答案
我在尝试使用基于具有多态关系的关联模型的动态样式的项目中遇到了相同的 Paperclip 鸡/蛋问题.我已经根据您现有的代码调整了我的解决方案.解释如下:
I ran into the same Paperclip chicken/egg issue on a project trying to use dynamic styles based on the associated model with a polymorphic relationship. I've adapted my solution to your existing code. An explanation follows:
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
基本上,要解决 Paperclip 在传播项目关系信息之前尝试检索动态样式的问题,您可以将所有 image
属性分配给非 Paperclip 属性(在这个实例,我把它命名为 deferred_image
).after_save
钩子将 @deferred_image
的值分配给 self.image
,这将启动所有 Paperclip 爵士乐.
Basically, to get around the issue of Paperclip trying to retrieve the dynamic styles before the project relation information has been propagated, you can assign all of the image
attributes to a non-Paperclip attribute (in this instance, I have name it deferred_image
). The after_save
hook assigns the value of @deferred_image
to self.image
, which kicks off all the Paperclip jazz.
您的控制器变为:
# 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_attributes
用于 Project
模型中的 assets
关系(这是我目前使用它的方式 -上传资产作为创建/编辑项目的一部分).
This solution also allows using accepts_nested_attributes
for the assets
relation in the Project
model (which is currently how I'm using it - to upload assets as part of creating/editing a Project).
这种方法有一些缺点(例如,验证回形针 image
与 Asset
实例的有效性相关变得棘手),但这是我能做到的最好的想出没有猴子修补 Paperclip 以某种方式将 style
方法的执行推迟到关联信息填充之后.
There are some downsides to this approach (ex. validating the Paperclip image
in relation to the validity of the Asset
instance gets tricky), but it's the best I could come up with short of monkey patching Paperclip to somehow defer execution of the style
method until after the association information had been populated.
我会一直关注这个问题,看看有没有人有更好的解决方案!
I'll be keeping an eye on this question to see if anyone has a better solution to this problem!
至少,如果您选择继续使用相同的解决方案,您可以对 Asset#styles
方法进行以下风格改进:
At the very least, if you choose to keep using your same solution, you can make the following stylistic improvement to your Asset#styles
method:
def styles
(@generators || project.generators).each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" }
end
与您现有的方法完全相同,但更简洁.
Does the exact same thing as your existing method, but more succinctly.
这篇关于Ruby on Rails - 回形针和动态参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!