2015年10月29日木曜日

AWS無料利用枠のインスタンスで、Ruby on Railsの環境を作成してみる②

こんにちは、井下です。

前回はAWS無料利用枠のインスタンスを使って、とりあえずRailsがコンソール起動できるところまでの環境を構築しました。

今回はデフォルトで入ってるツール・ソフトウェア群を、よりスペックの良いものに差し替える工程を説明していきます。
なお、差し替え対象は下記の3つです。
  • JavaScriptランタイム
  • データベース
  • アプリケーションサーバ・Webサーバ
今回はJavaScriptランタイムと、データベースの差し替えを行います。
アプリケーションサーバおよびWebサーバは、次回差し替えを行っていきます。

1.JavaScriptランタイムの差し替え

前回も触れましたが、気軽に利用できるJavaScriptランタイムとして、「therubyracer」がありますが、bundle installに失敗するケースが出てきたり、メモリ消費量が多めな問題があります。

そこでよく利用されるJavaScriptランタイムの、「node.js」を導入してみます。

まずはGit Hubに掲載されているnode.jsのインストール手順に従って、node.jsをインストールしてみます。

[ec2-user@ip-xxx ~]$ curl --silent --location https://rpm.nodesource.com/setup | bash -

## Inspecting system...

+ rpm -q --whatprovides redhat-release || rpm -q --whatprovides centos-release || rpm -q --whatprovides cloudlinux-release
+ uname -m

## Confirming "el7-x86_64" is supported...

+ curl -sLf -o /dev/null 'https://rpm.nodesource.com/pub/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'

## Downloading release setup RPM...

+ mktemp
+ curl -sL -o '/tmp/tmp.s0QxNR3NXV' 'https://rpm.nodesource.com/pub/el/7/x86_64/nodesource-release-el7-1.noarch.rpm'

## Installing release setup RPM...

+ rpm -i --nosignature --force '/tmp/tmp.s0QxNR3NXV'
error: can't create transaction lock on /var/lib/rpm/.rpm.lock (No such file or directory)

Error executing command, exiting

…が、ダメでした。権限の問題らしいので、sudoしてみても結果は変わらず。
仕方がないので、ソースを入手してインストールする方法を試してみたところ、こちらはインストールができました。

まずは、最新(2015年10月時点)のバージョン、0.12.7のソースコードを入手します。
[ec2-user@ip-xxx ~]$ mkdir work
[ec2-user@ip-xxx ~]$ cd work/
[ec2-user@ip-xxx work]$ wget http://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz
-bash: wget: command not found

…その前に、wgetがインストールされていなかったので、yumを使ってインストールします。

[ec2-user@ip-xxx work]$ sudo yum install wget
~略~
Complete!

今度こそ、0.12.7のソースコードを入手します。
[ec2-user@ip-xxx work]$ wget http://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz
--2015-10-25 21:00:46--  http://nodejs.org/dist/v0.12.7/node-v0.12.7.tar.gz
Resolving nodejs.org (nodejs.org)... 104.20.23.46, 104.20.22.46, 2400:cb00:2048:1::6814:162e, ...
Connecting to nodejs.org (nodejs.org)|104.20.23.46|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 20063992 (19M) [application/gzip]
Saving to: ‘node-v0.12.7.tar.gz’

100%[=====================================================================================>] 20,063,992  7.49MB/s   in 2.6s

2015-10-25 21:00:49 (7.49 MB/s) - ‘node-v0.12.7.tar.gz’ saved [20063992/20063992]
[ec2-user@ip-xxx work]$ ls
node-v0.12.7.tar.gz

lsコマンドでソースコードを確認できたので、解凍した後、お決まりの「./configure」⇒「make」⇒「make install」でインストールします。

[ec2-user@ip-xxx work]$ tar xvf node-v0.12.7.tar.gz
[ec2-user@ip-xxx work]$ ls
node-v0.12.7  node-v0.12.7.tar.gz
[ec2-user@ip-xxx work]$ cd node-v0.12.7
[ec2-user@ip-xxx node-v0.12.7]$ ./configure
creating  ./icu_config.gypi
~略~
creating  ./config.gypi
creating  ./config.mk


[ec2-user@ip-xxx node-v0.12.7]$ make
~略~
[ec2-user@ip-xxx node-v0.12.7]$ sudo make install
~略~
[ec2-user@ip-xxx node-v0.12.7]$ node -v
v0.12.7

バージョン確認コマンドによって、node.jsがインストールできたことが確認できました。
これでtherubyracerは不要になったので、アンインストールしてしまいます。

[ec2-user@ip-xxx node-v0.12.7]$ cd ~/sample
[ec2-user@ip-xxx sample]$ gem list therubyracer

*** LOCAL GEMS ***

therubyracer (0.12.2)
[ec2-user@ip-xxx sample]$ gem uninstall therubyracer
[ec2-user@ip-xxx sample]$ gem list therubyracer

*** LOCAL GEMS ***

therubyracerがアンインストールされたので、Railsをコンソール起動して、JavaScriptランタイムが見つからないエラーが発生しないことを確認します。

[ec2-user@ip-xxx sample]$ rails c
Loading development environment (Rails 4.2.4)
irb(main):001:0>exit
[ec2-user@ip-xxx sample]$

特に問題はなさそうです。最後にGemfileを編集して、therubyracerを再度コメントアウトの状態にし、bundle installした際にインストールされないようにします。

[ec2-user@ip-xxx sample]$ vi Gemfile

「Gemgile」(緑字の部分を追記)

~略~
# See https://github.com/rails/execjs#readme for more supported runtimes
# gem 'therubyracer', platforms: :ruby
~略~

これでJavaScriptランタイムの差し替えが完了しました!

2015年10月14日水曜日

AWS無料利用枠のインスタンスで、Ruby on Railsの環境を作成してみる①

こんにちは、井下です。

休日に犬と遊んでいるはずみで、セロテープを切るあのギザギザで出血する羽目になりました。身近な凶器ですよね、あれ…。

さて、今回はRuby on Railsの「環境構築」がテーマです。

Webアプリケーションを開発するうえで、Ruby on Railsは選択肢の1つとして挙げられるようになっていますが、実際に開発を行おうとすると、Ruby on Railsの稼動にこぎつけるまで、様々な障害が出てきます。
幸いそれなりに問題を解決するための情報は出回っているので、完全に手詰まりになることはありませんが、バージョンや環境の違いによって、情報が適切でないことも少なくありません。

そこで、AWS無料利用枠のインスタンスを利用して、1からRuby on Railsの環境構築を行ってみようと思います。

なお、後でも改めて記述しますが、インスタンスやRubyのバージョンなどは下記の通りです。

インスタンス⇒Red Hat Enterprise Linux 7.1(t2.micro) ※2015/10時点の無料利用枠インスタンス
Ruby⇒2.2.3
Ruby on Rails⇒4.2.4

1.インスタンス作成

まずはRuby on Railsをインストールするインスタンスを作成します。
選択するインスタンスは、現時点で無料利用枠となっている「Red Hat Enterprise Linux 7.1」、インストールタイプは同じく無料利用枠の「t2.micro」です。

今回はRuby on Railsを動かすことが目的なので、それ以降の設定はデフォルトのまま、インスタンスを作成します。

2.Rubyインストール

Rubyをインストールする方法は、ソースコードから直接ビルドする方法と、ツールを利用する方法に大別されますが、今回はRubyのインストールによく利用される「rbenv」というツールを利用します。

また、rbenvをインストールするためには、「git」というバージョン管理ツールが必要になります。
順序としてまとめると、次のようになります。

(1).gitをインストールするために必要なライブラリをインストールする
(2).gitをインストールする
(3).gitを利用してrbenvをインストールする
(4).rbenvを利用してRubyをインストールする

(1).gitをインストールするために必要なライブラリをインストールする

下記のライブラリをインストールします。記載しているコマンドそのままで通るはずです。
[ec2-user@ip-xxx ~]$ sudo yum install zlib zlib-devel openssl-devel sqlite-devel gcc-c++ glibc-headers libyaml-devel readline readline-devel zlib-devel libffi-devel

色々とコンソールに表示されますが、最終的に「Complete!」と出てくればライブラリの導入は完了です。
Installed:
  gcc-c++.x86_64 0:4.8.3-9.el7              glibc-headers.x86_64 0:2.17-78.el7  libffi-devel.x86_64 0:3.0.13-11.el7
  openssl-devel.x86_64 1:1.0.1e-42.el7_1.9  readline-devel.x86_64 0:6.2-9.el7   sqlite-devel.x86_64 0:3.7.17-6.el7_1.1
  zlib-devel.x86_64 0:1.2.7-13.el7

Dependency Installed:
  cpp.x86_64 0:4.8.3-9.el7                                 gcc.x86_64 0:4.8.3-9.el7
  glibc-devel.x86_64 0:2.17-78.el7                         kernel-headers.x86_64 0:3.10.0-229.14.1.el7
  keyutils-libs-devel.x86_64 0:1.5.8-3.el7                 krb5-devel.x86_64 0:1.12.2-15.el7_1
  libcom_err-devel.x86_64 0:1.42.9-7.el7                   libmpc.x86_64 0:1.0.1-3.el7
  libselinux-devel.x86_64 0:2.2.2-6.el7                    libsepol-devel.x86_64 0:2.1.9-3.el7
  libstdc++-devel.x86_64 0:4.8.3-9.el7                     libverto-devel.x86_64 0:0.2.5-4.el7
  mpfr.x86_64 0:3.1.1-4.el7                                ncurses-devel.x86_64 0:5.9-13.20130511.el7
  pcre-devel.x86_64 0:8.32-14.el7

Dependency Updated:
  krb5-libs.x86_64 0:1.12.2-15.el7_1   openssl.x86_64 1:1.0.1e-42.el7_1.9   openssl-libs.x86_64 1:1.0.1e-42.el7_1.9
  sqlite.x86_64 0:3.7.17-6.el7_1.1

Complete!

(2).gitをインストールする

gitをインストールします。やること自体は前の手順とほぼ変わりありません。
[ec2-user@ip-xxx ~]$ sudo yum install git

次のように表示されていれば、gitのインストールも完了です。
Installed:
  git.x86_64 0:1.8.3.1-4.el7

Dependency Installed:
  libgnome-keyring.x86_64 0:3.8.0-3.el7                   perl.x86_64 4:5.16.3-285.el7
  perl-Carp.noarch 0:1.26-244.el7                         perl-Encode.x86_64 0:2.51-7.el7
  perl-Error.noarch 1:0.17020-2.el7                       perl-Exporter.noarch 0:5.68-3.el7
  perl-File-Path.noarch 0:2.09-2.el7                      perl-File-Temp.noarch 0:0.23.01-3.el7
  perl-Filter.x86_64 0:1.49-3.el7                         perl-Getopt-Long.noarch 0:2.40-2.el7
  perl-Git.noarch 0:1.8.3.1-4.el7                         perl-HTTP-Tiny.noarch 0:0.033-3.el7
  perl-PathTools.x86_64 0:3.40-5.el7                      perl-Pod-Escapes.noarch 1:1.04-285.el7
  perl-Pod-Perldoc.noarch 0:3.20-4.el7                    perl-Pod-Simple.noarch 1:3.28-4.el7
  perl-Pod-Usage.noarch 0:1.63-3.el7                      perl-Scalar-List-Utils.x86_64 0:1.27-248.el7
  perl-Socket.x86_64 0:2.010-3.el7                        perl-Storable.x86_64 0:2.45-3.el7
  perl-TermReadKey.x86_64 0:2.30-20.el7                   perl-Text-ParseWords.noarch 0:3.29-4.el7
  perl-Time-HiRes.x86_64 4:1.9725-3.el7                   perl-Time-Local.noarch 0:1.2300-2.el7
  perl-constant.noarch 0:1.27-2.el7                       perl-libs.x86_64 4:5.16.3-285.el7
  perl-macros.x86_64 4:5.16.3-285.el7                     perl-parent.noarch 1:0.225-244.el7
  perl-podlators.noarch 0:2.5.1-3.el7                     perl-threads.x86_64 0:1.87-4.el7
  perl-threads-shared.x86_64 0:1.43-6.el7

Complete!

(3).gitを利用してrbenvをインストールする

先ほどの手順でインストールしたgitを利用して、rbenvをインストールします。
[ec2-user@ip-xxx ~]$ git clone https://github.com/sstephenson/rbenv.git ~/.rbenv

下記のような表示がされれば、rbenvのインストールは完了です。
Cloning into '/home/ec2-user/.rbenv'...
remote: Counting objects: 2162, done.
remote: Compressing objects: 100% (63/63), done.
remote: Total 2162 (delta 34), reused 0 (delta 0), pack-reused 2094
Receiving objects: 100% (2162/2162), 371.13 KiB | 175.00 KiB/s, done.
Resolving deltas: 100% (1312/1312), done.

rbenvでRubyをインストールするために必要なプラグインも、gitを利用してインストールします。
[ec2-user@ip-xxx ~]$ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build

下記のような表示がされれば、Rubyのプラグインのインストールは完了です。
Cloning into '/home/ec2-user/.rbenv/plugins/ruby-build'...
remote: Counting objects: 5056, done.
remote: Total 5056 (delta 0), reused 0 (delta 0), pack-reused 5056
Receiving objects: 100% (5056/5056), 944.89 KiB | 318.00 KiB/s, done.
Resolving deltas: 100% (2734/2734), done.

これでrbenvをインストールできましたが、rbenvを利用するためにパスを通す必要があります。
「.bash_profile」にrbenvのパスが通るように設定を追加し、以降のログインでrbenvのパスが通っている状態にします。
[ec2-user@ip-xxx ~]$ vi ~/.bash_profile

「.bash_profile」ファイル(緑字の部分を追記)
# .bash_profile

# Get the aliases and functions
if [ -f ~/.bashrc ]; then
        . ~/.bashrc
fi

# User specific environment and startup programs

PATH=$PATH:$HOME/.local/bin:$HOME/bin

export PATH
export PATH=$PATH:$HOME/.rbenv/bin:$HOME/.rbenv/shims

ここでログインし直すか、「source .bash_profile」コマンドで「.bash_profile」を読み直すと、rbenvにパスが通り、rbenvコマンドを認識するようになります。
[ec2-user@ip-xxx ~]$ source .bash_profile
[ec2-user@ip-xxx ~]$ rbenv -v
rbenv 0.4.0-169-g0f44c57

(4).rbenvを利用してRubyをインストールする

ここでようやくRubyをインストールします。バージョンは2015年10月時点での安定版、2.2.3を選択しました。
[ec2-user@ip-xxx ~]$ rbenv install 2.2.3

3~4分かかりましたが、無事Rubyがインストールできました。
Downloading ruby-2.2.3.tar.gz...
-> https://dqw8nmjcqpjn7.cloudfront.net/df795f2f99860745a416092a4004b016ccf77e8b82dec956b120f18bdc71edce
Installing ruby-2.2.3...
Installed ruby-2.2.3 to /home/ec2-user/.rbenv/versions/2.2.3

ただし、rbenvでRubyをインストールした場合、どのバージョンを利用するのか設定する必要があります(rbenvはRubyのバージョン管理を行っているため)

下記のコマンドによって、Rubyの2.2.3を利用する設定を行います。
[ec2-user@ip-xxx ~]$ rbenv rehash
[ec2-user@ip-xxx ~]$ rbenv global 2.2.3

最後にRubyのバージョン確認コマンドで、Ruby 2.2.3が確認できれば、Rubyのインストールおよび利用準備は完了です。
[ec2-user@ip-xxx ~]$ ruby -v
ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

2015年10月1日木曜日

Ruby on Railsで、実際にありそうなシーン別にテストコードを書いてみる

こんにちは。
鷲尾です。


現在Ruby on Railsに触れ始めて少し経ちますが、個人的には非常にとっつきにくく、理解に時間がかかることがたくさんあります。

例えば、テストコードを作成する際に、遷移先ページに表示されたことをテストするためにはどうすればよいのか、なにを書けばいいのかなど、戸惑うことがあるかと思います。(URLが指定通りに変化しているのか、正しい値がセットされているかなど・・・)

バリバリのプログラマーの方であればなにをそんなことと思うかもしれませんが、同じ初心者の方にもわかりやすいよう、シーン別にこのあたりを少しまとめてみようと思います。

そこで今回は、Ruby on Railsでテストコードを作成する際に、比較的多く使用されるマッチャーの使い方をシーン別に紹介していきたいと思います。


今回紹介するものは
1.なにかボタンを押されたりして、きちんと指定した遷移先に遷移しているかどうかをテストしたい
2.指定した文字列のメッセージが表示されているかどうかをテストしたい
3.登録処理などを行ったときに、本当にDBに登録されているかをテストしたい

といった3つのシーンを考えてみます。

※今回はRspecでテストを行っています。また、画面での操作をシミュレーションすることが出来るCapybaraというパッケージを導入していますので、事前に導入しておいてください。
参考:http://www.oiax.jp/rails/rspec_capybara_primer/rspec_capybara_the_first_step.html


1.きちんと指定した遷移先に遷移しているかどうかをテストしたい
例えば、ログイン画面でIDとパスワードを入力したあとに、正しい認証情報だったらトップ画面に遷移させたいといったことがあると思います。

こういった場合、例えば遷移後のURLを確認することで、テストを行うことが出来ます。

では、例文です。

let(:main_page_url){
  current_host << '/main_page'
}

let(:default_params){
  user_id => 'abcde1234'
}

  scenario 'ログイン後にmain_pageに遷移するか' do
      visit "ログインページURL"
      user = User.create default_params
     
      fill_in 'user_id', with: default_params[:user_id] ← 注1 
      click_button "ログイン"
         
      expect(current_url).to eq(main_page_url)
end

注1 ここでfill_in 'user_id'とありますが、これは入力を行うテキストボックスのIDを指しています。
例えば、ログインページにユーザIDを入力するテキストボックスがあり、そのテキストボックスにuser_idというIDが設定されていたとします。その際、Capybaraのfill_inメソッドを使って"user_idというIDがついたテキストボックス"に値を入力させることができます。



テストコードを実行する前にmain_page_urlなどの名前で遷移先のURLなどを設定しておきます。
※テストコードが書かれているファイルの初めに宣言しておくイメージです。
今回で言うと、main_page_url、default_paramsになります。

動作的には、遷移後のURLが設定したmain_page_urlと等しいかどうかを確認します。
current_urlには現在表示しているページのURLが格納されているため、

expect(今いるページのURL).to eq(遷移先のURL) 

となるわけです。
こうすれば、URLを比較することができますね。


2.指定した文字列のメッセージが表示されているかどうかをテストしたい
では、例えばなにかエラーメッセージが表示されるようにするときや、リダイレクト先にきちんと想定している文字列が表示されているかどうかを確認したいことがあると思います。
そういった場合は、上記のURLだけでなく、そのページに意図した文字列がきちんと表示されているかどうかを確認する必要があります。
例えば、ログイン画面を作っていて、ユーザIDに不適切な値を入力されたままログインボタンを押した場合。こういった場合、ページを遷移せずに、入力フォームの上や下などにエラーメッセージを表示することがありますね。





この場合、例えばこのように書くことができます。


let(:main_page_url){
  current_host << '/main_page'
}

let(:default_params){
  user_id => 'abcde1234'
}

let(:login_page_url){
  current_host << '/login_page'
}

let(:miss_params){
  user_id => 'abcde4321'
}
  
  scenario 'ログインに失敗した場合、ユーザ名は必須項目です!が表示されるか' do
      visit "ログインページURL"
      User.create default_params

      user = User.first



      fill_in 'user_id', with: miss_params[:user_id]

      click_button "ログイン"
         

      expect(current_url).to eq(login_page_url)  ← 注2
      expect(page).to have_content('ユーザ名は必須項目です!')
end
注2 ログインページにアクセスした状態が"/login_page"だったとして、ログイン失敗後も"/login_page"であるとしています。

ここで使用しているhave_contentというマッチャは、テスト対象のページに指定した文字列が存在するかどうかを確認します。

したがって、現在いるページの上部にエラーメッセージが表示される場合など、比較対象のページが現在いるページと同じページで、エラーメッセージだけが追記されるという場合に、よく利用されます。


3.登録処理などを行ったときに、本当にDBに登録されているかをテストしたい
実際にDBになにか登録する際に、本当に登録されているかどうかを確認するシーンがあると思います。
そう言った際に、どうすればDBに値が登録されているかを確認することができるのでしょうか。
この場合、登録先のDBのレコードが+1されているかどうかを確認します。
また、同じように、データ(レコード)の削除を行うメソッドの場合、DBからレコードが-1されているかどうかを確認することが出来ます。

例えば、以下のようになります。

let(:valid_attributes) {
  {
    :user_id => "abcde1234"
  }
}

it "ユーザ情報を1件登録出来ていること" do

 expect {

         post :create, {:user => valid_attributes}

     }.to change(User, :count).by(1)

 end

この場合、postでcreateメソッドが実行された際に、UserというDBにレコードが1件追加されているかどうかを確認しています。

同じように、DBから該当のレコードが削除される場合は、以下のように書きます。

let(:valid_attributes) {
  {
    :user_id => "abcde1234"
  }
}

it "ユーザ情報を1件登録出来ていること" do
 expect {

         post :delete, {:user => valid_attributes}

     }.to change(User, :count).by(-1)
 end

こうすると、レコードが1件減ったという状態をテストすることができるので、正常に削除されたかどうかをテストすることが出来ます。


Ruby on Railsでは、テストコード内で様々な状態をシミュレートするためのマッチャー(matchers)が他にもたくさん用意されています。

私も何度も経験していますが、はじめに「なにをテストしたいのか」ということを明確にしておかないと、テストコードを作成しても確認したいことが確認できていないテストコードが出来上がってしまいます。
当たり前のように聞こえるかもしれませんが、なにを確認したくて、そのためにはどのマッチャを使う必要があるのかをきちんと考えてから、テストコードを作成すると良いかと思います。

今回紹介したテストコードの例が少しでも参考になれば幸いです。

以上です。