はじめに
こんにちは、櫻です。
気がつけばRuby on Rails4.0がリリースされていたようです。
なんとなく雰囲気は知っていたけれど、きちんと触ったことがなかったというのと、ちょっとRailsとか紹介してよという声が聞こえたので、Getting Startedを見ながら動かしてみたという内容です。
準備
Rails4.0では以下のものが必要なようです。
- ruby >= 1.9.3
- gems
- sqlite3
- JavaScriptの処理系
「環境を揃えるのが楽そう」という理由でUbuntu上で準備をしました。
ruby(とgems) | rbenvで2.0.0-p247をインストール |
sqlite3 | sudo apt-get install sqlite3 libsqlite3-dev |
node.js | nvmでv0.10.13をインストール |
プロジェクト作成
Railsでは以下のコマンドでプロジェクトのテンプレートが生成されます。
[sakura@ubuntu13 ~]$ rails new プロジェクト名
railsのディレクトリ構成に関しては本家のGetting Startedを参考にしてください。
この状態で
[sakura@ubuntu13 ~]$ cd プロジェクト名 [sakura@ubuntu13 blog]$ rails server
と実行すると、テスト用のサーバが3000番ポートで起動します。
ポート番号を変更したい場合は --port=ポート番号 というオプションをつける必要があります。
この時点でJavaScriptの実行環境がインストールされていないと、起動に失敗しました。
CoffeeScriptのコンパイル時にJavaScriptの実行環境が必要になるようです。
http://localhost:3000 を開くと、このような画面が表示されます。
以下、プロジェクト名(Getting Startedと同じようにblog)ディレクトリ内での操作です。
Hello World
まずは動くページを作成してみましょう。
Getting Startedの通りにindexアクション付きのwelcomeコントローラを作成します。
[sakura@ubuntu13 blog]$ rails generate controller welcome index create app/controllers/welcome_controller.rb route get "welcome/index" invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit create test/helpers/welcome_helper_test.rb invoke assets invoke coffee create app/assets/javascripts/welcome.js.coffee invoke scss create app/assets/stylesheets/welcome.css.scss
対応するコントローラとビューが生成されました。
app/views/welcome/index.html.erbの内容を
<h1>Hello, Rails!</h1>
のように書き換えて http://localhost:3000/welcome/index を開くとその内容が表示されます。
リクエストされたURLがどのようにコントローラ/アクションと対応付けられるかはconfig/routes.rbに定義されています。
先ほどのrails generate ...コマンド実行時に
get "welcome/index"
という行が追加されていたので、手動で追加しなくても動作したということです。
ブログ投稿のCRUD作成
Getting Startedではこの後ブログの画面の作成が始まります。
はじめにconfig/routes.rbにresouces :postsという行を追加します。
※resourceと指定すると別のルールになるため、動作が変わってきます。
Blog::Application.routes.draw do get "welcome/index" resources :posts end
この1行で良くあるCRUDのルーティング定義がされます。
rake routesというコマンドで現在の定義の一覧が確認できます。
[sakura@ubuntu13 blog]$ rake routes Prefix Verb URI Pattern Controller#Action welcome_index GET /welcome/index(.:format) welcome#index posts GET /posts(.:format) posts#index POST /posts(.:format) posts#create new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit post GET /posts/:id(.:format) posts#show PATCH /posts/:id(.:format) posts#update PUT /posts/:id(.:format) posts#update DELETE /posts/:id(.:format) posts#destroy [sakura@ubuntu13 blog]$
この時点で http://localhost:3000/posts/new を開くとコントローラが無いと怒られます。
後はこの定義に出てくるコントローラ/アクションを作成していく事になります。
新規投稿
まずは新規投稿です。まだコントローラを作成していないので、以下のコマンドで雛形を作成します。
[sakura@ubuntu13 ~]$ rails generate controller posts
ここで再度 http://localhost:3000/posts/new を開くと今度は対応するアクション(new)が無いと怒られます。
コントローラ(app/controllers/posts_controller.rb)にnewメソッドを追加しましょう。
def new end
これでもう一度 http://localhost:3000/posts/new を開くと今度はテンプレートが無いと怒られます。
コントローラの自動生成ではビュー(テンプレート)は生成されていないので、app/vewis/posts/new.html.erbを作成しましょう。
<h1>New Post</h1> <%= form_for :post, url: posts_path do |f| %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %>
これで新規投稿の画面が表示されるようになりました。
このままボタンを押すとまたアクションが無いと怒られるので、コントローラにcreateメソッドを追加します。
def create render text: params[:post].inspect end
この状態でボタンを押すと、投稿内容がJSONとして表示されます。
新規投稿用のコントローラとビューが作成できたので、次はモデルです。
コマンド2つでモデルクラス作成、DBのテーブルの作成などが実行できます。
[sakura@ubuntu13 blog]$ rails generate model Post title:string text:text invoke active_record create db/migrate/20130724065248_create_posts.rb create app/models/post.rb invoke test_unit create test/models/post_test.rb create test/fixtures/posts.yml [sakura@ubuntu13 blog]$ rake db:migrate == CreatePosts: migrating ==================================================== -- create_table(:posts) -> 0.0012s == CreatePosts: migrated (0.0012s) =========================================== [sakura@ubuntu13 blog]$
デフォルトの設定ではdb/development.sqlite3にDBが作成されます。postsテーブルが作成されているのがわかります。
[sakura@ubuntu13 blog]$ sqlite3 db/development.sqlite3 .table posts schema_migrations [sakura@ubuntu13 blog]$
テーブルの作成ができたので、画面から登録できるようにしましょう。
コントローラのcreateメソッドを以下のように書き換えます。
def create @post = Post.new(post_params) @post.save redirect_to @post end def show @post = Post.find(params[:id]) end private def post_params params.require(:post).permit(:title, :text) end
さらに投稿内容表示用のapp/views/post/show.html.erbを作成します。
<p> <strong>Title:</strong> <%= @post.title %> </p> <p> <strong>Text:</strong> <%= @post.text %> </p>
これで投稿画面のSave Postボタンを押すと、投稿内容がDBに保存され投稿内容が表示されます。
[sakura@ubuntu13 blog]$ sqlite3 db/development.sqlite3 'select * from posts' 3|タイトル1|本文|2013-07-24 09:56:00.516646|2013-07-24 09:56:00.516646 [sakura@ubuntu13 blog]$
モデル生成時に指定した項目以外に、ID、登録時刻、更新時刻らしきものが自動で追加されているのが分かります。
投稿一覧
投稿ができるようになったので、次は一覧画面です。
一覧画面の用のアクションはindexになっているので、コントローラにindexメソッドを追加します。
def index @posts = Post.all end
同じくテンプレートをapp/views/post/index.html.erbに作成します。
<h1>Listing posts</h1> <table> <tr> <th>Title</th> <th>Text</th> </tr> <% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= post.text %></td> </tr> <% end %> </table>
これでhttp://localhost:3000/posts/へアクセスすると一覧が表示されます。
画面間のリンク
いくつか画面ができたので、一覧<=>新規投稿の画面間のリンクを作成します。
index.html.erbに
<%= link_to 'New post', new_post_path %>
new.html.erbに
<%= link_to 'Back', posts_path %>
を追加します。
バリデーション
単純なバリデーションはモデルで以下のように指定できます。
class Post < ActiveRecord::Base validates :title, presence: true, length: { minimum: 5 } end
バリデーションに失敗した際に入力画面へ戻る様にコントローラを書き換えます。
def new @post = Post.new end def create @post = Post.new(post_params) if @post.save redirect_to @post else render 'new' end end
更に入力画面でエラーを表示するようにします。
<%= form_for :post, url: posts_path do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %>
これでタイトルが5文字未満の場合はエラーになり、登録できなくなります。
更新画面
CRUDのCRができるようになったので、次はUです。
まずアクションの定義です。コントローラに以下のメソッドを定義します。editが編集画面表示のアクション、updateが更新時のアクションです。
def edit @post = Post.find(params[:id]) end def update @post = Post.find(params[:id]) if @post.update(params[:post].permit(:title, :text)) redirect_to @post else render 'edit' end end
次に編集画面のビュー(app/views/posts/edit.html.erb)を作成します。
Getting Startedをそのままコピーしたところ、シンタックスエラーになったため、form_forの行の"}"を削除しています。
<h1>Editing post</h1> <%= form_for :post, url: post_path(@post.id), method: :patch do |f| %> <% if @post.errors.any? %> <div id="errorExplanation"> <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2> <ul> <% @post.errors.full_messages.each do |msg| %> <li><%= msg %></li> <% end %> </ul> </div> <% end %> <p> <%= f.label :title %><br> <%= f.text_field :title %> </p> <p> <%= f.label :text %><br> <%= f.text_area :text %> </p> <p> <%= f.submit %> </p> <% end %> <%= link_to 'Back', posts_path %>
そして、一覧画面から更新画面へのリンクを作成します。ここも微妙に調整しています。(post_pathに引数を追加)
<% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= post.text %></td> <td><%= link_to 'Show', post_path(post) %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> </tr> <% end %>
これで投稿内容の変更ができるようになりました。
投稿の削除
最後に削除機能です。
削除のアクションdestroyをコントローラに追加します。
def destroy @post = Post.find(params[:id]) @post.destroy redirect_to posts_path end
そして、一覧画面に削除のリンクを作成します。
<% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= post.text %></td> <td><%= link_to 'Show', post_path(post) %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> <td><%= link_to 'Destroy', post_path(post), method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %>
data: { confirm: 'Are you sure?' }
の指定をすることで確認のダイアログが表示されます。
これで無事削除機能も追加できました。
scaffold
ここまででCRUD操作が一通り作成できました。
実はこの内容のほとんどは
[sakura@ubuntu13 blog]$ rails generate scaffold post title:string text:text [sakura@ubuntu13 blog]$ rake db:migrate
のコマンドで作成できます。
慣れればこちらの方が速いかもしれませんが、初めて利用した場合は何が起こるのか理解しづらいのでばらばらに生成しているのかもしれません。
まとめ
Getting Startedの内容を試してみました。
リンク先ではこの後has_manyの関連が続いています。
動くものを速く作成したい場合にはやはり強力なフレームワークですね。