attachment-fu/RMagick で画像アップロードサービス(その2)

アップロード系のデータ fixture_file_upload

models/photo_spec.rb

  it "should be valid" do
    # fixtures :photos
    # photos(:one).should be___
    uped = fixture_file_upload './ccc.jpg', 'image/pjpeg'
    photo = Photo.new :uploaded_data => uped
    photo.should be_valid
  end

fixture_file_upload を使うとアップロードされたファイルと同じ想定にできる。

というわけでスペック内で使えるように beforeで準備するようにしておく。

models/photo_spec.rb

  before(:each) do
    @photo = Photo.new
    @uped = fixture_file_upload './ccc.jpg', 'image/pjpeg'
    @valid_photo = Photo.new :uploaded_data => @uped
  end

Photo#thumb と #small

モデルにサムネールオブジェクトを得るメソッドを追加。

models/photo_spec.rb

  it 'should have a thumb method' do
    @valid_photo.save
    @valid_photo.should respond_to(:thumb)
    lambda{ @var = @valid_photo.thumb }.should_not raise_error
    @var.should be_instance_of(Photo)
    @var.parent_id.should == @valid_photo.id
    @var.thumbnail.should == 'thumb'
  end # it 'should have a thumb method' do

  it 'should have a small method' do
    @valid_photo.save
    @valid_photo.should respond_to(:small)
    lambda{ @var = @valid_photo.small }.should_not raise_error
    @var.should be_instance_of(Photo)
    @var.parent_id.should == @valid_photo.id
    @var.thumbnail.should == 'small'
  end # it 'should have a small method' do

models/photo.rb

class Photo < ActiveRecord::Base
  rails_conf = Rails::Configuration.new
  data_conf = rails_conf.database_configuration[rails_conf.environment]
  thumbnails = {:thumb => '60x', :small => '240x'}
  has_attachment :storage => :file_system \
    , :path_prefix => data_conf['photo_path'] \
    , :content_type => :image \
    , :max_size => 1.megabytes \
    , :thumbnails => thumbnails
  validates_as_attachment

  thumbnails.keys.each do |name|
    eval <<-EOdef
      def #{name}
        thumbnails.find :first, :conditions => {:thumbnail => '#{name}'}
      end # def #{name}
    EOdef
  end # thumbnails.keys.each do |name|
end

thumbnailsハッシュを導入したのは兎も角、それで evalを回すのはやり過ぎか。

そうすると、ビューは show_thumbや、show_smallといったアクションを呼ぶような参照をしなくてもよくなる。

views/photos/show.html.erb_spec.rb

<before に追加>
    @small = mock_model(Photo)
    @photo.stub!(:small).and_return(@small)
    response.should have_tag('img')
    #response.should have_tag('img[src=?]', "/photos/#{@photo.id}/show_photo")
    #response.should have_tag('img[src=?]', "/photos/#{@photo.id}/show_small")
    response.should have_tag('img[src=?]', "/photos/#{@photo.small.id}/show_photo")
    response.should have_tag('a[href=?]', "/photos/#{@photo.id}/show_photo")

views/photos/show.html.erb

  <%= tag 'img', :src => url_for(:action => :show_photo, :id => @photo.small) %>

views/photos/index.html.erb_spec.rb

<before に追加>
    @thumb_98 = mock_model(Photo)
    photo_98.should_receive(:thumb).and_return(@thumb_98)
< 略 >
    @thumb_99 = mock_model(Photo)
    photo_99.should_receive(:thumb).and_return(@thumb_99)
    response.should have_tag('tr>td>img[src=?]', "/photos/#{@thumb_98.id}/show_photo")
    response.should have_tag('tr>td>img[src=?]', "/photos/#{@thumb_99.id}/show_photo")

views/photos/index.html.erb

    <td><%= tag 'img', :src => url_for(:action => 'show_photo', :id => photo.thumb) %></td>

そしたらコントローラの show_thumb, show_small アクションは削除。
結局の所、ビューで url_for(:action => 'show_photo', :id => photo.thumb) と呼ぶのか url_for(:action => 'show_thumb', :id => photo) と呼ぶかの違いであり、
コントローラにメソッド増やすのとモデルにメソッド増やすのでは後者の方が見通しが良さそうなので。

indexでサムネールは持って来ない

それと、photosテーブルにはサムネールも入ってくるけど一覧には出さない。

controllers/photos_controller_spec.rb

<describe "handling GET /photos">
    it "should find all photos" do
      #Photo.should_receive(:find).with(:all).and_return([@photo])
      do_get
    end

    it "should find all photos without thumbnails" do
      Photo.should_receive(:find).with(:all \
        , :conditions => {:thumbnail => nil} ).and_return([@photo])
      do_get
    end # it "should find all photos without thumbnails" do
<describe "handling GET /photos.xml">
    it "should find all photos without thumbnails" do
      #Photo.should_receive(:find).with(:all).and_return(@photos)
      Photo.should_receive(:find).with(:all \
        , :conditions => {:thumbnail => nil} ).and_return(@photos)
      do_get
    end

controllers/photos_controller.rb

  # GET /photos
  # GET /photos.xml
  def index
    @photos = Photo.find(:all, :conditions => {:thumbnail => nil})

    respond_to do |format|
      format.html # index.html.erb
      format.xml  { render :xml => @photos }
    end
  end

attachment-fu と RMagick を使った画像アップロードサービス(の原型)としてこのくらいで一区切りかな。