Скрепка: стиль в зависимости от модели (has_many полиморфные изображения)

Я установил свои модели для использования полиморфной модели изображения. Это прекрасно работает, однако мне интересно, можно ли изменить настройку стилей для каждой модели. Нашли несколько примеров, используя STI (Model < Image). Однако это не вариант для меня, потому что я использую отношение has_many.

Искусство

has_many :images, :as => :imageable

Изображение

belongs_to :imageable, :polymorphic => true
has_attached_file :file, :styles => { :thumb => "150x150>", :normal => "492x600>"}
                         #Change this setting depending on model

UPDATE

Я попытался запустить отладчик внутри метода Proc. Заполняются только поля, связанные с вложенным файлом:

run'irb(Image):006:0> a.instance => #<Image id: nil, created_at: nil, updated_at: nil, imageable_id: nil, imageable_type: nil, file_file_name: "IMG_9834.JPG", file_content_type: "image/jpeg", file_file_size: 151326, file_updated_at: "2010-10-30 08:40:23">

Это объект из ImageController # create

ImageController#create
@image => #<Image id: nil, created_at: nil, updated_at: nil, imageable_id: 83, imageable_type: "Art", file_file_name: "IMG_9834.JPG", file_content_type: "image/jpeg", file_file_size: 151326, file_updated_at: "2010-10-30 08:32:49">

Я использую paperclip (2.3.5) и Rails 3.0.1. Независимо от того, что я делаю, объект a.instance - это изображение с полями, связанными с включенным вложением. Любые идеи?

UPDATE2

Прочитав много на форуме Paperclip, я не считаю возможным доступ к экземпляру до его сохранения. Вы можете видеть только материал скрепки и что это.

Я столкнулся с этой проблемой, предварительно создав изображение с контроллера Image с фильтром до фильтра - без вложения

  before_filter :presave_image, :only => :create

  ...

  private

  def presave_image
    if @image.id.nil? # Save if new record / Arts controller sets @image
      @image = Image.new(:imageable_type => params[:image][:imageable_type], :imageable_id => params[:image][:imageable_id])
      @image.save(:validate => false)
      @image.file = params[:file] # Set to params[:image][:file] if you edit an image.
    end
  end

Ответ 1

Я очень опаздываю на вечеринку здесь, но я хотел кое-что уточнить о доступе к данным модели для всех, кто происходит с этим потоком. Я просто столкнулся с этой проблемой при использовании Paperclip для применения водяных знаков на основе данных модели и заработал после многих исследований.

Ты сказал:

Прочитав много на форуме Paperclip, я не считаю возможным доступ к экземпляру до его сохранения. Вы можете видеть только материал скрепки и что это.

Фактически вы можете увидеть данные модели, если они были установлены в объекте до назначения вашего вложения!

Ваши процессоры скрепки и многое другое вызывают при назначении вложения в вашей модели. Если вы полагаетесь на массовое присвоение (или нет, на самом деле), как только вложение присваивается значение, скрепка делает свою вещь.

Вот как я решил проблему:

В моей модели с приложением (Фото) я выполнил все атрибуты ЗА ИСКЛЮЧЕНИЕМ вложения attr_accessible, тем самым сохраняя привязку к назначению во время массового присваивания.

class Photo < ActiveRecord::Base
  attr_accessible :attribution, :latitude, :longitude, :activity_id, :seq_no, :approved, :caption

  has_attached_file :picture, ...
  ...
end

В моем методе создания контроллера (например) я вытащил picture из params, а затем создал объект. (Возможно, нет необходимости удалять изображение из params, так как оператор attr_accessible должен помешать picture быть связанным, но это не повредит). Затем я назначаю атрибут picture, после того, как были установлены все остальные атрибуты фотообъекта.

def create
  picture = params[:photo].delete(:picture)
  @photo = Photo.new(params[:photo])
  @photo.picture = picture

  @photo.save
  ...
end

В моем случае один из стилей для изображения вызывает применение водяного знака, который является текстовой строкой, содержащейся в атрибуте attribution. Прежде чем я сделал эти изменения кода, строка атрибуции никогда не применялась, а в коде водяного знака attachment.instance.attribution всегда nil. Ниже приведенные изменения сделали всю модель доступной внутри процессоров скрепки. Ключ должен назначить последний атрибут вложений.

Надеюсь, это поможет кому-то.

Ответ 2

Я нашел способ обхода стилей при создании. Ключ состоит в том, чтобы реализовать крюки before_post_process и after_save.

class Image < ActiveRecord::Base

  DEFAULT_STYLES = {
    medium: "300x300>", thumb: "100x100>"
  }

  has_attached_file :file, styles: ->(file){ file.instance.styles }


  validates_attachment_content_type :file, :content_type => /\Aimage\/.*\Z/

  # Workaround to pickup styles from imageable model
  # paperclip starts processing before all attributes are in the model
  # so we start processing after saving

  before_post_process ->{
    [email protected]_reprocessed.nil?
  }

  after_save ->{
    if [email protected]_reprocessed && (file_updated_at_changed? || imageable_type_changed?)
      @file_reprocessed = true
      file.reprocess!
    end
  }


  belongs_to :imageable, polymorphic: true

  def styles
    if imageable_class.respond_to?(:image_styles)
      imageable_class.image_styles
    end || DEFAULT_STYLES
  end

  def imageable_class
    imageable_type.constantize if imageable_type.present?
  end


end

Итак, вы должны определить метод класса image_styles в imageable_class В моем случае это было

class Property < ActiveRecord::Base

  def self.image_styles
    {
      large: "570x380#",
      thumb: "50x70#",
      medium: "300x200#"
    }
  end

end

Ответ 3

Свойство :styles принимает аргумент Proc в качестве аргумента, поэтому вы можете делать всевозможные причудливые вещи:)

class Image < AR::Base
  has_attached_file :file, :styles => Proc.new { |a| a.instance.file_styles }

  def file_styles; { :thumb => "150x150>", :normal => "492x600>" } end
end

class Didum < Image
  def file_styles; { :thumb => "50x50>", :normal => "492x600>" } end
end

Примечание - приведенный выше код должен работать, но, честно говоря, у меня нет настройки для его проверки, но выглядит как Paperclip::Attachment#styles does call, если он отвечает на него, см. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment:styles

UPDATE объект, переданный в Proc, не является экземпляром, а Paperclip::Attachment, но экземпляр доступен через instance на вложении

PS: И я видел это в некоторых других местах, но не могу вспомнить, где...

Ответ 4

class Banner < ActiveRecord::Base  

  belongs_to :banner_categoria
  validates :banner_categoria_id ,{:presence =>{:message => "não informada"}} 

  has_attached_file :arquivo
  after_initialize :init_attachment




  def init_attachment
    self.class.has_attached_file :arquivo,
      :url => "/system/:class/:attachment/:id/:style/:basename.:extension",
      :path => ":rails_root/public/system/:class/:attachment/:id/:style/:basename.:extension",
      :styles => hash = {
      :banner => {
        :geometry => "#{self.banner_categoria.largura}x#{self.banner_categoria.altura}>",
        :quality => 80
      },
      :thumb => "100x100#"
    }
  end

конец

Ответ 5

Мне понравился ответ MarkGranoff, но я придумал гораздо более простую реализацию, которая делает трюк для меня и кажется более удобной.

#create new and instantiate the fields you need ( or all except the attachment )
@photo = Photo.new(:attribution => params[:photo][:attribution],
                   :latitude => params[:photo][:latitude ])

#then just assign all params as normal
@photo = Photo.assign_attributes(params[:node])

Второй оператор назначает вложение, поэтому процессор запускается, и поскольку вы уже назначили: атрибуция и: широта, их значения будут доступны в процессоре с помощью метода attachment.instance.

Спасибо всем за понимание!

Ответ 6

Я также встретился с той же проблемой, и после поиска элегантного решения я не нашел ничего "действительно" полезного. Итак, вот мое простое решение, которое сейчас выглядит хорошо; (я не уверен, есть ли у него какие-то недостатки на данный момент)

В модели;

class Asset < ActiveRecord::Base
  belongs_to :assetable, polymorphic: true

  has_attached_file :attachment,  path: ":rails_root/#{path}/assets/images/:style/:filename",
                                  url: '/assets/images/:style/:filename',
                                  styles: -> (a) { a.instance.send(:styles) }
private
  def styles
    raise 'Undefined assetable.' unless assetable

    if assetable.class == Photo
      { small: 'x40', medium: '120x', large: '300x' }
    elsif assetable.class == Avatar
      { small: 'x40', medium: '120x', large: '300x' }
    else
      raise "Styles for #{assetable.class} is not defined."
    end
  end
end

В контроллере

@photo = Photo.new
@photo.build_image(assetable: @photo, attachment: params[:photo][:image_attributes][:attachment])
@photo.save

Ответ 7

После нескольких часов работы в исходном коде обоих, ActiveRecord и Paperclip, я думаю, что есть решение, связанное с небольшим количеством хакерских атак с обезьяной.

Я не тщательно его протестировал, но, похоже, работает для моих скромных потребностей.

Ниже мой config/initializers/activerecord_associations_patch.rb:

module ActiveRecord
  module Associations
    class HasManyAssociation

      def build_record(attributes)
        if options[:as] && owner
          # Unicorns
          reflection.build_association({}) do |record|
            set_owner_attributes(record)
            unless foreign_key_for?(record)
              record.public_send "#{options[:as]}=", owner
            end
            initialize_attributes(record)
            record.assign_attributes(attributes)
          end
        else
          # Classic Rails way
          reflection.build_association(attributes) do |record|
            initialize_attributes(record)
          end
        end
      end

    end
  end
end

Так как reflection не знает нашего @owner, он сначала назначает attributes, который вызывает вызов record.#{instance}= (т.е. Image#file=), который, в свою очередь, переводит его на #assign и выполняет post_processing hook, который в итоге приводит к вызову styles Proc, предоставленному в has_attached_file, если не установлен полиморфный imageable. да...

Я переписал метод, чтобы сначала создать запись и назначить предоставленные атрибуты позже. Кроме того, он учитывает record#new_record? в foreign_key_for? check.

Таким образом attributes назначаются после правильной настройки записи. По крайней мере, я так думаю;)

Более удобные решения и конструктивные комментарии приветствуются:)

Ответ 8

У меня был аналогичный вопрос, касающийся динамических поведенческих изменений на моих моделях. Играя с irb, я узнал, что это работает:

module Foo
   attr_accessor :bar
end
class Bar
   extends Foo
end
bar.bar = 'test' # 'test'
bar.bar # 'test'
# also works for instances of Bar!

Итак, я бы создал атрибут image_style, который можно было бы изменить на любой модуль, который вы хотите добавить, используя этот код при инициализации изображения:

  def after_initialize
    if self.image_style?
       extend Object.const_get(image_style)
    else
       extend DefaultImageStyle
    end
  end

Мне просто интересно, работает ли это с paperclip, поскольку метод after_initialize может вызываться после того, как paperclip делает это волшебство. Стоит попробовать, хотя!

Ответ 9

Глядя на источник скрепки для прикрепления, похоже, хэш styles может взять объект, который отвечает на call, чтобы вы могли сделать что-то вроде:

class Image < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
  has_attached_file :file, :styles => lambda {|attachment| attachment.instance.imageable_type.constantize.image_styles }
end

Тогда в любой модели, которая имеет много изображений:

class Art < ActiveRecord::Base
  has_many :images, :as => :imageable

  def self.image_styles
    { :thumb => "150x150>", :normal => "492x600>" }
  end
end

Изменить. Похоже, кто-то избил меня в этом: P.

Ответ 10

имеют ту же проблему на сервере производства/промежуточного уровня... НО не в моей локальной среде. Я использую те же версии рельсов/скрепки на всех серверах (2.3.2 и 2.2.6)