Ruby on Rails - Paperclip和动态参数

Dav*_*ave 8 ruby ruby-on-rails paperclip

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

发电机型号:

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

  belongs_to :project

  def sym
    "#{self.width}x#{self.height}".to_sym
  end
end
Run Code Online (Sandbox Code Playgroud)

资产模型:

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
Run Code Online (Sandbox Code Playgroud)

资产控制器创建方法:

  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
Run Code Online (Sandbox Code Playgroud)

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

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

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

提前致谢!:)

Cad*_*ade 15

我在一个试图使用动态样式的项目上遇到了相同的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
Run Code Online (Sandbox Code Playgroud)

基本上,为了解决Paperclip尝试在项目关系信息传播之前检索动态样式的问题,您可以将所有image属性分配给非Paperclip属性(在本例中,我已将其命名deferred_image).所述after_save钩分配的值@deferred_imageself.image,这序幕所有回形针爵士乐.

你的控制器变成:

# 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
Run Code Online (Sandbox Code Playgroud)

并且观点:

<%= form_for @asset do |f| %>
  <%# other asset attributes %>
  <%= f.label :deferred_upload %>
  <%= f.file_field :deferred_upload %>
  <%= f.submit %>
<% end %>
Run Code Online (Sandbox Code Playgroud)

此解决方案还允许使用模型中accepts_nested_attributesassets关系Project(目前我正在使用它 - 在创建/编辑项目时上载资产).

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

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


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

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

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


Joe*_*Pym 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
Run Code Online (Sandbox Code Playgroud)

编辑:尝试将控制器更改为(假设项目有很多资产):

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
Run Code Online (Sandbox Code Playgroud)