簡単な蔵書管理アプリをRailsで作った話1

#はじめに
　プログラミングを勉強し始めて早一年、Rails以外にもFlaskやらDjangoやVue-cliなどのWebフレームワークをつついたりしてきました。
しかし、僕は気づいてしまいました。__あれ？僕、何か作れるの？__
このようなこともあり、今まで勉強したことを整理することもかねて簡単なWebアプリを制作しそのことについての記事を書こうと思いました。

見ていただけると今後の勉強の励みにもなります。
分からないところやおかしいところがあった際にはコメントよろしくお願いします。

テストについてはあまり知識がないのと単純に書きたくないということもあり作成しておりませんのでご了承ください。

#制作するもの
よくある書籍管理アプリです。
(書籍名は適当です。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/a207b684-bfa1-9112-6099-80e979524d6d.png" width=70%>

css関係についてはすべてmaterializeに丸投げしました。

#環境
- OS Windows10 Home
- Ruby 2.4.5
- Rails 5.2.3
- Node-js 10.15.1

#プロジェクトの作成
まずはプロジェクトを作成しましょう。

```
$ rails new book_app
```
そして```rails s```と入力し、(http://localhost:3000) を開いてウェルカムページが表示されるか確認しましょう。

#homeの作成
 では早速このアプリのHomeを作っていこうと思います。

```
rails g controller home index about help
```

すると以下のようにファイルが作成されたと思います。

```
 create  app/controllers/home_controller.rb
       route  get 'home/index'
get 'home/about'
get 'home/help'
      invoke  erb
      create    app/views/home
      create    app/views/home/index.html.erb
      create    app/views/home/about.html.erb
      create    app/views/home/help.html.erb
      invoke  test_unit
      create    test/controllers/home_controller_test.rb
      invoke  helper
      create    app/helpers/home_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/home.coffee
      invoke    scss
      create      app/assets/stylesheets/home.scss
```

次に```config/routes.rb```の一番上に次のように追記します。

```ruby:config/routes.rb
Rails.application.routes.draw do
  root to: 'home#index' #これを追加
  get 'home/index'
  get 'home/about'
  get 'home/help'
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
```
これにより、```localhost:3000```にアクセスしたとき```app/views/home/index.html.erb```の内容が表示されます。
そしてもう一度 (http://localhost:3000) にアクセスすると以下のように表示されていると思います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/2c579333-8fbd-e098-8107-44e012866c05.png" width=70%>

#Materializeの導入
　次にcssフレームワークのmaterializeを導入していきます。
```Gemfile```に次の内容を追記して下さい。

```:Gemfile
#materialize
+ gem 'materialize-sass'
+ gem 'material_icons'
+ gem 'jquery-rails'
```
しかしWindows環境ではこのままでは```ExecJS::ProgramError```が発生しjavascriptが正しく読み込めないたいなので、
```Gemfile```の```ducktape```をコメントアウトしてください。

```:Gemfile
#gem 'duktape'
```
そして```bundle install```としてください。
次に```app/assets/stylesheets/application.scss```に以下のように記述します。

```scss:app/asssets/stylesheets/application.scss
 /*
 *= require_tree .
 *= require material_icons
 *= require_self
 */
 @import 'materialize/components/color-variables';
 $primary-color: color("teal", "lighten-4") !default;
 @import 'materialize';
```

primary-colorを設定することによってmaterializeのテーマカラーをデフォルトのピンクから変更することができます。

最後に```app/assets/javascripts/application.js```に以下のように追記します。

```js:app/assets/javascripts/application.js
+ //= require jquery
+ //= require materialize
```

#ヘッダーの作成

ではheaderを作成していきたいと思います。
> (https://materializecss.com/navbar.html)
を使わせていただきます。

まず、```app/views/parts```と```app/views/parts/_header.html.erb```を作成してください。
そして次のように記述してください

```erb:app/views/parts/_header.html.erb
<nav>
  <div class="nav-wrapper container">
    <%= link_to image_tag("logo.png", class: "brand-logo left", size: '90x60'), root_path %>
    <ul id="nav-mobile" class="right hide-on-med-and-down">
      <li><%= link_to "Home", root_path %></li>
      <li><%= link_to "About", home_about_path %></li>
      <li><%= link_to "Help", home_help_path %></li>
    </ul>
    <ul id="dropdown-target" class="dropdown-content">
      <li><%= link_to "Home", root_path %></li>
      <li><%= link_to "About", home_about_path %></li>
      <li><%= link_to "Help", home_help_path %></li>
    </ul>
    <a  href="#" class="dropdown-trigger right hide-on-large-only" data-target="dropdown-target"><i class="material-icons">arrow_drop_down</i></a>
  </div>
</nav>
<script>
    $(".dropdown-trigger").dropdown();
</script>

```
```logo.png``` は(https://www.logofactoryweb.com/default_lg.asp?lg=ja#_ebcv=3kxCAMrX.liskul2so.1,3kxCAMrX.liskul2so.1) で作成させていただきました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/596a7cb3-4fd9-8abb-bf44-8e23db1ed200.png" width=30%>
*```logo.png```は```app/assets/images```において下さい。

最後に```app/views/layouts/application.html.erb```に以下のように追記してください。

```erb:app/views/layouts/application.html.erb
<body>
    +<%= render 'parts/header' %>
    <div class="container><%= yield %></div>
</body>
```
そして再び (http://localhost:3000) にアクセスすると以下のようになっていると思います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/955a47fe-3621-941e-6892-d873b5caa6da.png" width=70%>

#deviseのinstall
今回はログインに使うmodelを作成するために効率よくdeviseを使用します。

>deviseのinstallは[こちらの記事](https://qiita.com/ayacai115/items/aa947eac5595f0cbcb5e)を参考にさせていただきました。

#Ownerモデルの作成
いよいよ、modelを作成していきたいと思います。Userモデルじゃないの？と思う方もいるかもしれませんが、自分の本を登録するためのmodelなのでUserよりOwnerのほうがいいと思いこっちにしました。

まずOwnerモデルの作成をします。

```
rails g devise owner
```
次にログインやサインアップのviewを作成します。

````
rails g devise:views
```

```rails db:migrate```でマイグレーションを実行します。

また、materializeやbootstrapなどのcssフレームワークを使うとcheck_boxが表示されなくなることがあるのでログインページを編集します。
(```<%= f.label %>```で囲むと表示されるらしい)
> (https://railstutorial.jp/chapters/advanced_login?version=5.1#sec-remember_me_checkbox) より

```erb:app/views/devise/sessions.new.html.erb
<%= f.label :remember_me do >
  <%= f.check_box :remember_me %>
  <span>Remeber me</span>
<% end %>

```
これで、OKです。
(http://localhost:3000/owners/sign_in) にアクセスしてみてください。ログイン画面が表示されているはずです。

#サイドバーのみたいなものの作成
クリックしたら出てくるこういうやつをつくっていきます(サイドナビゲーションだからsidenavなのかな
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/7bacfb2d-fc0d-94b2-9dda-5001f6e4c25f.png" width=50%>

>https://materializecss.com/sidenav.html を参考にしました。


```erb:app/views/parts/_sidenav.html.erb
  <% if owner_signed_in? %>
  <ul id="slide-out" class="sidenav">
    <li><%= link_to 'アカウントの編集', edit_owner_registration_path %></li>
    <li><%= link_to 'ログアウト', destroy_owner_session_path, method: :delete %></li>
    <li><div class="divider"></div></li>
    <li><%= link_to "アカウント削除", registration_path(current_owner), data: { confirm: "本当によろしいですか?" }, method: :delete, class: "red-text text-darken-2" %></li>
    <li><div class="divider"></div></li>
  </ul>
  <a href="#" data-target="slide-out" class="sidenav-trigger"><i class="material-icons large-size menu position">menu</i></a>
<% end %>

<script>
    $('.sidenav').sidenav();
</script>
```
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/b2ac6aa1-8aaf-9830-00e3-a4f5274af014.png" width=70%>

#ログイン画面に強制リダイレクト
触って見て思うかもしれませんがログインするためにいちいち (http://localhost:3000/owners/sign_in)
に移動するのがめんどくさいのでログアウト状態でaboutとhelpのページにアクセスするとログイン画面に強制リダイレクトされるように```before_action```を登録しましょう。
```app/controllers/home_controller.rb```に以下を追記します。

```ruby:app/controllers/home_controller.rb
class HomeController < ApplicationController
  before_action :redirect_login, except: [:about, :help]
  # 中略
  def redirect_login
    unless owner_signed_in?
      redirect_to new_owner_session_path
    end
  end
end
```
これによりログアウトした後、ログイン画面にリダイレクトされていると思います。

#flashメッセージの表示
今回はこれで最後です。
やっぱり、ログイン機能をつけたのならflashメッセージはほしくなりますよねぇ。
ということでちゃちゃっと作っちゃいましょう。
>https://materializecss.com/toasts.html を参考にさせていただきました。

まず、```app/views/parts/_flash.html.erb```を作成し、
```app/views/layouts/application.html.erb```を以下のように追記します。

```erb:app/views/layouts/application.html.erb
.........
    <%= render 'parts/sidenav'  %>
    +<%= render 'parts/flash' %>
    <div class="container">
..........
```
そして```app/views/parts/_flash.html.erb```を以下のように編集します。

```erb:app/views/parts/_flash.html.erb
<% unless flash.empty? %>
  <script>
      <% flash.each do |key, message| %>
        M.toast({html: '<%= message %>'})
      <% end %>
  </script>
<% end %>
```
これでログインしたとき以下のように右上にflashメッセージが表示されているはずです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/281205/4003924c-2850-e0c1-fc26-193f666e5d25.png" width=70%>

---
今回はこれで終わりです。お疲れ様でした。次回はログイン画面とflashの見た目の編集と日本語化そして書籍の登録処理まで書こうと思います。貸し出し機能も書きたいので全部で4章ぐらい続きそうです....

最近はレポートがたまり消化が追いついていないので次回の更新はかなり遅れるかもしれません。
では。

---