herokuのpg_dump.sqlしかバックアップが無い状態からDBを復旧させるため、適当なpostgres環境が必要になったんですが、$ brew install postgresql はv9.0とか未来的な感じが面倒そうだったので簡単にVirtualBoxの仮想環境が作れるという噂のVagrantを使ってみました。

Vagrant - Welcome

Vagrant - Welcome

Vagrantとは?

VirtualBoxの仮想環境をCUIから操作するツールです。数回コマンドを叩くだけで環境が作れるのでとても便利。rubyで書かれたシンプルな設定ファイルを元に面倒な仮想環境構築を自動化してくれます。

VM自体はVagrantfileという設定ファイルで設定し、VMの中身はchefで設定するという感じです。僕はchef使ったこと無いので今回はVM作成したあとはsshで入ってpostgresをインストールしちゃいました。それだけでもOSのCDイメージ落としてきて、インストールして・・・って面倒だったのが簡単になったし、バックグラウンド動作してくれるし、環境がキモくなったらすぐ削除して作り直せるのが精神衛生上良いですね。

Ubuntu 10.04 (Lucid Lynx) 環境を作る

$ gem install vagrant
$ vagrant box add lucid32 http://files.vagrantup.com/lucid32.box
$ vagrant box list
lucid32

OSイメージをダウンロードするので結構時間かかります。lucid32ってのがbox名です。

$ mkdir env
$ cd env
$ vagrant init lucid32
create Vagrantfile
$ ls
Vagrantfile

適当なディレクトリを作ってvagrant initでVagrantfileを作ります。RakefileとかCapfileとかGemfileとかあるので分かると思いますが、このディレクトリで実行するvagrantコマンドがこの設定ファイルを参照します。

# Vagrantfile
Vagrant::Config.run do |config|
config.vm.box = "lucid32"
end
$ vagrant up

vagrant upで同ディレクトリのVagrantfileを元にVMイメージを作ってバックグラウンドで起動します。(2回目からは単に起動します)

$ vagrant ssh
Warning: Permanently added '[localhost]:2222' (RSA) to the list of known hosts.
Linux vagrantup 2.6.32-24-generic-pae #39-Ubuntu SMP Wed Jul 28 07:39:26 UTC 2010 i686 GNU/Linux
Ubuntu 10.04.1 LTS

Welcome to Ubuntu!
* Documentation: https://help.ubuntu.com/
Last login: Wed Aug 11 17:42:46 2010

vagrant sshでVMにvagrantユーザーでsshで接続します。(デフォルトではlocalhost:2222をVMの22にforwardします)

sudoできるので後は普通のubuntuです。通常、設定はchefでやるみたいです。

忙しい人のためのVagrant

これだけだと何がどうなってるのか解かり辛いのでもう一個環境を作ってみます。

$ mkdir env2
$ cd env2
$ vagrant init lucid32
$ vagrant up

この状態でVirtualBoxの設定画面を見てます。

Oracle VM VirtualBox

ちゃんと2個のVMが実行中です。何にも設定してないですが実際はどうなってるんでしょう?vagrant haltでVMを停止して設定を見てみます。

仮想メディアマネージャ

$ tree ~/Library/VirtualBox/HardDisks
/Users/komagata/Library/VirtualBox/HardDisks
├── lucid.vmdk
├── lucid_1.vmdk
└── windows_xp_ie6.vmdk

HDDイメージはそれぞれ別々に作ってくれています。

% tree ~/.vagrant 
/Users/komagata/.vagrant
├── boxes
│   └── lucid32
│   ├── Vagrantfile
│   ├── box.mf
│   ├── box.ovf
│   └── lucid.vmdk
├── logs
└── tmp

box(CDイメージとVMのデフォルト設定)の方は共通です。

$ vagrant destroy
$ cd ..
$ rm -rf env2
$ ls ~/Library/VirtualBox/HardDisks
lucid.vmdk windows_xp_ie6.vmdk

vagrant destroyで綺麗に消えるので気軽に作成・削除が出来ます。

作りがシンプルで気に入ってしまいました。当初の目的だったpostgresはデータを救い出したあと要らないのでVMごと消してしまいました。そのぐらい気軽に環境が作れるところがいいですね。

RailsのオススメファイルアップロードプラグインのPaperclipですが、最新版(2.1.4)で嬉しい機能が増えてました。

  • ImageMagick(convertコマンド)に自由にオプションを渡せるようになった。
  • 画像が無いときのパスを指定できるようになった。

これは嬉しい。ソース内のコメントの例では、アップされた画像をlargeとnegativeというstyleに分け、両方にprofileやexif情報を削除する-stripオプションを適応し、negativeには補色で補完する(つまり反転?)-negateオプションを適応してます。

class User < ActiveRecord::Base
  has_attached_file :avatar,
    :styles => {
      :large => "300x300",
      :negative => "100x100"},
    :convert_options => {
      :all => "-strip",
      :negative => "-negate"}
end

RMagickや他のAPIを使わず、直接convert使ってる利点が生かされてますな。 他にも-monochromeオプションでモノクロにしたりとか色々出来そうです。

画像無いときのパス指定は、「あれ?これ指定できないの?」ぐらいだったんで俺がすっきりしただけですが。

以前にも書きましたが、Capistrano利用時にはアップ画像を/public/system以下に置きたいのでオプション指定が必要です。

いちいち指定してたらModelがキモくなるので通常、下記を使ってます。

config/initializers/paperclip.rb:

module Paperclip::ClassMethods
  def has_attached_file_with_default(name, options = {})
    default = {
      :path => ":rails_root/public/system/:class/:attachment/:id/:style/:basename.:extension",
      :url => "/system/:class/:attachment/:id/:style/:basename.:extension",
      :default_style => :normal,
      :default_url => "/images/:class/:attachement/missing_:style.png" 
    }

    has_attached_file_without_default(name, default.update(options))
  end

  alias_method_chain :has_attached_file, :default
end

config/initializers/以下は一番最後(pluginよりあと)に読まれるのでこういうアプリの本質的でない部分の変更には向いてますな。

RMagickインストール地獄にハマってる方はぜひご検討ください! (Debian etch、CentOS 5.2共にimagemagick/ImageMagickパッケージを入れるだけで済みます)

参照: Capistrano利用時のPaperclip

大体の画像系と同じようにnilとかを入れるだけでOK。

% ./script/console
Loading development environment (Rails 2.1.0)
>> grave = Grave.first
=> (略)
>> grave.overview.url
=> "/system/graves/overviews/1/normal/test_picture_large.gif" 
>> grave.overview = nil
=> nil
>> grave.save
=> true
>> grave.overview.url
=> "/overviews/normal/missing.png"

しかも、削除した場合のURLが自動的にmissing.pngに変わってる。

この勝手な挙動が嫌いな人もいるかもしれないけど面倒臭がりの俺としては嬉しい。

画像があるかどうかは、grave.overview.exists? で分かる。

thoughtbot: Paperclip File Attachments

Requirements

Only ImageMagick, which you can install quite easily install via apt, yum, or port, or the package manager of your choice.

あとやっぱりRMagick無くても動く!そういやImageMagick自体が問題なんじゃなくて、殆どの場合、パッケージのImageMagickが言語バインディングと合わないことが問題だったんだよね。PureRubyで再実装しかないと思ってた。頭良いな作者。

月曜日なので一心不乱にコード(仕事の)。

画像アップするプラグインを前に目を付けていたPaperclipに変えてみた。

file_columnと同じでファイルに画像を保存する形式。で、attachment_fuと違って専用のテーブルを作らないタイプ。

うーん、ファイルに保存方式だとcapistranoでdeployしてるとき困るんだよな~。そんでsystem以下に保存するように変えたり。

とりあえず使ってみた。

class User < ActiveRecord::Base
  has_attached_file :avatar, 
    :styles => { :medium => "300x300>",
      :thumb => "100x100>" }
end

モデルにhas_attached_fileを定義。

class AddAvatarColumnsToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :avatar_file_name, :string
    add_column :users, :avatar_content_type, :string
    add_column :users, :avatar_file_size, :integer
  end
  (略)
end

DBは専用のじゃなくて普通にそのモデルのテーブルに上記3種を追加。

<% form_for :user, :html => { :multipart => true } do |form| %>
  <%= form.file_field :avatar %>
<% end %>

アップするフォームはこんな感じ。

<%= image_tag @user.avatar.url %>
<%= image_tag @user.avatar.url(:medium) %>
<%= image_tag @user.avatar.url(:thumb) %>

画像表示もこんな風に深く考えなくてもいいようになってる。

あとImageMagickだけでRMagickが要らないっぽいです。そこは嬉しい。

で、file_columnでちょっと面倒だったファイルの保存場所関係はhas_attached_fileのオプションで結構柔軟っぽい設定ができそうでした。

:url => "/:attachment/:id/:style_:basename:extension" 
:url => "http://some.other.host/stuff/:class/:id_:extension"

シンプルなところが気に入りました。使ってこ。

参照: thoughtbot: Paperclip File Attachments