Скрепка: стиль в зависимости от модели (has_many полиморфные изображения) - PullRequest
10 голосов
/ 28 октября 2010

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

Art

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

ОБНОВЛЕНИЕ

Я попытался запустить отладчик внутри метода 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">

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

UPDATE2

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

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

  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

Ответы [ 10 ]

8 голосов
/ 12 июля 2012

Я действительно опоздал на вечеринку здесь, но я хотел уточнить кое-что о доступе к данным модели для всех, кто происходит в этой теме.Я только столкнулся с этой проблемой, когда использовал 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.Обобщенные здесь изменения сделали всю модель доступной в процессорах скрепки. Ключ в том, чтобы назначить атрибут вложения последний .

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

5 голосов
/ 22 июня 2015

Я нашел обходной путь для выбора стиля при создании.Ключ заключается в реализации 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 ->{
    !@file_reprocessed.nil?
  }

  after_save ->{
    if !@file_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 голосов
/ 28 октября 2010

свойство :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 делает call, если ответит, см. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment:styles

ОБНОВЛЕНИЕ объект, переданный в Proc, не является экземпляром, но Paperclip::Attachment, но экземпляр доступен через instance на вложении

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

1 голос
/ 14 июля 2014

После нескольких часов копания в исходном коде обоих, 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? чеке.

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

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

1 голос
/ 25 мая 2014

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

В модели;

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
1 голос
/ 25 февраля 2013

Мне понравился ответ 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])

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

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

1 голос
/ 02 сентября 2011
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

конец

0 голосов
/ 05 апреля 2012

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

0 голосов
/ 28 октября 2010

Глядя на источник Paperclip для Attachment, похоже, что хеш 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.

0 голосов
/ 28 октября 2010

У меня был похожий вопрос, когда я имел дело с динамическими изменениями поведения в моих моделях. Поиграв с 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, который можно изменить на любой модуль, который вы хотите добавить, используя этот код при инициализации Image:

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

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

...