Ruby on Rails – Paperclip和动态参数

我正在使用Paperclip为Ruby on Rails编写一些图像上传代码,我有一个可行的解决方案,但它非常hacky所以我真的很感激如何更好地实现它的建议。 我有一个’资产’类,其中包含有关上传图像的信息,包括Paperclip附件,以及封装尺寸信息的’Generator’类。 每个“项目”都有多个资产和发电机; 所有资产应根据每台发电机规定的尺寸resize; 因此,每个项目都有一定的规模,其所有资产都应具备。

发电机型号:

class Generator < ActiveRecord::Base attr_accessible :height, :width belongs_to :project def sym "#{self.width}x#{self.height}".to_sym end end 

资产模型:

 class Asset  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 

我遇到的问题是鸡蛋问题:新创建的Asset不知道在正确实例化之后要使用哪些生成器(大小规范)。 我尝试使用@ project.assets.build,但是在资产获得项目关联集之前,Paperclip代码仍然执行,并且在我身上没有。

‘if @generators == nil’hack是这样的,更新方法将在没有进一步黑客攻击的情况下工作。

总而言之,它感觉非常糟糕。 任何人都可以建议如何以更明智的方式写这个,甚至是采取这种方式的方法吗?

提前致谢! 🙂

我试图在一个项目中遇到相同的Paperclip鸡/蛋问题,该项目试图使用基于具有多态关系的相关模型的动态样式。 我已经根据您现有的代码调整了我的解决方案。 解释如下:

 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的值@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_attributes用于Project模型中的assets关系(目前我正在使用它 – 在创建/编辑项目时上传资产)。

这种方法有一些缺点(例如,validationPaperclip imageAsset实例的有效性相关是棘手的),但它是最好的我可以想出没有猴子修补Paperclip以某种方式推迟执行style方法直到在填充关联信息之后。

我会密切关注这个问题,看看是否有人能更好地解决这个问题!


至少,如果您选择继续使用相同的解决方案,您可以对Asset#styles方法进行以下风格改进:

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

与现有方法完全相同,但更简洁。

虽然我真的很喜欢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 

我刚刚解决了类似的问题。 在我的“styles”lambda中,我根据“category”属性的值返回不同的样式。 问题是Image.new(attrs)和image.update_attributes(attrs)没有按可预测的顺序设置属性,因此我不能保证image.category在我的样式lambda之前会有一个值叫做。 我的解决方案是覆盖我的Image模型中的attributes =(),如下所示:

 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 

这确保了paperclip属性在其他属性之后设置,因此可以在:style lambda中使用它们。

在回形针属性“手动”设置的情况下,它显然无济于事。 但是,在这种情况下,您可以通过指定合理的顺序来帮助自己。 在我的情况下,我可以写:

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

(Paperclip 2.7.4,rails 3,ruby 1.8.7)