ActiveRecordの便利機能delegate

delegateはメソッドを異なるクラス間で簡単に使えるようにできます。

実装

Articleクラスは関連付としてcategoryとauthorを持っているとします。categoryにはname,slug authorにはnameカラムがあって参照できます。

article.category.nameのように名前を呼び出したい時があって、毎回メソッドチェーンしたものを書くと冗長に感じますよね。

なのでArticleクラスに name,slugメソッドを定義するとします。 以下のようなコードになります。 article.rb

class Article < ApplicationRecord
  belongs_to :category

def category
  article.name
end

def category
  article.slug
end

def author
  article.name
end


category.rb

class Category < Taxonomy
  has_many :articles
end


delegateなしだと、カテゴリーのnameとslug、著者のnameを取得しようとすると、上のようにcategoryとauthor越しに取得しなければいけない。

ここでdelegateの出番!delegateを使ってあげることで、短く書くことができます。
article.rb

class Article < ApplicationRecord
  belongs_to :category
 

  delegate :name, to: :category, prefix: true, allow_nil: true
  delegate :slug, to: :category, prefix: true, allow_nil: true
  delegate :name, to: :author, prefix: true, allow_nil: true


prefixとallow_nilについて

prefix

prefix:trueにすることによって、マクロによってnameの代わりにcategory_nameが生成されます。

こうすることでarticle.category_nameで呼び出すことができます。


allow_nil

委譲時にNoMethodErrorが発生して対象がnilの場合、例外が発生します。:allow_nilオプションを使うと、例外の代りにnilを返すようにすることができます。


参考URL

railsguides.jp

ポルモーフィック関連

ポリモーフィック関連付けを使うと、ある1つのモデルが他の複数のモデルに属していることを、1つの関連付けだけで表現できます。
例として、ArticleBlockモデルに複数のモデル(Sentence, Medium,Embed)を関連付けしてみましょう。 ※Sentence:文章、Medium:画像、Embed:動画を記事に埋め込みます。

コードの記載

article.rb

class Article < ApplicationRecord

  has_many :article_tags
  has_many :tags, through: :article_tags
  has_many :article_blocks, -> { order(:level) }, inverse_of: :article
  has_many :sentences, through: :article_blocks, source: :blockable, source_type: 'Sentence'
  has_many :media, through: :article_blocks, source: :blockable, source_type: 'Medium'
  has_many :embeds, through: :article_blocks, source: :blockable, source_type: 'Embed'
 _
.
.


article_block.rb

class ArticleBlock < ApplicationRecord
  belongs_to :article
  belongs_to :blockable, polymorphic: true, dependent: :destroy
 .
 .


sentence.rb

class Sentence < ApplicationRecord
  has_one :article_block, as: :blockable, dependent: :destroy
  has_one :article, through: :article_block
end


medium.rb

class Medium < ApplicationRecord
  has_one :article_block, as: :blockable, dependent: :destroy
  has_one :article, through: :article_block


embed.rb

class Embed < ApplicationRecord
  has_one :article_block, as: :blockable, dependent: :destroy
  has_one :article, through: :article_block


図を交えて説明

Image from Gyazo


これにより、sentence media emedを呼び出すことができる。

 article = Article.first
 article.sentence
 article.media
 article.emed

他にもこのように呼び出すことができます。

ArticleBlock.first.blockable
Sentence.first.article_block

参考URL

railsguides.jp

パンくず機能の設定

パンくず機能とは

Image from Gyazo

web上で自分がどの階層にいるのか、視覚的に分かりやすくしてくれます。


実装

gem gretelの導入

  gem 'gretel'

bundle installをします。


ターミナル上でrails g gretel: install

config/breadcrumbs.rbが生成されます。
今回は、Home > 著者 > 著者編集と表示されるように設定します。 breadcrumbs

 crumb :admin_dashboard do
   link '<i class="fa fa-dashboard"></i> Home'.html_safe, admin_dashboard_path
 end

 crumb :admin_authors do
   link '著者', admin_authors_path
   parent :admin_dashboard
 end

 crumb :edit_admin_author do |author|
   link '著者編集', edit_admin_author_path(author)
   parent :admin_authors
 end

parentというのは、この記述をしたビューがどのビューの子要素的な階層になるかを指定します。


View

app/views/layouts/admin.html.slim

         h1
            = yield 'content-header'
          == breadcrumbs style: :ol, class: 'breadcrumb'

app/views/admin/authors/index.html.slim

 = content_for 'content-header' do
   | 著者

 - breadcrumb :admin_authors

app/views/admin/authors/edit.html.slim

 = content_for 'content-header' do
   | 著者編集

 - breadcrumb :edit_admin_author, @author

著者編集ページで、@authorのつけ忘れに注意してください。

Webpackerを使ってBootstrapを導入したときのエラー

yarn add jquery bootstrap popper.js

package.json

{
  "name": "techpit_match",
  "private": true,
  "dependencies": {
    "@popperjs/core": "^2.9.2",
    "@rails/actioncable": "^6.0.0",
    "@rails/activestorage": "^6.0.0",
    "@rails/ujs": "^6.0.0",
    "@rails/webpacker": "4.3.0",
    "bootstrap": "^5.0.1",
    "jquery": "^3.6.0",
    "turbolinks": "^5.2.0"
  },
  "version": "0.1.0",
  "devDependencies": {
    "webpack-dev-server": "^3.11.2"
  }
}


app/javascript/stylesheets/application.scss

@import '~bootstrap/scss/bootstrap';


app/javascript/packs/application.js

import 'bootstrap';
import '../stylesheets/application';


app/views/top/index.html.erb

<%= link_to "仮のボタンです", "#", class: "btn btn-primary" %>

webpackerでBootstrapを導入したが、装飾されなかった。


原因と解決

bin/webpack-dev-server

上記のコマンドを実行して、コンパイルした結果にエラーが表示されているか確認したところ、Module not found: Error: Can't resolve '@popperjs/core'と表示されました。


yarn remove popper.js
yarn add @popperjs/core

とすることで、Bootstrapが依存しているpooperV2に変更できます。これでBootstrapの機能を使うことができました。


参考URL

blog.getbootstrap.com

Rspec spec/support内のファイルを読み込む

spec/supprt内に、以下のlogin_supportファイルを記載しました。

module LoginSupport

  def login_as(user)
    visit root_path
    click_link "Login"
    fill_in "Email", with: user.email
    fill_in "Password", with: user.password
    click_button "Login"
  end
end

これでログインする設定をspec/models内に共通で書くことができると思っていたが、エラーが出たしまった。


原因と解決

デフォルトでは、spec/support内のファイルを読み込むことができないので、読み込むように設定する。 spec/rails_helper.rb内の以下のコメントアウトを解除してあげる

Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }

これでspec/support内のファイルを読み込むことができます。

CarrierWave使用時のNill location provided Can't build URLエラー

プロフィール編集画面に、画像を表示しようとしたら以下のエラーが現れた。

 Nill location provided Can't build URL


原因 解決

app/uploders/avatar_uploader.rbに以下の記載を忘れていた。

def default_url
    'board_placeholder.png'
end

画像を選択していない時にboard_placeholder.pngが無かったことから、エラーが出ていた。