【问题标题】:Ruby on Rails - Paperclip and dynamic parametersRuby on Rails - 回形针和动态参数
【发布时间】:2013-02-06 06:41:06
【问题描述】:

我正在使用 Paperclip 为 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

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

'if @generators == nil' hack 是这样,更新方法无需在控制器中进一步破解即可工作。

总而言之,感觉很糟糕。任何人都可以建议如何以更明智的方式编写此内容,甚至可以为此类事情采取一种方法吗?

提前致谢! :)

【问题讨论】:

  • 这个问题及其答案对在我的应用程序中构建相同的功能有很大帮助。谢谢大家! :)

标签: ruby-on-rails ruby paperclip


【解决方案1】:

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

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 爵士乐。

你的控制器变成:

# 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 关系(这是我目前使用它的方式 - 作为创建/编辑项目的一部分上传资产)。

这种方法有一些缺点(例如,验证 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

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

【讨论】:

    【解决方案2】:

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

    【讨论】:

    • 虽然在哪里找到实际样式方法的问题是一个有趣的问题,但这不适用于新记录。在样式 lambda 中,a.instance.project 将是 nil,直到记录被保存,所以你会在 nil 上得到一个 NoMethodError
    • 我编辑了我的解决方案...我确定该实例必须指向@asset 变量?如果您像我一样更改控制器,那么它应该已经将项目包含在一个隐藏的实例变量中,供回形针使用...
    • Rails 在项目模型、样式操作上拍摄错误。说:syntax error, unexpected keyword_do_block, expecting keyword_end
    【解决方案3】:

    我刚刚解决了一个类似的问题。 在我的“样式” lambda 中,我根据“类别”属性的值返回不同的样式。但问题是 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
    

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

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

    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,ruby 1.8.7)

    【讨论】:

      猜你喜欢
      • 2015-11-14
      • 2017-01-14
      • 1970-01-01
      • 2015-06-26
      • 1970-01-01
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多