BookShelfアプリ作成手順

セミナーのスライドは[ Slide Share ](http://www.slideshare.net/n-fukidome/rails-48548945)より見ることが出来ます。

PC の環境設定は[Rails をはじめる時のセットアップ](http://qiita.com/n-fukidome/items/cf6a99161c8951540d1c)を参照してください。

# 初めの設定

## Rails プロジェクトの新規作成
まずは先程と同様、railsアプリを新規作成しましょう。

```:$
cd
rails new book_shelf
cd book_shelf
subl .
```

## Gemfileの編集
次にGemfileの設定を行います。
Gemfileを開いて、**文末に** 以下のコードをコピペしてください。

```ruby:Gemfile

# apps
gem 'haml-rails'
gem 'haml', '~> 4.0.5'

# bootstrap
gem 'bootstrap-sass', '~> 3.3.4'

group :development, :test do
  # apps
  gem 'pry'
  gem 'pry-doc'
  gem 'pry-stack_explorer'
  gem 'pry-byebug' if RUBY_VERSION >= '2.0.0'
  gem 'pry-coolline'
  gem 'pry-rails'
end
```

Gemfileを保存したら、ターミナル上で以下のコマンドを叩きましょう。

```:$
bundle install
```

# Book モデル作成
Book モデル作成のコマンドまとめ

```:$
rails generate scaffold book title:string author:string price:integer
rake db:migrate
```
※ 今回は、`scaffold` と呼ばれるMVC(Model View Controller)をまとめて作成してくれるコマンドを使用しています。

## コマンドの読み方
 - `rails generate scaffold` => railsでscaffoldを使って生成します。
 - `book` => book Modelを生成します。
 - `title:string` => titleというカラム(要素)を作り、titleにはstring(文字列)が入ります。
 - `price:integer` => priceというカラム(要素)を作り、priceにはinteger(整数)が入ります。

## rails generateの修正方法
もしタイプミスをしたのに気づかずに作成してしまった場合、一旦削除して、再作成しましょう。

削除は、先程のコマンドの `generate` を `destroy` にするだけです。

```
rails destroy scaffold book title:string author:string price:integer
```

## migrationの実行
データベースに反映させるのに必要なので、scaffoldで作成した後は以下のコマンドを打つようにしてください。

```:$
rake db:migrate
```

サーバーを立ち上げてアクセスできることを確認しましょう。

```:$
rails server
```

適当なブラウザ(chromeなど)から以下のURLにアクセスします。

```
http://localhost:3000/books
```

以下のような画面が表示されれば、成功です。
![view.png](https://qiita-image-store.s3.amazonaws.com/0/45114/aa11de0e-47d4-37dd-b237-efdbb7429317.png)

## Bookデータの作成
次に、New Bookからいくつかデータの作成をしてみましょう。
Bookモデルと、そこから作成されたデータの関係は下図のようになります。

![firstrails.png](https://qiita-image-store.s3.amazonaws.com/0/45114/86be6dc3-bdae-3146-7d7c-0d9e1f68b72e.png)

つまり、Bookモデルをもとに、bookの登録が可能になりました。また、作成したbookは、編集や削除の機能も追加されていることを確認しましょう。

# Book モデル編集
先程作成したBookは、

 - title(タイトル)
 - author(筆者)
 - price(価格)

の3つのデータを持っています。

これを見るだけでも、本のデータには発売日やレビュー、概要などがあることがわかります。
そこで、Bookモデルに新しくデータを追加して、保存できるデータの種類を増やしてみましょう。

今回は、Bookモデルに `neko_id` を追加してみます。

```:$
rails generate migration AddColumnNekoIdToBook neko_id:string
rake db:migrate
```

※ 本来であれば、neko_idは数値なので `neko_id: integer` とするのが普通ですが、あとで数値以外も入れることになるので string(文字列) を指定しています。 

## コマンドの解説
### カラムの追加
railsでは、マイグレーションファイルを作った後、それを反映させることで　データベースを操作します。

 - `rails generate migration AddColumnNekoIdToBook neko_id:string` は、マイグレーションファイルを作成するコマンドです。
 - ここでは、既に作成済みの booksテーブルに、新しく `neko_id` というカラムを追加するように書いています。
 - データベースに反映させるにはマイグレーションを実行する必要がありましたね。 `rake db:migrate` がそれです。

# 機能を追加する
本の登録、編集、削除の基本的な機能を、scaffoldを使って実装できました。
また、登録したいデータの追加もできました。

## Review Modelの構成

ここで、BookとReviewの関係性を見てみましょう。
図にすると以下のようになります。

![review.png](https://qiita-image-store.s3.amazonaws.com/0/45114/7bed25d2-c140-66a4-ca30-40a7263bf885.png)

ひとつのBookが複数のReviewを持ちます。

ReviewのデータがどのBookに紐づくかを判別するために、Reviewにはbook_idを持たせます。

Book Modelをscaffoldで作成したときに、idを追加した覚えは無いと思いますが、これはRailsが自動で作成してくれます。

なので、Review側がどのBookに紐づいているかidで指定することで、関連が表せるわけですね。

## Review Model作成

それでは実際にReview Modelを作成していきましょう。

```
rails generate scaffold review title:text body:string book_id:integer
```

- Reviewとして登録するデータは、title, body, book_idの3つにしてみました。

先程と同様、migrateします。

```:$
rake db:migrate
```

# Model同士の関連を定義しよう
次に、Review ModelとBook Modelの関連を定義します。
繰り返しになりますが、BookとReviewには下図のような関係性があります。

![review.png](https://qiita-image-store.s3.amazonaws.com/0/45114/958207c8-bc17-cdbe-0f53-d37377977dbb.png)

[Amazon](http://www.amazon.co.jp/%E5%AE%9F%E8%B7%B5Ruby-Rails-4-%E7%8F%BE%E5%A0%B4%E3%81%AE%E3%83%97%E3%83%AD%E3%81%8B%E3%82%89%E5%AD%A6%E3%81%B6%E6%9C%AC%E6%A0%BCWeb%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0-%E9%BB%92%E7%94%B0/dp/4844335928/ref=sr_1_1?s=books&ie=UTF8&qid=1438586713&sr=1-1&keywords=rails)をイメージするとわかりやすいと思うのですが、1つの本にはたくさんのレビューがつきますね。それと同じです。
これをコード上で表現すると、以下のようになります。

```app/models/book.rb
class Book < ActiveRecord::Base
  has_many :reviews
end
```

```app/models/review.rb
class Review < ActiveRecord::Base
  belongs_to :book
end
```

こうすることで、BookからReviewが、ReviewからBookが呼び出せるようになります。

## 入力フォームを改善する
`localhost:3000/reviews` にアクセスし、先程作成したbooksデータと紐づいたデータを作成します。

すると、レビューの新規作成画面では以下のような画面が表示されると思います。

![new_review.png](https://qiita-image-store.s3.amazonaws.com/0/45114/7563e4f8-5330-01e8-abe6-3ff8c75fb65c.png)

レビューを付ける場合は、どの本のレビューを付けるかを選択しなければなりませんが、デフォルトのフォームでは、book_idを数値で選択する形になっています。

非常にわかりにくいので、まずはこれをBookの名前(title)から選択できるようにViewを修正します。

```haml:app/views/reviews/_form.html.haml
〜中略〜
  / .field
  /   = f.label :book_id
  /   = f.number_field :book_id
  .field
    = f.label :book_id
    = f.select :book_id, options_for_select(Book.all.map{|book| [book.title, book.id]})

〜中略〜
```

[コードの全文はこちら](https://github.com/Takuya-Kaneko/book_shelf/blob/master/app/views/books/index.html.haml)

ここを理解するには多くのことを説明しなければならないのでコピペしてください。うまく反映されると以下のようなViewになります。

![fix-new-review.png](https://qiita-image-store.s3.amazonaws.com/0/45114/9c231cc9-bb52-63e7-3a87-6281fa6b5c99.png)

これで本を選択して、レビューを作成できるようになりました。

サーバを立ち上げて実際に関連のあるデータを作成してみましょう。

```:$
rails s
```

# ルーティング

ロジック的にはBookとReviewの関連付けができました。
…とは言ったものの、ブラウザの操作ではBookとReviewは切り離されていますね。
なので、Books一覧からReviewの新規作成ページに飛べるリンクを追加しましょう。

やることはそこまで難しくありません。

```haml:books/index.html.haml
/ 中略

= link_to 'New Book', new_book_path
```

このような記述があると思います。このnew_book_pathがbooksの新規作成ページに飛ぶ設定なので、reviewsの新規作成ページに飛ぶようにしたいのであれば、new_review_path にしてあげればいいだけです。

```app/views/books/index.html.haml
%h1 Listing Books
= link_to 'レビューの新規作成', new_review_path
```

これで、ページ上部にレビュー作成ページへのリンクボタンが追加されました。

[コードの全文はこちら](https://github.com/Takuya-Kaneko/book_shelf/blob/master/app/views/books/index.html.haml)

![スクリーンショット 2015-04-21 16.51.16.png](https://qiita-image-store.s3.amazonaws.com/1041/45114/9afe5436-28a8-7c71-e719-d01a4a6078f3.png)

※ 詳しく知りたい方は、Railsのroutingを後日調べてください。

# neko_idで書籍を管理する
## 課題の説明
ここからは課題です。
以下の要件を満たすように、 `neko_id` を実装してみましょう。

1. 8桁のランダムな英数字(全て半角)
2. 8桁すべてが 数字、または アルファベット は禁止
3. 先頭の文字は 1〜9の数字のいずれかが必ず入る

を実装してみましょう。

## irb(rubyのコード実行環境)
上記の実装は、Railsの実装というよりはrubyでのロジックの作成になります。
コマンドライン上で `irb` と打ってみましょう。
すると、rubyのコード実行環境が立ち上がります。

![スクリーンショット 2015-08-03 15.56.43.png](https://qiita-image-store.s3.amazonaws.com/0/45114/76cb3867-ccc3-6f55-daad-ec9409b16a4c.png)

あとは ググりながら色々試してみてください。

## neko_idのデータの登録と確認
上記のロジックができたら、登録と確認ができるように修正します。

気軽に質問もどうぞ。

# おまけ
## bootstrapの導入
気になっていることがあると思います。
あまりにも見た目がしょぼい！！

そこで、bootstrapの導入方法を紹介します。

```config/application.rb
config.sass.preferred_syntax = :sass
```

```sass:app/assets/stylesheets/application.css.sass
 /*
 *= require_tree .
 *= require_self
 */

// "bootstrap-sprockets" must be imported before "bootstrap" and "bootstrap/variables"
@import bootstrap-sprockets
@import bootstrap
```

```app/assets/javascripts/application.js
//= require jquery
//= require bootstrap-sprockets
```

使い方は[Boostrap CSS](http://getbootstrap.com/css/)を参考にしてください。

## Bootstrapを使ったデザイン
あまりここに時間を割いてほしくないので、books/indexを例に、bootstrapでデザインするとどうなるのか以下に紹介します。

```haml:app/views/books/index.html.haml
%h1 書籍一覧
= link_to '書籍の新規登録', new_book_path, class: 'btn btn-primary'
= link_to 'レビューの新規作成', new_review_path, class: 'btn btn-primary'

%table.table.table-striped
  %thead
    %tr
      %th Title
      %th Author
      %th Price
      %th
      %th
      %th

  %tbody
    - @books.each do |book|
      %tr
        %td= book.title
        %td= book.author
        %td= book.price
        %td= link_to '確認', book
        %td= link_to '編集', edit_book_path(book)
        %td= link_to '削除', book, :method => :delete, :data => { :confirm => 'Are you sure?' }

```

bootstrapのcssを適用するために、classの指定をしています。
それにあたるのが、以下に挙げた部分です。

```rb:例
# イコールのものは、文末に classを指定する。
= link_to '書籍の新規登録', new_book_path, class: 'btn btn-primary'
= link_to 'レビューの新規作成', new_review_path, class: 'btn btn-primary'

# %からタグ指定があるものは、その後ろにドットをつければclassが反映される。
%table.table.table-striped
```

うまく反映されると以下のようになります。

![スクリーンショット 2015-04-21 17.12.15.png](https://qiita-image-store.s3.amazonaws.com/1041/45114/7e609cd4-fd0f-237a-5584-9ba507dfce08.png)

少し綺麗になりました！