railsでPaperclipを使ってみる

PaperclipはRailsで画像を扱うためのプラグインです。
http://www.thoughtbot.com/projects/paperclip/
Railsで画像を扱うプラグインにはattachment_fuなどがあるのですが
attachment_fuでは画像を取り扱うために専用のモデルを必要としているのですが
こちらは専用のモデルが必要がなく、自分で定義するモデルに自由に画像を取り込めるようです。
さっそく使ってみる。


プラグインのインストール

script/plugin install git://github.com/thoughtbot/paperclip.git


とりあえずrdocを読んでみる

less vendor/plugins/paperclip/README.rdoc 


色々書いてあるが押さえておきたいのは画像の保存場所に関しての説明
画像の保存場所はモデルに追加するオプションで指定するか

:path

デフォルト設定にまかせてpubulic/system以下の下記のパスに保存させることができる。

:rails_root/public/system/:attachment/:id/:style/:basename.:extension


Imageモデルを作成してみる。その際、様々なモデルに関連付けできるようにしておく

$ script/generate model Image type:sting parent_type:string parent_id:integer image_file_name:string
     image_content_type:string image_file_size:integer image_updated_at:datetime  #長いので途中で改行してます

こんな感じのマイグレーションファイルができる

class CreateImages < ActiveRecord::Migration
  def self.up
    create_table :images do |t|
      t.sting :type
      t.string :parent_type
      t.integer :parent_id
      t.string :image_file_name
      t.string :image_content_type
      t.integer :image_file_size
      t.datetime :image_updated_at

      t.timestamps
    end
  end

  def self.down
    drop_table :images
  end
end

次にモデルを書き換える

class Image < ActiveRecord::Base
  belongs_to :parent, :polymorphic => true

  # Paperclip
  has_attached_file :image,
    :styles => {
      :medium => "250x250>", # >はリサイズ
      :thumb => "100x100#"   # #はトリミング
    },
#    :url => "/store/:attachment/:id/:style/:basename.:extension",  # 表示用url
#    :path => ":rails_root/public/store/:attachment/:id/:style/:basename.:extension"  #	画像保存path

  # 拡張子の制限
  validates_attachment_content_type :image,
    :content_type => ['image/jpg', 'image/jpeg', 'image/pjpeg', 'image/gif', 'image/png', 'image/x-png'],
    :message => "JPG, GIF, PNGのみアップロードできます"

  # ファイルサイズ制限
  validates_attachment_size :image,
    :less_than => 2.megabytes,
    :message => "ファイルサイズが大きすぎます(最大 :max バイトまで)"

end

次にエントリーモデルの作成

class Entry < ActiveRecord::Base
  has_many :images, :as => :parent, :dependent => :destroy
end

parentでポリモーフィック関連させる。
次にmodels/image/entry_image.rbを作成

class Image::EntryImage < Image
end

あとはビューとコントローラをちょいちょいと修正する
コントローラ

class EntriesController < ApplicationController
#その他省略
  # POST /entries
  # POST /entries.xml
  def create
    @entry = Entry.new(params[:entry])
    @image = Image::EntryImage.new(params[:image])

    respond_to do |format|
      if @entry.save
        @entry.images << @image  # 追加
        flash[:notice] = 'Entry was successfully created.'
        # format.html { redirect_to(@entry) }
        format.html { redirect_to('/') }
        format.xml  { render :xml => @entry, :status => :created, :location => @entry }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @entry.errors, :status => :unprocessable_entity }
      end
    end
  end
# その他省略
end


投稿画面のview
multipartをtrueにして投稿できるようにする

<h1>新規投稿</h1>

<% form_for(@entry, {}, { :html => { :multipart => true } }) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label '題名' %><br />
    <%= f.text_field :title %>
  </p>
  <p>
    <%= f.label '本文' %><br />
    <%= f.text_area :body %>
  </p>
  <p>
    <%= file_field :image, :image %>
  </p>
  <p>
    <%= f.submit '投稿' %>
  </p>
<% end %>


閲覧ページのビュー
コントローラで@entry取得したら
こんな感じに記述すればいい
modelの中でdelegateしてあるからimage.urlとスマートな呼び出し方ができる

<p>
  <b>Photos:</b>
  <%- @entry.images.each do |image| -%>
    <%= image_tag image.url %>
    <%= image_tag image.url(:medium) %>
    <%= image_tag image.url(:thumb) %>
  <%- end -%>
</p>

こんなかんじで動作確認してみたところ無事動きました。

参考
http://d.hatena.ne.jp/hichiriki/20081130
http://d.hatena.ne.jp/lov2much/20090105/1231173820