Getting Started with Rails

Qiitaも裾野が広がりすぎて、自分のようなどうでもいいエントリーでも投稿して大丈夫かなと思い、始めてみた。

今回は、
http://guides.rubyonrails.org/getting_started.html
を読みながらRuby on Railsを始めてみる。
内容としてはブログを作りながらRailsの基礎を学ぶもの。
当方、普段はPHPメインのサーバサイド寄りでRubyもRailsも初めて。

# 準備とインストール

```
$ ruby -v
ruby 2.4.1p111 (2017-03-22 revision 58053) [x86_64-darwin16]
$ sqlite3 --version
3.16.0 2016-11-04 19:09:39
$ gem install rails
$ rails --version
Rails 5.1.2
$ rails new blog
$ cd blog
$ ls
.            .git         Gemfile      README.md    app          config       db           log          public       tmp
..           .gitignore   Gemfile.lock Rakefile     bin          config.ru    lib          package.json test         vendor
```

自分のマシンにrbenvを使ってRubyの最新版をインストールするのが結構手間取った。

### 参考

[rbenvで2.4.1をインストールする手順 - Qiita](http://qiita.com/paranishian/items/8a18aba3c2f9e7e5d00f)
[rbenv installに失敗する - Qiita](http://qiita.com/kshimadutsu/items/26aff85950fa28fffc09)

# ローカルサーバを動かす

```
$ bin/rails server
```

http://localhost:3000/

![スクリーンショット 2017-07-20 11.55.38.png](https://qiita-image-store.s3.amazonaws.com/0/123511/b0e47c26-ce2a-a90c-a871-83d4eff973f0.png)

**簡単！！**

# コントローラを作ってSay "Hello"

```
$ bin/rails generate controller Welcome index
Running via Spring preloader in process 19807
      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
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/welcome.coffee
      invoke    scss
      create      app/assets/stylesheets/welcome.scss

$ echo '<h1>Hello, Rails!</h1>' > app/views/welcome/index.html.erb

$ cat config/routes.rb
Rails.application.routes.draw do
  get 'welcome/index'

  root 'welcome#index'
end
```

http://localhost:3000/welcome/index
http://localhost:3000/
rootを設定することで、トップページでも同じ画面が表示される。

# 記事ページを作る

```
$ cat config/routes.rb
Rails.application.routes.draw do
  get 'welcome/index'

  resources :articles

  root 'welcome#index'
end

$ bin/rails routes
       Prefix Verb   URI Pattern                  Controller#Action
welcome_index GET    /welcome/index(.:format)     welcome#index
     articles GET    /articles(.:format)          articles#index
              POST   /articles(.:format)          articles#create
  new_article GET    /articles/new(.:format)      articles#new
 edit_article GET    /articles/:id/edit(.:format) articles#edit
      article GET    /articles/:id(.:format)      articles#show
              PATCH  /articles/:id(.:format)      articles#update
              PUT    /articles/:id(.:format)      articles#update
              DELETE /articles/:id(.:format)      articles#destroy
         root GET    /                            welcome#index
```

http://localhost:3000/articles/new

![スクリーンショット 2017-07-20 12.18.30.png](https://qiita-image-store.s3.amazonaws.com/0/123511/4940ab4d-280a-9084-ee62-1d1edaec4176.png)

コントローラがないと怒られる。。

コントローラを作る。

```
$ bin/rails generate controller Articles
Running via Spring preloader in process 20190
      create  app/controllers/articles_controller.rb
      invoke  erb
      create    app/views/articles
      invoke  test_unit
      create    test/controllers/articles_controller_test.rb
      invoke  helper
      create    app/helpers/articles_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/articles.coffee
      invoke    scss
      create      app/assets/stylesheets/articles.scss
```

http://localhost:3000/articles/new

![スクリーンショット 2017-07-20 12.19.54.png](https://qiita-image-store.s3.amazonaws.com/0/123511/e897dca6-1b38-82b9-d731-07df2f5cf9f3.png)

アクションがないと怒られる。。

アクションを作る。

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def new
    end
end
```

http://localhost:3000/articles/new

![スクリーンショット 2017-07-20 12.21.07.png](https://qiita-image-store.s3.amazonaws.com/0/123511/568d77b1-c1c1-4406-dc99-a33feae2c189.png)

ビューテンプレートがないと怒られる。。
ちなみにgetting_started.htmlのほうでもエラーメッセージが長過ぎると嘆いてますね。

![スクリーンショット 2017-07-21 13.59.03.png](https://qiita-image-store.s3.amazonaws.com/0/123511/8ac92814-d3db-fc5b-d2c2-9715dd3be4b5.png)

フォームを作る。

```
$ cat app/views/articles/new.html.erb 
<h1>New Article</h1>
<%= form_for :article, url: articles_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 %>
```

http://localhost:3000/articles/new
![スクリーンショット 2017-07-20 12.32.37.png](https://qiita-image-store.s3.amazonaws.com/0/123511/6c8624fa-cd38-17b3-dff3-2cc3b3ff9cc7.png)
![スクリーンショット 2017-07-20 12.34.43.png](https://qiita-image-store.s3.amazonaws.com/0/123511/89c20eee-e86a-aaa8-f78d-e610b2bc0a12.png)

"Save Article"ボタンを押すとアクションがないと怒られる。。
怒られ慣れてきた。

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def new
    end

    def create
    end
end
```

"Save Article"ボタンを押しても何も変わらない。
createアクションで何も返してないので、ステータスコード204(No Content)が返ってきているようです。
では何か返しましょう。

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def new
    end

    def create
        render plain: params[:article].inspect
    end
end
```

とすると、

```
<ActionController::Parameters {"title"=>"ビッグニュース", "text"=>"梅雨明けた。"} permitted: false>
```

入力した値が返ってきた。

## 記事の保存
次に記事を保存しなければなりません。
なのでモデルを作る。

```
$ bin/rails generate model Article title:string text:text
Running via Spring preloader in process 20843
      invoke  active_record
      create    db/migrate/20170720040354_create_articles.rb
      create    app/models/article.rb
      invoke    test_unit
      create      test/models/article_test.rb
      create      test/fixtures/articles.yml
```

マイグレーションファイルが作られるので、マイグレーション実行。

```
$ bin/rails db:migrate
== CreateArticles: migrating ===================================
-- create_table(:articles)
   -> 0.0018s
== CreateArticles: migrated (0.0032s) ==========================
```

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def new
    end

    def create
        @article = Article.new(params[:article])

        @article.save
        redirect_to @article
    end
end
```

![スクリーンショット 2017-07-20 14.15.41.png](https://qiita-image-store.s3.amazonaws.com/0/123511/955b9e03-7f14-c604-8520-718a7b158bcd.png)

また怒られた。。
モデルには本当に必要なデータしか渡してはいけないらしい。
セキュリティ的にいいこと！

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def new
    end

    def create
        @article = Article.new(article_params)

        @article.save
        redirect_to @article
    end

    private
        def article_params
            params.require(:article).permit(:title, :text)
        end
end
```

これで保存成功。

## 記事の表示

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def show
        @article = Article.find(params[:id])
    end

    def new
    end

$ cat app/views/articles/show.html.erb 
<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>
<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>
```

http://localhost:3000/articles/1

![スクリーンショット 2017-07-20 14.25.04.png](https://qiita-image-store.s3.amazonaws.com/0/123511/1e33ff12-763f-a660-4e11-83a44390e0a2.png)

表示できた。

Ruby初心者の自分には変数名の前のアットマークやコロンなどの意味がちゃんと理解できてないままにここまで進んだけれど大丈夫だろうか。

## 記事の一覧表示

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def index
        @articles = Article.all
    end

    def show
        @article = Article.find(params[:id])
    end

    def new
    end

$ cat app/views/articles/index.html.erb 
<h1>Listing articles</h1>

<table>
    <tr>
        <th>Title</th>
        <th>Text</th>
    </tr>

    <% @articles.each do |article| %>
        <tr>
            <td><%= article.title %></td>
            <td><%= article.text %></td>
            <td><%= link_to 'Show', article_path(article) %></td>
        </tr>
    <% end %>
</table>
```

## リンク
トップページにリンクを追加する。

```
$ cat app/views/welcome/index.html.erb 
<h1>Hello, Rails!</h1>
<%= link_to 'My Blog', controller: 'articles' %>
<%= link_to 'New article', new_article_path %>
```

戻るリンクも。

```
$ cat app/views/articles/new.html.erb 
<h1>New Article</h1>
<%= form_for :article, url: articles_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 %>

<%= link_to 'Back', articles_path %>

$ cat app/views/articles/show.html.erb 
<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>
<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<%= link_to 'Back', articles_path %>
```

## バリデーション

```
$ cat app/models/article.rb 
class Article < ApplicationRecord
    validates :title, presence: true,
                      length: { minimum: 5}
end

$ cat app/controllers/articles_controller.rb 

    def new
        @article = Article.new
    end

    def create
        @article = Article.new(article_params)

        if @article.save
            redirect_to @article
        else
            render 'new'
        end
    end

$ cat app/views/articles/new.html.erb 
<h1>New Article</h1>
<%= form_for :article, url: articles_path do |f| %>
    <% if @article.errors.any? %>
        <div id="error_explanation">
            <h2>
                <%= pluralize(@article.errors.count, 'error') %> prohibited
                this article from being saved:
            </h2>
            <ul>
                <% @article.errors.full_messages.each do |msg| %>
                    <li><%= msg %></li>
                <% end %>
            </ul>
        </div>
    <% end %>
```

![スクリーンショット 2017-07-20 15.11.51.png](https://qiita-image-store.s3.amazonaws.com/0/123511/e7cb124f-3b61-6a0d-89fd-2888797b242e.png)

エラーメッセージも表示される。

## 記事の更新

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController
    def index
        @articles = Article.all
    end

    def show
        @article = Article.find(params[:id])
    end

    def new
        @article = Article.new
    end

    def edit
        @article = Article.find(params[:id])
    end

    def create
        @article = Article.new(article_params)

        if @article.save
            redirect_to @article
        else
            render 'new'
        end
    end

    def update
        @article = Article.find(params[:id])

        if @article.update(article_params)
            redirect_to @article
        else
            render ‘edit’
        end
    end

    private
        def article_params
            params.require(:article).permit(:title, :text)
        end
end
```

ビューの共通部分をまとめる。

```
$ cat app/views/articles/_form.html.erb
<%= form_for @article do |f| %>
    <% if @article.errors.any? %>
        <div id="error_explanation">
            <h2>
                <%= pluralize(@article.errors.count, 'error') %> prohibited
                this article from being saved:
            </h2>
            <ul>
                <% @article.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 %>

$ cat app/views/articles/new.html.erb 
<h1>New Article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>

$ cat app/views/articles/edit.html.erb 
<h1>Edit Article</h1>

<%= render 'form' %>

<%= link_to 'Back', articles_path %>
```

## 記事の削除

```
$ cat app/controllers/articles_controller.rb 
    def destroy
        @article = Article.find(params[:id])
        @article.destroy

        redirect_to articles_path
    end

$ cat app/views/articles/index.html.erb 
<h1>Listing articles</h1>

<%= link_to 'New article', new_article_path() %>
<table>
    <tr>
        <th>Title</th>
        <th>Text</th>
        <th colspan="3"></th>
    </tr>

    <% @articles.each do |article| %>
        <tr>
            <td><%= article.title %></td>
            <td><%= article.text %></td>
            <td><%= link_to 'Show', article_path(article) %></td>
            <td><%= link_to 'Edit', edit_article_path(article) %></td>
            <td><%= link_to 'Destroy', article_path(article),
                    method: :delete,
                    data: { confirm: 'Are you sure?' } %></td>
        </tr>
    <% end %>
</table>
```

確認ダイアログの出し方もこれがRails流なのだろうか。

# Active Recordを試す

RailsといえばORマッパー、Active Recordを使うために2つ目のモデルを作る。
ということで記事といえばコメントが必要。

```
$ bin/rails generate model Comment commenter:string body:text article:references
Running via Spring preloader in process 24720
      invoke  active_record
      create    db/migrate/20170720071707_create_comments.rb
      create    app/models/comment.rb
      invoke    test_unit
      create      test/models/comment_test.rb
      create      test/fixtures/comments.yml

$ cat app/models/comment.rb 
class Comment < ApplicationRecord
  belongs_to :article
end

$ bin/rails db:migrate
== CreateComments: migrating ===================================
-- create_table(:comments)
   -> 0.0051s
== CreateComments: migrated (0.0052s) ==========================
```

Articleモデルにhas_manyでCommentモデルを関連付ける。
Articleモデルが継承しているのがApplicationRecordモデルらしいけど、これはActiveRecordとは違うのだろうか。

```
$ cat app/models/article.rb 
class Article < ApplicationRecord
    has_many :comments
    validates :title, presence: true,
                      length: { minimum: 5}
end

$ cat config/routes.rb
Rails.application.routes.draw do
  get 'welcome/index'

  resources :articles do
      resources :comments
  end

  root 'welcome#index'
end
```

コメントのコントローラを作る。

```
$ bin/rails generate controller Comments
Running via Spring preloader in process 24956
      create  app/controllers/comments_controller.rb
      invoke  erb
      create    app/views/comments
      invoke  test_unit
      create    test/controllers/comments_controller_test.rb
      invoke  helper
      create    app/helpers/comments_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/comments.coffee
      invoke    scss
      create      app/assets/stylesheets/comments.scss
```

記事の画面にコメントを表示する。

```
$ cat app/views/articles/show.html.erb 
<p>
  <strong>Title:</strong>
  <%= @article.title %>
</p>
<p>
  <strong>Text:</strong>
  <%= @article.text %>
</p>

<h3>Comments</h3>
<% @article.comments.each do |comment| %>
    <p>
        <strong>Commenter:</strong>
        <%= comment.commenter %>
    </p>
    <p>
        <strong>Comment:</strong>
        <%= comment.body %>
    </p>
<% end %>

<h3>Add a comment:</h3>
<%= form_for([@article, @article.comments.build]) do |f| %>
    <p>
        <%= f.label :commenter %><br>
        <%= f.text_field :commenter %>
    </p>
    <p>
        <%= f.label :body %><br>
        <%= f.text_area :body %>
    </p>
    <p>
        <%= f.submit %>
    </p>
<% end %>

<%= link_to 'Edit', edit_article_path(@article) %> | 
<%= link_to 'Back', articles_path %>
```

article.commentsでコメントの配列にアクセスできるのですね。

コメントを保存するアクションを作る。

```
$ cat app/controllers/comments_controller.rb 
class CommentsController < ApplicationController
    def create
        @article = Article.find(params[:article_id])
        @comment = @article.comments.create(comment_params)
        redirect_to article_path(@article)
    end

    private
        def comment_params
            params.require(:comment).permit(:commenter, :body);
        end
end
```

![スクリーンショット 2017-07-20 19.19.24.png](https://qiita-image-store.s3.amazonaws.com/0/123511/fb728965-de87-706f-e03f-4d81b02ab647.png)

# Basic認証

最後にBasic認証。とりあえずBasicで我慢してくれ。。

```
$ cat app/controllers/articles_controller.rb 
class ArticlesController < ApplicationController

    http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]

    def index
        @articles = Article.all
    end
```

# まとめ

普段PHPでLaravelを使っている者が、ほぼ初めてのRubyとRailsを触ったのだけど、
言語もWebフレームワークも似ているといえば似ているので、つまずくこともなく進めることが出来た。
ただまだ初歩の初歩なので、ここからRailsで複雑なことをしようとするといろいろと壁が立ちはだかるに違いない。