我有这个模型用于附加到另一个模型(Work
)的图片:
class WorkPicture < ActiveRecord::Base
after_initialize :default_values
after_update :reprocess_picture, if: :cropping_changed?
belongs_to :work, inverse_of: :pictures
has_attached_file :picture, styles: lambda { |attachment| { big: { geometry: "1500x>", format: :png },
thumb: attachment.instance.thumb_options } },
url: "/pictures/:hash.:extension",
hash_secret: ENV["PAPERCLIP_SECRET_KEY"]
validates_attachment :picture, presence: true,
content_type: { content_type: /image/ }
scope :illustrative, -> { where(is_cover: false) }
# Default values
def default_values
self.is_cover = false if self.is_cover.nil?
self.crop_x = 0 if self.crop_x.nil?
self.crop_y = 0 if self.crop_y.nil?
end
def thumb_options
return { geometry: "", format: :png, convert_options: "-crop 360x222+#{crop_x.to_i}+#{crop_y.to_i}" }
end
private
def cropping_changed?
self.crop_x_changed? || self.crop_y_changed?
end
def reprocess_picture
picture.reprocess!
end
end
这些图片有双重用途:如果is_cover
设置为false
,则会将其显示在相关作品的图库页面中。如果is_cover
设置为true
,则会在列出所有作品的页面中裁剪并将其用作相关作品的“图标”。
在工作编辑页面上,您可以更改裁剪的x和y坐标。这意味着一旦新坐标进入,图像应该被重新处理,以便显示正确的区域。
为此,我在模型上设置了两个属性crop_x
和crop_y
,它们存储裁剪窗口的位置。通过after_update
,我首先检查这些坐标是否已更改,如果是,我告诉Paperclip重新处理图像以正确裁剪。
运行此代码时,执行进入无限循环。我想我已经确定了原因:after_update
doesn't clear ActiveRecord::Dirty
属性(例如self.crop_x_changed?
),以使其对开发人员更有用,但是当我致电{图片上的{1}} Paperclip本身会对我的模型进行更改并调用reprocess!
,这会导致save
回调再次被触发。由于after_update
和self.crop_x_changed?
从此链的开头仍然是self.crop_y_changed?
(这是因为它们似乎只在true
运行后才被清除),这会导致我的代码再次致电after_update
,继续这无休止的循环。
我已尝试过许多这类代码的类似变体,但似乎无论是循环还是失败。有没有办法让我的代码正确执行这个功能?
答案 0 :(得分:0)
感谢朋友的帮助,我设法找到了一种方法让它发挥作用。这就是我解决它的方法:
我在WorkPicture::Recropper
中创建了一个新课程app/models/work_picture/recropper.rb
(为了进行比较,work_picture.rb
文件位于app/models/
)。这是代码:
class WorkPicture::Recropper
def initialize work_picture
@work_picture = work_picture
@crop_x = @work_picture.crop_x
@crop_y = @work_picture.crop_y
end
def recrop
@work_picture.reload
if @work_picture.crop_x != @crop_x || @work_picture.crop_y != @crop_y
@work_picture.picture.reprocess!
end
end
end
我将控制器的更新操作更改为使用WorkPicture::Recropper
:
class WorksController < ApplicationController
# ... Other actions ...
def update
@work = Work.find(params[:id])
recropper = WorkPicture::Recropper.new(@work.cover_picture)
if @work.update_attributes(work_params)
recropper.recrop
redirect_to @work
else
render :edit
end
end
private
def work_params
params.require(:work).permit(:name, :description, :category_id, pictures_attributes: [:crop_x, :crop_y, :is_cover, :picture, :id])
end
end
简单地说,我做的是通过在更新其属性之前将封面图片传递给它的实例,以便它有机会存储旧的裁剪坐标。更新属性后,recrop
方法会重新加载模型,并将属性的新值与其存储的值进行比较。如果他们改变了,它会重新处理图片。
此解决方案有效,但可能不是最干净的解决方案。如果有人有更好的想法,请告诉我。