ラベル ruby の投稿を表示しています。 すべての投稿を表示
ラベル ruby の投稿を表示しています。 すべての投稿を表示

2011年8月10日水曜日

S3ってなんじゃ?(バージョン管理編)

今回は、S3のバージョン管理機能について触れます。S3ではデフォルトではバージョン機能は無効になっていますが、バケットごとにバージョン管理を有効にすることができます。
AWS SDK for Rubyを利用して、その動作を簡単に見てみましょう。

あらかじめ認証情報設定用のファイルを用意しておきます。
$ vi config.rb
require 'rubygems'
require 'aws'

AWS.config({
:access_key_id => 'アクセスキー',
:secret_access_key => 'シークレットキー'
})

指定されたバケットのファイルとバージョンの全状態を確認するスクリプトを作ります。
$ vi check_version.rb
# 設定情報を読み込む
require File.expand_path(File.dirname(__FILE__) + '/config')

# 引数からバケット名を読み込む
bucket_name = ARGV[0]
unless bucket_name
  puts "Usage: check_version.rb <BUCKET_NAME>"
  exit 1
end

# S3インスタンスの取得
s3 = AWS::S3.new
# バケットオブジェクトの取得
bucket = s3.buckets[bucket_name]
# バケットのバージョンが有効かどうかを出力
puts "state :" + bucket.versioning_state.to_s
# バケットの全バージョン情報を取得
versions = bucket.versions
# 全てのバージョン情報を出力
versions.each(){|obj_ver|
  puts "key=" + obj_ver.object.key + "; version_id=" + obj_ver.version_id + ";"
}

これを実行して全バージョンの情報を確認してみます。
$ ruby check_version.rb hoge-bucket

state :unversioned
key=_1308236999981.png; version_id=null;
key=welcome.txt; version_id=null;

ここで、stateがunversionedとなっているのはバケットがデフォルトでバージョニング無効になっているためです。元々アップされていた_1308236999981.pngとwelcome.txtの2つのファイルのversion_idもnullになっています。

次に、このバケットをバージョニング有効にするためのスクリプトを書きます。
$ vi enable_version.rb
# 設定情報を読み込む
require File.expand_path(File.dirname(__FILE__) + '/config')

# 引数からバケット名を読み込む
bucket_name = ARGV[0]
unless bucket_name
  puts "Usage: check_version.rb <BUCKET_NAME>"
  exit 1
end

# S3インスタンスの取得
s3 = AWS::S3.new
# バージョニングの有効化
s3.buckets[bucket_name].enable_versioning

これを実行して、再度バージョニング状態を確認します。
$ ruby enable_version.rb hoge-bucket
$ ruby check_version.rb hoge-bucket

state :enabled
key=_1308236999981.png; version_id=null;
key=welcome.txt; version_id=null;

state: enabledとなっており、バージョニングが有効になっていることが確認できます。

次に、ファイルをアップロードするスクリプトを作ります。
$ vi upload.rb
# 設定情報を読み込む
require File.expand_path(File.dirname(__FILE__) + '/config')

# 引数からバケット名、ファイル名を読み込む
(bucket_name, file_name) = ARGV
unless bucket_name && file_name
  puts "Usage: upload.rb <BUCKET_NAME> <FILE_NAME>"
  exit 1
end

# S3インスタンスの取得
s3 = AWS::S3.new

# バケットがなければ作成する
b = s3.buckets[bucket_name]
unless b.exists?
  b = s3.buckets.create(bucket_name)
end

# アップロード
basename = File.basename(file_name)
o = b.objects[basename]
o.write(:file => file_name)


このアップロードスクリプトを使用して、バージョニング有効になった状態のバケットに、連続して違う内容のwelcome.txtをアップロードしてみます。
$ echo "hoge" > welcome.txt
$ ruby upload.rb hoge-bucket welcome.txt
$ echo "moge" > welcome.txt
$ ruby upload.rb hoge-bucket welcome.txt

ここで、再度バージョン情報を確認します。
$ ruby check_version.rb hoge-bucket

state : enabled
key=_1308236999981.png; version_id=null;
key=welcome.txt; version_id=1ut2HAksA7WCEkbdlDD3rnBC_OxP8Lvf;
key=welcome.txt; version_id=VpCOAn8Et9ZbOQyk_bKVJGeYaYp2x2W_;
key=welcome.txt; version_id=null;

このように、version_idがついたwelcome.txtが2つ増えていることが確認できます。
ここで、バージョンを指定してファイルを読み込むスクリプトを作成します。
$ vi read_by_version.rb
# 設定情報を読み込む
require File.expand_path(File.dirname(__FILE__) + '/config')

# 引数からバケット名、ファイル名、バージョンIDを読み込む
(bucket_name, file_name, version_id) = ARGV
unless bucket_name && file_name && version_id
  puts "Usage: version_check.rb <BUCKET_NAME>"
  exit 1
end

# S3インスタンスの作成
s3 = AWS::S3.new

# バージョンを指定して読み込んだものを出力
bucket = s3.buckets[bucket_name]
puts bucket.objects[file_name].read({:version_id=>version_id}).to_s

これを先ほどの出力で確認したバージョンIDを指定して個別に実行すると、以下のようにそれぞれのバージョンのファイルの内容を確認することができます。
$ ruby read_by_version.rb hoge-bucket welcome.txt 1ut2HAksA7WCEkbdlDD3rnBC_OxP8Lvf
> moge
$ ruby read_by_version.rb hoge-bucket welcome.txt VpCOAn8Et9ZbOQyk_bKVJGeYaYp2x2W_
> hoge  

このように、S3を使用してバージョン管理ができることを確認できました。
ちなみに、このバージョン機能は、ファイル単位でのバージョン機能のようです。

本日はここまで。

2011年8月7日日曜日

S3ってなんじゃ?(RUBY SDK鈍行編3 PaperclipでRailsからS3へファイルアップロード)

今回はRuby on RailsからS3へファイルをアップロードしてみたいと思います。
RailsにはPaperclipというファイルアップロードプラグインがあり、Marcel Molina Jr.さん謹製のAWS::S3というgemを使用したS3へのアップロードもサポートしているのですが、今回はせっかくAmazonの公式ruby sdkが公開されたので、そちらを利用したPaperclip拡張である@igor-alexandrovさんのpaperclip-awsを使用してS3ファイルアップロードを試してみます。

PaperclipはImageMagickを使用するので、まずImageMagickをインストールします。
yumからだとバージョンが古い場合があるので、ソースからインストールします。
# cd /usr/local/src
# wget ftp://ftp.imagemagick.org/pub/ImageMagick/ImageMagick.tar.gz
# tar xzvf ImageMagick.tar.gz
# cd ImageMagick-6.7.1-2
# ./configure
# make
# make install

次にldconfigでライブラリの更新をシステムに知らせます。
# ldconfig /usr/local/lib

Gemfileに以下のgemをインストールします。
  • RMagick(ImageMagickのRubyラッパー)
  • Paperclip(ファイルアップロードプラグイン)
  • paperclip-aws(paperclipでのS3アップロード機能拡張)
$ vi Gemfile
gem 'rmagick'
gem "paperclip", "~> 2.3"
gem "papepclip-aws"

gemのインストールと更新を行います。
$ bundle install vendor/bundle
$ bundle update

ImageMagickへのパスを通します。
$ vi config/environments/development.rb 
Paperclip.options[:command_path] = "/usr/local/bin/"

railsでScaffoldを作成してみます。
今回は簡易ブログを作成する想定で、blogというモデルのscaffoldを作成しました。
$ rails g scaffold blog title:string description:text

モデルにファイルアップロードの項目を追加します。
paperclipのhas_attacched_fileメソッドの引数では、デフォルトのpaperclipの項目以外にpaperclip-awsで追加されたs3用の項目も指定できます。
項目についてはここで解説されています。
ここではphotoという名前で追加してみます。このブログの鈍行編1の設定でaws.ymlにアクセスキーとシークレットキーしか設定していなかったので、今回その他の設定は直接指定していますが、これらもaws.ymlに含めてしまうこともできます。以下赤字が追加した部分です。
$ vi app/models/blog.rb
class Blog < ActiveRecord::Base

  def self.s3_config
    @@s3_config ||= YAML.load(ERB.new(File.read("#{Rails.root}/config/aws.yml")).result)[Rails.env]
  end

  has_attached_file :photo,
                    :styles => {
                      :thumb => [">75x"],
                      :medium => [">600x"]
                    },
                    :storage => :aws,
                    :s3_credentials => {
                      :access_key_id => self.s3_config['access_key_id'],
                      :secret_access_key => self.s3_config['secret_access_key'],
                      :endpoint => 's3-ap-northeast-1.amazonaws.com'
                    },
                    :s3_bucket => 'myfirst-bucket',
                    :s3_host_alias => self.s3_config['s3_host_alias'],
                    :s3_acl => :public_read,
                    :s3_protocol => 'http',
                    :path => "images/:id/:style/:data_file_name"

end

続いて、viewに画像ファイルの項目を追加します。
viewは通常のpaperclipのviewの書き方とまったく同じです。
まずは、入力画面にphoto属性のファイル選択フィールドを追加します。
$ vi app/views/blogs/_form.html.erb
<%= form_for(@blog, :html => { :multipart => true } ) do |f| %>
  <% if @blog.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@blog.errors.count, "error") %> prohibited this blog from being saved:</h2>

      <ul>
      <% @blog.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :description %><br />
    <%= f.text_area :description %>
  </div>

  <div class="field">
    <%= f.label :photo %><br />
    <%= f.file_field :photo %>
  </div>

  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

詳細画面や完了画面で写真が確認できるようにを表示するようにphotoのimgタグを追加します。
$ vi app/views/blogs/show.html.erb
<p id="notice"><%= notice %></p>

<p>
  <b>Title:</b>
  <%= @blog.title %>
</p>

<p>
  <b>Description:</b>
  <%= @blog.description %>
</p>


<%= image_tag @blog.photo.url(:medium) %>

<%= link_to 'Edit', edit_blog_path(@blog) %> |
<%= link_to 'Back', blogs_path %>

一覧画面にサムネイルを表示するようにサムネイル用のimgタグを追加します。
$ vi app/views/blogs/index.html.erb
<h1>Listing blogs</h1>

<table>
  <tr>
    <th>Title</th>
    <th>Description</th>
    <th>Photo</th>
    <th></th>
    <th></th>
    <th></th>
  </tr>

<% @blogs.each do |blog| %>
  <tr>
    <td><%= blog.title %></td>
    <td><%= blog.description %></td>
    <td><%= image_tag blog.photo.url(:thumb) %></td>
    <td><%= link_to 'Show', blog %></td>
    <td><%= link_to 'Edit', edit_blog_path(blog) %></td>
    <td><%= link_to 'Destroy', blog, :confirm => 'Are you sure?', :method => :delete %></td>
  </tr>
<% end %>
</table>

<br />

<%= link_to 'New Blog', new_blog_path %>


次に、paperclip用の項目をDBに追加するためのマイグレーションファイルを作成します。
$ rails g migration add_photo_columns_to_blog
      invoke  active_record
      create    db/migrate/20110805111437_add_photo_columns_to_blog.rb
$ vi db/migrate/20110805111437_add_photo_columns_to_blog.rb 
class AddPhotoColumnsToBlog < ActiveRecord::Migration

  def self.up
    add_column :blogs, :photo_file_name,    :string
    add_column :blogs, :photo_content_type, :string
    add_column :blogs, :photo_file_size,    :integer
    add_column :blogs, :photo_updated_at,   :datetime
  end

  def self.down
    remove_column :blogs, :photo_file_name
    remove_column :blogs, :photo_content_type
    remove_column :blogs, :photo_file_size
    remove_column :blogs, :photo_updated_at
  end

end

マイグレーションを行います。
$ rake db:migrate
(in /var/www/html/myfirstcloud)
==  CreateBlogs: migrating ====================================================
-- create_table(:blogs)
   -> 0.0011s
==  CreateBlogs: migrated (0.0014s) ===========================================

==  AddPhotoColumnsToBlog: migrating ==========================================
-- add_column(:blogs, :photo_file_name, :string)
   -> 0.0007s
-- add_column(:blogs, :photo_content_type, :string)
   -> 0.0004s
-- add_column(:blogs, :photo_file_size, :integer)
   -> 0.0005s
-- add_column(:blogs, :photo_updated_at, :datetime)
   -> 0.0004s
==  AddPhotoColumnsToBlog: migrated (0.0028s) =================================

これで、S3にアップロードすることができるようになりました。

ブラウザで確認してみます。
http://IPアドレス/blogs
にアクセスすると、以下のようにscaffoldで作成された一覧画面が表示されます。



New Blogのリンクをクリックして、新規登録画面を表示すると、
画像のアップロードのフィールドがあることが確認できます。
テキストを入力し、画像を選択したらCreate Blogボタンをクリックします。



すると、登録完了の画面として、アップロードした写真が表示されます。



また、一覧画面に戻ると、一覧にもサムネイルが表示されているのがわかります。



表示されている画像のURLをみると以下のようにS3からホストされていることが確認できます。

http://s3-ap-northeast-1.amazonaws.com/myfirst-bucket/images/1/thumb/:data_file_name
簡単にS3ファイルアップロードを実装することができました。

本日はこれにて。

2011年8月3日水曜日

Amazon SimpleDBってなんじゃ?(RUBY SDK鈍行編2 railsでsimpledbを使う)

前回とタイトルが微妙につながってませんが、ruby on railsのインストールが終わったので、続いてawsのsdkをつかったrailsアプリケーションを作ってみます。

aws-sdkでは、完全ではないですがrailsのサポートもしており、SimpleDBをrailsのORMとして利用することができます。方法は以下のとおりです。

まず、必要なライブラリを全てインストールします。
# yum -y install ruby-sqlite3
# yum -y install sqlite
# yum -y install sqlite-devel
# yum -y install ncurses-devel
# yum -y install readline-devel
readlineのコンパイルとインストールを行います。
$ cd /usr/local/src/ruby-1.9.2-p290/ext/readline
$ ruby extconf.rb
$ make
# make install

新規にrailsアプリを作成します。(前回と同じものです)
# cd /var/www/html
# rails new myfirstcloud -J -T
# chown memorycraft:memorycraft -R myfirstcloud
$ cd /var/www/html/myfristcloud

bundlerでインストールするgemを定義します。今回sqlite3は使用しませんが、エラー回避のため便宜上入れて起きます。
$ vi Gemfile
...
gem 'aws-sdk'
#gem 'sqlite3'
gem 'sqlite3-ruby', '1.2.5', :require => 'sqlite3'
...

awsのアクセス認証用に証明書情報を定義します。rubyコードでの記述も可能ですが、ここではYAMLで定義します。記法はconfig/database.ymlとほぼ同じです。
$ vi config/aws.yml
development:
  access_key_id: アクセスキー
  secret_access_key: シークレットキー

test:
  <<: *development

production:
  <<: *development


SimpleDBはRDBMSではなくキーバリューストアなので、db:migrateは必要がなく、属性の定義はモデルで行います。aws_sdkのSimpleDBには仮想属性というものがあり、裏側で通常Railsでサポートしているデータ型とSimpleDBの属性が以下のようなメソッドで紐付けられています。
  • string_attr
    stringの属性
  • integer_attr
    integer(整数型)の属性
  • sortable_integer_attr
    値の範囲が決まっているソート可能なintegerの属性

    範囲を:rangeで指定します。
    validates_numericality_ofで最大値と最小値の制約を適用することが一般的で、
    もし範囲外だった場合、実行時エラーを引き起こします。

    通常のintegerとちがうところとして、SimpleDBは数値型をサポートしておらず全ての値は文字列に変換されます。
    そのため、数値でのソートに問題が生じます。たとえば2と10を昇順に並べると文字列比較のため10,2の順になってしまいます。
    このsortable_integerは、この問題に対応するため0詰めして保存されます。
  • float_attr
    float(浮動小数点型)の属性
  • sortable_float_attr
    値の範囲が決まっているソート可能なfloatの属性
    sortable_integerと同じくfloat値を0詰めで保持します。

    【正のfloat】
    たとえばsortable_float_attr :score, :range => (0..10)と指定した場合、
    辞書的にソートされても問題ないように5.5は"05.5"と文字列保存されます。

    【負のfloat】
    たとえば
    sortable_float_attr :position, :range => (-10..10)
      
    のように、範囲が負の値を含んでいいた場合、すべての値を正になるようにマイナス分だけずらして保存されます。
    つまり"000"から"020"として保存されることにより、ソートを可能にしています。

    注意としては、こういった方式で表示したり入力したりした値と、実際に保存される値がことなるので、
    一度運用を開始した後に値の範囲を変更したい場合、保存済みの全ての値を更新してあげる必要があるので要注意です。
  • boolean_attr
    boolean型の属性
    なにも指定しないでnewされたとき、デフォルトはfalseです。
  • datetime_attr
    日時型の属性
    created_atとupdated_atに対して指定された場合、自動管理されます。
  • timestamps
    timestamps
    datetime_attr属性のcreated_atとupdated_atを生成するための便利メソッドです。

ここでは以下のようにモデルを定義します。
$ vi app/models/my_first_domain.rb 
class MyFirstDomain < AWS::Record::Base
  string_attr :title
  integer_attr :num
  sortable_integer_attr :sort_num, :range => 0..99
  boolean_attr :bool_val
  timestamps

  validates_presence_of  :title, :num
end

ドメインがまだ存在していない場合は、rubyコンソールから作成することも可能です。
$ rails console
> MyFirstDomain.create_domain

モデルとドメインの作成が終わったら、それ以外の部分をscaffoldで作成します。
rails generate scaffold_controller MyFirstDomain title:string num:integer sort_num:sortable_integer bool_val:boolean

最後にroutesに追加します。
$ vi config/routes.rb
Myapp::Application.routes.draw do
    # 追加
    resources :my_first_domains
end

それでは確認してみましょう。

httpd.confの設定は前回のとおりであることを確認したら、
ブラウザで
http://IPアドレス/my_first_domains
を見てみましょう。
このとおり、my_first_domainのscaffoldのインデックス画面が確認できます。


それでは、アイテムを新規作成してみます。
ためしにSort Numを範囲外の1000で 登録しようとすると、下記のようにエラーが発生します。



これを防ぐため、モデルにvalidates_numericality_ofを追加します。
$ vi app/model/my_first_domain.rb
validates_numericality_of :sort_num, :greater_than_or_equal_to => 0, :less_than_or_equal_to => 99

再度登録しようとすると、今度はちゃんとバリデーションメッセージが表示されるようになりました。


範囲内の数値で登録すると、正常に登録が成功しました。


このとおり、railsからもさくっとSimpleDBを使うことができました。

つかれた。。今日はここまで。

2011年7月29日金曜日

S3ってなんじゃ?(RUBY SDK鈍行編1 ruby on railsの準備)

今回はS3をruby sdkで使うためにもう少し細かく見ていきます。
はじめに、rubyruby on rails のインストールをEC2にインストールしてみます。

rubyをインストール
まず動作に必要な各種ライブラリをパッケージインストーラでインストールし、rubyは最新をソースからインストールします。
# yum install git gettext-devel gcc rpm-build zlib-devel gcc-c++ openssl openssl-devel httpd-devel libxml-devel←まちがいでしたごめんなさい。正しくは→ libxml2-devel libxslt-devel
....
# cd /usr/local/src 
# wget ftp://ftp.ruby-lang.org/pub/ruby/1.9/ruby-1.9.2-p290.tar.gz
# tar xzvf ruby-1.9.2-p290.tar.gz 
# cd ruby-1.9.2-p290
# ./configure
# make
# make install

次にrailsを動かすためのいくつかのツールをインストールします。

ruby opensslをインストール
# cd /usr/local/src/ruby-1.9.2-p290/ext/openssl
# ruby extconf.rb
# make
# make install

passengerとpasserngerのapacheモジュールをインストール
# gem install passenger
# passenger-install-apache2-module

そしてrailsのインストール
gem install rails

インストールはこれで終了です。

次にrailsのプロジェクトを作ります。
$ cd /var/www/html
$ rails new -J -T myfirstcloud

作成したらpassengerモジュールをロードし、ドキュメントルートをプロジェクトディレクトリにするようにApacheの設定をします。
# vi /etc/httpd/conf/httpd.conf
....
LoadModule passenger_module /usr/local/lib/ruby/gems/1.9.1/gems/passenger-3.0.7/ext/apache2/mod_passenger.so 
PassengerRoot /usr/local/lib/ruby/gems/1.9.1/gems/passenger-3.0.7
PassengerRuby /usr/local/bin/ruby 
....
<VirtualHost *:80>
   ServerAdmin webmaster@dummy-host.example.com
   ServerName ec2-175-41-210-229.ap-northeast-1.compute.amazonaws.com
   ErrorLog logs/ec2-175-41-210-229.ap-northeast-1.compute.amazonaws.com-error_log
   CustomLog logs/ec2-175-41-210-229.ap-northeast-1.compute.amazonaws.com-access_log common
   DocumentRoot /var/www/html/myfirstcloud/public
   RackEnv development
   <Directory /var/www/html/myfirstcloud/public >
       AllowOverride all
       Options -MultiView
   </Directory>
</VirtualHost>
これで設定は完了です。
ブラウザでアクセスしてみます。



RAILSの最初の画面が表示されました。
今日はここまで。続きはWEBで。

2011年7月27日水曜日

S3ってなんじゃ?(RUBY SDK特急編)

こんどは最近公開されたAWS SDK for Rubyをつかってみます。
rubyがインストールされている前提です。

まずgemでsdkをインストールします。
# gem install aws-sdk

次にSDKをgitから落としてサンプルディレクトリを見ます。
$ git clone git://github.com/amazonwebservices/aws-sdk-for-ruby.git
$ cd aws-sdk-for-ruby/samples/

アクセスキーの設定をするためにサンプルディレクトリ内に「config.yml」を作ります。
$ echo "access_key_id: アクセスキー" > config.yml
$ echo "secret_access_key: シークレットキー" >> config.yml

そして、S3にアップするためのテストファイルを用意します。
$ echo "Welcome Ruby" > welcome.txt

アップロードします。
$ ruby s3/upload_file.rb hoge-bucket welcome.txt

するとS3にファイルアップロードされ、AWSコンソール上でもアップを確認できました。
 

今日は特急編ということで、ここまで、次回もうちょっと詳しく説明したりしなかったり。


追記:2011/07/29
もし、
ERROR:  Error installing aws-sdk:
        ERROR: Failed to build gem native extension.

/usr/local/bin/ruby extconf.rb
checking for libxml/parser.h... yes
checking for libxslt/xslt.h... no
....
と出る場合は、yumなどでlibxml2-develとlibxslt-develをインストールしてください。