Rails5.2で色々やってみる

宮崎勉強会 Advent Calendar 15日目の記事です。勉強会の中では年上の方なので、「枯れた」技術の良さを再確認するという主旨で、私の好きなRubyとRails、そのオススメのgemを幾つか紹介します。（本当はRails6の新機能を紹介する記事を書くつもりでいたんですが、環境構築で手こずって路線変更しています。6を使いたいけど、まだ導入のハードルが高かった…）

今回使用するRailsのバージョンは5.2.4です。vagrant上に構築したUbuntuでコマンドを実行しています。

```bash

$ rails -v
Rails 5.2.4
```

まずは、新しいプロジェクトを作成します。今回は `wiki` という名前にしました。生成されたプロジェクトのディレクトリに入って、データベースの初期化とサーバの立ち上げもします。PostgreSQLを指定しているのは、このあとherokuというプラットフォームに載せてみようとしてるからです。

```bash
rails new wiki --database=postgresql
cd wiki/
rails db:create
rails s -b 0.0.0.0
```

ブラウザで http://localhost:3000 にアクセスすると以下のような画面が表示されます（vagrantで仮想ネットワークを使っている場合はlocalhostの部分が192.168.33.10などのIPになります）。

![Screen Shot 2019-12-12 at 19.22.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50780/dd571f2e-e438-6d18-62b7-cbb6acb204ac.png)


続けてこのアプリ上で管理したいテーブルの定義をします。 `page` という名前のモデルに
`title,body,published` という3つのフィールドを定義しました。

```bash
rails g scaffold page title body published:boolean
rails db:migrate
```

ホストのルート(URLのホスト名以下に何も書かない状態)からアクセスできるように、rootを定義しておきます（先程のRailsの画面が見えなくなる代わりに新しく作ったモデルの一覧ページが表示されるようになります）。

```ruby:config/routes.rb
  root to: 'pages#index'
```

このモデルを使って色んなgemの紹介をしていこうと思います。

## markdownを使ってみる(redcarpet)
ここ(qiita)でも使っているMarkdown記法のテキストをhtmlにパースしてくれるのが redcarpet です。Gemfileに以下のバージョンを登録しました（Gemfileを更新したら`bundle`を実行してください、これ以降も同様です）。

```ruby:Gemfile
gem 'redcarpet', '~> 3.5'
```

直接Markdownとは関係ないですが、複数行のテキスト入力をするために、`body`フィールドの編集エリアを text_field から text_area に変更します。

```erb:app/views/pages/_form.html.erb
<div class="field">
  <%= form.label :body %>
  <%= form.text_area :body %>
</div>
```

Redcarpetのパーザを使うためのヘルパメソッドを定義します。

```ruby:app/helpers/pages_helper.rb
  def markdown(text)
    unless @renderer
      @renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true)
    end
    @renderer.render(text).html_safe
  end
```

このメソッドで`body`の出力を囲うと、Markdownで保存されたテキストがHTMLとして整形されて出力されます。

```erb:app/views/pages/show.html.erb
<p>
  <strong>Body:</strong>
  <%= markdown(@page.body) %>
</p>
```

![Screen Shot 2019-12-12 at 20.14.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50780/04a40c6e-281a-7579-2afc-721147e1efb5.png)

## 変更履歴を管理してみる(paper_trail)
paper_trailは、モデルの変更履歴(生成,更新,削除)を記録してくれます。papertrail(アンダースコアなし)という似たgemが存在するので注意してください。

```ruby:Gemfile
gem 'paper_trail', '~> 10.3', '>= 10.3.1'
```

バージョン履歴を保存するテーブルを作ったりなどするために、以下のコマンドを実行します。

```bash

rails generate paper_trail:install
rails db:migrate
```

バージョン管理を行いたいモデルに`has_paper_trail`と記載します。

```ruby:app/models/page.rb
class Page < ApplicationRecord
  has_paper_trail
end
```

これだけで、モデルの各操作に対する履歴が保存されるようになります。例えば、最初に「１」という内容で作成されたpageモデルが、続けて「２」「３」と2回更新された場合、以下のように3つのバージョン履歴が残っていて、それぞれの時点で保存されていた値を参照することが可能です。

```irb
irb> p = Page.last
irb> p.body
=> "3"
irb> p.versions[2].reify.body
=> "2"
irb> p.versions[1].reify.body
=> "1"
irb> p.versions[0].reify
=> nil
```

IDをベースに記録されているため、モデルが削除された場合でも、同一IDのモデルを新しく作ってから、versionsを参照すると過去の状態を参照することができます。以下の例では一度削除したモデルを削除直前の状態に戻しています。

```irb
irb> p = Page.new(id:3)
irb> p = p.versions.last.reify
irb> p.save
=> true
```

`devise` などのgemと組み合わせると「誰が操作したか」という記録も合わせて保管できます。


## 画像をアップロードしてみる(active_storage)
Rails5.2で導入された新しい機能です。追加のgemは必要なく、以下のコマンドで有効化できます。

```bash
rails active_storage:install
rails db:migrate
```

今回は、pageモデルに複数の画像(images)を追加出来るようにしてみました。

```ruby:app/models/page.rb
class Page < ApplicationRecord
  has_many_attached :images
```

ファイルをアップロードするためのフィールドを追加します。`multiple: true`を忘れずに。

```erb:app/views/pages/_form.html.erb
<div class="field">
  <%= form.label :images %>
  <%= form.file_field :images, multiple: true %>
</div>
```

コントローラでパラメタを受け取れるようにします。

```ruby:app/controllers/pages_controller.rb
def page_params
  params.require(:page).permit(:title, :body, :published, images:[])
end
```

アップロードされたファイルを表示する部分です。

```erb:app/views/pages/show.html.erb
<% if @page.images.attached? %>
  <% @page.images.each do |i| %>
    <%= image_tag i %>
  <% end %>
<% end %>
```

最終的にはAWSのS3などオブジェクトストレージに繋いで使うことを想定されているようですが、ひとまずこれだけでローカルディスクに画像などのアップロードされたファイルが貯められるようになります。

## レスポンシブにしてみる(bootstrap)
[Bootstrap](https://getbootstrap.com/)はレスポンシブ対応のウェブサイトを構築するのに便利なライブラリです。色んなインストール方法がありますが、gemを入れてみます。

```ruby:Gemfile
gem 'bootstrap', '~> 4.4.1'
gem 'jquery-rails'
```

[bootstrap-rubygem](https://github.com/twbs/bootstrap-rubygem/blob/master/README.md)のREADMEに従って幾つか設定の調整が必要です。例えば、Rails5.2では jquery-rails も一緒にインストールが必要です。

application.cssの代わりにapplication.scssを使用します。コピーする場合、`*= require`が使えないので注意してください。

```scss:app/assets/stylesheets/application.scss
@import "bootstrap";
```

jsのインストールも必要です。

```javascript:app/assets/javascripts/application.js
//= require jquery3
//= require popper
//= require bootstrap-sprockets
```

これでbootstrapが使える状態になりますが、（昔のgemにはあった気がする）自動的にスタイルを適用するgeneratorなどは見当たらないので、地道にクラスをあてて行きましょう（多分、要調査）。

## アイコンを使ってみる(font-awesome-sass)
前述のBootstrap3.xまではglyphiconsというアイコンセットが一緒に使えていたのですが、4になってからどうも使い方がややこしくなっている感じです。Font Awesomeという別のセットをインストールします。

```ruby:Gemfile
gem 'font-awesome-sass', '~> 5.12.0'
```

類似のgemに`font-awesome-rails`というものがあって、こちらの方がダウンロード数が多いのですが、更新が止まっているようで最近はこちらが主になっているのかな？という感じでした。

任意のscss内にインポートします。

```sass:app/assets/stylesheets/pages.scss
@import "font-awesome-sprockets";
@import "font-awesome";
```

iconというヘルパ関数を使って、以下のようにアイコンを使用することができます。普通にhtml(`<i class="fas fa-flag"></i>`)を書いてもOKです。

```erb
<%= link_to icon('fas', 'flag')+' New Page', new_page_path %>
```

![Screen Shot 2020-04-26 at 11.46.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50780/fd060c0b-b6dd-8b18-bda7-458f2ca75177.png)

## ユーザ管理してみる(devise)
[Devise](https://github.com/heartcombo/devise#starting-with-rails)というgemを使います。

```
rails g devise:install
rails g devise user
```

## インターネットに公開してみる(heroku)
ここまで作ったアプリを[heroku](https://jp.heroku.com/)に乗せてみましょう（アカウントの作成とherokuコマンドのインストールは省略しています）。一番シンプルには、以下のコマンドを順番に実行するだけです。

```
heroku login
heroku create
git push heroku master
heroku run rake db:migrate
```

createの際に名前を指定しないと、ランダムな単語の組み合わせが割り当てられます。今回は以下のURLでアプリケーションが起動されました。

https://radiant-refuge-52835.herokuapp.com/

※ active_recordの保存先をlocalに指定しているため、データが永続化されないherokuのサーバが再起動される度に画像が消えてしまいます

## まとめ
思いつくままに幾つかの機能(gem)を紹介してみました。詳しい使い方はそれぞれの公式ドキュメントを参照してみてください。Railsの真価は、「こういうときはこのgemを使ったらいいや」というストックが個人の中に溜まってきてから発揮されると感じています。デフォルトのCRUDはページ遷移を基本とした古風なインターフェースですが、これだけでも仕組み化の手助けになる場面は多いかなと思います。

## 参考リンク

- https://rubygems.org/
- https://github.com/vmg/redcarpet
- https://github.com/paper-trail-gem/paper_trail
- [Railsのプロジェクトにgem 'redcarpet' を用いてマークダウンエディタを導入してみた](https://qiita.com/yuuuking/items/3f88454e164a5f7d4733)
- [【Rails 5.2】 Active Storageの使い方](https://qiita.com/hmmrjn/items/7cc5e5348755c517458a)
- https://railsguides.jp/active_storage_overview.html
- [Ruby | Heroku Dev Center](https://devcenter.heroku.com/categories/ruby-support)
- [Active Storage Overview](https://edgeguides.rubyonrails.org/active_storage_overview.html)
- [Active Storage on Heroku | Heroku Dev Center](https://devcenter.heroku.com/articles/active-storage-on-heroku)
- [docker-composeでRails5.2開発環境](https://qiita.com/lumbermill/items/f5d04035753c1ea4c3e1)