プログラミングをしていると、必ず課題に直面します。 課題を解決するためにwebで検索をかけたり、書籍を参考にしていくのが一般的です。
一方でほとんどの課題や問題は、過去自分以外の他のエンジニアも直面していて、結果として解決している場合がほとんどです。 それを一から自力で解決していくのは非効率です。
そこで、先人の過去の事例や知恵を蓄積させ、それをもとに効率的にプログラミングができる環境が提供されるようになりました。
この環境のことを「フレームワーク」と呼びます。 フレームワークは数学に例えると「公式」と似ています。数学の証明問題は解くのに時間がかかりますが、公式を使えば答えはすぐに導き出されます。
「 Ruby on Rails 」とは、Rubyのプログラミング言語で作られているフレームワークです。
このRuby on Railsを利用することで、効率的にプログラミングができるようになります。
Railsのリファレンスはこちらです。
Railsの開発を進める上でよく使いますので、ブックマークしておきましょう。
序章-2 Ruby on Railsを使うメリット
Ruby on Railsを利用するメリットを紹介します。
開発効率が向上する
例えば、「ECサイト」「ログイン機能」「決済システム」などwebでよく見かけるシステムについては、1から開発するとかなりの日数と知識量が必要ですが、Ruby on Railsを使うことでその日数を大幅に削減できます。
トレンドの技術を取り込める
Ruby on Railsは日々進化しているフレームワークです。年々登場する新しい技術を取り込みながら進化していますので、Ruby on Railsを利用していれば自然と新技術を簡単に導入できるケースが多いです。
品質の向上
Ruby on Railsでは、過去に誰かが直面したバグなどの問題は随時解決され、フレームワーク自体がアップデートを繰り返しています。
そのため、フレームワークを利用し続ければ図らずともまだ発生していないバグを潰すことができます。
Model View Controller モデルを採用
世の中には様々なプログラミングのフレームワークが存在します。
それぞれで設計思想が違いますが、似通っている部分もあり、よく「MVC」というモデルが採用されます。
Ruby on Railsにも採用されている「MVC」モデルは、プログラム部分を「Model/View/Controller」という名前をつけてそれぞれの役割ごとに分割します。
1つのファイルで記述すると長すぎるプログラムを、MVCという役割ごとに分割することで、全体の見通しをよくしようという試みです。
- M :「Model」の略で、ビジネスロジックを担当します
- V :「View」の略で、ユーザーインターフェースを担当します
- C :「Controller」の略で、ModelとViewの制御や中継を担当します
このMVCの詳細についてはアプリケーションを作成しながら学習していきますので、今の段階では Ruby on Railsはプログラムが「Model」「View」「Controller」といった役割ごとに分かれている、ということだけ覚えておきましょう。
1-1 Railsの開発環境
Railsの開発環境も、Rubyと同じくAWS Cloud9を使います。
Rubyの学習で使っていた開発環境とはまた別の開発環境を作成し、その中でRailsの環境を構築していきましょう。
1-3 タイムゾーンの設定
AWS Cloud9では、デフォルトのタイムゾーンがUTCになっており、日本時間と9時間ずれています。 以下2つのコマンドを画面下のコンソール部分で実行してください。
ターミナルで date
コマンドを実行すると、時間がずれていることを確認できます。
このままだとデータの保存時間にズレなども発生してしまうため、下記1行をターミナルで実行して日本標準の時間に変更します。
bash
<(curl -s https://ide-api.codecamp.jp/prepare/os_timezone.php)
先ほど同様に date
コマンドでJSTに変わっていることを確認しましょう。
1-4 Railsのインストール
次に、バージョンを指定してRailsをインストールします。
コンソール部分に、以下のコマンドを入力してEnterを押してください。
gem install
rails -v
5.1.1
しばらくすると(約1〜2分)Railsのインストールが完了し、開発環境で利用可能な状態になります。
1-5 Railsプロジェクトを作成
Railsはインストールしただけでは意味がありません。Railsのプロジェクトを新しく作成するには、以下の書式のコマンドを実行してRailsプロジェクトを作成する必要があります。
rails new プロジェクト名
今回はまずはじめに「bbs」というプロジェクトを作成してみましょう。
以下のコマンドをターミナルで実行してください。
rails new bbs
このコマンドも、実行後結果が返ってくるまで少し待ちますが、Railsが自動で必要なプログラムをインストールしています。
コマンドが完了すると、プロジェクトツリーに先程入力した「プロジェクト名」のフォルダが作成されます。
Railsプロジェクトの設定ファイルにもタイムゾーンを設定する必要があります。
「/config/application.rb」ファイルを開き、以下の2行を追記して保存してください。
application.rb12config.time_zone = 'Tokyo'config.active_record.default_timezone = :local
1-6 Railsの起動
Railsに付属されているHTTPサーバー(Puma)を起動して、Railsを立ち上げてみましょう。
Railsを立ち上げるためには、そのプロジェクトのフォルダでコマンドを実行する必要があります。
以下のコマンドをターミナルに入力し、「bbs」のフォルダ内へ移動しましょう。「cd」コマンドでフォルダの移動ができます。
cd
bbs
その後、以下のコマンドをターミナルで実行してください。
rails s
AWS Cloud9でのRailsサーバー起動は上記のコマンドになります。 今後はこのコマンドをよく使いますので、覚えておきましょう。
ターミナルには以下のように表示されます。
もしRailsサーバーをストップする時は、ターミナルに書いてあるとおり「Ctrl + C」キー(Macは「control + C」)を押します。
1-7 ブラウザで確認
Railsのプロジェクトをブラウザで確認してみましょう。
AWS Cloud9のメニューにある「Preview」をクリックし、「Preview Running Application」を選択します。
別タブでRailsの以下のようなページが表示されれば成功です。
URLは以下のようになっており、AWS Cloud9が開発環境ごとに自動で割り当てています。 https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com
Tips: リージョンIDとは
AWSのサーバが設置されている場所をリージョンと呼びます。そして、そのリージョンにはすべてリージョンIDが振られており、例えば「us-east-1」は「米国東部 (バージニア北部)」を差します。
1-8 Railsプロジェクトのディレクトリ構成について
Railsプロジェクトを作成した時に、複数のフォルダやファイルが一度に作成されました。
それぞれよく利用するフォルダ・ファイルの意味や役割について、簡単に紹介します。
Railsを学びながら自然と覚えていきますので、ここで全部覚えなくても大丈夫です。
appフォルダ
序章で紹介した「MVC」のプログラムも、このappフォルダの中に入っています。
他にはCSS/Javascriptのような静的なファイルについても、このappフォルダ内に置かれています。
binフォルダ
Railsで開発する上で、開発をサポートしてくれるプログラムが配置されています。
基本的に、開発する際には直接編集することはありません。
configフォルダ
Railsの設定ファイルが格納されています。データベース設定・環境設定・言語設定など、様々な設定ファイルが格納されています。
dbフォルダ
データベースを作成する際のプログラムファイルが格納されます。
Railsとデータベースを連携する際に、こちらのフォルダの中のファイルを利用します。
libフォルダ
定期実行するプログラムや、独自で作成したプログラムなどを配置するフォルダです。
logフォルダ
Railsで作成したサービスにアクセスした際のログ情報が格納されます。
publicフォルダ
ドキュメントルートに設定されるフォルダです。静的なコンテンツはpublicフォルダ配下に配置します。
testフォルダ
Railsのテストプログラムが格納されるフォルダです。
tmpフォルダ
キャッシュファイルなど、Railsのサービスを動かす際に必要となる一時ファイルの置き場です。基本的に編集することはありません。
vendorフォルダ
外部のモジュールなど、サードパーティ製のプログラムを置くフォルダです。
Gemfile/Gemfile.lock
Gemと呼ばれる、Rubyの機能をまとめたパッケージに関する定義ファイルです。
README.md
プロジェクトの説明用のマークダウンファイルです。Githubにホスティングした時にこのドキュメントがトップに表示されます。
例: https://github.com/rails/rails
2-1 ひとことBBS
「bbs」というRailsプロジェクトの準備ができました。
これから、簡単なひとことBBSを作りながらRailsの使い方を学習していきます。
トップページ
新規投稿ページ
ひとことBBSは、以下の機能を持つアプリケーションです。
- ユーザー名とつぶやきを入力して投稿ができる
- 投稿した内容を一覧で見ることができる
データの登録には、CSVファイルを使用します。
3-1 トップページの作成
まず、ひとことBBSのトップページレイアウトを作成してみましょう。
Railsでは、コマンドを使って簡単にページを作成することができます。
Railsを立ち上げるためのコマンドを実行しているターミナルタブはそのままにしておき、新しく別のターミナルタブを開いてください。
AWS Cloud9では、ターミナルのところの「+」ボタンをクリックして「New Terminal」を選択すると新しいターミナルタブが開きます。
新しく開いたターミナルで、以下のコマンドを実行しましょう。
cd
bbsrails generate controller static_pages top
このコマンドを実行すると、新しいページが自動的に作成されます。
ブラウザで確認しているRailsのURLに、以下を追加してください。
以下のようなページが表示されれば成功です。
3-2 generateコマンドについて
「generate」とは、直訳すると「生む、起こす、生成する」といった意味になりますが、 generate コマンドを使うことで、ページを表示させるために必要なコントローラやビューのファイル、テスト、アプリの土台などを自動で生成することができます。
今回はひとまずページを作成するために、コントローラを生成するgenerateコマンドを実行しました。 (コントローラ名は「static_pages」、アクション名は「top」と指定しています)
rails generate controller コントローラ名 アクション名
# 「generate」部分を「g」に省略することもできますrails g controller コントローラ名 アクション名
generateコマンドは上記のように省略することもできますが、本テキストではわかりやすくするため省略しない形式で記述していきます。
コントローラを生成すると、以下の処理も同時に実行されます。
- ルーティングの設定
- コントローラファイルの作成
- ビューファイルの作成
- CSSファイルの作成 など
generateコマンドを実行する時は、cdコマンドを使ってrailsプロジェクトの中に移動してから実行してください。
generateコマンドで自動生成したファイルは、destroyコマンドでまとめて削除することもできます。
3-3 MVCモデル
ルーティングやコントローラ、ビューという言葉が出てきましたが、これは 「MVC」モデル というソフトウェアの設計モデルで使われる言葉です。
序章で簡単にMVCについて触れましたが、モデル・ビュー・コントローラにはそれぞれ次のような役割があります。
それぞれがどのような動きになっているのか、先ほどgenerateコマンドで作成したファイルを使ってもう少し詳しく見ていきましょう。
ルーティング
ルーティング とは、送られてきたリクエストURLに応じて、処理の受け渡し先を決める仕組みのことです。
まず、ブラウザから https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/static_pages/top
というURLが送られると、Railsは最初にルーティングのファイルを見に行きます。
ルーティングは「config/routes.rb」に定義されています。 routes.rbを開くと、以下のように記述されています。
routes.rb123Rails.application.routes.draw do get 'static_pages/top'end
ここで次の部分を見てみましょう。
get 'static_pages/top'
これは、「static_pages/top」というURLが送られてきたら、StaticPagesコントローラのtopアクションを呼び出してください という設定になります。
そのため、次はこのルーティングファイルからコントローラを呼び出すことになります。
コントローラ
コントローラは、先ほど実行したgenerateコマンドによって、「app/controllers」フォルダの中に「static_pages_controller.rb」というコントローラファイルができています。
static_pages_controller.rbを開くと、以下のように記述されています。
static_pages_controller.rb1234class
StaticPagesController < ApplicationController def
top endend
ここで次の部分を見てみましょう。
def
topend
これは、コントローラクラスに宣言されている アクションメソッド で、先ほど実行したgenerateコマンドによって作成されたものです。
今はまだtopアクションの中に何も処理は記載されていませんが、「static_pages/top」というURLが送られてきた時に、このtopアクションの中の処理が実行されます。
アクションで処理を実行する時に、データベースに接続したり複雑な処理を実行したい場合があります。
その時は、コントローラのアクションからモデルを呼び出します。
モデル用のファイルは「app/models」フォルダの中に設置しますが、先ほど実行したgenerateコマンドではモデルのファイルは作成されません。
今回は特にデータベースに接続したり複雑な処理をすることはないため、モデルファイルについての説明は後述します。
ビュー
コントローラ内のアクションの処理が終わると、コントローラと同じ名前のビューフォルダから、アクションと同じ名前のHTMLを自動的に探して呼び出す処理がおこなわれます。
ビューは、リクエストが送られた時にブラウザへ表示するページになります。ビューのファイルは「app/views/コントローラ名」フォルダの中に設置します。
先ほど実行したgenerateコマンドによって、「app/views/static_pages/top.html.erb」というファイルが作成されています。
top.html.erbを開くと、以下のように記述されています。
top.html.erb12<h1>StaticPages#top</h1><p>Find me in app/views/static_pages/top.html.erb</p>
「erb」は、HTMLファイルの中にRubyのコードが書けるようになっているファイルで、処理を実行した結果(データベースから取得した値など)をHTMLの中に出力することができます。
コントローラでビューを呼び出して処理をしたら、そのビューをブラウザへ表示させるようにレスポンスを返します。
これがRailsにおけるMVCの構造と処理になるので、しっかり覚えておきましょう。
今後Railsで新しいページを作る時の流れは、以下のようになります。
- ルーティングを設定する
- コントローラにアクションを設定する
- ビューを作成する
3-4 ルーティングの設定
generateコマンドで生成された、ルーティングとコントローラ、そしてビューをもとに、処理の流れを確認しました。
現在は、以下のURLの時にトップページが表示されるようになっています。https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/static_pages/top
これを、以下のURLの時にトップページが表示されるようルーティングを変更しましょう。https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/
「/config/routes.rb」を開き、以下のように変更します。
routes.rb1234Rails.application.routes.draw do # get 'static_pages/top' root 'static_pages#top'end
「get ‘static_pages/top’」を「root ‘static_pages#top’」に変更しました。
これで、 https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/
というURLにアクセスがあった時にトップページが表示されるようになります。
https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/
にアクセスしてトップページが表示されることを確認してください。
3-5 ビューのレイアウトを整える
現在のトップページは、generateコマンドでコントローラを作成した時に生成されたデフォルトのページになっているので、こちらをひとことBBSのトップページのレイアウトにしていきましょう。
「app/views/static_pages/top.html.erb」ファイルの中身を、以下のHTMLに書き換えてください。
top.html.erb12345678910111213141516171819202122232425262728<header> <h1>ひとことBBS</h1> <ul> <li> <a
href="#">新規投稿</a> </li> </ul></header>
<div
class="top"> <h2>いまどうしてる?をつぶやこう</h2> <div
class="tweets"> <ul> <li> 田中: こんにちは! <span>2017/08/01 12:00:00</span> </li> <li> 林田: はじめまして <span>2017/08/01 12:03:00</span> </li> <li> 斉藤: よろしくおねがいします。 <span>2017/08/01 12:04:00</span> </li> </ul> </div></div>
RailsのCSSファイルは、「app/assets/stylesheets」フォルダにあります。
generateコマンドを実行した時に、該当するビューのページのCSSファイルも自動で作成されます。
今回は「static_pages.scss」というファイルが作成されていますので、ファイルの中身を以下の内容に書き換えてください。
static_pages.scss12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364body { margin: 0; padding: 0; background-color: #f5f8fa;}header { background-color: rgb(21, 44, 85); color: white; height: 70px; padding: 10px
20px; display: flex; justify-content: space-between; h1
{ margin: 0; padding: 0; line-height: 70px; a { color: white; cursor: pointer; text-decoration: none; } } ul { display: flex; list-style-type: none; margin: 0; padding: 0; li { margin-right: 15px; line-height: 70px; a { color: white; cursor: pointer; text-decoration: none; &:hover { text-decoration: underline; } } } }}
.top
{ width: 60%; padding: 15px
0; margin: 0
auto; h2
{ text-align: center; font-size: 46px; color: rgb(21, 44, 85); } .tweets { ul { li { padding-right: 50px; position: relative; span { position: absolute; right: 0; } } } }}
ファイルを保存して再度トップページのURLにアクセスすると、以下のようなレイアウトになります。
これで、トップページのレイアウト作成ができました。
3-6 SCSS
前節のCSSは、通常のCSSの書き方ではなくSCSS(Sassy CSS) の書き方になります。ファイルの拡張子も「.scss」になっています。
SCSSは、CSSのコードを生成するための言語で、CSSを拡張したものです。
入れ子や変数、文字列展開のような機能を持っており、CSSを効率的かつコンパクトに書くことができます。
もちろん、通常のCSS記法も使うことができます。
Railsでは、/app/assets/stylesheet フォルダに「.scss」拡張子のファイルを作成するだけで利用できます。
ブラウザ上で簡単にSCSSの記述をCSSに変換することができる SassMeister というサイトがありますので、 SCSSに慣れないうちは、こちらも使いながら理解をすすめてください。
先ほど作成したstatic_pages.scssの中身を貼り付けると、次のようにCSSに変換されます。
ネスト
SCSSの最大の特徴は、スタイル定義をネスト(入れ子)できるという点です。
ネストによって、例えば「header ul」「header ul li」「header ul li a」「header ul li a:hover」のように親子関係にあるセレクタの記述をシンプルにできます。
セレクタだけでなく、プロパティもネストさせることができます。
「a:hover」のような擬似クラスで親セレクタを参照させる場合は、「&:hover」のように、SCSSの予約文字を利用します。
変数
プログラミングと同じように、変数を使うこともできます。
フォントや色、サイズの指定など、複数の箇所で参照するような値は、「$変数」の形式で変数を宣言しておくことで、メンテナンス性が向上します。
$main_color: #ff0000;$sub_color: #ffffff;
.box { h1
{ color: $main_color; } h2
{ color: $sub_color; }}
ディレクティブ
ディレクティブ とは、「@名前」の形式で指定する命令のことです。
たとえば「@mixin」ディレクティブを定義すると、「@include」ディレクティブを使って任意の場所に埋め込むことができます。
/* @mixinディレクティブを定義 */@mixin hoge { border: solid
1px
#ffffff; font-size: 11px;}
.menu { @include hoge; /* ここで定義したスタイルを読み込んでいる */ text-align: center;}
このようなSCSS記法を使うことで、メンテナンスのしやすいCSSを効率的に書けるようになるので、ぜひ使いこなせるようSCSSの記法を調べながら学習を進めてください。
4-1 新規投稿ページの作成
次に、つぶやきを投稿する新規投稿ページのレイアウトも作成していきましょう。
新規投稿ページのURLは、 https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com/tweets
とします。
以降、URLの「
https://自動で割り当てられるID.vfs.リージョンID.amazonaws.com
」の部分は省略して記述します。
以下のコマンドを実行して、新規投稿ページを作成しましょう。
トップページの時は、コントローラ名の後にアクション名も入れて実行しましたが、今回はルーティングとアクションを後から設定していくので、 コマンドには「tweets」というコントローラ名だけ設定しています。
rails generate controller tweets
コマンド実行後、「app/controllers」フォルダの中に、「tweets_controller.rb」ファイルが作成されたことを確認してください。
4-2 ルーティングの設定
今の状態で /tweets
にアクセスすると、以下のようなエラーが表示されます。
これはルーティングされていないURLにアクセスした時に表示されるエラーなので、まずはルーティングの設定をしていきましょう。
「config/routes.rb」を開き、以下の内容を記述します。
routes.rb1234Rails.application.routes.draw do root 'static_pages#top' get '/tweets', to:'tweets#new'
# この行を追加end
さっきのトップページの書き方と少し異なっていますが、これは /tweets
にアクセスしたら、 Tweetsコントローラのnewアクションを呼び出してください という設定になります。
4-3 コントローラにアクションを追加
ルーティングを設定し、再度 /tweets
にアクセスすると、今度は以下のようなエラーが出ます。
これはコントローラの中に「new」というアクションがない時に表示されるエラーなので、コントローラにアクションを追加しましょう。
「app/controllers/tweets_controller.rb」を開き、以下の内容を記述します。
tweets_controller.rb12345class
TweetsController < ApplicationController # newアクションを追加 def
new endend
アクション内の処理はまだ何も記述しなくて大丈夫です。
4-4 ビューの作成
コントローラにアクションを作成し、再度 /tweets
にアクセスすると、今度は以下のようなエラーが表示されます。
これは出力するビューが存在しない時に表示されるエラーなので、ビューファイルを作成しましょう。
ビューは「app/views/コントローラ名」フォルダの中に作成します。
今回は「app/views/tweets/new.html.erb」というファイルを作成し、以下の内容を記述しましょう。
new.html.erb1<p>新規投稿ページです</p>
generateコマンドでコントローラを作成する時にアクションを指定しない場合、ビューファイルは自動的に作成されないので手動で作成する必要があります。
ファイルを保存して再度 /tweets
にアクセスすると、今度はエラーなくページが表示されます。
Railsで新しいページを作る時の流れをおさらいしておきましょう。
- ルーティングを設定する
- コントローラにアクションを設定する
- ビューを作成する
また、処理の中で何かしらの問題があった場合、Railsでは画面にエラー内容をわかりやすく表示してくれます。
エラーが起きた時は落ち着いてエラー内容を確認し、どのあたりに問題がありそうなのかを考える癖をつけておくとよいです。
「Rails 表示されたエラー文」で検索すると、ネット上で解決策をたくさん見ることもできますので、エラーと上手く付き合いながらプログラミングを進めてください。
4-5 ビューのレイアウトを整える
ビューの方はまだ「新規投稿ページです」という文字列しか表示されていないので、新規投稿ページのレイアウトを整えていきましょう。
「app/views/tweets/new.html.erb」ファイルの内容を、以下の内容に書き換えてください。
new.html.erb1234567891011121314151617181920<header> <h1>ひとことBBS</h1> <ul> <li> <a
href="#">新規投稿</a> </li> </ul></header>
<div
class="new-tweet"> <form
method="post"> <div
class="text-wrap"> <input
type="text"
name="name"
placeholder="ユーザー名"
value=""/> </div> <div
class="textarea-wrap"> <textarea
name="tweet"
placeholder="つぶやきを140文字以内で記述"></textarea> </div> <input
type="submit"
value="送信"
/> </form></div>
ファイルを保存して再度 /tweets
にアクセスすると、以下のような画面が表示されます。
ヘッダーの部分は、トップページで作成した「app/assets/stylesheets/static_pages.scss」の設定が反映されているためキレイに表示されています。
フォームの部分を、CSSを使ってもう少し整えていきましょう。
Railsでは、「app/assets/stylesheets」フォルダの中にあるSCSSファイルが、全てのビューに適用されます。
「app/assets/stylesheets/tweets.scss」ファイルに、以下の内容を記述しましょう。
tweets.scss.new-tweet { width: 60%; margin: 0
auto; padding: 20px; form { text-align: right; textarea { width: 100%; height: 120px; border: 1px
solid
#eee; font-size: 15px; resize: none; box-sizing: border-box; padding: 5px
0
5px
5px; margin: 0; } input[type="text"] { max-width: 100%; min-width: 100%; box-sizing: border-box; margin-right: 5px; height: 30px; border: 1px
solid
#eee; padding: 5px
0
5px
5px; margin-bottom: 8px; display: block; font-size: 15px; } input[type="submit"] { background-color: skyblue; color: white; border: 1px
solid
darken(skyblue, 5%); padding: 5px
8px; opacity: 0.8; cursor: pointer; width: 100px; &:hover { opacity: 1; } } .textarea-wrap { margin-bottom: 10px; } }}.errors { color: red;}
ファイルを保存して再度 /tweets
にアクセスすると、以下のようなレイアウトになります。
これで、新規投稿ページのレイアウト作成ができました。
5-1 HTMLの共通部分をまとめる
トップページと新規投稿ページのレイアウトを作成しました。
次は、ヘッダーに トップページのリンク と 新規投稿ページのリンク を追加していきます。
現在は「top.html.erb」と「new.html.erb」2つのファイルに、同じヘッダーのHTML記述があるので、共通する部分を1つにまとめます。
共通部分をまとめることで、例えば今後ヘッダーに変更があった場合でも、1つのファイルだけ変更すれば全てのヘッダーに変更した内容が反映されるようになります。
Railsでは、「 views/layouts/application.html.erb 」に共通のHTMLを書きます。 「top.html.erb」や「new.html.erb」に <head>
タグや <body>
タグを書かなくても、きちんとHTMLが表示されていたのは、このapplication.html.erbに<head>
タグや<body>
タグが書かれていたからです。
以下は初期状態のapplication.html.erbですが、 <%= yield %>
と書いてあるところに「top.html.erb」や「new.html.erb」に書かれている内容が出力され、ブラウザに表示される仕組みになっています。
<!DOCTYPE html><html> <head> <title>BBS</title> <%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> </head>
<body> <%= yield %> <!-- この部分に各ビューファイルの内容が出力される --> </body></html>
この仕組みにより、ヘッダー、フッター、メニューのようなサイトの共通レイアウトをapplication.html.erbにまとめることができます。 application.html.erbのことを、 レイアウトテンプレート と呼びます。
今回は、以下のようにapplication.html.erbにヘッダー部分を追記し、どのページでも共通のヘッダーが表示されるようにしましょう。
top.html.erbとnew.html.erbの以下の部分を、「views/layouts/application.html.erb」のbodyタグの後に追加します。
application.html.erb12345678<header> <h1>ひとことBBS</h1> <ul> <li> <a
href="#">新規投稿</a> </li> </ul></header>
application.html.erbに追加できたら、top.html.erbとnew.html.erbの上記部分は削除してください。
ここまでできたらファイルを保存して、トップページと新規投稿ページにアクセスし、レイアウトが崩れず表示されることを確認してください。
5-2 ヘッダーにリンクを設置する
ヘッダーを共通化できたので、ヘッダーにリンクを設置しましょう。
application.html.erbファイルを以下のように書き換えて、ヘッダーにリンクが設置されることを確認してください。
コピーapplication.html.erb1234567891011121314151617181920212223<!DOCTYPE html>
<
html
>
<
head
>
<
title
>BBS</
title
>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
</
head
>
<
body
>
<
header
>
<
h1
><%= link_to "ひとことBBS", "/" %></
h1
>
<!-- ここを書き換え -->
<
ul
>
<
li
>
<%= link_to "新規投稿", "/tweets" %>
<!-- ここを書き換え -->
</
li
>
</
ul
>
</
header
>
<%= yield %>
</
body
>
</
html
>
5-3 ヘルパーメソッド
先ほど追加したリンクは、HTMLの<a>
タグを使わずに<%= link_to "文字列", "URL" %>
という形式になっています。
この<%= link_to "文字列", "URL" %>
を ヘルパーメソッド といいます。
ヘルパーメソッドとは、テンプレートファイルを記述する際に役立つメソッドのことです。
ヘルパーメソッドを利用することで、フォーム要素の生成、文字列や数値の整形、エンコード処理など、ビューでよく利用する操作をよりシンプルなコードで記述することができます。
たとえば、ここで使用しているlink_toメソッドは、与えられた引数をもとにハイパーリンクを生成するためのメソッドです。
書式は以下のようになります。
コピー1<%= link_to "表示する文字列", "URL" %>
今回はURLのところに “/” や “/tweets” といったパスの指定をしていますが、ルーティングに名前を設定して「○○○_path」と記述することもできます。 ルーティングに名前をつける場合は「as」オプションを使用します。
例えば新規投稿ページのパスに名前をつける場合は、config/routes.rbで以下のように「as: ‘名前’」を設定します。
コピーroutes.rb123get
'/tweets'
, to:
'tweets#new'
↓
get
'/tweets'
, to:
'tweets#new'
, as:
'new_tweet'
application.html.erbのlink_toメソッドでは、以下のように記述します。
コピーapplication.html.erb123<%= link_to "新規投稿", "/tweets" %>
↓
<%= link_to "新規投稿", new_tweet_path %>
URLパスの指定についてはどちらを利用しても構いません。
他にも沢山の種類のヘルパーメソッドがありますが、学習を進めていく中で随時使い方を覚えていきましょう。
これでひとことBBSのレイアウト部分は完成したので、次は中身の処理部分を作成していきます。
6-1 フォームの設定
新規投稿ページからユーザーが「送信」ボタンを押した時に、登録処理をするcreateアクションが実行されるよう実装していきます。
新規投稿ページのビューファイル(new.html.erb)は、現在以下のようになっています。
コピーnew.html.erb1234567891011<
div
class
=
"new-tweet"
>
<
form
method
=
"post"
>
<
div
class
=
"text-wrap"
>
<
input
type
=
"text"
name
=
"name"
placeholder
=
"ユーザー名"
value
=
""
/>
</
div
>
<
div
class
=
"textarea-wrap"
>
<
textarea
name
=
"tweet"
placeholder
=
"つぶやきを140文字以内で記述"
></
textarea
>
</
div
>
<
input
type
=
"submit"
value
=
"送信"
/>
</
form
>
</
div
>
ビューファイルの以下2箇所(formタグ部分)を変更しましょう。
コピー1234567891011<
div
class
=
"new-tweet"
>
<%= form_tag("/tweets/create") do %>
<!-- この部分を変更 -->
<
div
class
=
"text-wrap"
>
<
input
type
=
"text"
name
=
"name"
placeholder
=
"ユーザー名"
value
=
""
/>
</
div
>
<
div
class
=
"textarea-wrap"
>
<
textarea
name
=
"tweet"
placeholder
=
"つぶやきを140文字以内で記述"
></
textarea
>
</
div
>
<
input
type
=
"submit"
value
=
"送信"
/>
<% end %>
<!-- この部分を変更 -->
</
div
>
form_tag メソッドは、HTMLの <form>
タグを自動生成し、フォームに入力されたデータを送信することができます。
form_tagの書式は以下のようになっています。
コピー123form_tag(
"送信先のURL"
)
do
end
送信先のURLには、formタグのaction属性に該当する値を設定するので、上記のコードでは「”/tweets/create”」と指定しています。<input type="submit">
ボタンをクリックした時、フォームのデータが送信先URLへ送られます。
また、form_tagメソッドで作成したformタグは POST形式 でデータが送信されます。
formタグ生成のヘルパーメソッドは、「form_tag」「form_for」の2種類があります。
今回はモデルを利用しないためform_tagメソッドを利用しますが、データの作成や更新などモデルに紐づくフォームの場合はform_forメソッドを利用します。
form_forについてはデータベース以降の章で取り扱います。
6-2 GETとPOSTについて
ブラウザからサーバに対してURLを送る際、「GET」と「POST」という送信形式があります。 GETとPOSTの使い分けは次のとおりです。
GET
フォームのデータを送る等はせず、データを取得したりページを表示する時に使う
POST
フォームのデータをサーバへ送ったり、データの登録・更新・削除処理をおこなう時に使う
今回は、フォームに入力されたデータをサーバへ送って登録処理をおこなうため、POSTの形式でURLを送信します。
6-3 ルーティングの設定
/tweets/create
のURLにリクエストがきた場合は、createアクションを実行するようルーティングの設定をします。
routes.rbに以下の内容を追記しましょう。
コピーroutes.rb12345Rails.application.routes.draw
do
root
'static_pages#top'
get
'/tweets'
, to:
'tweets#new'
post
'/tweets/create'
, to:
'tweets#create'
# この行を追加
end
トップページのルーティングはgetの形式ですが、今回は投稿した内容を登録する処理を実行したいのでpost形式で指定しています。
6-4 コントローラにアクションを追加
ルーティングから呼び出される、Tweetsコントローラのcreateアクションを追加します。
tweets_controller.rbに以下の内容を追記しましょう。
コピーtweets_controller.rb12345678class
TweetsController < ApplicationController
def
new
end
# 登録処理のアクション
def
create
end
end
追記したら、新規投稿ページのフォームに何か入力して「送信」ボタンをクリックしてみましょう。
まだ処理は実装していないので何も変わりませんが、エラーが出ていないことを確認してください。
AWS Cloud9のコンソールで、フォームで入力した内容を確認することもできます。
6-5 CSVファイルの準備
今回は、フォームに入力されたデータを CSVファイル に書き込んで保存します。
CSVファイルとは、カンマ形式でデータが区切られたファイルのことです。CSVファイルはMS OfficeのExcelで開くこともできます。
まず投稿内容を保存するCSVファイルを作成しましょう。 「tmp」フォルダの中に「tweets.csv」というファイルを作成してください。
次に、createアクションの中にデータを保存する処理を実装していきます。
tweets_controller.rbを開き、以下の処理を記述してください。
コピーtweets_controller.rb12345678910111213require
"csv"
# CSVライブラリの読み込み
class
TweetsController < ApplicationController
def
new
end
# 登録処理
def
create
# CSVファイルの書き込み
csv =
CSV
.open(
'tmp/tweets.csv'
,
'a'
)
csv.puts([
"ユーザー名"
,
"つぶやき"
,
"投稿日時"
])
csv.close
end
end
この状態で、新規投稿ページからユーザー名とつぶやきを入力し「送信」ボタンをクリックすると、tweets.csvファイルに以下の内容が書き込まれるようになります。
コピーtweets.csv1ユーザー名,つぶやき,投稿日時
6-6 CSVファイルの操作
コードの方を見ていきましょう。
以下の部分では、CSVファイルを操作するためのライブラリを読み込んでいます。
require "csv"
以下の部分では、CSVファイルを開いて書き込み、閉じるまでの処理を記述しています。
123csv = CSV.open('tmp/tweets.csv', 'a')csv.puts(["ユーザー名", "つぶやき", "投稿日時"])csv.close
Rubyの「ファイル操作」では「File.open( )」を使用していましたが、今回はCSV形式のファイルを操作するため「CSV.open( )」を使用しています。
CSVファイルを開くためのプログラムは、次のような書式になります。
変数 = CSV.open("ファイル名", モード)
モードに関しては、今回は書き込みモードでファイルにどんどん追記していくので「’a’」としています。
また、CSVファイルに書き込む場合は 配列を使って書き込む 書式になるため、上記のコードでは「ユーザー名」「つぶやき」「投稿日時」の文字列を配列にしています。
6-7 フォームデータの受け取り
今の状態だと、フォームのデータを受け取っていないため、CSVファイルに書き込まれる値は「ユーザー名」「つぶやき」「投稿日時」という文字列に固定されています。
フォームのデータをコントローラのアクションで受け取るには、 変数params を使って次のように記述します。
コピー12345# <input type="text" name="name"> のデータを受け取る場合
params[
:name
]
# <textarea name="tweet"></textarea> のデータを受け取る場合
params[
:tweet
]
createアクションの中を、以下の内容に変更しましょう。
コピー12345def
create
csv =
CSV
.open(
'tmp/tweets.csv'
,
'a'
)
csv.puts([params[
:name
], params[
:tweet
],
"投稿日時"
])
# フォームのデータを受け取れるよう変更
csv.close
end
変更したらファイルを保存し、ブラウザの新規投稿ページからフォームに値を入力して送信すると、「ユーザー名」と「つぶやき内容」がファイルに書き込まれるようになります。
6-8 日付のフォーマット
「ユーザー名」「つぶやき」の内容がフォームのデータで書き込まれるようになったので、次は投稿日時が送信ボタンを押した時の日時になるよう実装します。
createアクションの中を以下の内容に変更しましょう。
コピー123456def
create
csv =
CSV
.open(
'tmp/tweets.csv'
,
'a'
)
time =
Time
.now
# Timeで現在日時を取得
csv.puts([params[
:name
], params[
:tweet
], time.strftime(
'%Y/%m/%d %H:%M:%S'
)])
# 日時をフォーマット変換
csv.close
end
Rubyの「ライブラリ」のところで、日付を取り扱う「Date」ライブラリについて学習しましたが、今回は日付だけでなく時間も必要なため「Time」ライブラリを使用します。
「Time.now」で現在日時を取得したら、 strftime を使ってフォーマット変換しています。
6-9 リダイレクト
今の状態では、送信ボタンを押した後も新規投稿ページが表示されたままになっています。
送信ボタンを押してCSVファイルにデータが保存できたら、トップページに戻るようにリダイレクトの処理を実装しましょう。
他のURLに転送することを リダイレクト といいます。 リダイレクト処理をするには、「 redirect_to メソッド」を使います。
createアクションの中で、CSVファイルを閉じた後にリダイレクトする処理を追加しましょう。
コピー12345678def
create
csv =
CSV
.open(
'tmp/tweets.csv'
,
'a'
)
time =
Time
.now
csv.puts([params[
:name
], params[
:tweet
], time.strftime(
'%Y/%m/%d %H:%M:%S'
)])
csv.close
# トップページへリダイレクト
redirect_to(
'/'
)
end
ブラウザの新規投稿ページからフォームに値を入力して送信したら、CSVファイルにデータが保存されトップページに遷移することを確認してください。
これで登録処理の完成です。
7-1 CSVファイルから読み込み
登録処理ができたので、次はCSVに保存した内容をトップページに表示させる処理を実装していきます。
トップページの処理はStaticPagesコントローラで制御しているので、static_pages_controller.ebファイルを開き、topアクションに以下の内容を記述しましょう。
コピーstatic_pages_controller.eb12345678require
"csv"
# CSVライブラリの読み込み
class
StaticPagesController < ApplicationController
def
top
# CSVファイルの読み込み
csv =
CSV
.read(
'tmp/tweets.csv'
)
@tweets
= csv
end
end
以下の部分では、CSVファイルの中身全部を読み込み、変数「@tweets」にファイルの内容を格納する処理を記述しています。
コピー12csv =
CSV
.read(
'tmp/tweets.csv'
)
@tweets
= csv
「@tweets」という @変数 ですが、通常はコントローラのアクションで定義した変数をビューで使うことはできません。 しかし、変数名の先頭に「@」をつけることでビューファイルでもその変数を使うことができるようになります。
@がついた変数を インスタンス変数 といい、アクションメソッドとビューの間でデータを受け渡しすることができます。
上記では、CSVから読み込んだデータをビューファイルに出力するため、「@tweets」という変数を使っています。
それではビューファイルの方で、「@tweets」の中身を表示してみましょう。
「views/static_pages/top.html.erb」を開き、以下の内容を記述してください。
コピーtop.html.erb1234567891011121314151617181920<%= @tweets %>
<!-- この1行を追加 -->
<
div
class
=
"top"
>
<
h2
>いまどうしてる?をつぶやこう</
h2
>
<
div
class
=
"tweets"
>
<
ul
>
<
li
>
田中: こんにちは!
<
span
>2017/08/01 12:00:00</
span
>
</
li
>
<
li
>
林田: はじめまして
<
span
>2017/08/01 12:03:00</
span
>
</
li
>
<
li
>
斉藤: よろしくおねがいします。
<
span
>2017/08/01 12:04:00</
span
>
</
li
>
</
ul
>
</
div
>
</
div
>
ファイルを保存してトップページをブラウザで確認すると、以下のようにファイルの中身が配列で表示されます。
「erb」形式のファイルでは、 <%
と %>
で囲むことで、HTMLファイルの中にRubyのコードを記述することができます。
今回のように、Rubyのコードを記述するのではなくブラウザに表示したい場合は <%=
と %>
を使います。
<% %>
は変数を定義する場合などに使い、 <%= %>
は変数の値などをブラウザに表示したい場合に使います。
7-2 一覧の表示
CSVファイルのデータが配列になっているので、each文を使って1つずつ出力していきます。
top.html.erbの内容を以下のように変更してみましょう。
コピーtop.html.erb12345678910111213141516171819202122232425<!-- each文に変更 -->
<% @tweets.each do |tweet| %>
<%= tweet[0] %>
<%= tweet[1] %>
<%= tweet[2] %>
<%end %>
<
div
class
=
"top"
>
<
h2
>いまどうしてる?をつぶやこう</
h2
>
<
div
class
=
"tweets"
>
<
ul
>
<
li
>
田中: こんにちは!
<
span
>2017/08/01 12:00:00</
span
>
</
li
>
<
li
>
林田: はじめまして
<
span
>2017/08/01 12:03:00</
span
>
</
li
>
<
li
>
斉藤: よろしくおねがいします。
<
span
>2017/08/01 12:04:00</
span
>
</
li
>
</
ul
>
</
div
>
</
div
>
ファイルを保存してトップページをブラウザで確認すると、以下のように表示されます。
今はヘッダーの下にデータを出力していますが、データを出力したいのは以下の <ul>
タグ内です。 <li>
タグ部分を、each文を使って繰り返していきます。
top.html.erbの <ul>
タグ内を、以下のように変更してみましょう。
コピーtop.html.erb12345678910111213<
div
class
=
"top"
>
<
h2
>いまどうしてる?をつぶやこう</
h2
>
<
div
class
=
"tweets"
>
<
ul
>
<% @tweets.each do |tweet| %>
<
li
>
<%= tweet[0] %>: <%= tweet[1] %>
<
span
><%= tweet[2] %></
span
>
</
li
>
<% end %>
</
ul
>
</
div
>
</
div
>
ファイルを保存してトップページをブラウザで確認し、以下のように表示されれば一覧表示処理の完成です。
8-1 入力チェック
登録処理と一覧表示処理が完成したので、最後に入力チェックの処理を実装します。
今の状態では、ユーザー名やつぶやき内容が空だったり、つぶやき内容が140文字を超えても登録できてしまいます。
送信されたフォームのデータをチェックし、不正なデータは登録できないよう処理する仕組みのことを バリデーション といいます。
今回は以下のバリデーションチェックを行います。
ユーザー名
- 空だったらエラーにする
つぶやき内容
- 空だったらエラーにする
- 140文字を超えたらエラーにする
8-2 バリデーションの実装
バリデーションは通常、モデルの方で実装していきますが、今回はデータベースを使用しないため、コントローラの方にバリデーションの実装をしていきます。 モデルでのバリデーション実装は、「データベース」以降で説明します。
「tweets_controller.rb」ファイルを開き、createアクションに以下の内容を追記しましょう。
コピーtweets_controller.rb12345678910111213141516171819202122232425262728def
create
# エラー文を格納するための配列
@errors
= []
# ユーザー名の空チェック
if
params[
:name
].empty?
@errors
<<
'ユーザー名が未入力です。'
end
# つぶやき内容の空チェック
if
params[
:tweet
].empty?
@errors
<<
'つぶやき内容が未入力です。'
end
# つぶやき内容の文字数チェック
if
params[
:tweet
].length >
140
@errors
<<
'ツイートは140文字以内で入力して下さい。'
end
# エラーがあったら新規投稿ページを表示する
if
@errors
.present?
render
'new'
and
return
end
# CSVファイルに書き込み
csv =
CSV
.open(
'tmp/tweets.csv'
,
'a'
)
time =
Time
.now
csv.puts([params[
:name
], params[
:tweet
], time.strftime(
'%Y/%m/%d %H:%M:%S'
)])
csv.close
# トップページへリダイレクト
redirect_to(
'/'
)
end
新規投稿ページで、フォームに何も入力せず「送信」ボタンをクリックすると、投稿内容がファイルに登録されず、トップページにも遷移しなくなりました。
コードの詳細を見ていきましょう。
コピー12# エラー文を格納するための配列
@errors
= []
まず、上記の部分で、ビューに表示するためのエラー文を格納する配列を準備しています。
ビューにエラーの値を表示したいので、@変数で宣言しています。
コピー12345678# ユーザー名の空チェック
if
params[
:name
].empty?
@errors
<<
'ユーザー名が未入力です。'
end
# つぶやき内容の空チェック
if
params[
:tweet
].empty?
@errors
<<
'つぶやき内容が未入力です。'
end
上記では、フォームの入力値が空かどうかの判定をしています。
空かどうかを判定する時は、 empty メソッドを使います。
「 文字列.empty? 」とすることで、「文字列が空だったら」という条件式になります。
配列に要素を追加する時、Rubyではpushメソッドを使う方法を学習しましたが、上記のように「配列 << 格納する値」という形式で配列末尾に要素を追加することもできます。
コピー1234# つぶやき内容の文字数チェック
if
params[
:tweet
].length >
140
@errors
<<
'ツイートは140文字以内で入力して下さい。'
end
上記では、つぶやき内容が140文字よりも多いかどうかの判定をしています。
文字列の長さを調べる時は、 length メソッドを使います。
コピー1234# エラーがあったら新規投稿ページを表示する
if
@errors
.present?
render
'new'
and
return
end
上記では、エラーの配列の中身があるかどうかの判定をしています。
中身があるかどうかを判定する時は、 present メソッドを使います。
「 配列.present? 」とすることで、「配列の中身があったら」という条件式になります。
(“配列の中身があったら” = “配列が空じゃなかったら” = emptyの逆)
エラーの配列に何か文字列があった場合はエラーチェックに引っかかったことになるので、条件式がtrueの時は以下の処理が実行されます。
コピー1render
'new'
and
return
エラーがあった場合はトップページに戻らず、新規投稿ページにとどまってエラーを出力する必要があります。
そのため、上記のように render メソッドを使って新規投稿ページのビュー(new)を表示するよう指定しています。
renderメソッドは、テンプレートを指定して表示することができるメソッドです。
アクションの途中でrenderメソッドを呼び出す場合は、以降の処理を終了するため明示的に「and return」の記述が必要なので注意してください。
8-3 エラーメッセージの表示
バリデーションチェックが実装できたので、エラーメッセージを新規投稿ページに表示するようにします。
「new.html.erb」ファイルを開き、以下の内容を記述しましょう。
コピーnew.html.erb1234567891011121314151617<
div
class
=
"new-tweet"
>
<!-- エラー内容を表示 -->
<
div
class
=
"errors"
>
<% @errors.each do |error| %>
<
p
><%= error %></
p
>
<% end %>
</
div
>
<%= form_tag("/tweets/create") do %>
<
div
class
=
"text-wrap"
>
<
input
type
=
"text"
name
=
"name"
placeholder
=
"ユーザー名"
value
=
""
/>
</
div
>
<
div
class
=
"textarea-wrap"
>
<
textarea
name
=
"tweet"
placeholder
=
"つぶやきを140文字以内で記述"
></
textarea
>
</
div
>
<
input
type
=
"submit"
value
=
"送信"
/>
<% end %>
</
div
>
上記のビューファイルを保存して新規投稿ページを表示すると、以下のようなエラーが表示されます。
@error変数に関するエラーです。
コントローラのcreateアクションでは@error変数が宣言されているのですが、newアクションの方では宣言されていないため、「@errorが定義されていない」というエラーになります。
tweets_controller.rbのnewアクションで、以下のように@error変数を定義しましょう。
コピー123def
new
@errors
= []
end
ファイルを保存して再度新規投稿ページを開くと、今度はエラーなく表示されます。
エラーチェックに引っかかった場合も、以下のようにエラーが表示されるようになりました。
8-4 値の保持
今の状態だと、エラーがあった場合フォームに入力していた値が消えてしまいます。
エラーがあった場合はフォームに入力した値を保持しておくようにしましょう。
new.html.erbファイルを開き、以下の内容を追加します。
コピーnew.html.erb12345678910111213141516<
div
class
=
"new-tweet"
>
<
div
class
=
"errors"
>
<% @errors.each do |error| %>
<
p
><%= error %></
p
>
<% end %>
</
div
>
<%= form_tag("/tweets/create") do %>
<
div
class
=
"text-wrap"
>
<
input
type
=
"text"
name
=
"name"
placeholder
=
"ユーザー名"
value="<%= params[:name] %>"/>
<!-- 値を保持 -->
</
div
>
<
div
class
=
"textarea-wrap"
>
<
textarea
name
=
"tweet"
placeholder
=
"つぶやきを140文字以内で記述"
><%= params[:tweet] %></
textarea
>
<!-- 値を保持 -->
</
div
>
<
input
type
=
"submit"
value
=
"送信"
/>
<% end %>
</
div
>
「<%= params[パラメータ名] %>」と指定することで、フォームに入力された値を保持しておくことができます。
これで、CSVファイルを使ったひとことBBSの実装が完成です。