仕事でrailsを使うことになったので、調べたり書籍読んだりして学んだことをプロジェクト作成、マイグレーション、モデルあたりを中心に備忘メモ(コントローラ辺りで疲れて雑になってる)
ruby自体もほぼ未経験(Vagrantfileとかchefレシピ書く時使った程度)です、今の自分の知識レベルを残す意図も兼ねて残しておく
自分のspec
経験言語(業務で)
- Java
- PHP
- ワタシ、SQL(MySQL)、チョットワカル
経験フレームワーク(業務で)
- 何となくMVCぽいの。名前は書いて良いのか分からないので伏せる(Java)
- 何となくMVCぽいの。名前は書いて良いのか分からないので伏せる(PHP)
学習期間
- 1週間ほど
準備
- railsでググってはてブ数多めの記事をほとんど読まずにひたすらブクマ
- 何でrails関連記事はこんなに100ブクマ超えだらけなんだ?
- 環境構築 - 他言語経験者による初めてのrails(環境構築編) - Qiita
学習の変遷
- パーフェクトRuby on Rails, 1章→2章と目を通し、6章→7章と目を通しつつ写経
- rubyとrailsに対する事前知識が無さ過ぎた為か、内容に敷居の高さを感じる事があった
- 優し目の入門記事を複数読んで知識の補完・リカバリ
簡易ブログアプリ作成
- DBはMySQLを使う
- 本番でデフォルトのSQLiteを使ってるイメージが余り無いし、だったら最初から事例多そうなmysqlを使いたかった
- テストはRSpecで書く(事を想定はしたが、この記事ではRSpecには触れてない)
- git管理下に置く前提で(完成したら
git commit
してゴール、のイメージ)
プロジェクトの新規作成
-
rails new PROJECT_NAME -T -d mysql --skip-bundle
で作成する- 雛形作成
と使用するgem(が走るGemfile
に記載されている)のインストール -
-T
オプション - デフォルトのテストフレームワークTest::Unit
に関連するファイル作成をスキップする- テストを RSpec で書きたい場合とかは指定すると良い
-
-d mysql
オプション - DBにMySQLを使いたい場合に指定- mysql2 というgemを使うっぽい(Gemfileに記載されている)
- DBの設定ファイル
config/database.yml
もmysql向けのものになっている(後で修正)
-
--skip-bundle
オプション - 通常rails new
時には関連するgemのインストールが プロジェクト・ローカルではない場所に インストールされるが、それをスキップする- プロジェクト・ローカルで閉じた環境に置くことで管理しやすくなる
- もう一つ挙げるなら
ctags -R .
するだけでライブラリ込みのタグ作ってくれるのも良かった(当方 vimmer)
- 雛形作成
$ rails new blog -T -d mysql --skip-bundle
:
:
create ....
rails new時にスキップしたgemのインストール
-
cd PROJECT_ROOT
してbin/bundle --path vendor/bundle
する-
--path
オプションでインストール先を指定。インストール先情報とかは.bundle
ってファイルに保存されていて、以降gemを追加した際は普通にbin/bundle
するだけで同じ場所にインストールしてくれる - パスに
vendor/bundle
を指定するのは、目に付いた事例がそうだったからで、この名前に意味があるのかは分かってない - Gem - Bundler概要 - Qiita
-
git init
-
rails new
は.gitignore
も吐いてくれるんだけど、git init
までしてくれるわけではないので自分でやる - デフォルトの
.gitignore
は不足があるので追加-
/vendor/bundle
を追加 -
*.swp
を追加(vimmerなので!)
-
- なんかいじったら
git add
でstagingしておくとして、これだけファイルが色々あるとgit commit
をどのタイミングでやるべきか悩みそう
config/database.yml の修正
mysqlサーバの場所やスペック、ymlの項目についての詳細は省略。とりあえずvagrantで立てたmysqlサーバを使った
-
default
,development
,test
,production
という4セクションあって、default
で汎用的な設定を記述し、個別上書きが必要ならその他3つで記述するっぽい-
<<: *default
これが「defaultの内容を継承する」って意味だろう(調べてない)
-
- DB名は
DBNAME_env
って形式。DB名を環境別に変えるので本番DBを丸っとdevにインポートって事をしてもそのままでは動かない- DB名を全環境で揃える事は出来ない(出来てもやらない方が良い)のか
- そもそもrailsは環境識別を何処でどのようにやってるのか?
default: &default
adapter: mysql2
encoding: utf8
pool: 5
username: root
password:
host: 192.168.56.150
development:
<<: *default
database: blog_development
- そういえば
encoding
をutf8mb4に変更出来るんだっけ?- おお出来るっぽい!railsパナぃ! ActiveRecordをutf8mb4で動かす - Qiita
DBを作成する
-
rake db:create
で作成- CREATE DATABASE はこのコマンドで動かす。つまり
rake db:create
を叩くサーバ(と接続に使うmysqlユーザ)にCREATE権限が必要ということ(巻き戻しも考えたらDROP権限もいる)- CREATE/DROP TABLEまでWAFのコマンドでやる事には正直違和感ある。。本番でもこのコマンド使うのだろか
- rails/mysql使用時に最低限必要っぽい権限 = "CREATE,DROP,INDEX,ALTER,SELECT,INSERT,UPDATE,DELETE"
- コマンドは
PROJECT_ROOT/bin
下のものを使う習慣を付けとくべきなのか。rails new
した時点のスナップショット、という立ち位置であるのなら。
- CREATE DATABASE はこのコマンドで動かす。つまり
$ bin/rake db:create
- 正常終了したら
DBNAME_development
とDBNAME_test
の2つのDBが作成される - わざわざmysqlサーバに入らなくても
rails db
を叩けばmysqlにログイン出来る- これも同じく、接続を可能にする権限設定をサーバ側で行っている事が前提だけど
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| blog_development |
| blog_test |
| mysql |
| performance_schema |
| test |
+--------------------+
アプリケーションサーバの起動と確認
-
rails s
でアプリケーションサーバを起動 - WEBrick という、rails付属のアプリケーションサーバが起動する
- WEBrickを使うためのメモ - 再帰の反復
- 本番運用時にはApacheやNginxの後ろで動いている、というイメージで良いのかな
- そもそもこのWEBrickは本番でどの程度利用されているのだろうか?
- 仮に別のものに差し替える時に
rails s
の挙動(起ち上げるサーバ)をどうやって変更するのだろか?
- 仮に別のものに差し替える時に
-
http://localhost:3000
で確認
アプリに機能を追加していく
railsアプリの動きはザックリこういう事だろうと理解している
- ユーザーがURL指定でリクエストする
-
config/route.rb
の内容を元に、URLと実行すべきコントローラの紐付けを行い、指定したコントローラ(のアクション)を実行する - 実行されたコントローラ(のアクション)内では、DBのCRUD操作を呼び出しモデルにその内容をセットする
- コントローラ(のアクション)の処理が完了したら、ビューをレンダリングする、この際モデルをビューに渡す
- ユーザーにビューのレンダリング結果を表示する
rails g コマンドで必要なファイルを生成
rails g 何か
と叩くだけで、rails的にヨサゲな雛形群を生成してくれる便利コマンドさん
コントローラ(とその中のアクションメソッド)を生成
-
rails g controller CTL ACT
- 名前ミスった!とかで巻き戻したい -
rails d controller CTL
- 名前ミスった!とかで巻き戻したい -
ルーティング
-
config/route.rb
にURLとそのコントローラのマッピング設定を追加する
モデルを生成
-
rails g model MODEL COLUMNS
-
db/migrate
ディレクトリにテーブル用のマイグレーション(作成・修正)ファイルが生成される -
MODEL
で指定する名前は単数形を指定。z生成されるテーブル名はMODELs
と複数形の名前になる模様 - INDEX追加とか各種制約が欲しければこのファイルに追記すれば良い
- ファイル名にタイムスタンプが含まれてるんだけど、タイムゾーンがUTCっぽいのでどうにかしたい
-
app/model
ディレクトリにモデルクラスが生成される - 名前ミスった!とかで巻き戻したい -
rails d model MODEL
-
マイグレーション適用後に、さらなるマイグレーション
-
rails g migration MIGRATION COLUMNS
- このとき生成されるマイグレーションファイル(のクラス)には
up
(適用する),down
(戻す),change
(変更する)といったメソッドが定義されていて、それぞれに必要な処理を記述すれば良いらしい - 名前ミスった!とかで巻き戻し(削除)したい -
rails d migration MIGRATION
- このとき生成されるマイグレーションファイル(のクラス)には
コントローラ生成+route.rb
修正+モデル生成 まで一発でやる
-
resource
サブコマンド -rails g resource MODEL COLUMNS
- 修正後のroute.rbを見れば分かるが、 RESTfulを意識したアプリ向けのファイルを生成してくれるのが
rails g resource
- GET(show),POST(create),PUT(update),DELETE(destroy)
- 名前ミスった!とかで巻き戻したい -
rails d resource MODEL
- 修正後のroute.rbを見れば分かるが、 RESTfulを意識したアプリ向けのファイルを生成してくれるのが
さらにビュー関連ファイルまでも生成してくれる "全部入りgenerator"
-
scaffold
-rails g scaffold MODEL COLUMNS
-
rails g resource
+ ビュー生成 で良いのか - 名前ミスった!とかで巻き戻したい -
rails d scaffold MODEL
Rails4の「Getting Start」からは、scaffold(スキャフォールド)の説明がとり除かれたようです。 これは、おそらくscaffoldを使用することで、かえって初心者の理解を妨げる要因になると考えられてのことだと思われます。
-
-
今回の開発では
rails g {model,controller}
を用い、一括生成系のrails g {resource,scaffold}
は使わないことにした- 学びながら作る、のが目的なら、こうすべきなのかなと思ったので
マイグレーションをDBに適用する
-
rails g model
とかで生成されたマイグレーションファイルの内容を、rake db:migrate
を実行して適用し、テーブル定義の修正を行う- カラム定義間違えた!とかで巻き戻したい -
rake db:rollback
- rollback前 にマイグレーションファイルをいじると、rollback 実行時にエラーになる。いつ・どんな内容のマイグレーションを行ったかを管理しているからだろう
- カラム定義間違えた!とかで巻き戻したい -
-
rake db:migrate:status
すると、DB適用済/未適用のマイグレーションを確認可能 -
仕様変更等でTBLの変更が発生した場合は、まず
rails g migrate
でマイグレーションファイルを生成し、それを適用する というのがrails流なんですかね
実際に作ってみる
TBLのPKにはintegerのidがデフォルトで使われるらしい。rails g
のカラム指定で明示する必要はない
$ bin/rails g model article title:string content:text # id は未指定
$ bin/rails g model comment article_id:int comment:text # id は未指定
で生成すると、下記のようなマイグレーションファイル(実体はクラス)が出来る。
最低限のCREATE TABLE処理だけが定義されるので、INDEX付与したい場合とかは 専用のメソッドで 指定する
- index追加 -
add_index TBL_NAME, COLUMNS
- その他使用できるメソッドやオプションを探すには マイグレーション(migration) - Railsドキュメント を見ると良さげ
class CreateArticles < ActiveRecord::Migration
def change
create_table :articles do |t|
t.string :title
t.text :content
t.timestamps null: false
end
end
end
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :article_id
t.text :comment
t.timestamps null: false
end
add_index :comments, :article_id # 手動で追記
end
end
これをrake db:migrate
でDBに適用。railsがマイグレーションクラスをDBMS毎に良しなにDDLに変換して実行してくれる
$ bin/rake db:migrate
== 20150227171411 CreateArticles: migrating ===================================
-- create_table(:articles)
-> 0.0158s
== 20150227171411 CreateArticles: migrated (0.0160s) ==========================
== 20150227173657 CreateComments: migrating ===================================
-- create_table(:comments)
-> 0.0276s
-- add_index(:comments, :article_id)
-> 0.0381s
DBサーバ側でarticlesのDDLを覗いてみる。:string
型指定したカラムはvarchar(255)
になる。長さを指定したければrails g model
の際に、title:string{100}
と{NUM}
形式で指定すれば良い
mysql> show create table articles;
CREATE TABLE `articles` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) COLLATE utf8_unicode_ci DEFAULT NULL,
`content` text COLLATE utf8_unicode_ci,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
ここまでして、 テーブル間のリレーション設定にはrails g model
の際にNAME:references
とreferences
を使えば良い事を知った 。
ので戻して再作成する
戻しの流れがちと分かりにくかったが、rake db:rollback
してrails d model
でイケた。 → 参考: 間違ったrake db:migrateを元に戻す | Morizotter Blog
(実際の運用ではSQLでいうところのALTER
相当の操作はrails g migration``rake db:migrate
で行うのだろな、と想像している)
$ bin/rails g model comment article:references comment:text # references でリレーション指定
$ bin/rake db:migrate
references
指定のあるテーブルはmysqlではこういうFOREIGN KEY
指定がなされたCREATE文になるらしい。 FOREIGN KEY 出来ればあまり使いたくない人です
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`article_id` int(11) DEFAULT NULL,
`comment` text COLLATE utf8_unicode_ci,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `index_comments_on_article_id` (`article_id`),
CONSTRAINT `fk_rails_f4641449be` FOREIGN KEY (`article_id`) REFERENCES `articles` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
モデル
そもそもモデル(/app/model
下のクラス)には何を書くのかというと、ザックリこの3つっぽい
- モデル間のリレーション(association)
- モデルで保持する値のバリデーション
- モデル固有のビジネスロジック
リレーション
マイグレーションで設定したリレーション(railsではassociationと呼んでいるぽい)を、モデルクラスにも独自DSL使って明記する。
こうすることで、モデルを使う側(V層C層)にはDB(インフラ層)を隠蔽するという意図が見える
実は comment のモデルクラスにはrails g model
時点でリレーションについての処理が入っている。それが下記。belongs_to
は名前の通り「所属」つまり一対多リレーションの一側を指定する メソッド 。引数には 対象モデルクラス名を小文字にしたシンボル を指定するようだ
belongs_to :article
もう一方のarticle(一対多の一側)はどうするか?
rails g model
時点では空のクラスが定義されているだけだが、同じくリレーションについての処理を追加する。
これは開発の流れとしてはよく有るケースっぽくて、一対多な関係性を持つモデルをrails g model
で生成する際、一を生成した時点ではまだ多側がどのモデルになるか分からない。なので多側を追加生成する度に一側にhas_many
を追加していくのかと
例えば dependent: :destroy
オプションは、「削除の際に関連する多側も一緒に削除する」事を指定する
class Article < ActiveRecord::Base
has_many :comment, dependent: :destroy
end
バリデーション
取りうる値に制限を設ける。validates
かvalidate
メソッドを使う。カラムごとの指定には前者、より複雑な・別途メソッドを呼んだりするような場合は後者を使うっぽい
バリデーションで使えるメソッド・オプションは 検証(validation) - Railsドキュメント を見ると良さげ
class Article < ActiveRecord::Base
has_many :comment, dependent: :destroy
# 記事のタイトルは必須(presence)で50文字(length)まで
validates :title, length: { maximum: 50 }, presence: true
# 記事の本文は必須(presence)
validates :content, presence: true
end
class Comment < ActiveRecord::Base
belongs_to :article
# コメントは必須(presence)
validates :comment, presence: true
end
コントローラ(とビュー)
rails g controller
すると、コントローラと対応するビューが生成される。コントローラ名は複数形で指定すれば良いらしい
$ bin/rails g controller articles
コントローラクラスにアクションメソッドを追加し、config/routes.rb
にURLとアクションの紐付けも追加していく。rails g {resource,scaffold}
を使えばこの辺りまで自動生成してくれて便利なのだろう
articleの一覧を拾って表示するだけの処理を書いてみる
class ArticlesController < ApplicationController
def index
# modelの all メソッドで取得した全件を @付きの変数(インスタンス変数)にセット
@articles = Article.all
respond_to do |format|
format.html
format.json
end
end
end
Rails.application.routes.draw do
# "http://localhost:3000/articles" にアクセス時に起動するアクションを 'コントローラ名#メソッド名' で定義
get '/articles' => 'articles#index'
ビューにはデフォルトでerbと呼ばれるテンプレートが使われるらしい。jspっぽい(と言っていいのか)。
画面ごとのビューファイルには、その画面固有の内容だけ書けば良くて、ヘッダとかフッタとか所謂共通部分はapp/views/layouts
下のファイルを使ってくれる。デフォルトではapplication.html.erb
が使われるようで、このレイアウトファイル内の<%= yield %>
と記述した箇所に固有のビューが埋め込まれる
<div>
記事
<div>
<% @articles.each do |article| %>
<ul>
<li><%= article.title %> | <%= article.content %></li>
</ul>
<% end %>
</div>
</div>
作成・編集・削除とか、コメント機能の追加も同様にコントローラとビューを追加してガシガシ作っていけば良い。それはまたおいおい纏めようと思う
所感として、自分のような他言語/他FW経験者は、「どういう仕組み・流れで、どういう機能を実現したいか?」は分かっていても「どう書けばよいか?」が分からない/調査に時間が掛かる事が多いと思う。
見やすい・探しやすい API一覧とか逆引きリファレンスを手元に置いておくとだいぶ効率が違ってくるはず
最後にRailsとは?系の記事を読んで、目に止まった言葉をいくつか
全てのプログラマがWebアプリケーションを作り始めるにあたって、必要であろう物を仮定することによって簡単にプログラム出来るように設計されています
他の多くの言語やフレームワークと比べ、なるべくコードを書かずに済むようになっています
Railsは固執(?)的なソフトウェアです。 Railsは、それを行うのはこれが"ベスト"な方法であると仮定してしまい、その方法を奨励し別の方法は推奨しないように設計されています。 もし、"The Rails Way"を学ぶと、生産性が飛躍的に向上するかもしれません。 もし、Railsの開発に他の言語の古い手法を用いるのであれば、幸福な体験を得ることは難しいでしょう。