Ruby on Railsで初めての日記アプリを作ろう

Created by Piotr Szotkowski (chastell) and Tomasz Stachewicz (tomash) / 翻訳者 Shuto Nakano(shoooout)

RailsというRubyのWeb開発フレームワークを使って簡単な投票アプリを1から作成します。最初のアプリケーションは何にするか考えてみましょう。例えば、ToDoリストや日記など、何らかのコレクションを含むシンプルなものが理想です。ここでは日記をベースにしたアプリケーションを作成します。

COACH: この初心者向けチュートリアルの背景については、 こちらをご覧ください。

Railsがインストールされていることを確認してください。 セットアップ方法はインストールガイド に従ってください。

ツールについて知る

 

テキストエディタ

  • Visual Studio CodeSublime Text、Vim、Emacsなどのテキストエディタは、コードを書いたりファイルを編集することができます。

 

ターミナル (Windowsのコマンドプロンプト)

  • Railsサーバーを起動したり、コマンドを実行する場所です。

 

Webブラウザ

  • Firefox、Safari、ChromeなどのWebブラウザで、作成したアプリケーションを閲覧できます。

COACH: インストールを補助してください。テキストエディタが正しく設定されているか確認してください(例:エンコーディングがUTF-8であるか)。

重要事項

使用しているオペレーティングシステムに適した説明を選ぶことが重要です。Windowsコンピュータ上で実行するコマンドは、MacやLinuxのものとは少し異なります。クラウドサービス(例: nitrous)を使用している場合は、WindowsコンピューターであってもLinuxコマンドを実行する必要があります。

HTML

ファイルとフォルダ

新しく作成したディレクトリ(フォルダ)の中にindex.htmlを作成してください。そして、そのファイルをエディタとブラウザで開いてください。

COACH: ローカルファイルはブラウザで開くことができ、URLが通常とは異なることを説明してください。

HTMLの雛形

index.htmlに以下の記述を追加し、HTMLの雛形を作成します。

<!doctype html>
<html>
  <head>
    <title>My Little Webapp: Coding Is Magic</title>
    <meta charset="UTF-8" />
    <link rel="stylesheet" href="https://rawgithub.com/krzysztofbialek/Rails-Girls-Warsaw-App/master/style.css" />
  </head>
  <body>
  </body>
</html>

COACH: HTMLのメインパーツである<head><body>について説明してください。<title>タグに加え、必要に応じて<meta><link><script>について簡単に説明してください。参加者の希望が無い場合は、CSSについてはBootstrapがあるので省略可能です。

最初に表示される内容

<body></body>の間にHTMLを追加します(内容は自由に調整してください)。

<h1>My Rails Girls Diary</h1>
<div>
  <h2>Submitted a Rails Girls application</h2>
  <p>1.02.2014</p>
  <p>Just submitted an application to a Rails Girls workshop. Can’t wait to see whether I’ll get in!</p>
  <h2>Got in!</h2>
  <p>15.02.2014</p>
  <p>Received an email that my application got accepted! I’ll be at a RG workshop next week!</p>
  <h2>The first day starts…</h2>
  <p>22.02.2014</p>
  <p>Today is the first day of the Rails Girls workshop. My coach is quite strange but it seems we all have Rails installed now and can start learning.</p>
</div>

これらがあなたの最初の3つの日記です。繰り返しの構成や、タグが違うとどのように表示されるのかに注目してください。

COACH: HTMLタグとその意味について簡単に説明してください。

HTMLを追加する

先ほどindex.htmlに追加したものの前後どちらかに以下を追加しましょう(これも好みで調整してください)。

<div>
  <h1>My favourite websites</h1>
  <ul>
    <li><a href="http://railsgirls.com">Rails Girls</a></li>
    <li><a href="https://en.wikibooks.org/wiki/Ruby_Programming">Wikibooks</a></li>
    <li><a href="http://guides.rubyonrails.org">Ruby on Rails Guides</a></li>
  </ul>
</div>
<img src="https://railsgirls.com/images/rg-warsaw.png" />

これはHTMLの順序なしリストで、各項目には他ページへのハイパーテキスト参照(URL)をもつリンクが貼られています。その後に画像を含む段落があり、画像のソースは指定されたURLにあります。

COACH: Webの仕組みと、HTMLの要素や属性について簡単に説明してください。

CSSが適用された、ベースとなるアプリのレポジトリへのリンクはこちらです。

Railsで動かそう

COACH: もし参加者がWindowsを使用している場合、これより先をベースにしてNitrous.IOを使用することを検討してください。

Railsアプリケーションを新規作成

ターミナル(Windowsではコマンドプロンプト)を開き、アプリを作成するディレクトリにcdコマンドで移動し、rails new diaryを実行します。(新しいRailsアプリケーションの作成には時間がかかることがあります。)cd diaryを実行し、作成されたアプリケーションのディレクトリに移動します。

COACH: コマンドを実行してディレクトリを移動する方法について説明してください。

サーバーを起動する

diaryディレクトリでrails serverを実行してサーバーを起動します。サーバーが起動したら、ブラウザでhttp://localhost:3000にアクセスします。「Welcome aboard」のページが表示されます。また、ctrl-cを押すことでサーバーを停止できます。

COACH: 何が起きたのか、ターミナルの出力はどのようになっているのかを説明してください。JavaScriptのランタイムが原因でサーバーの起動に失敗した場合は、gem install therubyracerを実行し、Gemfileの該当行のコメントを外してください。

最初のルーティングとビュー

コントローラーとルーティングを作成します。

rails generate controller welcome indexを実行します。これによって、最初のコントローラーとそれへのルーティングが生成されます。 サーバーを起動し、http://localhost:3000から/welcome/indexのルーティングが作られていることを確認してください。

サーバーを停止し、rake routesを実行することでアプリケーションのルーティングを確認できます。

COACH: URLとURLの階層について説明してください。RailsにおけるURLに対応する裏側でおきていることについて説明してください。

ビューをトップページにする

config/routes.rbを編集し、root 'welcome#index'のコメントアウトを削除(先頭の#を削除)します。これにより、アプリケーションのルートはWelcome#indexによってレンダリングされるビューになります。http://localhost:3000にアクセスし、アプリケーションのトップページがこのビューになっていることを確認してください。(「Welcome aboard」のページではなくなっているでしょう)

COACH: なぜURLのルートがアプリケーションのメインページになり、ブラウザのアドレスバーにホスト名を入力したときに表示されるページになるのかを説明してください。

HTMLを追加する

先ほど作成したindex.html<body>タグの内容(日記やリンク)をコピーして、app/views/welcome/index.html.erb<h1><p>と置き換えてください。ブラウザを更新すると、ページが正しい内容になっていることを確認してください。

COACH: ビューには<body></body>の間の部分しか含まれず、それ以外はアプリケーション全体に共通で、別のファイルで定義されていることを説明してください。

繰り返し処理

内容を繰り返す

リンクのリストの構造を見てみると、どれも他の項目と似ています。リンクをHTMLとしてそのまま記述するのではなく、抽象化してURLと名前の組み合わせを繰り返し処理することにします。

<ul>タグの内容を以下のように書き換えてください:

<%
  @websites = [
    ["http://railsgirls.com", "Rails Girls"],
    ["https://en.wikibooks.org/wiki/Ruby_Programming", "Wikibooks"],
    ["http://guides.rubyonrails.org", "Ruby on Rails Guides"],
  ]
%>
<% for url, name in @websites %>
  <li><a href="<%= url %>"><%= name %></a></li>
<% end %>

ブラウザを更新すると、先ほどまでと同じように、名前とそのリンクが貼られていることが確認できます。

COACH: 配列とは何か、<%<%=のERBタグの意味、繰り返し処理の仕組みについて説明してください。

@websites配列のように、データをビューで定義できますが、これは長期的に見た時問題になります。まずは@websites配列をビューからコントローラーに移動させます。ビューから削除し、app/controllers/welcome_controller.rbindexメソッドに以下のように追加してください。

class WelcomeController < ApplicationController
  def index
    @websites = [
      ["http://railsgirls.com", "Rails Girls"],
      ["https://en.wikibooks.org/wiki/Ruby_Programming", "Wikibooks"],
      ["http://guides.rubyonrails.org", "Ruby on Rails Guides"],
    ]
  end
end

この@で始まる変数はインスタンス変数と呼ばれ、ビューとコントローラーの両方からアクセスできます。ブラウザを更新しても変化しないのはこのためです。

COACH: WelcomeController#indexのアクションとビューの関係について説明してください。@websitesの配列で処理した場合と、<ul><li>でURLとリンク名を記載した場合の違いに注意し強調してください。

モデルを作成する

リンクがviewに直接書かれていたものを修正したので、次は日記の記事についてです。今回は配列のようなRubyの構造体ではなく、日記の記事を表すモデルを作成します。まずはモデルを生成します。rails generate model Entry title:string date:date contents:textを実行して、タイトル、日付、内容を扱えるEntryモデルを作成してください。

COACH: モデルとは何なのか、それを生成するためのfield:typeの記法について説明してください。必要に応じて、stringtextの違いについても説明してください。

データベースをマイグレーションする

rake db:migrateを実行してデータベースをマイグレーションし、日記の記事のためのテーブルを作成してください。

COACH: データベースとは何なのか(抽象的に説明すると、データを保存しモデル構造を提供するためのもの)、なぜ必要なのかについて説明してください。メモリ上にあるものはデフォルトでは永続化されないので、次で書かれているような手法で永続化する必要があることを説明してください。

Railsコンソールでモデルを操作する

モデルができたので、次はそのモデルのインスタンス、つまりHTMLビューにハードコーディングされていない実際の日記エントリーを作成することにしましょう。そのためにRailsコンソールについて学びます。rails consoleを実行してRailsコンソールを起動し>>が表示されたら、次のようにしていくつか記事を作成してください。

>> Entry.create "title" => "Submitted a Rails Girls application", "date" => Date.new(2014, 2, 1), "contents" => "Just submitted an application to a Rails Girls workshop. Can’t wait to see whether I’ll get in!">> Entry.create "title" => "Got in!", "date" => Date.new(2014, 2, 15), "contents" => "Received an email that my application got accepted! I’ll be at a RG workshop next week!">> Entry.create "title" => "The first day starts…", "date" => Date.new(2014, 2, 22), "contents" => "Today is the first day of the Rails Girls workshop. My coach is quite strange but it seems we all have Rails installed now and can start learning."

Railsコンソールでは、rails serverと同様に、バックグラウンドで何が起こっているかをログとして表示してくれます。例えば、Entry.allを実行すると、すべての記事の配列を取得できます。

COACH: 何が起きているのかをゆっくり説明してください。

DBに保存された内容を表示する

モデルのインスタンスを既存のビューに追加する

WelcomeController#indexアクション(app/controllers/welcome_controller.rb)を編集し、@websitesを定義した前後に次の行を追加してください。

@entries = Entry.all

app/views/welcome/index.html.erbビューを編集し、日記の記事を表示している部分を次のように置き換えてください。

<% for entry in @entries %>
  <h2><%= entry.title %></h2>
  <p><%= entry.date %></p>
  <p><%= entry.contents %></p>
<% end %>

COACH: 何が起きたのかを話し合ってください。記事の順番はどのようになっているのか、どのように並び替えることができるのか(日付の降順にするなど)、どこで並び替えるべきかについて話し合いましょう。

日記の記事を操作するためのコントローラを作成する

モデルができたので、モデルのインスタンスに関連する処理するためのコントローラが必要です。(新しい記事の作成、表示、編集、削除など)。rails generate controller Entriesを実行してください。これにより、EntriesControllerクラスが生成されます。rake routesを確認しましょう。コントローラを作成するだけでは不十分で、URLをコントローラのアクションに指定する必要があることに注意してください。

config/routes.rbを編集し、Diary::Application.routes.drawブロックの中にresources "entries"を追加してください。再度rake routesを実行すると、新しいルートが追加されていることが確認できると思います。

COACH: Railsのルートリソースがどのように動作して、URLを作成し、コントローラのアクションにマッピングするのかを説明してください。

全ての記事を表示するビューを作成する

rake routesの出力を見ると、URLがそれぞれのコントローラのアクションにマッピングされていることがわかります。では、何が足りないのかを確認しましょう。ブラウザでhttp://localhost:3000/entriesを開いてください。どうやらindexアクションがないので追加しましょう。app/controllers/entries_controller.rbを開き、クラスの中に次の空のメソッドを追加してください。

def index
end

ブラウザを更新すると、「unknown action」のエラーは出なくなりましたが、「template is missing」のエラーが出るようになりました。app/views/entries/index.html.erbという空のファイルを作成し、ブラウザを更新してください。ビューには何も入力していないため空のページが表示されるはずです。

COACH: アクションがデフォルトで関連するビューをレンダリングする仕組みを説明してください。

app/controllers/welcome_controller.rbファイルを開き、(def indexで始まる)WelcomeController#indexメソッドを探してください。@entries変数を定義している行(@entries =で始まる行)を、EntriesController#indexapp/controllers/entries_controller.rbにあるEntriesControllerindexメソッド)にコピーしてください。同様に、app/views/welcome/index.html.erbビューの、@entries.eachブロック(インデントされたすべての行と対応するendを含む)をapp/views/entries/index.html.erbビューにコピーしてください。ブラウザを更新すると、日記の記事の一覧が表示されるはずです。

COACH: ほとんど変化がないように見えますが、重要な変化があることを説明してください。これまではアプリケーションのメインページのコンテキストで動作していましたが、今は日記の記事の一覧のコンテキストで動作しています(例えば、メインページで表示していた他のウェブサイトへのリンクなどはありません)。

一つの記事を表示するビューを作成する

rake routesを実行すると、/entries/:id(.:format)entries#showアクションにマッピングされていることがわかります。一つ目の日記の記事のURLであるhttp://localhost:3000/entries/1にアクセスしてください。EntriesControllershowアクションがないことに気づくと思います。空のアクションとビューを追加し、ブラウザを更新してください。

COACH: 必要であれば、足りないアクションとビューを追加するプロセスを説明してください。rake routesの出力を理解することから始めてください。

これでURLの末尾の1を解釈して、表示することができるようになりました。EntriesController#showアクションを次のように変更してください。

def show
  @entry = Entry.find(params["id"])
end

これは、「idパラメータを取得し、Entry.findメソッドで該当する記事を見つける」という意味です。次に、app/views/entries/show.html.erbビューを編集し、次のコードを追加してください。

<h2><%= @entry.title %></h2>
<p><%= @entry.date %></p>
<p><%= @entry.contents %></p>

http://localhost:3000/entries/1http://localhost:3000/entries/2で表示される内容を比較し、params['id']を使用することでどう違いがあるかを確認します。

COACH: rake routesのURLテンプレートの:id部分が、paramsハッシュのキーになること、paramsハッシュには他に何が含まれるのかについて説明してください。

記事へのリンクを作成する

Run rake routes again; notice how the row for the entries#show action starts with entry in the ‘prefix’ column. Go to the app/views/entries/index.html.erb view and change the line responsible for displaying the title to the below:

再度 rake routesを実行し、entries#showアクションの行は’prefix’列がentryで始まっていることを確認してください。app/views/entries/index.html.erbビューに移動し、タイトルを表示する行を以下のように変更してください。

<h2><%= link_to(entry.title, entry_path(entry)) %></h2>

表示するテキスト(entry.title)とリンク先のパスを2つのパラメータとして受け取るlink_toメソッドを使用しています。パスは、引数としてentryを渡してentry_pathメソッドを呼び出すことで作成されます。

COACH: link_toメソッドのHTMLがどのようになるかを説明してください。rake routesentryプレフィックスとentry_pathの関係を説明してください。entry_pathメソッドがentry引数を必要とする理由を説明してください。entry_pathメソッドと異なるentry_urlメソッドが何をするのかを説明してください。

次に、各記事の画面から全記事の一覧に戻れるようにしてみましょう。app/views/entries/show.html.erbテンプレートを編集し、次のように全記事の一覧へのリンクを追加してください。

<p><%= link_to("Back to all entries", entries_path) %></p>

rake routesentriesというプレフィックスがentries_pathのメソッド名に使用されていることに注目してください。このメソッドはパラメータは必要ありません。

ビューから記事を作成する

「新しい記事」のフォームを追加する

全記事の一覧と1つの記事を表示できるようになったため、新しい日記の記事を作成できるようにしましょう。rake routesを実行し、新しい記事の作成を担当するURLとアクションを確認してください。

全記事の一覧に移動し、新しい記事を作成するためのリンクを追加してください。

<%= link_to("New entry", new_entry_path) %>

リンクをクリックすると、アクションとビューが不足しているエラーが表示されます。

COACH: この過程をしっかりと理解できるようにしましょう。

app/views/entries/new.html.erbビューを作成し、以下のコードを追加してください。

<%= form_for(Entry.new) do |form| %>
  <p><%= form.label("title") %></p>
  <p><%= form.text_field("title") %></p>
  <p><%= form.label("contents") %></p>
  <p><%= form.text_area("contents") %></p>
  <p><%= form.submit %></p>
<% end %>
<p><%= link_to("Back to all entries", entries_path) %></p>

Note: ここではlabelについては省略可能です。

COACH: form_forのヘルパーが生成するHTMLは、どのように表示され動作するのかを説明してください。

「新しい記事」のフォームを扱う

ブラウザを更新し、新しい記事を追加してみてください。これまでに何度も目にした「unknown action」エラーが表示されるはずです。EntriesControllerにアクションを追加し、まずはアクションが受け取るものを表示してみましょう。

def create
  render(:text => params.inspect)
end

ブラウザを更新し、アクションがどのようなパラメータを受け取るかを確認してください。

COACH: テキストエリアとテキストフィールドを入力してフォームを送信すると、すべてのパラメータがコントローラのアクションにPOSTされることを説明してください。.inspectは何をするものなのかを説明してください。

新しい記事を作成して永続化する

createアクションを以下のように編集してください。

def create
  entry_params = params["entry"]
  entry = Entry.create(entry_params)
  redirect_to(entry_path(entry))
end

Railsコンソールでの作成と同じように、新しい記事のパラメータ(タイトルと内容)をparamsハッシュから取得し、それらから新しい記事を作成しています。フォームを再度送信しActiveModel::ForbiddenAttributesErrorが表示されることを確かめてください。

このエラーはセキュリティ対策のためです。ユーザーがPOSTしたいパラメータを簡単に設定できるため、Railsはユーザーが設定すべきでないパラメータ(例えば’id’)を設定しようとする悪意のあるユーザーから保護してくれます。createアクションの最初の行を以下のように変更して、ユーザーが設定できるパラメータを宣言する必要があります。

entry_params = params["entry"].permit("title", "contents")

再度フォームを送信すると、今度はうまくいき、新しく作成された記事にリダイレクトされるはずです。

COACH: 新しい記事のパラメータをparamsから取得することが理解できていることを確認し、明示的に許可したフィールドが受け入れられていることを確認してください。

「記事の編集」フォームを追加する

記事の表示と作成ができるようになったので、記事の編集も追加しましょう。rake routesを実行し、記事の編集の役割を持つルートを確認してみてください。

COACH: これまでの過程を今一度よく理解できるようにしましょう。

app/views/entries/show.html.erbビューを編集し、以下のコードを追加してください。

<p><%= link_to("Edit this entry", edit_entry_path(@entry)) %></p>

ブラウザを更新し、リンクをクリックしてください。’Unknown action’のエラーが表示されるので、アクションと空のビューを追加してください。

COACH: ここでもしっかりと理解を深めましょう。

では、editアクションが正しい記事を表示していることをビューで確認しましょう。editアクションがURLのidに基づいて正しい記事を取得するようにしてください。

def edit
  @entry = Entry.find(params["id"])
end

app/views/entries/new.html.erbの内容をapp/views/entries/edit.html.erbにコピーしてください。ただし、最初の行を指定の記事のフォームに変更し、オプションでこの記事の表示画面へのリンクを追加してください。

<%= form_for(@entry) do |form| %>
  <p><%= form.label("title") %></p>
  <p><%= form.text_field("title") %></p>
  <p><%= form.label("contents") %></p>
  <p><%= form.text_area("contents") %></p>
  <p><%= form.submit %></p>
<% end %>
<p><%= link_to("Back to this entry", entry_path(@entry)) %></p>
<p><%= link_to("Back to all entries", entries_path) %></p>

COACH: これまでの過程をここでしっかりと理解できるようにしましょう。

では、フォームを送信してみましょう。どのアクションが不足していますか? コントローラに追加してください。

def update
  entry_params = params["entry"].permit("title", "contents")
  entry = Entry.find(params["id"])
  entry.update(entry_params)
  redirect_to(entry_path(entry))
end

全てが動作し、記事の編集ができるようになったことを確認してください。

COACH: パラメータの許可から記事の検索、リダイレクトまで、updateアクションの内容が理解できていることを確認してください。

その他機能のアイデア

初めて作成したアプリケーションで遊んでみましょう! 以下の機能を追加してみてください。