Kilka dni temu, musiałem zwalidować rozmiar obrazka dodawanego do posta, z pomocą paperclipa. Domyślnie nie istnieje jakaś prosta metoda w stylu validates_picture_resolution, można jednak sobie z tym poradzić.

Pierwszą rzeczą jaką należy pamiętać jest to, że paperclip nie służy tylko do obsługi obrazków ale i innych rodzajów plików. Będzie to dla nas ważne w późniejszym etapie.

Ważne: nasz walidator MUSI być ostatnim walidatorem. No może przesadziłem. Nie tyle ostatnim, co ostatnim walidatorem danego elementu paperclipa.

Dla przykładu załóżmy że walidujemy rozmiar miniaturki (thumb).
W kodzie modelu dodajemy więc linijkę odpowiedzialną za uruchamianie walidacji oraz domyślne minimalne rozmiary miniaturki:

THUMB_MIN_WIDTH = 80
THUMB_MIN_HEIGHT = 50

validate :thumb_resolution

Oznaczać to będzie że jako walidator zostanie uruchomiona metoda thumb_resolution która skorzysta ze stałych i sprawdzi czy obrazek ma minimum takie wymiary.

Dlaczego wyżej napisałem, że walidator musi być jako ostatni? Ponieważ zanim sprawdzimy wymiary pliku, sprawdzić musimy czy jest to plik graficzny. Z tego względu przed naszym walidatorem, musimy uruchomić validates_attachment_content_type o którym można poczytać w dokumentacji paperclipa.

Mając ten walidator przed naszym, możemy sprawdzić czy istnieją dla miniaturki jakieś błędy. Jeśli tak to nie uruchamiamy walidacji. Kiedy plik będzie poprawny pod innymi względami, wtedy przeprowadzimy naszą walidację.

W kodzie naszego walidatora umieszczamy taki warunek:

return if self.errors.on(:thumb)

Po tym warunku, pozostaje nam jedynie sprawdzić czy plik istnieje i czy jego wymiary są poprawne. Wykonamy to wyciągając oryginalną wersję pliku i skorzystamy z Paperclip::Geometry aby ocenić wymiary.

    if self.thumb && self.thumb.queued_for_write[:original]
      dimensions = Paperclip::Geometry.from_file( self.thumb.queued_for_write[:original])
       if dimensions.width < THUMB_MIN_WIDTH || dimensions.height < THUMB_MIN_HEIGHT
         self.errors.add(:thumb, I18n.t("activerecord.errors.messages.paperclip.resolution",
           {:width => THUMB_MIN_WIDTH, :height => THUMB_MIN_HEIGHT}))
       end
    end

To wszystko :) a tak wygląda gotowa metoda:

  # Sprawdza czy rozdzielczość obrazka jest poprawna
  def thumb_resolution
    return if self.errors.on(:thumb)
    if self.thumb && self.thumb.queued_for_write[:original]
      dimensions = Paperclip::Geometry.from_file( self.thumb.queued_for_write[:original])
       if dimensions.width < THUMB_MIN_WIDTH || dimensions.height < THUMB_MIN_HEIGHT
         self.errors.add(:thumb, I18n.t("activerecord.errors.messages.paperclip.resolution",
           {:width => THUMB_MIN_WIDTH, :height => THUMB_MIN_HEIGHT}))
       end
    end
  end