土日でRubyを学習しようと思い、macにRuby開発環境構築してRSpec試したあと、Rails入れてローカルでWebサーバを立ち上げました。最後にRailsにBootstarp3を組み込んでます。学習メモ。ツッコミ歓迎
1.構成概要
Mac OSX El Capitan + Homebrew + git + rbenv + ruby,ruby-build,rails,bundler,bootstrap-sass
2.macにruby環境構築するまで
macで開発する場合、xcodeが必要。AppStoreでダウンロードしておく(大変時間が掛かる)
その後macのパッケージ管理ソフトHomebrewをインストール。rbenvはmacで複数versionのRubyを扱えるようになるコマンドです。便利。
# brewインストールして最新化する
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
sudo xcodebuild -license # xcodeのライセンスを承諾する
echo 'export PATH=/usr/local/bin:$PATH' >> .bash_profile
source .bash_profile
brew doctor
brew update
# gitとrbenvをインストール
brew install git
brew install readline
brew install openssl
brew install ruby-build
brew install rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> .bash_profile
echo 'eval "$(rbenv init -)"' >> .bash_profile
source .bash_profile
参考にした記事:http://qiita.com/keneo/items/1772adc2ebbde229fb71
3.rbenvで最新の安定版Rubyをインストールする。
Rubyには安定板と開発版という概念があるみたいです。
手元の本には偶数と奇数で安定版と、開発版を区別するといった記述があったのですがver1.9以降は異なる模様です。さらに調べてみるとRuby公式に現在の安定版は 2.3.1 と掲載されていたので、2.3.1が安定版だと判りました。(実際は2.2.5を利用しました)
https://www.ruby-lang.org/ja/downloads/
# インストール可能なRuby version 一覧表示
rbenv install -l
# 2.3.1をインストール
rbenv install 2.3.1
# rbenvで切り替え可能なRuby versionを表示
rbenv versions
# defaultのRuby versionを切り替える
rbenv global 2.3.1
# rubyのversion確認(2.3.1となっていること)
ruby -v
4.RailsインストールとWebサーバ起動まで
# railsのインストール
gem install rails
gem install bundler
# 反映
rbenv rehash
source ~/.bash_profile
# version確認(Rails 5.0.0と表示された)
rails -v
# Railsプロジェクトを構築する
rails new rails_study
# RailsのDBモデル構築
cd ./rails_study
rails generate scaffold user name:string age:integer
# DB migrate
rake db:migrate
4-1.rails new とは?
railsで開発する上での雛形を作成してくれる便利コマンド。ログを見るとMVCモデルで開発するためのcontrollerから始まって、HTML用のtemplateファイルまで生成された。cssはsassで、jsはcoffie_scriptで書くことを推奨しているみたいだ。test用のfixtureデータまで生成されてしまった。
4-2.rails generate scaffoldとは
scaffold(スキャフォールド)はモデルを自動生成する便利コマンド。日本語だと骨格。
4-3.rake db:migrate
rakeコマンドは、rubyで処理を記述できるビルドコマンド
rake db:migrate でDBマイグレーションを実行した。
- マイグレーションの実行(rake db:migrate)
optionには環境指定が可能なようなので、上手く設定すればproductionだけ垂直分割したDBに適切にテーブル作成とかできるのだろうか?当然のようにrollbackに対応している。他社事例調べてみたら、色々出てきた。垂直分割や水平分割などを考慮したDBschema運用については各社独自ノウハウがありそう。
4-4.DBはどこへ?
mysqlの設定しなくても、db:migrate出来てしまった。生成されたRailsのソースをgrepしたらDBにsqlite3が設定されていた
default: &default
adapter: sqlite3
pool: 5
timeout: 5000
development:
<<: *default
database: db/development.sqlite3
5.RailsのWebServer起動して疎通確認
# 起動
>>> rails server
=> Booting Puma
=> Rails 5.0.0 application starting in development on http://localhost:3000
=> Run `rails server -h` for more startup options
あとはブラウザを開いてhttp://localhost:3000 にアクセスするとwelcomeページが表示された
6.開発環境IntelliJ IDEAをインストールする
ここまで一切Rubyのコード書いてません。Rubyを本格的に書くためにIDEを設定します。Pythonの書き方はPyCharm先生に教わりました。Rubyの書き方はIntelliJ IDEA先生に教えてもらおうと思います。
良いIDEを利用すると、言語の正しい書き方と初心者にありがちなミスが減って学習効率が高まるんじゃないかなー
JavaIDEとか書いてあるけど、plugin入れたらPythonもErlang/OTPも、頑張ればC#だってIntelliJ IDEAでかけます。(C#はVisualStudio利用した方がよい)
https://www.jetbrains.com/idea/
6-1.colorを最適化する
文字色はcolor-themesにユーザ作成済みの配色が多数掲載してあるので、好きな配色をダウンロードして設定する。設定はFile >> import settings
http://color-themes.com/?view=index
6-2.Rubyプラグインを設定する
IntelliJ IDEA >> Preference
6-3.ProjectSettingsを設定してコードジャンプを有効にする
File >> Project Structureを開いて設定していきます。
6-3-1.SDKにRubyを設定する
6-3-2.classpathにgemを設定する
インストールしたgemがIDE内で適切にimportされない問題を解消するためにClassPathにを設定する
# rubyのgem
~/.rbenv/versions/2.2.5/lib/ruby/gems/2.2.0/gems/
# bundler
~/.rbenv/versions/2.2.5/lib/ruby/gems/2.2.0/gems/bundler-1.12.5/
# ProjectPath
~/ruby/ruby_study/
6-4.RailsのコードをIntelliJ IDEAから読む
正しく設定してあるなら、コードジャンプが完全につながるため、Railsのコードを追うことができるようになっているはずです( ・ㅂ・)و ̑̑
■ 1. 生成したControllerの継承基classを追いかける
7.ほぼ初めてのRuby
いよいよコード書いてみます。まずはパスを通すおまじないから
# プロジェクトのディレクトリ作成
mkdir -p ~/ruby/ruby_study
# rubyのPATHを確認
ruby -e 'puts $LOAD_PATH'
# プロジェクトのディレクトリを登録
export RUBYLIB=~/ruby/ruby_study
# プロジェクトのPATHが登録されていることを確認
ruby -e 'puts $LOAD_PATH'
7-1.足し算の関数とテストコードを書いた
開発する内容は、足し算の関数とテストコードです。
def extend_number(a, b)
a + b
end
p extend_number(1, 2)
$ ruby ./extend_number.rb
3
return要らないのが気持ち悪いけど完成!
7-2.RSpecで関数のテストを書く
RSpecをインストールして、テストコードを書いていきます。テストは{FileName}_spec.rb という名前にするお約束があるみたいです。
# rspecのインストール
gem install rspec
# テスト作成
touch extend_number_rspec.rb
# テスト実行
rspec ./
テストを書いていきます。正常系と異常系を書き分けてみた。
require 'spec_study/spec/spec_helper'
require 'spec_study/extend_number'
describe 'add' do
it 'extend正常系' do
expect(extend_number(1,2)).to eq 3
expect(extend_number(5,5)).to eq 10
expect(extend_number(-5,5)).to eq 0
end
it 'extend異常系' do
expect(extend_number(1,1)).not_to eq 1
expect(extend_number(5,5)).not_to eq 1
end
end
$ rspec ./
..
Finished in 0.00068 seconds (files took 0.07552 seconds to load)
2 examples, 0 failures
8. RailsでURL追加
次はRailsのプロジェクトでURLを追加してWebページを実装しようと試みました。最初は勉強のために rails generate コマンドを使わずに手で全部書きたかったのですが、Web上にあまり資料がない。そもそも標準的な開発方法だとModelもcontroller追加もコマンドから実施するのだろうか? と道に迷って手が止まったのでAmazonで評判が良かった本を近所の本屋で買ってきました。
パラパラ読むとコマンドで生成するのが正しいみたいなので実行してみました
$ rails g controller tea
Running via Spring preloader in process 1471
create app/controllers/tea_controller.rb
invoke erb
create app/views/tea
invoke test_unit
create test/controllers/tea_controller_test.rb
invoke helper
create app/helpers/tea_helper.rb
invoke test_unit
invoke assets
invoke coffee
create app/assets/javascripts/tea.coffee
invoke scss
create app/assets/stylesheets/tea.scss
# コントローラの削除コマンド
# rails g controller tea
色々生成されてしまったので調べてみました。html,js,cssまで生成されてしまうんですね。
# controller
app/controllers/tea_controller.rb
# js と css
app/assets/javascripts/tea.coffee
app/assets/stylesheets/tea.scss
# テスト用
app/helpers/tea_helper.rb
test/controllers/tea_controller_test.rb
8-1. テスト実行と、あえてテストを失敗させてみる
$ rake test
Finished in 0.681177s, 10.2763 runs/s, 13.2124 assertions/s.
テスト失敗させるコードを書く
require 'test_helper'
class TeaControllerTest < ActionDispatch::IntegrationTest
test "the truth" do
assert true
assert nil # エラー
end
end
$ rake test
F
Failure:
TeaControllerTest#test_the_truth [/work/ruby_study/rails/sample/test/controllers/tea_controller_test.rb:6]:
Expected nil to be truthy.
bin/rails test test/controllers/tea_controller_test.rb:4
無事エラーが出たことで、自動生成されたテストコードがちゃんと試験されていることが判りました。
8-2.ルーティング定義してRailsでHelloWorld
生成したcontrollerで render text を定義して、/config/routes.rb にルーティングを定義してやれば新規発行したURLでHelloWorldができる模様です。routes.rbの定義方法は公式ドキュメントのここ見ろってコード内に書いてありました。親切!
class TeaController < ApplicationController
def index
render text: 'Hello World hogehoge!'
end
end
Rails.application.routes.draw do
resources :users
# tea
get '/tea/index', to: 'tea#index'
end
あとは ** rake test ** でテストが通ることを確認したあと、 rails server でWebサーバ起動して指定したURLにブラウザからアクセスしてみます。
8-3.GET
HTTP GETでtea_idを指定して、適切に表示文字を切り替えてみます。
class TeaController < ApplicationController
def index
render text: 'Hello World hogehoge!'
end
def get
tea_list = {
1=>'orange tea',
2=>'apple tea',
3=>'japanese tea',
}
tea_id = params['id'].to_i
tea_name = tea_list[tea_id]
render text: sprintf('id:%{number} is %{tea_name}',
:number=>tea_id.to_s,
:tea_name=>tea_name)
end
end
http://localhost:3000/tea/get/2 へブラウザへアクセスした結果
8-4.テンプレート導入する
文字列を直接表示するのは、ちょっとアレなので、現代風にテンプレートを新規作成してみました。
<h1>Tea Get</h1>
<%= @tea_name %>[<%= @tea_id %>]
class TeaController < ApplicationController
def index
render text: 'Hello World hogehoge!'
end
def get
tea_list = {
1=>'orange tea',
2=>'apple tea',
3=>'japanese tea',
}
tea_id = params['id'].to_i
@tea_id = tea_id
@tea_name = tea_list[tea_id]
end
end
8-4-1.IntelliJ IDEAにRails用設定を入れる
テンプレート内での補完が効かなかったので、設定を入れました。Rails用の設定詳細はIntelliJ公式ドキュメントに詳しく書いてありました
9.paramsはどこからきたのか?
HTTP GETのviewを実装したときに、疑問に思ったのですがHTTP GETパラメーターのparamsはどこからやってきたのでしょうか? ソースコードを追っていくとActionController に到着しました。読みきれなかったのでstack overflowから回答もってきた Rails: Where “params” is defined?
If you look at the Rails API at http://api.rubyonrails.org/, you will find params is just a function that returns the paramaters of the request object.
requestの関数だよ!ってことみたい。action_controller/metal.rbに定義されてました。 しかしなんでMetalなんて名前にしたんだろう。Ruby - Metalということ?
10.インスタンス変数が判らない
Railsのコードを読んでいて、理解できなかったのが @で始まる変数 これはインスタンス変数と呼ぶらしいです。手元の本を紐とくと、Rubyには変数が4種類あるそうです。
# ローカル変数とグローバル変数では、スコープが異なる
# ローカル変数
f = 1
# グローバル変数
$f = 1
# インスタンス変数
@f = 1
# クラス変数
@@f = 1
10-1. インスタンス変数
インスタンス変数はインスタンス毎に異なった範囲を管理できる(インスタンス単位のスコープ)。コード書いたらインスタンス変数の概念が腑に落ちた。
class Dog
def initialize(name)
@dog_name = name
end
def bow()
sprintf('%{dog_name}< bowbow', dog_name:@dog_name)
end
end
p Dog.new('pochi').bow
p Dog.new('kuro').bow
$ ruby dog.rb
"pochi< bowbow"
"kuro< bowbow"
10-2. クラス変数
クラス変数はclass毎に値を管理する(スコープはclass単位)
class Dog
@@count = 0
def initialize(name)
@dog_name = name
end
def bow
# 犬が5回以上鳴いたらオーナーが鳴く
@@count += 1
if @@count > 5
"owner< shut up!!"
else
sprintf('%{dog_name}< bowbow', dog_name:@dog_name)
end
end
end
p Dog.new('pochi').bow
p Dog.new('kuro').bow
p Dog.new('shiro').bow
p Dog.new('pochi').bow
p Dog.new('kuro').bow
p Dog.new('shiro').bow
p Dog.new('pochi').bow
p Dog.new('kuro').bow
p Dog.new('shiro').bow
$ ruby dog.rb
"pochi< bowbow"
"kuro< bowbow"
"shiro< bowbow"
"pochi< bowbow"
"kuro< bowbow"
"owner< shut up!!"
"owner< shut up!!"
"owner< shut up!!"
"owner< shut up!!"
なおクラス変数を利用するコードを書いたらIDEから非推奨な書き方だと指摘された
Reek Code Smells(笑)
10-3.Code smell detector for Ruby
IntelliJ のメッセージで知ったこの規約のようなものは問題あるコードを静的解析で見つけて指摘してくれるツールみたいです。Pythonでいうところのpep8違反を検知するflake8みたいなものなのでしょうか。ReekCodeSmellに掲載されているExampleのDirtyクラスでの実行結果をみれば、だいたいイメージがつくと思います。パラメータ4個以上ダメとか、変数xダメとか、Booleanパラメータは関数内で処理をswitchしてるからそれなら最初からメソッド分割しろとか...etc
11.RailsにBootstrap3組み込む
CSSテンプレートの中でもBootstrap3はよくできています。簡単にレスポンシブ(ブラウザサイズによって動的にサイトレイアウトが変化するので、スマホとPC両方に対応できる)なWebサイトを構築できるので、よく利用します。ご存知の人も多いと思いますがBootstarp3のサンプル実装をPCで開いて、ブラウザの横幅をマウスで変更すると、いかにレスポンシブデザインが強力か理解できると思います。
11-1. Gemfileにbootstrap3を登録
早速RailsにBootstrap3を組み込んでみました( ・ㅂ・)و ̑
# bootstrap3
gem 'bootstrap-sass', '~> 3.3.4'
$ gbundler install
bootstrap3のjsを組み込みます
//= require jquery
-//= require jquery_ujs
+//= require bootstrap-sprockets
+// = require jquery_ujs
最後にsassを新規作成して登録
@import "bootstrap";
これで Railsにbootstrap3が組み込まれました。
11-2.Bootstrap3のコードを書いていく
Bootstarp3のサンプル実装を参考にしてゴリゴリ書いていきます。
class TeaController < ApplicationController
def index
render text: 'Hello World hogehoge!'
end
def get
teas = {
1=>'orange tea',
2=>'apple tea',
3=>'japanese tea',
}
tea_id = params['id'].to_i
@tea_id = tea_id
@tea_name = teas[tea_id]
@tea_list = teas.values # 新規に追加した
end
end
<h1>Tea Get</h1>
<%= @tea_name %>[<%= @tea_id %>]
<div class="page-header">
<h1>teas</h1>
</div>
<div class="row">
<div class="col-md-6">
<table class="table">
<thead>
<tr>
<th>id</th>
<th>name</th>
</tr>
</thead>
<tbody>
<% @tea_list.each_with_index do |tea, index| %>
<tr>
<td><%= index + 1 %></td>
<td><span class="label label-warning"><%= tea %></span></td>
</tr>
<% end %>
</tbody>
</table>
</div>
</div>
あとはブラウザで開いで確認してみましょう
まとめ Rails至れり尽くせり
いろいろ楽そうなんじゃー _(:3」∠)_