- 投稿日:2020-05-21T23:33:01+09:00
【学習メモ】Rails Bootstrapの均等配置ができない原因について
【解決したいこと】
Railsアプリ制作する際に、Bootstrapの均等配置ができない問題を解決したい【試していたこと】
下記のようなコードを打っていた。.d-flex.align-center(flex要素と中央揃え) .col-4(3分割) = link_to 'A(文字列)', new_record_path(遷移先リンク), class: 'btn btn-primary' .col-4 = link_to 'B(文字列)', reminder_record_path, class: 'btn btn-primary' .col-4 = link_to 'C(文字列)', report_record_path, class: 'btn btn-primary'それまでの
【原因】
・デベロッパーツールからCSSを確認し、ブロック要素のみ中央揃えになっていたが、インライン要素が左揃えのまま(=設定されていなかった)だったことが原因と判明。【解決策】
・下記の通りに変更したら成功。.d-flex .col-4.text-align: center = link_to '新規登録', new_record_path, class: 'btn btn-primary' .col-4.text-align: center = link_to 'リマインダー設定', reminder_record_path, class: 'btn btn-primary' .col-4.text-align: center = link_to 'レポートを見る', report_record_path, class: 'btn btn-primary'【学び】
1.Bootstrapでレイアウトがうまく行っていなければ、検証ツールでレイアウトを確認すると見えてくる!
2.他の場合も同様の考え方で成功できるのでは?
- 投稿日:2020-05-21T22:51:34+09:00
Rails5.2系+webpackerでログアウト機能を実装しようとしたらno route matches [get] "/logout"のエラーが出てしまい、postメソッドが上手く動作しなかった件について
はじめに
投稿系のアプリケーションを作成中、Rails5.2にwebpackerを導入してログアウト機能を実装しようとした際、
no route matches [get] "/logout"
エラーが出てdeleteリクエストを送信できなくなってしまったので、これを解決します。環境
Ruby 2.6.5
Rails 5.2.4.2
webpacker 5.1.1エラー発生地点
webpackerビルド後に、application.html.slimにwebpackerのスクリプトタグを読み込んだ瞬間から。
app/views/layouts/application.html.slimhead . . . - = javascript_include_tag 'application','data-turbolinks-track': 'reload' + = javascript_pack_tag 'application' . . .
解決策
1.まずはyarnのパッケージファイルに
rails-ujs
を追加します。そしてpackage.jsonにこれが追加されていることを確認します。$ yarn add rails-ujspackage.json{ "name": , "dependencies": { "@rails/webpacker": "5.1.1", "rails-ujs": "^5.2.4-3", }, }
2.app/javascript/packsディレクトリ配下に
rails-ujs.js
ファイルを作成し、下記の記述を追加します。app/javascript/packs/rails-ujs.jsimport Rails from 'rails-ujs'; Rails.start();
3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。
app/javascript/packs/application.jsrequire("./rails-ujs.js");
4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。app/views/layouts/application.html.slimhead . . . = javascript_pack_tag 'application' . . .以上で、DELETEリクエストを受けてくれるようなったかと思います。
何故これで動作するのか
色々調べた結果、辻褄を合わせただけのため断言はできませんが、こうだろうみたいな予想を立ててみました。
もし間違っていたら、ご指摘して頂けるととてもありがたいです!
まず前提として、デフォルトの状態ではHTMLはGET,POSTのリクエストを送信することしかできません(なのでログインなどは通常通り行えます)
しかしRailsにてDELETE,PATCHなどのRESTful APIを発行する場合は、JavaScriptに処理を任せているため
gem rails-ujs
もしくはgem jquery-rails
を用いています。Rails5.1以降ではJavaScriptの統括ファイルにこの
rails-ujsがデフォルトで読み込まれています
(下記ファイルを参照)。なので我々は特に気にしなくとも、ただRails側でmethod: :deleteなどのオプションを記述すれば、あとはJavaScriptが勝手に処理してくれるため、HTMLからDELETEリクエストを送信することができるようになっています。
ちなみにjquery-ujsはRails5.1以降サポートが廃止されています(Railsガイドより。)
app/assets/javascript/application.js//= require rails-ujs //= require activestorage //= require turbolinks //= require_tree .
しかしwebpackerを導入すると、デフォルトの状態ではrails-ujsがそもそも導入されていないので、サポートを受けることができません。なのでrails-ujsを導入し、packs/application.jsファイルに新たにrails-ujs読み込みさせてあげなければ、RESTfulなAPIの処理を使用できないのは当然だったのかなと思いました。
参考
http://docs.komagata.org/5456
https://www.inodev.jp/entry/2019/12/03/234210
- 投稿日:2020-05-21T22:51:34+09:00
Rails5.2系にwebpackerを導入した途端元から実装してあったログアウト機能がno route matches [get] "/logout"エラーで動かなくなった
はじめに
投稿系のアプリケーションを作成中、Rails5.2にwebpackerを導入した途端、元から実装してあったログアウト機能が動作しなくなりました。
no route matches [get] "/logout"
エラーが出てdeleteリクエストを送信できなくなってしまったので、これを解決します。環境
Ruby 2.6.5
Rails 5.2.4.2
webpacker 5.1.1エラー発生地点
webpackerビルド後に、application.html.slimにwebpackerのスクリプトタグを読み込んだ瞬間から。
app/views/layouts/application.html.slimhead . . . - = javascript_include_tag 'application','data-turbolinks-track': 'reload' + = javascript_pack_tag 'application' . . .
解決策
1.まずはyarnのパッケージファイルに
rails-ujs
を追加します。そしてpackage.jsonにこれが追加されていることを確認します。$ yarn add rails-ujspackage.json{ "name": , "dependencies": { "@rails/webpacker": "5.1.1", "rails-ujs": "^5.2.4-3", }, }
2.app/javascript/packsディレクトリ配下に
rails-ujs.js
ファイルを作成し、下記の記述を追加します。app/javascript/packs/rails-ujs.jsimport Rails from 'rails-ujs'; Rails.start();
3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。
app/javascript/packs/application.jsrequire("./rails-ujs.js");
4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。app/views/layouts/application.html.slimhead . . . = javascript_pack_tag 'application' . . .以上で、DELETEリクエストを受けてくれるようなったかと思います。
何故これで動作するのか
色々調べた結果、辻褄を合わせただけのため断言はできませんが、こうだろうみたいな予想を立ててみました。
もし間違っていたら、ご指摘して頂けるととてもありがたいです!
まず前提として、デフォルトの状態ではHTMLはGET,POSTのリクエストを送信することしかできません(なのでログインなどは通常通り行えます)
しかしRailsにてDELETE,PATCHなどのRESTful APIを発行する場合は、JavaScriptに処理を任せているため
gem rails-ujs
もしくはgem jquery-rails
を用いています。Rails5.1以降ではJavaScriptの統括ファイルにこの
rails-ujsがデフォルトで読み込まれています
(下記ファイルを参照)。なので我々は特に気にしなくとも、ただRails側でmethod: :deleteなどのオプションを記述すれば、あとはJavaScriptが勝手に処理してくれるため、HTMLからDELETEリクエストを送信することができるようになっています。
ちなみにjquery-ujsはRails5.1以降サポートが廃止されています(Railsガイドより。)
app/assets/javascript/application.js//= require rails-ujs //= require activestorage //= require turbolinks //= require_tree .
しかしwebpackerを導入すると、デフォルトの状態ではrails-ujsがそもそも導入されていないので、サポートを受けることができません。なのでrails-ujsを導入し、packs/application.jsファイルに新たにrails-ujs読み込みさせてあげなければ、RESTfulなAPIの処理を使用できないのは当然だったのかなと思いました。
参考
http://docs.komagata.org/5456
https://www.inodev.jp/entry/2019/12/03/234210
- 投稿日:2020-05-21T21:15:05+09:00
Railsテスト
前提
本日学んだことを書いていきます。
本題
present?はRailsによって追加されたObjectクラスのインスタンスメソッド。
このメソッドはblank?メソッドの否定。
blank?メソッドの意味はクラスによって異なる。
文字列の場合は、❶長さが0である、または❷全ての文字列が空白文字(半角スペース、タブ、改行、復帰、改ページ)である場合にblank?メソッドがtureを返す。
配列の場合は、要素数が0の時にblank?メソッドがtrueを返す。
nil.blank?は常にtrueを返す。アセットパイプライン
初期状態のRailsアプリケーションでは、app/assetsディレクトリにimages、javascripts、stylesheetsと言う3つのサブディレクトが存在する。
それぞれ画像ファイル、JavaScriptファイル、CSSファイルを置くためのディレクトリ。
Railsではこれらのファイルをアセット(assets)と呼ぶ。
アセットパイプライン機能の最大の売りは、ファイルの変換と統合。メリット
ブラウザとRailsアプリケーションの間のHTTP通信の回数が減ること。
HTTP通信は接続開始処理のところで比較的大きな負荷をサーバーにかけるため、回数を減らすことが大事。Sacc/SCSSとは
Sassは、CSSを拡張したスタイルシート言語。
CSS3と互換性を保ちつつ、ネスティング、変数、ミックスインなどの特徴を備えている。
ブラウザはSassを理解できないため、サーバー側でSassをCSSに変換してブラウザに送る必要がある。
この処理をコンパイルと呼ぶ。
Sassにはインデントによって構造を表現するオリジナルの書式と、波括弧{}によって構造を表現するSCSSと呼ばれる書式がある。RSpecテストの導入
Gemfile.#省略 group :development, :test do gem 'rspec-rails', '~> 4.0.0.beta2' # 追記 #省略 end$ bundle install続いて、RSpecの各種設定ファイルやフォルダを作成するため次のコマンドを実行。
$ rails generate rspec:install生成された.rspecファイルに、テストの出力結果をわかりやすく表示させるようにするための設定を追加。
/.rspec--require spec_helper --format documentation # 追記さらに、binstubをインストールしてテストの起動時間を早くする設定を行う。
bin/rspecを実行できるようにし、テストを早くする設定をしていく。これを実現するには、Springというアプリケーションの起動時間を素早くするgemが必要になる。
Gemfileに下記を追記し、bundle installを実行。Gemfile.#省略 group :development do gem 'spring-commands-rspec' # 追記 #省略 end$ bundle install新しいbinstubを作成し、bin/rspecが実行できるようにする。
$ bundle exec spring binstub rspecbin/rspecが実行できるかどうか、試してみる。
$ bin/rspec #省略 Running via Spring preloader in process 21348 No examples found. Finished in 0.00045 seconds (files took 0.17522 seconds to load) 0 examples, 0 failuresまだテストは0個のため、これでひとまずOK。
Rubocopの導入
デフォルトのRubocopを導入してもいいが、デフォルトに加えてたくさんルールを追加しなければならず手間となる。そこで、Airbnb社が公開しているカスタマイズ済みのrubocop-airbnbというgemを使用。
Gemfile.group :development, :test do #省略 gem 'rubocop-airbnb' # 追記 end$ bundle installRubocop導入に必要なファイルを作成
rubocop_airbnb.ymlrequire: - rubocop-airbnbrubocop.ymlinherit_from: - .rubocop_airbnb.yml Rails: Enabled: trueRails: Enabled: trueは、Rails用に最適化されたモードにする設定。基本trueにしておく。
Rubocopを実行
$ bundle exec rubocop Inspecting 59 files .....C...CC.....C...CCC...C...CC...CC.....C...CC....CC.C.... Offenses: app/helpers/application_helper.rb:2:18: C: Airbnb/OptArgParameters: Do not use default positional arguments. Use keyword arguments or an options hash instead. def full_title(page_title = '') ^^^^^^^^^^^^^^^ #省略 59 files inspected, 32 offenses detected#余分な改行がある spec/requests/static_pages_spec.rb:27:1: C: Layout/EmptyLinesAroundBlockBody: Extra empty line detected at block body end.#末尾に改行がない spec/support/capybara.rb:11:4: C: Layout/TrailingBlankLines: Final newline missing.
- 投稿日:2020-05-21T20:44:27+09:00
100日後に1人前になる新人エンジニア(1日目)
100日後に1人前になる新人エンジニア(1日目)
どうもこんばんは今日が1日目になります。
ちなみに昨日から始まっていますのでよかったらこちらからどうぞ
100日後に1人前になる新人エンジニア(0日目)今日やったこと
本日より本格的に業務に取り組みました。
さっそくissueに取りかかり頑張るぞ...
と思ってましたが、Dockerの使い方がわからん...
ということでまずそこで悪戦苦闘した前半。その後Railsでフォームの改修に取り組みましたが...
プロジェクトのフォルダ多すぎて何が何だかわからんぞという始まり方でした。
少しずつ理解を深めていきアーキテクチャとかクラスごとの繋がりも分かってきたら
面白いんだろうなあって思っています。Dockerあれこれ
今日はここからでした。いくつか使ったコマンドを
dockerコマンドdocker-compose build #サービス(web,DB)のビルドを実行 docker-compose up #コンテナを作成して、起動 docker-compose down #コンテナを停止し、そのコンテナとネットワークを削除 docker-compose ps #コンテナの一覧を表示 コンテナの状態がわかるよきっと他にもいっぱいあるので少しずつキャパを増やして行こう。
あれ..ファイルの変更時に情報が反映されないぞ...そんな時は
dockerコマンドdocker-compose build #サービス(web,DB)のビルドを実行 docker-compose up -d #デタッチド・モード:バックグラウンドでコンテナを実行し、新しいコンテナ名を表示なるほどねー。これで大丈夫みたい。
なんで情報の変更を反映時はバックグラウンドで起動するんだろ?
あと変更した時にこれを行わないで画面リロードで良い時もあったけど何が違うんだ?
本日の疑問点です。明日までに解決します。そしてコツコツとドキュメントを読んでいきます
--追記--
docker-compose.yml or ./docker/rails/Dockerfile に書かれている内容に変更があったら 再度buildする必要あり。疑問点は無事解決Rails あれこれ
プロジェクトがとても大きい...
どんなroutesになってるのかこんがらがっちまったよ・僕が知っていた方法...
config/routes見れば全部書いてあるだろ!!
って思ってたけど大変そうでした。よって以下の方法で確認$ rails routes他にも色々とあるようでして
Tipsを先輩社員の方からいただきました。・localhost:3000/rails/infoで
すげーroutes全部出てきたしちゃんと検索もできる!!・docker-compose upから
ログが出力されているのでどのコントローラを使って
画面が遷移しているのかが確認できるよありがたきアドバイスでした。
デバッグって
コードの途中にraiseを入れると...
アプリを立ち上げるとエラー画面になっているけど
その画面でコンソール?を打ち込むと各情報が得られました。すげー例えばform controllerのコードの途中にraiseを入れておいて
アプリを立ち上げ→エラー画面からコンソールを入力
params と入力してみるとどんな値が送られているのかちゃんと確認できました。
明日から使います。ありがたや...他にもやり方は色々とあるはずなのでそこは確認してみます。
formに関してはまだ完成していないので続きは明日に回します。
今日のさいごに
今日が連載初日でした。
何事も積み重ねだと思うので一日ひとつづつ。フィルヒースの言葉をいただきます。
Winning is habitualですね1人前のエンジニアになるまであと99日
- 投稿日:2020-05-21T20:12:34+09:00
【Rails】Carrierwaveを導入する
タイトルと本文のみの投稿ができるブログアプリに
写真の投稿機能を追加した時の、Carrierwaveを導入する
手順に付いてまとめました。1. Gemfileファイルを編集する
Gemfilegem 'carrierwave' gem 'mini_magick'ターミナル% bundle install2. アップローダーを作成する
ターミナルrails g uploader image実行後、app/uploaders 下に image_uploader.rbが作成されます。
3. アップローダーをマウントする
app/models/message.rbclass Message < ApplicationRecord belongs_to :group belongs_to :user belongs_to :heven validates :content, presence: true, unless: :image? mount_uploader :image, ImageUploader ⬅️ この1行 end4. 画像のリサイズを可能にする
include CarrierWave::MiniMagick のコメントアウトを外し、
任意の行にprocess resize_to_fit: [800, 800]と追記。
これにより、縦横比を維持したまま、縦横を800px以内にリサイズ可能になります。app/uploaders/image_uploader.rbclass ImageUploader < CarrierWave::Uploader::Base # Include RMagick or MiniMagick support: # include CarrierWave::RMagick include CarrierWave::MiniMagick ⬅️ 有効化 # 〜省略〜 process resize_to_fit: [600, 600] ⬅️ 追記 # 〜省略〜 end
以上で画像のアップロードの準備ができました。
ご覧いただきありがとうございました。
- 投稿日:2020-05-21T18:45:59+09:00
#Rails で指定回数/複数回 db:rollback する ( STEP=n rails db:rollback )
例
STEP=2 rails db:rollbackruby on rails - How to rollback just one step using rake db:migrate - Stack Overflow
Original by Github issue
- 投稿日:2020-05-21T17:43:27+09:00
has_secure_token を後から追加した時に token を埋める
結論
既存レコードのバリデーションが通る場合
User.where(token: nil).find_each(&:regenerate_token)既存レコードのバリデーションが通らない場合
User.where(token: nil).find_each do |user| user.token = User.generate_unique_secure_token user.save(validate: false) end蛇足
背景
Ruby on Rails で動いていたプロジェクトがフロントだけ Next.js 等に移行することになって API サーバーとして動かすことになることってよくあるじゃないですか。で、安直にやるとRails に API トークンを発行させて認証っぽいことを実現したりすることってよくあるじゃないですか。
そんな時に Ruby on Rails 5 から追加された
has_secure_token
という素晴らしい ActiveRecord のメソッドがあって、データベースにapi_token
列を追加しておけば、モデル側はこんな感じで1行追加するだけで勝手に新規作成されたユーザーにapi_token
を保存してくれます。class User < ApplicationRecord has_secure_token :api_token end
has_secure_token
コードリーディング
has_secure_token
の実装は実にシンプルです。本体のコードを読みに行くことに抵抗感のある方は是非読みに行ってみてください。記事執筆時点ではたった数行のシンプルなコードです。
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/secure_token.rbdef has_secure_token(attribute = :token, length: MINIMUM_TOKEN_LENGTH) if length < MINIMUM_TOKEN_LENGTH raise MinimumLengthError, "Token requires a minimum length of #{MINIMUM_TOKEN_LENGTH} characters." end # Load securerandom only when has_secure_token is used. require "active_support/core_ext/securerandom" define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) } before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") } end肝はメソッド最後の2行です。軽く読んでみましょう。
has_secure_token
には:api_token
を与えたものとして読んでいきます。引数を見て分かるようにデフォルトは:token
です。
regenerate_api_token
メソッドの定義さて、この
define_method
ではregenerate_api_token
のようなメソッドを新たに定義しています。メソッドの実装内容は、.generate_unique_secure_token
なるSecureRandom.base58
を返すメソッドでトークンを生成して、update!
するというものです。define_method("regenerate_#{attribute}") { update! attribute => self.class.generate_unique_secure_token(length: length) }
before_create
コールバックで値の代入最後の行は、
before_create
コールバックで先ほどと同様に.generate_unique_secure_token
なるSecureRandom.base58
を返すメソッドでトークンを生成して、api_token
に代入してくれています。before_create { send("#{attribute}=", self.class.generate_unique_secure_token(length: length)) unless send("#{attribute}?") }とてもシンプルですね。
本題
さて、ようやくここからが本題ですが、前述の通り
has_secure_token
がapi_token
にセットしてくれるのはbefore_create
コールバックなので、元々動いていたシステムで後からhas_seruce_token
をコールしても元々存在していたユーザーのapi_token
は空のままです。ちょっとしたスクリプトを流して全レコードのapi_token
を埋めることにしましょう。ここで先ほどdefine_method
されていたregenerate_api_token
を使うのが良さそうです。User.where(api_token: nil).find_each(&:regenerate_api_token)しかし、ここでお粗末なシステムを運用していたツケが回ってきます。
regenerate_api_token
は先ほど実装を読んだ通りupdate!
を使ってapi_token
を更新するので、バリデーションが通っていないレコードに対してコールすると例外を投げます。バリデーションが通らないようなレコードがシステムに存在しているのが問題なのは百も承知ですが現実は残酷なもので、今更データを綺麗にするような余裕はありません。バリデーション無効の状態でなんちゃってregenerate_api_token
を実現しましょう。ここで、先ほど軽くコードリーディングしてきた経験が活きてきます。regenerate_api_token
はトークンの生成にself.class.generate_unique_secure_token
というメソッドを使っていることが分かったので、それに倣って一旦代入してsave(validate: false)
すれば ok です。User.where(api_token: nil).find_each do |user| user.api_token = User.generate_unique_secure_token user.save(validate: false) end以上。
- 投稿日:2020-05-21T17:04:43+09:00
Rails 基礎
current_userメソッド
deviseでログイン機能を実装すると使えるようになるメソッド。
idが1のレコードの場合、User.find(1)と同じ意味を持ちます。
current_user.name
などで値を取得できます。ルーティングでユーザーごとに異なるページを表示するには普通とは違う記述方法を使います。
routes.rbget 'users/:id' => 'users#show'whereメソッド
Active Recordの1つ
モデル.where(条件)
で引数にとった条件に一致したレコードのインスタンスを配列型で取得できます。
条件を連続で記述すれば複数の条件にあったレコードを取得できます。Sample.where('id < 3').where('user_id: 1')アソシエーション
モデル間の関係を定義することで、モデルを跨いだデータの呼び出しが簡単になります。
- 投稿日:2020-05-21T16:26:04+09:00
ExecJS::RuntimeErrorというエラー
初の投稿ですので、暖かく見守ってください
昨日の体験したエラーについて投稿します!前提
git cloneしたファイルを「bundle install」→「rails db:create」→「rails db:migrate」→「rails s」をして、google chromeにて確認したら下記のエラーが出ました。
エラー文
ExecJS::RuntimeError上記のエラーが出てきました。
初めてみたエラーだったので絶望しましたが調べてみました.やったこと1
https://qiita.com/timadayon/items/02e16b491c5f32efea80
上記の記事を参考にして
gem 'execjs'をbundle installしてみました。
そもそもgem 'execjs'はrubyからjavaScriptを呼び出すもののようです。
エラー文を見てみると該当するところがstylelinkの場所でした。
できたかと思い更新しましたが、全く変わらなかったです。やったこと2
ターミナルを確認したところ
ActionView::Template::Errorというエラーが書いてあったので調べてみました。
https://muuuuukun.hatenablog.com/entry/2018/12/25/144608
上記の記事を発見しました。
ターミナルでnode -vbrew reinstall nodeをしてみました。
見事解決できました!今回感じたこと
エラーはターミナルも読んでからエラー解決すると良いことに気付きました!
- 投稿日:2020-05-21T16:19:26+09:00
LINEみたくメッセージの投稿日時毎に日付で区切りを付けてみた
初めに
2020年2月からプログラミング学習を始めたばかりの初学者です。
今回が初投稿なので、誤っている点もいくつかあるかと思いますが、暖かく見守っていただけると幸いです。
修正が必要な箇所がありましたら遠慮なくご指摘ください。現在、チャット機能を実装中。
今回はその中で取り入れた機能の一部を備忘録も兼ねて投稿します。LINEのように日付毎に送信したメッセージを区切る方法について
環境
ruby-2.6.3
Rails 5.2.4.1
前提として
投稿内容の自動保存機能とプレビュー機能を実装しているため、created_atによる投稿日時ではなく、updated_atを用いています。
コード
route.rbresources :groups, shallow: true do resources :messages end endcontrollers/groups_controller.rbdef show group = Group.find(params[:id]) @message = Message.new @messages = group.messages.order(updated_at: :ASC).includes(:user, :group) post_dates = @messages.group_by{|post_date| post_date.updated_at.to_date} @first_post_time = [] post_dates.each do |pd| first_pd = pd.flatten[1] @first_post_time << first_pd.updated_at end endgroups/show.html.haml.message_contents = render partial:"messages/message", collection: @messagesmessages/_message.html.haml- if @first_post_time.include?(message.updated_at) == true .post_datetime_area .datetime = message.post_datetime - if user_signed_in? && current_user.id == message.user_id .current_user_messages.messages_container{data: {message: {id: message.id}}} .message_content .message_updated-at = message.updated_at.strftime("%H:%M") .message_text.current_user_message_text = safe_join(message.text.split("\n"), tag(:br)) 以下省略models/message.rbdef post_datetime weeks = ["日","月","火","水","木","金","土"] post_date = self.updated_at.strftime("%Y/%m/%d") post_week = weeks[self.updated_at.wday] if post_date == Time.current.strftime("%Y/%m/%d") return "今日" elsif post_date == (Time.current - 1.days).strftime("%Y/%m/%d") return "昨日" else return post_date + "(#{post_week})" end endgroups_controller.rbでやっていること
@message = Message.new @messages = @group.messages.order(updated_at: :ASC).includes(:user, :group) post_dates = @messages.group_by{|post_date| post_date.updated_at.to_date}メッセージの一覧表示するために定義した@messagesに対してgroup_byメソッドを用いて日付毎にグルーピングしています。
post_dates = [投稿年月日①,[投稿内容1,2,3], 投稿年月日②, [投稿内容4,5]...]
このように多次元配列によって投稿年月日毎に投稿内容が格納されています。
予め昇順(updated_at: :ASC)で並べ替えてあるため、投稿内容も早い順になっています。次に
@first_post_time = [] post_dates.each do |pd| first_pd = pd.flatten[1] @first_post_time << first_pd.updated_at end空の配列を用意し、インスタンス変数に代入。
each文で配列を抜き出した、多次元配列をflattenメソッドで1次元配列とします。
[投稿年月日①,[投稿内容1,2,3]
↓
[投稿年月日①,投稿内容1,2,3]
1次元配列となったため、配列の[1]が投稿内容1に相当し、その日の最初の投稿となります。
その内容からupdated_atを取得し、@first_post_timeに代入していきます。_message.html.hamlでやっていること
messages/_message.html.haml- if @first_post_time.include?(message.updated_at) == trueif文の条件として
groupメソッドで定義した、@first_post_timeにmessage.updated_atが含まれるか否かの判定をinclude?メソッドを用いています。
trueの場合、すなわちその日の最初に投稿したものが、ある場合に
= message.post_datetime
が表示されることになります。
日付の表示方法を定義したpost_datetimeメソッドはmodels/message.rb
に作成しています。今回は区切りの付け方ということなので、その詳細については今回は割愛します。作成の流れは以上になります。
他にもっと簡単な方法やもっとこうした方が良いなどありましたらご教授いただけると幸いです。
よろしくお願いいたします。参考サイト
- 投稿日:2020-05-21T16:08:37+09:00
RSpecとFactoryBotを利用してRailsアプリの単体テストを実施してみた
概要
ポートフォリオとして作成した個人開発アプリのテストを実施したので
個人的なアウトプットとしてテストの流れを振り返りたいと思います。
今回はモデルのバリデーションに関するテストの基礎的な内容になります。前提
プログラミングにおけるテストとは、「プログラムが意図した通りに動くことを確かめる」ことを指します。
環境
Ruby 2.5.1
Rails 5.2.3gem 'devise'を使用してユーザーモデル作成済み
はじめに
まずはテストに使用するGemをインストールしていきます。
今回使用するGem
- rspec-rails ▶︎RSpecというテストに特化した言語のRails用gem
- factory_bot ▶︎簡単にダミーのインスタンスを作成できるgem
Gemfilegroup :development, :test do 省略 gem 'factory_bot_rails' gem 'rspec-rails' endターミナル$ bundle installこれでgemのインストールが完了。
RSpecの設定をする
まずはRSpec用のファイルを生成する必要があるので下記のコマンドを実行する。
ターミナル$ rails g rspec:installこれで下記のファイルが生成される。
ターミナルcreate .rspec create spec create spec/spec_helper.rb create spec/rails_helper.rb.rspecに以下の記述を追加する
.rspec--format documentation
ここまでくれば下記のコマンドでテストは実行できます。ターミナル$ bundle exec rspecモデルクラスのテストコードを書く
いよいよテストコードを書いていくわけですが
テストコードを書くspecファイルは途中で生成されたspecという名前のディレクトリに配置します。
また、モデルのspecファイルはモデル用のディレクトリにまとめておくため、テキストエディタでディレクトリとファイルを作成していきましょう。
- specディレクトリの配下にmodelsというディレクトリを作成する。
- spec/modelsディレクトリの中にモデル用specファイルを作成する。
※specファイルの命名規則はspecファイルは、対応するクラス名_spec.rbという名前になります。
今回はUserモデルのバリデーションに関するテストを実施するという想定で進めていくのでuser_spec.rbを作成します。
まずは新規ユーザー登録時の各バリデーションが適用されるか、一つずつテストしていきます。
今回、Userモデルのバリデーションは以下の条件です。app/models/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable validates :nickname, :email, :password, :password_confirmation, presence: true endvalidatesのpresence: trueが適用されている4カラムをテストしていきます。
まずは基本となる記述でnicknameのバリデーションをテストします。
spec/models/user_spec.rbrequire 'rails_helper' describe User do describe '#create' do it "nicknameがない場合は登録できないこと" do user = User.new(nickname: "", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567") user.valid? expect(user.errors[:nickname]).to include("can't be blank") end end end1行目はテストする上で読み込むファイルを記載。
2行目はモデルクラスを記載。
3行目はテストするアクション名を記載。4~8行目のitからendの括りが一つあたりのテストコードになります。
4行目はテストする内容を記載。
5行目はインスタンスの作成を記載するのですが、nicknameが空という事以外はリアルな想定で記載。
6行目は作成したインスタンスに大してvalid?メソッドを記載すると、ActiveRecord::Baseを継承しているクラスのインスタンスを保存する際に「バリデーションにより保存ができない状態であるか」を確かめることができます。
7行目は6行目の内容に対して予想されるテスト結果を記載します。
valid?メソッドの返り値はtrue or falseですが、valid?メソッドを利用したインスタンスに対してerrorsメソッドを利用すると、バリデーションにより保存ができない状態である場合なぜできないのかを確認することができます。
今回の場合、to以下のincludeマッチャを利用して「"can't be blank"というエラーメッセージが出るだろう」という予想をします。ザックリ日本語で言うと
「ユーザーのニックネームが空だから、"空にはできませんよ"というエラーが返ってくるだろうと予想を書く」
といった感じです一旦ここでテストを実行してみましょう
ターミナル$ bundle exec rspec 1 example, 0 failures上の表示のように、"1つのテストに対して失敗が0"というメッセージが表示されればOKです。
あとは他のバリデーションもテストしていくのですが、
最初に導入したfactory_botを使用することでuserインスタンス作成部分のテストコードを共通化できるので、かなり便利です。factory_botを使用する
まずはspecディレクトリ配下にfactoriesというディレクトリを作成しましょう。
その中にusers.rbというファイルを作成します。
※ファイルの命名はモデル名複数形で統一そして空のファイルにモデルのインスタンス作成で共通となる部分を記述していきます。
spec/factories/users.rbFactoryBot.define do factory :user do nickname {"田中太郎"} email {"test-account@gmail.com"} password {"1234567"} password_confirmation {"1234567"} end end特に説明することもありませんが
テストコードでユーザーモデルのインスタンスを作成すると記載した内容で毎回実行してくれます。#これが user = User.new(nickname: "田中太郎", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567") #これで実現できる user = FactoryBot.build(:user)なんとさらにこのコードを簡略化できます。
user_spec.rbの中に'rails_helper'を読み込む記述がしてありますが、
このrails_helper.rbファイルの中に少し手を加えます。spec/rails_helper.rb#省略 RSpec.configure do |config| #下記の記述を追加 config.include FactoryBot::Syntax::Methods #省略 endこれで準備は完了です。
そうするとですね...。#これが user = User.new(nickname: "田中太郎", email: "test-account@gmail.com", password: "1234567", password_confirmation: "1234567") #これで実現できる user = FactoryBot.build(:user) #さらに省略できる user = build(:user)かなりコード量が減りますね...。
自分は最初これを知ったとき、全部のカラムに値を入れてるけど、バリデーションのテストする上で空のカラムとか再現できるの?
という疑問がありました➡︎問題なくできます残りのテストコードで見ていきましょう。
spec/models/user_spec.rbrequire 'rails_helper' describe User do describe '#create' do it " nicknameがない場合は登録できないこと" do user = build(:user, nickname: "") user.valid? expect(user.errors[:nickname]).to include("can't be blank") end it "emailがない場合は登録できないこと" do user = build(:user, email: "") user.valid? expect(user.errors[:email]).to include("can't be blank") end it "passwordがない場合は登録できないこと" do user = build(:user, password: "") user.valid? expect(user.errors[:password]).to include("can't be blank") end it "passwordが存在してもpassword_confirmationがない場合は登録できないこと" do user = build(:user, password_confirmation: "") user.valid? expect(user.errors[:password_confirmation]).to include("doesn't match Password") end it " passwordが5文字以下であれば登録できないこと " do user = build(:user, password: "00000", password_confirmation: "00000") user.valid? expect(user.errors[:password]).to include("is too short (minimum is 6 characters)") end #登録ができる場合のテストも実施 it "nicknameとemail、passwordとpassword_confirmationが存在すれば登録できること" do user = build(:user) expect(user).to be_valid end it " passwordが6文字以上であれば登録できること " do user = build(:user, password: "000000", password_confirmation: "000000") user.valid? expect(user).to be_valid end end end上記の解説です
user = build(:user) user = build(:user, nickname: "")2行目のように再度カラム名と値を指定することで
事前にセットした値を上書きできます。
この例の場合はnickname: "田中一郎" をnickname: "" に上書きしています。
こうすることで事前にセットした値を柔軟に変えることができます(nilでも可)
it "passwordが存在してもpassword_confirmationがない場合は登録できないこと" do user = build(:user, password_confirmation: "") user.valid? expect(user.errors[:password_confirmation]).to include("doesn't match Password") endまた、上記のincludeマッチャ部分ですが
"can't be blank"もそうだけど
"doesn't match Password"っていうエラーメッセージは
どこから来たの?
それっぽいことを書こうとは思うんだけど
最初からわからないとエラーメッセージの予想とかできなくね...?そう思っていた時期が私にもありました(注:まだ初学者です)
これらのエラーメッセージはRailsのGemで元々用意されているモノですが
極論、これでもテストは実行できます.to include("")そうすると、テストの実行結果がターミナルに表示されるわけですが
あなたは、""というエラーメッセージが出ると予想していたけど実際出たエラーメッセージは"doesn't match Password"だったわよ!
と返ってきます。
そうすると、
ああ!そうそう!ワイがテストで書きたかったのはそういうことなんや!
と、わかるわけですねコンソールを開いて流れを検証して確認するということもできますが
初学者レベルで個人開発アプリのテスト量が少ない場合はこの方法で十分な気がします。
(怒られそう)また、最後の2つのテストはユーザー登録ができる場合のテストをしているわけですが
be_validマッチャというのが出てきます。
これは"全てのバリデーションをクリアするだろう"という場合に使用します。最後にテストを実行してみて
ターミナル$ bundle exec rspec 7 example, 0 failuresテストの数に対して失敗が0ならOKです。
まとめ
RSpecとFactoryBotを利用して、Railsの簡単なモデルテストを振り返ってみました。
今回実施したテストはほんの一部ですが、基本的な流れと小ネタを書いたので学習初めてまもない方の一助となりましたら幸いです。
また、記載している内容で不備等ございましたら教えていただけると助かります。
最後に私の学習中に参考にさせていただいたQiita記事を記載しておきます。
ありがとうございました。参考記事
- 投稿日:2020-05-21T14:42:01+09:00
Ruby on rails を初心者向けに解説④ ~命名規則とform_Tagの使い方について~
はじめに
今回は前回の記事の続きになります。
よろしければ以前の記事も御覧ください。
Ruby on rails を初心者向けに解説①
Ruby on rails を初心者向けに解説② ~リンクの作成~
Ruby on rails を初心者向けに解説③ ~データベースの作成~Railsの命名規則について
ここまで、コントローラーやモデルを作成してきました。
それらには、命名規則があります。学んでいきましょう。
Modelの命名規則
モデルとは、データベースを作成するための設計図です。
設計図は一つであるため、モデルクラス名は
単数形
で作成します。テーブルは、そのモデルのデータが複数あるため、自動的に
複数形
で表されます。前回の記事で
user
という名前でモデルを作成しました。そうすると、
users
という名前でテーブルが作成されます。また、クラス名は
User
というふうに、最初が大文字になります。Viewの命名規則
Viewフォルダは配下に複数のファイルを持つため、
複数形
になります。Controllerの命名規則
Controllerは複数のアクションを持つため、
複数形
で作成します。データベースにデータを登録
以前の記事では、railsのコンソールを使用してデータベースにデータを登録しました。
今回は、ブラウザをユーザーに操作してもらうことでデータベースにデータを登録しましょう。
その前に、一度データベースの確認を行います。
ターミナルに以下のコマンドを打ち込みましょう。
rails dbconsole次のコードでカラム名の表示をONにできます。
sqlite> .headers onこの状態で、次のSQLの文を書くと中身を確認することができます。
sqlite> select * from users; id|name|password|created_at|updated_at 1|poco|maru|2020-05-20 10:50:13.177731|2020-05-20 10:50:13.177731現在は、usersテーブルにnameカラムとpaswordカラムが格納されていることが分かりますね。
それでは、入力フォームを作成してユーザーから送られてくるデータをデータベースに格納してみましょう。
/users/new.html.erbファイルに以下のコードを書いてください。
new.html.erb<%= form_tag("/users/create") do %> <p>ユーザー名</p> <input name="name"> <p>password</p> <input name="password"> <input type="submit" value="送信"> <% end %>
form_tag
は、viewファイルからコントローラーに何か値を送ったり、削除したりするときに使います。いわゆるPOSTリクエスト
と呼ばれるものです。getリクエストとpostリクエストの違いについては、こちらの記事を参考にしてください。
type="submit"
となっているボタンがユーザーから送られると、users/create
に対応するpostリクエストが実行されます。今回は、以下のようにルーティングしています。
routes.rbpost "users/create" => "users#create"つまり、上記のデータはusersコントローラーのcreateアクションに送信されます。createアクションでは以下のコードを書きます。
users_controller.rbdef create user = User.new(name: params[:name], password: params[:password]) user.save endこのコードで、userモデルのUserクラスを用いて、データベースのusersテーブルにデータを格納しています。
form_tagで送られてきたデータにおいて、name属性が指定されているタグが存在した場合、コントローラー内において
params[name属性]
として値を扱うことができます。この場合、
<input name="name">
内の値がparams[:name]
に、<input name="password">
の値がparams[:password]に格納されます。その送られてきたデータに対して
User.new(name: params[:name], password: params[:password])
を用いることでモデルを作成し、データベースに格納しています。ユーザーからは以下のようになっています。
この状態で
送信
を押してみましょう。すると、usersコントローラーのcreateアクション内において、thisという文字列がparams[:name]に格納され、testという文字列がparams[:password]に格納されます。以下のようにデータを格納することができました。
3|this|test|2020-05-21 05:30:36.718523|2020-05-21 05:30:36.718523
しかし、このままだと送信を押しても特に変化はありません。退屈なので、他のファイルにリダイレクトさせましょう。
送信を押した場合にリダイレクト
送信を押すとusersコントローラーのcreateアクションが実行されるので、このcreateアクション内にリダイレクトのコードを書けば、他のファイルにリダイレクトされるようになります。
usersコントローラーに以下のようにコードを追記しましょう。
users_controller.rbdef create user = User.new(name: params[:name], password: params[:password]) user.save redirect_to("/users/index") endこのように書くと、送信を押した場合に
/users/index
に対応するファイルにリダイレクトされます。ちなみに、リダイレクトを書く場合は、パスの書き始めに
/
を入れることを忘れないようにしてください。終わりに
今回の記事はここまでになります。
お疲れさまでした。
- 投稿日:2020-05-21T11:56:47+09:00
Rails フォロー機能を非同期で実装 js.hamlを使用
なにこれ
フォロー機能を実装できたので、備忘録と言語化するために書いてます。
間違いがありましたら、ご指摘いただけると大変ありがたいです!コツ
current_user.idと@user.idの違いを理解すること
前提条件
UserモデルとUsersテーブルを作成済み
データベース設計
フォローしたデータを保存するためのrelationshipsテーブルを作成します
コントローラーも作成しておきます。ターミナルrails g model Relationship rails g controller Relationships/db/migrate/作成日時_create_relationships.rbt.references :follower, foreign_key: { to_table: :users } t.references :following, foreign_key: { to_table: :users } t.index [:follower_id, :following_id], unique: truefollower_idがフォローされたユーザーのidにします。
follower_idがフォローしたユーザーのidにします。
t.index [:follower_id, :following_id], unique: true
は、
follower_idとfollowing_idが一意の関係になるように設定してます。被らないように。
つまり、同じユーザーを何回もフォローできたらおかしいので!
終わったらrails db:migrate
します。モデルの編集
Userモデル
/model/user.rbhas_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship" has_many :following, through: :following_relationships has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship" has_many :followers, through: :follower_relationships #フォローしているかを確認するメソッド def following?(user) following_relationships.find_by(following_id: user.id) end #フォローするときのメソッド def follow(user) following_relationships.create!(following_id: user.id) end #フォローを外すときのメソッド def unfollow(user) following_relationships.find_by(following_id: user.id).destroy enduserモデルのアソシエーションがフォロー機能を実装する上で一番の山場だと思います。
フォロー機能を実装するための様々なモデルの組み合わせを調べましたが、
個人的にこれが一番シンプルかつ直感的に分かりやすいと感じたものを使用します。
has_many :following
という存在しないモデルとアソシエーションを組んでますが、
これは「仮想のモデルとアソシエーションを組んでいる状態」と考えて下さい。
仮想のモデルを作成して、そちらとアソシエーションを組んでます。followingとfollowerに注目した方がいいです。
日本語的に見るとfollowとfollowingの表現がおかしいところがありますが、これで正しいです。
ご自身で紙に図で書いてみると理解しやすいと思います。
フォローしているユーザーを調べたい!と思ったら、
そのユーザーのフォロワー(follower_id)を探してくる。みたいなイメージです逆にフォロワーのユーザーを調べたい!と思ったら、
そのユーザーをフォローしているユーザー(following_id)を取ってくる。イメージです。?following_relationships
class_name: “Relationship”
モデルになってるので、そちらを参照してます。
foreign_key: “follower_id”
にすることで、カラムにfollower_idが追加されます。
つまり、実際にカラムになるのは、Relationshipモデルのfollower_idです。申し訳ないですが、この関係を言語化するのは自分には無理なので紙に書いてイメージして下さい。正直、文章書いてる途中でもゴチャ混ぜになってきます。笑
Relationshipモデルのfollower_idを経由して、
Userモデルと仮想のfollowing_relationshipsモデルと
アソシエーションを組んでます。
つまり、following_relationshipsモデルにはRelationshipモデルのfollower_idが
外部キーとして存在してる。みたいなイメージです?following
こいつがやってることは、
フォローしているユーザー達の情報を持ってくるためのアソシエーションです。
through: :following_relationships
としてあるので、
仮想モデルのfollowing_relationshipsの情報をガバーっと持ってくるイメージです。使用例)
/users/show.html.haml@user.following.each do |user| user.nickname上記のコードを書くと、@userがフォローしている全てユーザーのidを表示できます。
別の具体例)
URL: /users/1/
@userにid=1というユーザーの情報が代入されてます。
user_id[1]が、user_id[2]とuser_id[3]をフォローしているとします。
上記の使用例で例えると、
eachを実行するとuser_id[2]とuser_id[3]の情報がeach分で出力できます。?follower_relationships
外部キーをfollowing_idとして、Relationshipモデルを参照しています。
follower_relationshipsにはfollowing_idのカラム(情報)が追加されてます。
Relationshipモデルのfollowing_idを経由してUserモデルとアソシエーションを組みます。?followers
こいつがやってることはfollowingのフォロワーバージョンです。
follower_relationshipsを通じて、そのユーザーのフォロワーを探してきてくれます。使用例)
/users/show.html.haml@user.follower.each do |user| user.nickname@userにはuser_id[1]が代入されてます。
user_id[1]はuser_id[4]とuser_id[5]にフォローされてます。
上記のeach文を実行すると、user_id[4]とuser_id[5]の情報が出力されます。?def following?
端的に言うと、following_relationshipsテーブルのfollowing_idにuserのidが存在するか確認しています。すごく長く説明します。
ruby: /users/show.html.haml
- if user_signed_in? && @user != current_user
#follow_form
- if current_user.following?(@user)
上記の例ですと、3行目でcurrent_userがそのユーザーをフォローしているか判別しています。
この時のDBの流れは、current_user(follower_idに当たる。)が、
following_idを探してくる。と言った流れです。
1行目でcurrent_user.idが@user.idと一致してないのを確認済みです。例)
current_user.id[3]の場合だと、現在/users/2/のページを表示しているので、
変数@userにはuser_id[2]が代入されています。
Relationshipテーブルには、follower_idに[3]が、following_idには[2]が代入されます。
一致する条件があるならtrueを返します。みたいなイメージです。その先は既にフォローしているならフォローを外すリンクを表示、フォローしてないなら
フォローするリンクを表示します。以下は頑張って言語化しただけなので、読み飛ばしてokです。
仮想モデルのfollowing_relationshipsから、find_byでfollowing_idを基準にfollower_idを探します。
別の言い方をすると、follower_idに対応するfollowing_id
(Relationshipモデルの同じidのレコード)を探してきます。
理由は、仮想モデルのfollowing_relationshipsの内容(カラム)は、実際はRelationshipsモデルのfollower_idだからです。?follow
following?メソッドのidを作る版です。
(user)には現在表示しているページのユーザーが代入されてます。
following_idに@user.id(表示しているページのuser_id)が入ります。
follower_idにはcurrent_user.idが入ります。?unfollow
following?の条件で、following_id(user.id)とfollower_id(current_user.id)の
一致する組み合わせががあったらdestroyしてフォローを解除する流れです。Relationshipモデル
#自分をフォローしているユーザー belongs_to :follower, class_name: "User" #自分がフォローしているユーザー belongs_to :following, class_name: "User" #バリデーション validates :follower_id, presence: true validates :following_id, presence: true
belongs_to :followerは
has_many :followers
Userモデルに記述したに該当します。
belongs-to :following
同じくも
has_many :following
Userモデルに記述したに該当します。
class_name “User”`にすることで存在しないfollowerモデルを参照することを防いでいます。
ルーティングの作成
routes.rbRails.application.routes.draw do resources :users do member do get :following, :followers end end resources :relationships, only: [:create, :destroy] endフォローする(create)とフォローを解除する(destroy)をアクションを追加しました。
get :following, :followers
はフォローしてる人とフォロワーを一覧で表示するのに使えます。
この記事ではやり方は紹介しませんが、気になる人は実装してみて下さい。relationships_controllerの編集
relationships_controller.rbclass RelationshipsController < ApplicationController def create @user = User.find(params[:following_id]) current_user.follow(@user) end def destroy @user = User.find(params[:id]) current_user.unfollow(@user) end endcreateアクションではparamsでfollowing_idを探してきて@userに代入しています。
ここで言うfollowing_idに当たるものは、
usersのshowアクションで表示しているユーザーページのuser_idです。
例)/users/1/ なら、user_id[1]destroyもcreateアクションとやってることは同じです。
rails routesで確認すると、destroyアクションは以下のようになってます。relationship DELETE /relationships/:id(.:format)paramsが[:id]になってる理由は、
後述するフォローを解除するリンクでfollower_id(current_user.id)が代入されている状態なのと、
既にfollowing_idに値が入っているので、params[:id]でクリックした値を代入するだけでokっていう認識です。
ここは曖昧なので、もしかしたら違うかも知れません。ビューの作成
必要部分のみ抜粋して表示します。
/users/show.html.haml= render "relationships/follow"renderで部分テンプレートを表示させてます。
@userに値が入ってるならお好きな部分に設置してもらってokです/relationships/_follow.html.haml- if user_signed_in? && @user.id != current_user.id #follow_form - if current_user.following?(@user) = form_with(model: current_user, url: relationship_path(@user.id), method: :delete, remote: true) do |f| = f.submit "フォロー解除", class: "btn btn-outline-secondary" - else = form_with(model: current_user, url: relationships_path, method: :post, remote: true) do |f| = hidden_field_tag :following_id, @user.id = f.submit "フォローする", class: "btn btn-outline-secondary"rails_routesrelationship DELETE /relationships/:id(.:format) relationships#destroy relationships POST /relationships(.:format) relationships#createここはしっかり解説します。
1行目でユーザーがサインインしている、
かつ@user.idとcurrent_userのidが違うことを確認してます。
3行目でcurrent_userが@userをフォローしているか確認しています。
繰り返しですが、
この@userに代入されているのはuser_id(URLの/users/1/
の数字の部分)です。
4行目でform_withでフォロー解除ボタンを表示しています。
model: current_user
でfollower_idにcurrent_user.idが代入されていることを表しています。
url: relationship_path(@user.id)
とはrelations#destroyを発火させるパスを指定します。
つまりフォロー解除ボタンです。@user.idは表示しているページのuser_idです。
urlと書いてある理由はデータベースに保存するためか、引数が2つあるからです。
リファレンスを見ても参考になるところが分からなかったので、憶測です。
違ったらすみません!
remote: true
でjs形式でデータを送信してます。
f.submit
でフォームを送信します。下から3行目でフォローするボタンを表示しています。
model: current_user
は先程と同じくfollower_idにcurrent_user.idを代入するためです。urlでrelationships#createアクションを発火させるパスを書いてます。
先ほどと一点違う点があります。
= hidden_field_tag :following_id, @user.id
この文が存在する理由です。
following_idに@user.idを追加させるためです。
この文がないと、フォローしようとしてもデータベースに
「誰をフォローすんの?」って分からなくて怒られます。余談ですが、自分はform_withでmodelを指定する理由と、
:following_id, @user.id
が書いてある理由が分からなくて悩みました笑これでようやく最後です!
createとdestroyのjs.hamlファイルを作成します。
js.hamlファイルについて理解されている前提で進めます。
端的に言うと、アクション発火時に自動的に実行されるJavascriptです。relationships/create.js.haml$("#follow_form").html("#{j(render("relationships/follow"))}");relationships/destroy.js.haml$("#follow_form").html("#{j(render("relationships/follow"))}");内容はどちらも同じです。
follow_formにhtml形式でrenderします。
これで非同期通信ができます。以上で実装完了です!お疲れ様です。
ここまで読んでいただき、ありがとうございます!
- 投稿日:2020-05-21T11:30:26+09:00
【Ruby/Rails】Rubocopでupdate_allのLintを非活性にする
rubocop.ymlRails/SkipsModelValidations: Whitelist: - update_all参考:https://github.com/ManageIQ/manageiq-schema/pull/441/files
- 投稿日:2020-05-21T10:56:12+09:00
STIについて、少し理解できた人が記事を書いてみた
はじめに
Rails最近頑張ってます。
ここ知らなかったってところや自分的に知識がまだ浅いなと思う部分をQiitaにまとめていきたいと思います!
まだまだな部分が多いですが、すこしでも皆様の理解の助けになれたら、嬉しいです。STIって?
単一テーブル継承といって、一つのテーブル(親にあたるもの)から継承して、新しいテーブル(子にあたるもの)を作りましょう!!みたいな考え方です。親のテーブルに含まれるカラムなども引き継がれるので、同じ部分を子クラスで定義しなくてよくなるので、手間も減るし、わかりやすくなるので、利点が多いですね!子クラスで、カラムを新たに追加することもできるので、割と簡単に拡張?もできます。
どんな時に、使うと便利??
似たような(ほぼ同じような機能を持つ)モデルがたくさんあるときに、1つ1つに対して、カラムの定義などを行わなくてよくなるので、モデルがたくさんあるときに、使うと、便利かもしれません。
構造的にどうなってるの?
こんな感じです。1つのクラスを継承して、子クラスを生成します。子クラスでは、
親クラスで持っているもの+自クラスで定義したもの
を持つことができます。作り方について
まず、親クラスの生成をします。
bundle exec rails g model Tweet
を実行して、マイグレーションファイルの中に、クラスに持たせる要素などの値を書き込んで、
bundle exec rails db:migrate
を実行します。子クラスについては、モデルファイルのおいてあるフォルダに
モデル名.rb
で新しくファイルを新規作成して、親を継承するように設定します。親クラス.rbclass Tweet < ApplicationRecord validates :content, length: { maximum: 140 } belongs_to :user has_many :retweets belongs_to :reply, foreign_key: :reply_id, class_name: 'Tweet' ,optional: true has_many :replies, foreign_key: :reply_id, class_name: 'Tweet' has_many :likes end子クラス.rbclass TweetImage < Tweet has_one :image, foreign_key: :tweet_id accepts_nested_attributes_for :image, allow_destroy: true end※クラス名のあとの部分に気をつけましょう!!
親クラスはApplicationRecord
になっていますが、子クラスの方は、モデルを継承して作るので、親クラスのモデル名
を入れなければ、なりません。さいごに
最後まで、読んでいただきありがとうございます。
まだまだ、説明下手ですが少しでもみなさんに伝わりやすい記事をかけるように、努力していきたいと思います!
- 投稿日:2020-05-21T10:46:21+09:00
Ruby on rails を初心者向けに解説③ ~データベースの作成~
はじめに
今回は、以前の記事の続きになっています。
よろしければ以前の記事も御覧ください。
Ruby on rails を初心者向けに解説② ~リンクの作成~
データベースとは
データベースとは、データを保存しておく場所のことです。
Ruby on Railsにおいては、コントローラーからモデルを作成することによりデータベースを操作することができます。
コントローラーの作成
今回は、ユーザーのIDとパスワードを保存するデータベースを作成することを考えます。
データベースを作成する前に、一度コントローラーとアクションを作成してみましょう。
ターミナルで以下のコードを実行してください。
rails g controller users indexこれでuserコントローラーを作成し、その中にindexアクションを追加することができました。rails g コマンドにおいて、コントローラー名とアクション名の2つを設定することができます。
以下のようにルーティングしてください。
routes.rbget 'users/index' => "users#index"これで、ユーザーから
user/index
というURLが送られてきたときに、userコントローラーのindexアクションを行うことができるようになりました。userコントローラーを確認してみましょう。今回は、最初からアクションが追加されています。
users_controller.rbclass UsersController < ApplicationController def index end end実はviewファイルも自動で生成されています。
以下のようにindex.html.erbファイルを書きましょう。
index.html.erb<h1>Users#index</h1> <p>Find me in app/views/users/index.html.erb</p>以下のURLでこのファイルにアクセスしましょう。
http://localhost:3000/users/index以下の画面がでてきます。
アクションから変数をviewファイルに渡す
コントローラーがviewファイルを探してきて、それをユーザーに返します。
その時、コントローラーの中のアクションに変数を定義することで、その変数をviewファイル内で用いることができます。
以下のように変数を定義しましょう。
users_controller.rbclass UsersController < ApplicationController def index @users = ["maru", "poco"] end end@userのように、変数の前に@を用いることでその変数はインスタンス変数になります。こちらに解説がありました。
このように、コントローラーからviewファイルに変数を渡すときは、ローカル変数ではなくインスタンス変数を用います。
Railsのコントローラでインスタンス変数を使用するのは、以下の場合です。
・メソッド間でのデータの受け渡し(典型的には、before_actionでデータをロードしておくとか)
・ビューへのデータの受け渡しこのように、@変数で作成したインスタンス変数はviewファイル内で利用することができます。
index.html.erb<% @users.each do |user| %> <div> <%= user %> </div> <% end %>@usersは配列が格納されているので、.each do ~ end で取り出すことができます。この部分は全てRubyのコードなので、<%%>ではさみましょう。ブラウザに表示する必要がないので、<%=%> ではなく <%%>ではさむところがポイントです。
<%= user %>
の部分で、ブラウザに表示させます。今回は、コントローラーのアクション内で配列を定義して、それをviewファイルに渡すことで利用しました。
今度は、データベースからデータをアクション内に持ってきて、それをviewファイルに渡すことを考えてみましょう。
モデルの作成
データベースを操作するためには、モデルを作成する必要があります。
モデルとは、
データベースの情報を操作する仕組み
のことです。または、データベースとのやり取りを行うクラス
とも言うことができます。以下のコードでモデルと。マイグレーションファイルを作成します。
rails g model user name:string password:stringUserがモデル名で、id、passwordがカラムになります。カラムとは、データベースの縦のデータ、つまり列のことです。
このコードで作成したデータベースは、以下のような表になっています。
この表全体を
テーブル
と呼び、縦のデータをカラム
、横のデータをレコード
と呼びます。モデルとは、このデータベースに対応するRubyのクラスであり、モデルクラスのインスタンスは一つの行(レコード)を表すオブジェクトであり、テーブルの列(カラム)に対応する属性をもちます。上記のコードを実行すると、データベースを作成するためのmigrationファイルと、モデルが作成されます。
class CreateUsers < ActiveRecord::Migration[5.2] def change create_table :users do |t| t.string :name t.string :password t.timestamps end end enduser.rbclass User < ApplicationRecord end上記の作成したマイグレーションファイル以下のコードで取り込むことで、データベースを作成することができます。
rails db:migrateここまでで、データベースを作成することができました。
それでは、マイグレーションファイルとは何なのかについてまとめていきましょう。
マイグレーションファイルとは
マイグレーションファイルとは、データベースを生成する際の設計図のようなものです。
マイグレーションファイルを実行することで、その内容に基づいたデータテーブルを作成することができます。
railsでは、
rails g model
コマンドを実行すると、マイグレーションファイルとモデルの両方が生成されます。マイグレーションファイルはデータベースの枠組みを作成するための道具で、モデルはデータベースとコントローラーを橋渡しする道具のようなものだと覚えておいてください。
データをデータベースに保存
以下のコードでRailsのコンソールを起動しましょう。
rails consoleコンソールを起動したら、モデルを使ってデータベースにデータを保存してみましょう。
user = User.new(name: "poco", password: "maru") user.saveこれでデータベースにデータを保存することができました。
データベースの確認
それでは作成したデータベースを確認していきましょう。
データベースクライアントの起動
以下のコードでデータベースクライアントを起動できます。
rails dbconsoleテーブルの一覧表示
データベースクライアントを起動した後は、以下のコードでテーブル一覧を表示することができます。
sqlite>.tablear_internal_metadata schema_migrations users
スキーマの表示
以下のコードでスキーマを表示することができます。スキーマとは、構造という意味です。指定したデータベースの構造を確認することができます。
sqlite>.schema usersCREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "password" varchar, "created_at" datetime NOT NULL, "updated_at" datetime NOT NULL);
データベースクライアントの停止
以下のコードでデータベースクライアントを停止することができます。
sqlite>.quitここまでで、データベースを作成することができました。
それでは、実際にデータベースを利用してみましょう。
データベースの利用
データベースには、モデルを用いてアクセスします。
データベースを利用する際は、コントローラーからモデルを用いてアクセスします。
users_controller.rbclass UsersController < ApplicationController def index @user = User.first end endこれで、index.html.erbファイル内でインスタンス変数@userを使用することができるようになりました。
@userには、usersテーブルの一番最初の列のレコードが格納されています。
以下のように、index.html.erbファイル内で使用します。
index.html.erb<p><%= @user.name %></p> <p><%= @user.password %></p>終わりに
今回はここまでになります。
ここまでお付き合い頂きありがとうございました。
- 投稿日:2020-05-21T08:35:29+09:00
RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleしてみよう
困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い
RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。実際のスピードについては以下のツイートに載せた動画を参考にしてください。
@yusuke RubyMine 2020.1から日本語入力が超遅いです。この記事( https://t.co/JEwYPo19He )を見ながらJBR8を設定しましたが改善されませんでした。全速力でタイピングしてこれぐらいの表示スピードです。実際に1行目の半分ぐらいでタイピングはすべて終わってます。他に何か原因は考えられますか? pic.twitter.com/fGoRHAlR3p
— Junichi Ito (伊藤淳一) (@jnchito) May 10, 2020問題が発生した実行環境
僕の実行環境は以下のとおりです。
- RubyMine 2020.1.1
- macOS 10.15.4
- MacBook Pro (13-inch, 2017)
- Memory 16GB
RubyMineが遅い原因
RubyMineが遅い理由はどうやら4Kディスプレイにあるようです。
僕は普段Mac本体のディスプレイではなく、USB-Cで接続した4Kディスプレイ(EIZO EV2785)を使っているのですが、RubyMineのウインドウを4Kディスプレイで表示したときだけ、この現象が発生しました。
Mac本体のディスプレイに表示したときは、必要十分なスピードで入力できました。同じような現象はIntelliJ IDEAやPhpStorm、PyCharmといった他のJetBrains製IDEでも発生しているようです。
解決策(というか、軽減策)
下記の情報を参考にして、IDEとEditorのアンチエイリアス設定をSubpixelからGreyscaleに変更すると、2019.3とほぼ同等の入力スピードに戻すことができました。
Maybe it will help someone: the integrated terminal was extremely slow, I was able to fix the performance issue by changing the Antialiasing to Greyscale for both the editor and IDE.
https://youtrack.jetbrains.com/issue/JBR-526#focus=streamItem-27-4144635.0-0
アンチエイリアス設定はPreferences > Appearance & Behavior > Appearanceで変更できます。
こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。
また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。
できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。
参考:あまり効果がなかった対策
JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。
ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。
情報募集!
その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?
謝辞
本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!
- 投稿日:2020-05-21T08:35:29+09:00
RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleにしてみよう
困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い
RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。実際のスピードについては以下のツイートに載せた動画を参考にしてください。
@yusuke RubyMine 2020.1から日本語入力が超遅いです。この記事( https://t.co/JEwYPo19He )を見ながらJBR8を設定しましたが改善されませんでした。全速力でタイピングしてこれぐらいの表示スピードです。実際に1行目の半分ぐらいでタイピングはすべて終わってます。他に何か原因は考えられますか? pic.twitter.com/fGoRHAlR3p
— Junichi Ito (伊藤淳一) (@jnchito) May 10, 2020問題が発生した実行環境
僕の実行環境は以下のとおりです。
- RubyMine 2020.1.1
- macOS 10.15.4
- MacBook Pro (13-inch, 2017)
- Memory 16GB
RubyMineが遅い原因
RubyMineが遅い理由はどうやら4Kディスプレイにあるようです。
僕は普段Mac本体のディスプレイではなく、USB-Cで接続した4Kディスプレイ(EIZO EV2785)を使っているのですが、RubyMineのウインドウを4Kディスプレイで表示したときだけ、この現象が発生しました。
Mac本体のディスプレイに表示したときは、必要十分なスピードで入力できました。同じような現象はIntelliJ IDEAやPhpStorm、PyCharmといった他のJetBrains製IDEでも発生しているようです。
解決策(というか、軽減策)
下記の情報を参考にして、IDEとEditorのアンチエイリアス設定をSubpixelからGreyscaleに変更すると、2019.3とほぼ同等の入力スピードに戻すことができました。
Maybe it will help someone: the integrated terminal was extremely slow, I was able to fix the performance issue by changing the Antialiasing to Greyscale for both the editor and IDE.
https://youtrack.jetbrains.com/issue/JBR-526#focus=streamItem-27-4144635.0-0
アンチエイリアス設定はPreferences > Appearance & Behavior > Appearanceで変更できます。
こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。
また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。
できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。
参考:あまり効果がなかった対策
JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。
ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。
情報募集!
その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?
謝辞
本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!
- 投稿日:2020-05-21T01:37:02+09:00
[Rails]最低限の通知機能
最低限の通知機能の実装
自分の投稿にコメントがついた時、通知が来るようにします。初心者なりに考えて書いてみましたがこれでええんかな、、、備忘録も兼ねて。ゆくゆくはいいね/DMが来た時も通知が来るようにしたいです。
ここ間違ってる!とかコメントいただけたら喜びます。db/schema.rbcreate_table "notifications", force: :cascade do |t| t.integer "post_id" t.integer "user_id" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false end create_table "posts", force: :cascade do |t| t.text "body" t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.integer "user_id" end create_table "comments", force: :cascade do |t| t.string "content" t.integer "user_id", null: false t.integer "post_id", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.index ["post_id"], name: "index_comments_on_post_id" t.index ["user_id"], name: "index_comments_on_user_id" end create_table "users", force: :cascade do |t| t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false t.string "username" endapp/models/notification.rbclass Notification < ApplicationRecord belongs_to :post belongs_to :user endapp/models/post.rbclass Post < ApplicationRecord belongs_to :user has_many :comments, dependent: :destroy has_many :notifications, dependent: :destroy endapp/models/user.rbclass User < ApplicationRecord has_many :posts, dependent: :destroy has_many :comments, dependent: :destroy has_many :notifications, dependent: :destroy endapp/controllers/comments_controller.rbclass CommentsController < ApplicationController def create @comment = Comment.new(comment_params) @comment.user_id = current_user.id if @comment.save Notification.create(post_id: @comment.post_id, user_id: current_user.id) #コメントつけた投稿のid、コメントした人のid end redirect_back(fallback_location: root_path) end endapp/controllers/notifications_controller.rbclass NotificationsController < ApplicationController def index @notifications = Notification.all.order(created_at: :desc) end endapp/views/notifications/index.html.erb<% @notifications.each do |n| %> <% if n.post.user.id == current_user.id %> <p><%= n.post.user.username %>(あなた)の投稿「<%= n.post.body %>」に</p> <p><%= n.user.username %> がコメントしました</p> <% end %> <% end %>これで、自分のどの投稿に、誰がコメントしたかがわかるようになりました。
- 投稿日:2020-05-21T01:29:47+09:00
コメントの投稿者名を表示したい
User,Post,Commentの3つのリレーションが、以下の時にコメント一覧でコメントの投稿者名を表示させたい。
user.rbhas_many :posts has_many :commentspost.rbbelongs_to :user fas_many :commentscomment.rbbelongs_to :user belongs_to :postshow.html.erb<p>コメント一覧</p> <% @comments.each do |c| %> <hr> <a href="/users/#{c.user_id}"><%= c.user.name %></a> <%= c.content %> <% end %>c.user.nameのnameがNoMethodErrorとなるので、posts.controllerに以下の1行追加することでcommentに関係したuserの情報を取り入れることができる。
posts.controller.rbdef show @comments = @post.comments.includes(:user).all end
- 投稿日:2020-05-21T00:37:18+09:00
個人アプリ制作2 WEBフォントの導入
今日の実装
① controller users tweet
② model user(devise) tweet
③ haml scssの導入
④ viewの作成 tewwt indexwebフォントの導入 Google Web Fonts
今回はおしゃれフォントをにしするためにGoogle Web Fontsを使用した。
導入方法
今回はscssで導入方法を記述する。
1 使いたいフォントを探す
google fonts から使いたいフォントを探す。
Google Web Fonts
右上にタブのlink href="https://fonts.googleapis.com/css2?family=Anton&display=swap" rel="stylesheetのURLをコピーする。
2 SCSSに読み込ませる
app/assets/stylesheets/application.scss@import url(https://fonts.googleapis.com/css?family=Anton);これでSCSS内でfontfamilyでAntonを使用することができる。
3 SCSSに使う。
app/assets/stylesheets/modules/_tweets.scssfont-family: 'Anton', sans-serif;すると 下のstudy-supportにAntonフォントが適用される。
注意
上のサイトgoogle fontsは日本語が対応していないものがほとんどなので日本語は適用されない場合が多い。
私は、日本語だけ適用されないことに気づかず40分ほど時間を浪費した笑今回はapplication.scssに読み込ませたが他の導入方法をあります。
参考サイト
今日のアイディア
投稿機能に動画を埋め込む
投稿機能(アウトプット)を今日勉強したことの自分のアウトプット動画をいれる
経緯
昨今はyoutubeなどの動画をみて学習したり、Twitter等でアウトプットをすることが動画でアウトプットをしているのが少なく見えた。時代の流れから推測するにSNSは、文字による表現➯文字+写真(インスタグラム)➯動画(youtubeにより個人の発信)となり、時代は動画にあると感じる。今よりももっと個人で発信できることを踏まえ、個人アプリstudy-supportでは、投稿機能に動画によりアウトプットもできるようにしたい。人に何かを伝えることが1番の勉強である。動画という選択肢はアウトプットの幅を広げることだと感じる。
- 投稿日:2020-05-21T00:11:41+09:00
【Rails】 Hamlの導入
Hamlとは
Hamlとは、HTMLよりも簡単に書くためのビューテンプレートエンジンです。
今回の記事では、haml
の導入方法についてまとめてみました。Hamlの導入手順
1. Gemfileへ記載
Gemfile# 省略 gem 'haml-rails'2. ターミナルで
bundle install
を実行ターミナル$ bundle install新規作成されるファイル
- app/views/layouts/application.html.haml
- app/views/layouts/mailer.html.erb
- app/views/layouts/mailer.text.erb
3. ターミナルで
rails haml:erb2haml
を実行ターミナル$ rails haml:erb2haml上記のコマンドを実行すると、自動的に拡張子をhamlに変換してくれます。
これで、hamlの導入は完了です。
- 投稿日:2020-05-21T00:11:41+09:00
【Rails】Hamlの導入
Hamlとは
Hamlとは、HTMLよりも簡単に書くためのビューテンプレートエンジンです。
今回の記事では、haml
の導入方法についてまとめてみました。Hamlの導入手順
1. Gemfileへ記載
Gemfile# 省略 gem 'haml-rails'2. ターミナルで
bundle install
を実行ターミナル$ bundle install新規作成されるファイル
- app/views/layouts/application.html.haml
- app/views/layouts/mailer.html.erb
- app/views/layouts/mailer.text.erb
3. ターミナルで
rails haml:erb2haml
を実行ターミナル$ rails haml:erb2haml上記のコマンドを実行すると、自動的に拡張子をhamlに変換してくれます。
これで、hamlの導入は完了です。