アイキャッチの表示サイズ・位置指定

2021.5.27

応用編7

 

アイキャッチ

投稿ページの記事タイトルの下に表示される画像のこと

 

Active Storage gem について

ファイルアップロードを簡単に実装できるgem。rails 5.2から導入されている。

 

使い方

1

gemfileに追加インストール後、

$ rails active_storage:install

↑active_storage_blobsactive_storage_attachmentsという2つのテーブルを作成するマイグレーションファイルを作成する

$ rails db:migrate

active_storage_blobs は実際にアップロードしたファイルが保存されるテーブル

active_storage_attachments 中間テーブル(今回でいえばarticle.rbとの)

 

2モデルに対して Active Storage用の設定を追加する

<一つのファイルを選択する>

has_one_attached :カラム名

<複数のファイルを選択する>

has_many_attached :カラム名

< 今回>

articleモデルに「eye_catch」というカラムで保存したい。

(article.rb)

has_one_attached :eye_catch

 

ポイント

上記をすれば、わざわざarticleテーブルにカラムを追加する必要がないので便利

 

3controllerでストロングパラメーターを定義すればOK

(articles_controller.rb)

def article_params
params.require(:article).permit(
:title, :description, :slug, :state, :published_at, :eye_catch,
:category_id, :eyecatch_align, :eyecatch_width, :author_id, tag_ids:
)
end

 

4view部分

こんな感じで使用できる

= image_tag article.eye_catch_url(:lg),
class: 'img-fluid', width: article.eyecatch_width

 

今回の実装の流れ

アイキャッチの横幅用と位置用のカラムを作成する

add_column :articles, :eyecatch_width, :integer
add_column :articles, :eyecatch_align, :integer, default: 0, null: false

eyecatch_width (横幅用のカラム)のデフォルトを100にするが、カラムにデフォルトを追加すると変更するときに大変になるので、ここでは設定しない。

ayacatch_align (位置用のカラム) defaul:0やnul:falseを追加することで、記事作成でバリテーションエラーとなる。

 

enumとvalidatesを追加

(article.rb)

enum eyecatch_align: { left: 0, center: 1, right: 2 } #enum設定
validates :eyecatch_width, numericality: { less_than_or_equal_to: 700,
greater_than_or_equal_to: 100 }, allow_blank: true
#100~700を指定

allow_blank:true 値が空の場合はバリデーションを実行しない。
属性の値がblank?に該当する場合にバリデーションがパスする。blank?に該当する値にはnilと空文字も含まれる。

ここの部分について

numericality: { less_than_or_equal_to: 700, greater_than_or_equal_to: 100 }
numericality: { in: 100..700 } #これで設定できるようになったらしい

Add validate numericality in range by mpapis · Pull Request #41022 · rails/rails · GitHub

 参照

Railsバリデーションまとめ - Qiita

 

 

controller部分

(admin/articles_controller.rb)

def article_params
params.require(:article).permit(
:title, :description, :slug, :state, :published_at, :eye_catch,
:category_id, :eyecatch_align, :eyecatch_width, :author_id, tag_ids:
) #追加
end

 

view部分

 

(articles/edit.html.erb)

simple_formとenum_help(日本語化のため)という2つのgemを使用することで↓の一文を作成することができる。

= simple_form_for @article, url: admin_article_path(@article.uuid) do |f|
.box-body
.
.
省略
= f.input_field :eyecatch_align, as: :radio_buttons

このようなHTMLが作成される。

f:id:mmm_st:20210601090826p:plain

simple_form_forに渡した第一引数のモデルインスタンス@articleから、

input_fieldに渡した第一引数の:eyecatch_align情報を読み取ってフィールドを生成している。

ラジオボタンのHTML生成を指示しているのはas: :radio_buttons部分。

他にもデフォルトで指定できる。

参照

https://github.com/heartcombo/simple_form#available-input-types-and-defaults-for-each-column-type

「右寄せ」「中央」「左寄せ」と日本語化して表示されているのはenum_helpのおかげ。

simple_formと組み合わせることで自動的に翻訳される。READMEに詳細あり。

 

= f.input :eyecatch_width, placeholder: '100'
#デフォルトの100。ここでvalidatesでallow_blank: trueにしているので、blankの際は
100が入る。

 

 

(shared/_article.html.slim)

- if article.eye_catch.attached?
section class="eye_catch text-#{article.eyecatch_align}" #enumを使用し位置指定
= image_tag article.eye_catch_url(:lg), class: 'img-fluid',
width: article.eyecatch_width #eyecatch_widthを使用して横幅指定

 

 

RSpec

attach_file

アップロードのinput要素にテスト用の画像を添付する。

第一引数にアップロードするinput要素のname属性の値、第二引数にアップロードする画像のパス、第三引数にオプション(今回はなし)を設定する。

Module: Capybara::Node::Actions — Documentation for jnicklas/capybara (master)

Rails RSpec による画像添付結合テスト - Qiita

 

have_css

指定されたCSSクレターに一致する要素が存在するかどうか確認

Method: Capybara::RSpecMatchers#have_css — Documentation for jnicklas/capybara (master)

 

$("[属性 $= '値’ ]")

ex) img[src$=’test.jpg’]

指定した値が属性の値と後方一致する要素

参照

jQuery リファレンス:[ 属性名 $= '値' ]

 

choose

ボタンを見つけて、チェック済みとしてマークする

参照

Method: Capybara::Node::Actions#choose — Documentation for jnicklas/capybara (master)

 

rand関数

擬似乱数をを生成する。引数に整数を与えると0からその整数値未満の整数を返す。

ex) rand(100) → 0から100

 

気になったところ

expect(page).to have_selector('section.eye_catch.text-left'),
'アイキャッチが「左寄せ」で表示されていません'

ここ

'section.eye_catch.text-left'

HTMLはこんな感じ

f:id:mmm_st:20210601100902p:plain

 

「classにeye_catch.text-leftクラスなんてないし、eye_catchとtext-left両方を検証してるのか...?」

と思ったので、テストの形を変えながら確認してみた。

'section.eye_catch.text-left'

 section → sectionタグ内に範囲を狭めている。なくても成功

. → クラスを指定してる

section.text-left.eye_catch → これでもで成功

結論

sectionタグ内のeye_catchクラスとtext-leftクラスを検証している。