20200521のRailsに関する記事は24件です。

【学習メモ】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.他の場合も同様の考え方で成功できるのでは?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.slim
head
    .
    .
    .
    - = javascript_include_tag 'application','data-turbolinks-track': 'reload'
    + = javascript_pack_tag 'application'
    .
    .
    .


解決策

1.まずはyarnのパッケージファイルにrails-ujsを追加します。そしてpackage.jsonにこれが追加されていることを確認します。

$ yarn add rails-ujs
package.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.js
import Rails from 'rails-ujs';
Rails.start();


3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。

app/javascript/packs/application.js
require("./rails-ujs.js");



4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。

app/views/layouts/application.html.slim
head
    .
    .
    .
    = 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.slim
head
    .
    .
    .
    - = javascript_include_tag 'application','data-turbolinks-track': 'reload'
    + = javascript_pack_tag 'application'
    .
    .
    .


解決策

1.まずはyarnのパッケージファイルにrails-ujsを追加します。そしてpackage.jsonにこれが追加されていることを確認します。

$ yarn add rails-ujs
package.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.js
import Rails from 'rails-ujs';
Rails.start();


3.app/javascript/packs/application.jsファイルに、rails-ujs.jsファイルを読み込ませます。

app/javascript/packs/application.js
require("./rails-ujs.js");



4.app/views/layouts/application.html.slimファイルに、webpackerスクリプトを記述します。

app/views/layouts/application.html.slim
head
    .
    .
    .
    = 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

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 rspec

bin/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 install

Rubocop導入に必要なファイルを作成

rubocop_airbnb.yml
require:
  - rubocop-airbnb
rubocop.yml
inherit_from:
  - .rubocop_airbnb.yml

Rails:
  Enabled: true

Rails: 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.
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

100日後に1人前になる新人エンジニア(1日目)

100日後に1人前になる新人エンジニア(1日目)

どうもこんばんは今日が1日目になります。
ちなみに昨日から始まっていますのでよかったらこちらからどうぞ
100日後に1人前になる新人エンジニア(0日目)

今日やったこと

本日より本格的に業務に取り組みました。
さっそくissueに取りかかり頑張るぞ...
と思ってましたが、

Dockerの使い方がわからん...
ということでまずそこで悪戦苦闘した前半。

その後Railsでフォームの改修に取り組みましたが...
プロジェクトのフォルダ多すぎて何が何だかわからんぞ:joy:

という始まり方でした。

少しずつ理解を深めていきアーキテクチャとかクラスごとの繋がりも分かってきたら
面白いんだろうなあって思っています。

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 と入力してみるとどんな値が送られているのかちゃんと確認できました。
明日から使います。ありがたや...:joy_cat:

他にもやり方は色々とあるはずなのでそこは確認してみます。

formに関してはまだ完成していないので続きは明日に回します。

今日のさいごに

今日が連載初日でした。
何事も積み重ねだと思うので一日ひとつづつ。

フィルヒースの言葉をいただきます。
Winning is habitualですね

1人前のエンジニアになるまであと99日

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Rails】Carrierwaveを導入する

タイトルと本文のみの投稿ができるブログアプリに
写真の投稿機能を追加した時の、Carrierwaveを導入する
手順に付いてまとめました。

1. Gemfileファイルを編集する

Gemfile
gem 'carrierwave'
gem 'mini_magick'
ターミナル
% bundle install

2. アップローダーを作成する

ターミナル
rails g uploader image

実行後、app/uploaders 下に image_uploader.rbが作成されます。

3. アップローダーをマウントする

app/models/message.rb
class Message < ApplicationRecord
  belongs_to :group
  belongs_to :user
  belongs_to :heven

  validates :content, presence: true, unless: :image?

  mount_uploader :image, ImageUploader  ⬅️ この1行
end

4. 画像のリサイズを可能にする

include CarrierWave::MiniMagick のコメントアウトを外し、
任意の行にprocess resize_to_fit: [800, 800]と追記。
これにより、縦横比を維持したまま、縦横を800px以内にリサイズ可能になります。

app/uploaders/image_uploader.rb
class ImageUploader < CarrierWave::Uploader::Base
  # Include RMagick or MiniMagick support:
  # include CarrierWave::RMagick
  include CarrierWave::MiniMagick  ⬅️ 有効化

# 〜省略〜

  process resize_to_fit: [600, 600]  ⬅️ 追記

# 〜省略〜
end

以上で画像のアップロードの準備ができました。

ご覧いただきありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

#Rails で指定回数/複数回 db:rollback する ( STEP=n rails db:rollback )

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb

def 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_tokenapi_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

以上。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails 基礎

current_userメソッド

deviseでログイン機能を実装すると使えるようになるメソッド。
idが1のレコードの場合、User.find(1)と同じ意味を持ちます。
current_user.nameなどで値を取得できます。

ルーティングでユーザーごとに異なるページを表示するには普通とは違う記述方法を使います。

routes.rb
get 'users/:id' => 'users#show'

whereメソッド
Active Recordの1つ
モデル.where(条件)で引数にとった条件に一致したレコードのインスタンスを配列型で取得できます。
条件を連続で記述すれば複数の条件にあったレコードを取得できます。

Sample.where('id < 3').where('user_id: 1')

アソシエーション

モデル間の関係を定義することで、モデルを跨いだデータの呼び出しが簡単になります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ExecJS::RuntimeErrorというエラー

初の投稿ですので、暖かく見守ってください:sweat_smile:
昨日の体験したエラーについて投稿します!

前提

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 -v
brew reinstall node

をしてみました。
見事解決できました!

今回感じたこと

エラーはターミナルも読んでからエラー解決すると良いことに気付きました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

LINEみたくメッセージの投稿日時毎に日付で区切りを付けてみた

初めに

2020年2月からプログラミング学習を始めたばかりの初学者です。
今回が初投稿なので、誤っている点もいくつかあるかと思いますが、暖かく見守っていただけると幸いです。
修正が必要な箇所がありましたら遠慮なくご指摘ください。

現在、チャット機能を実装中。
今回はその中で取り入れた機能の一部を備忘録も兼ねて投稿します。

LINEのように日付毎に送信したメッセージを区切る方法について

作成イメージ
ezgif.com-video-to-gif.gif

環境

ruby-2.6.3
Rails 5.2.4.1

前提として

投稿内容の自動保存機能とプレビュー機能を実装しているため、created_atによる投稿日時ではなく、updated_atを用いています。

コード

route.rb
  resources :groups, shallow: true do
    resources :messages
    end
  end
controllers/groups_controller.rb
  def 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
  end
groups/show.html.haml
    .message_contents
      = render partial:"messages/message", collection: @messages
messages/_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.rb
def 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
  end

groups_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) == true

if文の条件として
groupメソッドで定義した、@first_post_timeにmessage.updated_atが含まれるか否かの判定をinclude?メソッドを用いています。
trueの場合、すなわちその日の最初に投稿したものが、ある場合に
= message.post_datetime
が表示されることになります。
日付の表示方法を定義したpost_datetimeメソッドはmodels/message.rbに作成しています。今回は区切りの付け方ということなので、その詳細については今回は割愛します。

作成の流れは以上になります。
他にもっと簡単な方法やもっとこうした方が良いなどありましたらご教授いただけると幸いです。
よろしくお願いいたします。

参考サイト

https://pikawaka.com/rails/strftime

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RSpecとFactoryBotを利用してRailsアプリの単体テストを実施してみた

概要

ポートフォリオとして作成した個人開発アプリのテストを実施したので
個人的なアウトプットとしてテストの流れを振り返りたいと思います。
今回はモデルのバリデーションに関するテストの基礎的な内容になります。

前提

プログラミングにおけるテストとは、「プログラムが意図した通りに動くことを確かめる」ことを指します。

環境

Ruby 2.5.1
Rails 5.2.3

gem 'devise'を使用してユーザーモデル作成済み

はじめに

まずはテストに使用するGemをインストールしていきます。

今回使用するGem

  • rspec-rails ▶︎RSpecというテストに特化した言語のRails用gem
  • factory_bot ▶︎簡単にダミーのインスタンスを作成できるgem
Gemfile
group :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ファイルはモデル用のディレクトリにまとめておくため、テキストエディタでディレクトリとファイルを作成していきましょう。

  1. specディレクトリの配下にmodelsというディレクトリを作成する。
  2. spec/modelsディレクトリの中にモデル用specファイルを作成する。

※specファイルの命名規則はspecファイルは、対応するクラス名_spec.rbという名前になります。

今回はUserモデルのバリデーションに関するテストを実施するという想定で進めていくのでuser_spec.rbを作成します。

まずは新規ユーザー登録時の各バリデーションが適用されるか、一つずつテストしていきます。
今回、Userモデルのバリデーションは以下の条件です。

app/models/user.rb
class 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
end

validatesのpresence: trueが適用されている4カラムをテストしていきます。



まずは基本となる記述でnicknameのバリデーションをテストします。

spec/models/user_spec.rb
require '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
end

1行目はテストする上で読み込むファイルを記載。
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.rb
FactoryBot.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.rb
require '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記事を記載しておきます。
ありがとうございました。

参考記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
post "users/create" => "users#create"

つまり、上記のデータはusersコントローラーのcreateアクションに送信されます。createアクションでは以下のコードを書きます。

users_controller.rb
def 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])を用いることでモデルを作成し、データベースに格納しています。

ユーザーからは以下のようになっています。

image.png

この状態で送信を押してみましょう。すると、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.rb
def create
  user = User.new(name: params[:name], password: params[:password])
  user.save
  redirect_to("/users/index")
end

このように書くと、送信を押した場合に/users/indexに対応するファイルにリダイレクトされます。

ちなみに、リダイレクトを書く場合は、パスの書き始めに/を入れることを忘れないようにしてください。

終わりに

今回の記事はここまでになります。

お疲れさまでした。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails フォロー機能を非同期で実装 js.hamlを使用

なにこれ

フォロー機能を実装できたので、備忘録と言語化するために書いてます。
間違いがありましたら、ご指摘いただけると大変ありがたいです!

コツ

current_user.idと@user.idの違いを理解すること

前提条件

UserモデルとUsersテーブルを作成済み

データベース設計

フォローしたデータを保存するためのrelationshipsテーブルを作成します
コントローラーも作成しておきます。

ターミナル
 rails g model Relationship
 rails g controller Relationships
/db/migrate/作成日時_create_relationships.rb
      t.references :follower, foreign_key: { to_table: :users }
      t.references :following, foreign_key: { to_table: :users }
      t.index [:follower_id, :following_id], unique: true

follower_idがフォローされたユーザーのidにします。
follower_idがフォローしたユーザーのidにします。

t.index [:follower_id, :following_id], unique: trueは、
follower_idとfollowing_idが一意の関係になるように設定してます。被らないように。
つまり、同じユーザーを何回もフォローできたらおかしいので!
終わったらrails db:migrateします。

モデルの編集

Userモデル

/model/user.rb
   has_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
  end

userモデルのアソシエーションがフォロー機能を実装する上で一番の山場だと思います。
フォロー機能を実装するための様々なモデルの組み合わせを調べましたが、
個人的にこれが一番シンプルかつ直感的に分かりやすいと感じたものを使用します。

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は
Userモデルに記述した
has_many :followersに該当します。
同じく
belongs-to :following
Userモデルに記述した
has_many :followingに該当します。
class_name “User”`にすることで存在しないfollowerモデルを参照することを防いでいます。

ルーティングの作成

routes.rb
Rails.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.rb
class 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
end

createアクションでは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_routes
 relationship 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します。
これで非同期通信ができます。

以上で実装完了です!お疲れ様です。
ここまで読んでいただき、ありがとうございます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Ruby/Rails】Rubocopでupdate_allのLintを非活性にする

rubocop.yml
Rails/SkipsModelValidations:
  Whitelist:
    - update_all

参考:https://github.com/ManageIQ/manageiq-schema/pull/441/files

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

STIについて、少し理解できた人が記事を書いてみた

はじめに

Rails最近頑張ってます。:clap:
ここ知らなかったってところや自分的に知識がまだ浅いなと思う部分をQiitaにまとめていきたいと思います!
まだまだな部分が多いですが、すこしでも皆様の理解の助けになれたら、嬉しいです。

STIって?

単一テーブル継承といって、一つのテーブル(親にあたるもの)から継承して、新しいテーブル(子にあたるもの)を作りましょう!!みたいな考え方です。親のテーブルに含まれるカラムなども引き継がれるので、同じ部分を子クラスで定義しなくてよくなるので、手間も減るし、わかりやすくなるので、利点が多いですね!子クラスで、カラムを新たに追加することもできるので、割と簡単に拡張?もできます。

どんな時に、使うと便利??

似たような(ほぼ同じような機能を持つ)モデルがたくさんあるときに、1つ1つに対して、カラムの定義などを行わなくてよくなるので、モデルがたくさんあるときに、使うと、便利かもしれません。

構造的にどうなってるの?

image.png

こんな感じです。1つのクラスを継承して、子クラスを生成します。子クラスでは、親クラスで持っているもの+自クラスで定義したものを持つことができます。

作り方について

  1. まず、親クラスの生成をします。
    bundle exec rails g model Tweetを実行して、マイグレーションファイルの中に、クラスに持たせる要素などの値を書き込んで、
    bundle exec rails db:migrateを実行します。

  2. 子クラスについては、モデルファイルのおいてあるフォルダにモデル名.rbで新しくファイルを新規作成して、親を継承するように設定します。

親クラス.rb
class 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

子クラス.rb
class TweetImage < Tweet
  has_one :image, foreign_key: :tweet_id
  accepts_nested_attributes_for :image, allow_destroy: true
end

※クラス名のあとの部分に気をつけましょう!!
親クラスはApplicationRecordになっていますが、子クラスの方は、モデルを継承して作るので、親クラスのモデル名を入れなければ、なりません。

さいごに

最後まで、読んでいただきありがとうございます。
まだまだ、説明下手ですが少しでもみなさんに伝わりやすい記事をかけるように、努力していきたいと思います!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby on rails を初心者向けに解説③ ~データベースの作成~

はじめに

今回は、以前の記事の続きになっています。

よろしければ以前の記事も御覧ください。

Ruby on rails を初心者向けに解説①

Ruby on rails を初心者向けに解説② ~リンクの作成~

データベースとは

データベースとは、データを保存しておく場所のことです。

Ruby on Railsにおいては、コントローラーからモデルを作成することによりデータベースを操作することができます。
image.png

コントローラーの作成

今回は、ユーザーのIDとパスワードを保存するデータベースを作成することを考えます。

データベースを作成する前に、一度コントローラーとアクションを作成してみましょう。

ターミナルで以下のコードを実行してください。

rails g controller users index

これでuserコントローラーを作成し、その中にindexアクションを追加することができました。rails g コマンドにおいて、コントローラー名とアクション名の2つを設定することができます。

以下のようにルーティングしてください。

routes.rb
get 'users/index' => "users#index"

これで、ユーザーからuser/indexというURLが送られてきたときに、userコントローラーのindexアクションを行うことができるようになりました。

userコントローラーを確認してみましょう。今回は、最初からアクションが追加されています。

users_controller.rb
class UsersController < ApplicationController
  def index
  end
end

実はviewファイルも自動で生成されています。

image.png

以下のように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

以下の画面がでてきます。

image.png

アクションから変数をviewファイルに渡す

image.png

コントローラーがviewファイルを探してきて、それをユーザーに返します。

その時、コントローラーの中のアクションに変数を定義することで、その変数をviewファイル内で用いることができます。

以下のように変数を定義しましょう。

users_controller.rb
class 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 %>

image.png

@usersは配列が格納されているので、.each do ~ end で取り出すことができます。この部分は全てRubyのコードなので、<%%>ではさみましょう。ブラウザに表示する必要がないので、<%=%> ではなく <%%>ではさむところがポイントです。

<%= user %>の部分で、ブラウザに表示させます。

今回は、コントローラーのアクション内で配列を定義して、それをviewファイルに渡すことで利用しました。

今度は、データベースからデータをアクション内に持ってきて、それをviewファイルに渡すことを考えてみましょう。

モデルの作成

image.png

データベースを操作するためには、モデルを作成する必要があります。

モデルとは、データベースの情報を操作する仕組みのことです。または、データベースとのやり取りを行うクラスとも言うことができます。

以下のコードでモデルと。マイグレーションファイルを作成します。

rails g model user name:string password:string

Userがモデル名で、id、passwordがカラムになります。カラムとは、データベースの縦のデータ、つまり列のことです。

このコードで作成したデータベースは、以下のような表になっています。

image.png

この表全体をテーブルと呼び、縦のデータをカラム、横のデータをレコードと呼びます。モデルとは、このデータベースに対応する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
end
user.rb
class 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>.table

ar_internal_metadata schema_migrations users

スキーマの表示

以下のコードでスキーマを表示することができます。スキーマとは、構造という意味です。指定したデータベースの構造を確認することができます。

sqlite>.schema users

CREATE 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

ここまでで、データベースを作成することができました。

それでは、実際にデータベースを利用してみましょう。

データベースの利用

データベースには、モデルを用いてアクセスします。

image.png

データベースを利用する際は、コントローラーからモデルを用いてアクセスします。

users_controller.rb
class 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>

image.png

終わりに

今回はここまでになります。

ここまでお付き合い頂きありがとうございました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleしてみよう

困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い

RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。

実際のスピードについては以下のツイートに載せた動画を参考にしてください。

問題が発生した実行環境

僕の実行環境は以下のとおりです。

  • 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本体のディスプレイに表示したときは、必要十分なスピードで入力できました。

20200426090206.jpg

同じような現象は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で変更できます。

Screen Shot 2020-05-21 at 7.45.04.png

こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。

NyO7vIOBwK.gif

ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。

Subpixelモード↓
Screen Shot 2020-05-21 at 7.47.58.png

Greyscaleモード↓
Screen Shot 2020-05-21 at 7.47.40.png

また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。

No antialiasingモード↓
Screen Shot 2020-05-21 at 7.48.20.png

できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。

参考:あまり効果がなかった対策

JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。

ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。

情報募集!

その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?

謝辞

本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyMine 2020.1が遅いときはアンチエイリアス設定をGreyscaleにしてみよう

困っていたこと:RubyMine 2020.1がめちゃくちゃ遅い

RubyMineのバージョンが2020.1に上がってから文字入力がめちゃくちゃ遅くなりました。
どれくらい遅いのかというと、人間のタイピングスピードにIDEがまったく追いついていない、っていうぐらい遅いです。

実際のスピードについては以下のツイートに載せた動画を参考にしてください。

問題が発生した実行環境

僕の実行環境は以下のとおりです。

  • 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本体のディスプレイに表示したときは、必要十分なスピードで入力できました。

20200426090206.jpg

同じような現象は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で変更できます。

Screen Shot 2020-05-21 at 7.45.04.png

こちらはGreyscaleモードで入力したときの動画です。
これだと人間のタイピングスピードにもほぼ追従できています。

NyO7vIOBwK.gif

ただし、Greyscaleにすると画面の文字が少し細く(または暗く?)表示されるという副作用があります。

Subpixelモード↓
Screen Shot 2020-05-21 at 7.47.58.png

Greyscaleモード↓
Screen Shot 2020-05-21 at 7.47.40.png

また、アンチエイリアスを完全になくすNo antialiasingというモードもありますが、これだとエディタの文字がガタガタして見栄えが悪いので、僕は選択しませんでした。

No antialiasingモード↓
Screen Shot 2020-05-21 at 7.48.20.png

できればアンチエイリアス設定を変えずに、デフォルトのSubpixelモードで使いたいところですが、この問題は4Kディスプレイ、OS、JVMの兼ね合いに起因しているらしく、根本的な解決にはもうちょっと時間がかかりそうな気がします。

参考:あまり効果がなかった対策

JavaのRuntimeをJBR11からJBR8に変更する(もしくはJBR8からJBR11に戻す)と改善するかも、という情報がありましたが、これは効果がありませんでした。

ちなみに現在、僕のRubyMine 2020.1ではJBR8(jbsdk8u202b1491_osx_x64.tar.gz)を使用しています。

情報募集!

その他、この問題を解決するためのいい情報をご存じの方がいましたら、コメント欄等で情報をお願いします?

謝辞

本件については株式会社サムライズムのユースケさん(@yusuke)に解決のヒントをいただきました。
ユースケさん、どうもありがとうございました!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Rails]最低限の通知機能

最低限の通知機能の実装

自分の投稿にコメントがついた時、通知が来るようにします。初心者なりに考えて書いてみましたがこれでええんかな、、、備忘録も兼ねて。ゆくゆくはいいね/DMが来た時も通知が来るようにしたいです。
ここ間違ってる!とかコメントいただけたら喜びます。

db/schema.rb
create_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"
  end
app/models/notification.rb
class Notification < ApplicationRecord
    belongs_to :post
    belongs_to :user
end
app/models/post.rb
class Post < ApplicationRecord
    belongs_to :user
    has_many :comments, dependent: :destroy
    has_many :notifications, dependent: :destroy
end
app/models/user.rb
class User < ApplicationRecord
  has_many :posts, dependent: :destroy
  has_many :comments, dependent: :destroy
  has_many :notifications, dependent: :destroy
end
app/controllers/comments_controller.rb
class 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
end
app/controllers/notifications_controller.rb
class NotificationsController < ApplicationController
    def index
    @notifications = Notification.all.order(created_at: :desc)
    end
end
app/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 %>

これで、自分のどの投稿に、誰がコメントしたかがわかるようになりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

コメントの投稿者名を表示したい

User,Post,Commentの3つのリレーションが、以下の時にコメント一覧でコメントの投稿者名を表示させたい。

user.rb
has_many :posts
has_many :comments
post.rb
belongs_to :user
fas_many :comments
comment.rb
belongs_to :user
belongs_to :post
show.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.rb
def show
   @comments = @post.comments.includes(:user).all
end
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

個人アプリ制作2 WEBフォントの導入

今日の実装

① controller users tweet
② model user(devise) tweet
③ haml scssの導入
④ viewの作成 tewwt index

webフォントの導入 Google Web Fonts

今回はおしゃれフォントをにしするためにGoogle Web Fontsを使用した。
study-support.png

導入方法

今回はscssで導入方法を記述する。

1 使いたいフォントを探す

google fonts から使いたいフォントを探す。
Google Web Fonts
google-fonts.png
右上にタブの

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.scss
font-family: 'Anton', sans-serif;

すると 下のstudy-supportにAntonフォントが適用される。
study-support.png

注意

上のサイトgoogle fontsは日本語が対応していないものがほとんどなので日本語は適用されない場合が多い。
私は、日本語だけ適用されないことに気づかず40分ほど時間を浪費した笑

今回はapplication.scssに読み込ませたが他の導入方法をあります。

参考サイト

Google Web Fontsを使ってみよう

今日のアイディア

投稿機能に動画を埋め込む

投稿機能(アウトプット)を今日勉強したことの自分のアウトプット動画をいれる

経緯

昨今はyoutubeなどの動画をみて学習したり、Twitter等でアウトプットをすることが動画でアウトプットをしているのが少なく見えた。時代の流れから推測するにSNSは、文字による表現➯文字+写真(インスタグラム)➯動画(youtubeにより個人の発信)となり、時代は動画にあると感じる。今よりももっと個人で発信できることを踏まえ、個人アプリstudy-supportでは、投稿機能に動画によりアウトプットもできるようにしたい。人に何かを伝えることが1番の勉強である。動画という選択肢はアウトプットの幅を広げることだと感じる。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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の導入は完了です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【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の導入は完了です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む