inuinu blog(開発用)

BOT @wagagun の開発ノウハウや、IT向け?の雑記ブログです。

WordPressから、はてなブログへの移行について(+Markdown記法に戻す)

この記事では、WordPressの記事を「はてなブログ」にインポートする手法について、解説します。

なぜ、はてなブログへ戻したか?

まあ、多くの方と同様に経済的な理由です。

知人らのアドバイスなどをもとに、個人サイト立ち上げるきっかけとして、まずは、アフィリエイト付きのオリジナルドメインWordPressブログをはじめましたが、割に合わないので停止しました。

仮に月1,000円+プラスドメイン料金だとすると、年間12,000円以上はかかりますよね。

ドメイン料金も払いたくないので停止し、はてはも無料プランのままとしました。

つまり、https://inuinu-tech.hatenablog.jp/entry/〜といったURLのままとしています。

参考:WordPressブログのダメなところ

WordPress(+アフィリエイト)ブログのハードルの高さについて

WordPressについては、ざっとこんな感じでしょうか。

  • サーバー選定やWordPressデザインテーマを選択するだけで、かなりの時間と精神を消耗する
  • WordPressの導入や初期設定で、相当な時間を費やす
  • SEO対策について、相当な時間を費やす
  • 固定ページ作成に時間を費やす
  • カテゴリやタグの体系づくりで時間を費やす
  • ブロックエディタ特有の冗長な文字列の塊が、Markdown使いには耐えられない

さらに、多分に切っても切れないアフィリエイトについても書き加えます。

  • アフィリエイト業者への申込みで時間を費やす
  • YMYL設定や、それに伴う企業参入で、アフィリエイトの旨味がどんどん減っている
  • ブログの書き方や文字数に気を使いすぎる

サーバー選定やWordPressデザインテーマを探す場合、ほぼ100%アフィリエイトブログにぶち当たります。
当然ながら、アフィリエイターにとって都合のいい話しか書いていません。
あまり参考にならないと思います。

これをクリアするには、有名そうなブログが何を使っているかを見るといいかもしれません。
ネットを探すと、ドメインからサーバーを検索し、さらにWordPressテーマも教えてくれるページがあったと思いますので、試してみて、それに倣う方が良いかもしれません。

あとは、某掲示板の書き込みなども参考になるかと思います。

その後、晴れて加入〜導入〜設定に移りますが、これもノウハウの収集を含め、かなりの時間を費やします。
特に注意したいのはプラグインで、ネットで見た「おすすめプラグイン」が既に時代遅れなんてことがよくあります。
また、多くのプラグインは徐々に肥大化して有料化したり、他のプラグインとバッティングする運命をたどります。
今人気のデザインテーマを導入⇒そのデザインテーマサイトがおすすめするプラグインを最低限入れてみる…という流れが賢明かと思います。

固定ページとは、自己紹介、免責事項、問い合わせフォームなどです。
問い合わせフォームはプラグインを利用しますが、その他は自分で作成する必要があります。
アフィリエイターも、ある種の個人商店みたいなものですから、下手なことは書けません。ここでプロの洗礼を受けるかと思います。

同時に自分が書きたいことの大系を考えて、カテゴリ(場合によってはディレクトリあるいはサブドメイン)を考える必要があります。

さらにアフィリエイト業者、アドセンス広告や、物販アフィリエイト、その他のアフィリエイト業者への登録が必須になると思いますが、年々厳しくなっていると思います。

さらに、アフィリエイトブログを書く場合、自分の経験に基づいた記事を書くのがセオリーですが、自分の経験が例えば「病気からの回復」だとして、それが例えば「お世話になった医療保険」とかに紐づくと、YMYLに引っかかる可能性が高まります。

私はやっていませんでしたが、DIYモノは多分行けると思いますが、これが業者にお願いするリフォームとかになると、難易度が上がるかも。
さらに、経験から「中古一戸建て・マンションの賢い買い方」となると、まあ、厳しいかもしれませんね。

ですので、B級グルメブログとか旅行記とか、家電使用記とか、そういった無難な方向に行きがちかもしれませんね。
ただライバルが多いですし、仕入れというか、売上原価が発生して旨味はほとんどなさそうですし、また、動画で配信できれば、ブログにこだわる必要もないですしね。

さらに、コツや経験をつかみつつ、どのコンテンツが当たるか?自分が記事を豊富に用意できるか?となると、相当な時間を費やすのでは?と思います。
言い方悪いですが、仕事で忙しい人には無理だと思います。

以上、こういったことのうち、いくつかが、「入門書」に書かれていますが、多分に読むだけで嫌になってしまうと思います。

私はエンジニアで、WordPress導入について興味があったので、上記を試しましたが、まあ、大変でしたね。
経験で得たものは沢山ありました(固定ページ作成は特に)。が、素人では手が出ないと思います。

物販アフィリエイトで小遣いを稼ぐならば、無料ブログで十分かと

多くの場合は、日常や趣味の延長でブログをかくと思いますので、そのついでに商品紹介でお小遣いを稼ぎたいのであれば、無料ブログで十分かと思います。

前準備

前置きが長くなりましたが、変換作業を始めていきます。

各エントリをブラウザで保存する

私の場合は、ドメイン及びサーバーの停止期限が迫っていたのと、100件程度のエントリしかなかったので、各エントリをブラウザで保存しました。
画像等でいくつか壊れた箇所がありましたが、それ以外は無事保存はできました。

これで、スタンドアロンで旧ブログのレイアウトを含めた内容が把握できます。

この保存したページは、後で、インポート後との比較(間違い探し?)をする際に使用しますが、次の点で特に役に立ちました。

  • ページの一部(共通している記述部分を)をプラグイン等で部品化している箇所の復旧
    (プラグインの箇所はエクスポートで展開してくれない。)
  • プログラムのソースコードのpreタグの復旧

ちょっと、億劫な作業ですが、やっておいた方が良いです。

WordPressからエクスポート

「ツール>エクスポート」でエクスポートします。

個人的には「投稿」で行いましたが「すべてのコンテンツ」でも大丈夫だと思います。

はてなブログへインポート

Markdownモードにしておく(任意)

私は、普段からMarkdownで記述していましたので、Markdownモードにしておきます。

インポートする

管理画面からインポートしてください。

100件程度でしたら、わりとすぐに終わります。

エントリとURLを確認

各エントリがインポートされているか、確認してください。

さらに、URLが、WordPress推奨の形でインポートされているかも確認してください。

(私はこちらは無問題でした。)

タグがカテゴリー化するので、不要ならば除去する

多分に、最初に???と思う箇所はこれです。

不要なタグがあれば除去しますが、エントリも多いですし、殆どの場合は放置かと思います。

ブログ内のリンクを貼り直す

私はプログラミングテクニックのハウツーでしたので、内部リンクが多数ありましたが、こちらは表に表示されず、さらに古いURLのままです。

これを、根気よく「はてな記法」にて貼り直していきます。
URLをペーストすれば、アシスタント画面が表示されますので、そちらに従います。

WordPressのブロック記法が残っていたので、Markdown記述に戻す

こちらの作業が思いの外かかりました。

これは、インポート前に、テキストファイル時点でリプレースすべきだったかも…

WordPressでは今流行り?のブロックエディタを使用していたので、中を見ると、それがそのまま残っています。
このままでは文字数が無駄に多くなってしまうので、ともかく、なんとかしたいところ。

修正しながら、CSSを手直ししていく

参考までに、こちらのブログのデザインテーマは、デフォルトの「Life」になります。
CSSでカスタマイズを行っています。

主な内容は、

  • タイトルおよびエントリータイトルの大きさ調整
  • 目次のカスタマイズ
  • h2/h3/h4を目立つように、さらに前後の間隔を開ける
  • strongをアンダーライン化
  • preタグの色を白黒反転
  • WordPressによくあるボックス表示の追加

こんなところでしょうか。
あまり凝らないように心がけます。

pタグ/brタグ

●変更前

<!-- wp:paragraph -->
<p>1行目です。<br>2行目です。</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>3行目です。</p>
<!-- /wp:paragraph -->

●変更後

1行目です。  
2行目です。

3行目です。

「1行目です。」の直後に2文字の半角スペースを挿入します。
これでbrタグの代わりになります。

これだけでも、相当スッキリします。

h2/h3/h4タグ

こちらも冗長化していますので、なんとかします。

●変更前

<!-- wp:heading -->
<h2 class="wp-block-heading">これが</h2>
<!-- /wp:heading -->

●変更後

## こうなる

h3の場合は###とします。

ul/olタグ

●変更前

<!-- wp:list {"className":"hoge"} -->
<ul class="foo><!-- wp:list-item -->
<li>やること1</li>
<!-- /wp:list-item -->

<!-- wp:list-item -->
<li>やること2</li>
<!-- /wp:list-item -->

<!-- /wp:list -->

●変更後(ulタグ)

* やること1
* やること2

olタグの場合は、

1. やること1
1. やること2

となります。

これはかなり文字数が圧縮できますね。

アンダーライン

WordPress系のブログを見ていると、アンダーラインをよく見かけますよね。
こちらはWordPressの機能というよりも、テーマの機能でしょうか。
こちらをstrongに変更し、CSSでアンダーラインをつけるようにします。

CSSは「管理画面>デザイン>カスタマイズ(スパナのアイコン)>{}デザインCSSのボックス内をクリック」で、エディタ(のようなもの)が開きますので、そこに記述します。

CSSについては、記述後に、テキストファイル等にバックアップしておきましょう。

●変更前(SWELLの例)

<span class="swl-marker mark_yellow">この箇所にはアンダーラインが付きます</span>

●変更後(本文)

**この箇所にはアンダーラインが付きます**

●変更後(CSS)

.entry-content strong {
    font-weight: normal;
    background: linear-gradient(transparent 50%, yellow 50%);
}

上記例はstrongでの例ですが、emタグ(*〜*)で対処したい方は、上記を応用してみてください。

WordPressテーマが用意したボックス類

私はWordPressの人気テーマ「SWELL」を使用していましたが、その他のテーマにも、見栄えを良くするオプションが多数用意されています。

代表的なものは「吹き出し」ですが、私が扱う内容はプログラミングテクニックなので、幸い無使用でした。
ただし、ボックス類はいくつか使用していました。

swell-theme.com

上記リンクで言うところの「アイコン付きボックス(小)」と「アイコン付きボックス(大)」ですね。
こちらを置き換えていきますが、こちらはMarkdownでも「はてなブログ」でも用意されていませんでしたので、以下のようにしました。

この記事に登場する「!」の例になります。

●変更後(本文)

<fieldset class="kakomi_alert"><legend class="kakomi_alert"></legend>
注意点を記述します。
</fieldset>

●変更後(CSS)

/* 共通 */
fieldset {
    margin-bottom: 3em;
    padding: 0px 20px 20px 20px;
    border: 2px solid;
    border-radius: 10px;
    -moz-border-radius: 10px;
    -webkit-border-radius: 10px;
}

fieldset ::before {
    margin-left: 10px; 
    margin-right: 10px; 
    font-family:blogicon;
    font-size:35px;
}

/* 警告 */
fieldset.kakomi_alert {
    border-color: gold;
}

fieldset .kakomi_alert::before {
    content:"\f041"; /* blogicon-warning */
    color: gold;
}

変更箇所が結構な数あったため、

  • そもそも、ボックスで囲む必要が本当にあるのか?
  • ボックス内の文章は適切か?

というチェックを行い、不要な文章、例えばフランクな表現や、独り言のようなものは、極力削除することにし、ボックスの数を減らしました。

参考までに、以下のボックスに絞っています。

チェックアイコン:記事や章の要約、リンク先の説明

/* チェック */
fieldset.kakomi_check {
    border-color: limegreen;
}

fieldset .kakomi_check::before {
    content:"\f029"; /* blogicon-check */
    color: limegreen;
}

メモアイコン:備考、これを行う意味など

/* メモ */
fieldset.kakomi_memo {
    border-color: orange;
}

fieldset .kakomi_memo::before {
    content:"\f024"; /* blogicon-pen */
    color: orange;
}

はてなアイコン:質問

/* はてな */
fieldset.kakomi_hatena {
    border-color: fuchsia;
}

fieldset .kakomi_hatena::before {
    content:"\f01c"; /* blogicon-help */
    color: fuchsia;
}

警告アイコン:これを行うとリスクがあることを警告

/* 警告 */
fieldset.kakomi_alert {
    border-color: gold;
}

fieldset .kakomi_alert::before {
    content:"\f041"; /* blogicon-warning */
    color: gold;
}

禁止アイコン:これは不可能、あるいは禁止

/* 禁止 */
fieldset.kakomi_stop {
    border-color: red;
}

fieldset .kakomi_stop::before {
    content:"\f025"; /* blogicon-close */
    color: red;
}

preタグにもどす

私はWordPressのテーマはSWELLを使用しており、その作者が作成したと思しき、「Highlighting Code Block」というプラグインを利用しておりましたが、こちらは一般的なpreよりも、以下の利点がありました。

  • 行番号の表示
  • プログラム名の表示
  • コピーボタン押下でソースコードがコピーできる
  • プログラムごとにカラー表示

反面、以下の欠点があります。

  • preタグではない(div+codeタグ)
  • 記号は&amp;といった参照文字に変換されてしまう

はてなブログでも```言語名でカラーリングをしてくれますが、「Highlighting Code Block」での記述箇所はインポートで変換してくれませんので、カラーリングはしてくれません。
なので、この変更は、私的には必須としました。

●変更前

<!-- wp:loos-hcb/code-block {"langType":"js","langName":"JavaScript","fileName":"hoge.js"} -->
<div class="hcb_wrap"><pre class="prism undefined-numbers lang-js" data-lang="JavaScript"><code>if (strStatus == &#39;1&#39;) {
  処理1
} else if (strStatus == &#39;2&#39;) {
  処理2
}</code></pre></div>
<!-- /wp:loos-hcb/code-block -->

●変更後(```の直後にjavascriptを付加)

if (strStatus == '1') {
  処理1
} else if (strStatus == '2') {
  処理2
}

&で始まる実体参照はとてもじゃないけど手作業で変換はできませんので、「前準備>各エントリを保存する」で保存したページをブラウザで表示し、コピペしました。
(運良く、コピーボタンが生きていたので助かりました。)

テーブル

●表示例

タグ 発火点 取得方法
SELECT onchange() elm.value
INPUT(radio) onclick() elm.value
INPUT(checkbox) onclick() elm.forEach(function (el)
⇒el.checkedを確認(true or false)

●変更前

<!-- wp:table -->
<figure class="wp-block-table"><table><thead><tr><th>タグ</th><th>発火点</th><th>取得方法</th></tr></thead><tbody><tr><td>SELECT</td><td>onchange()</td><td>elm.value</td></tr><tr><td>INPUT(radio)</td><td>onclick()</td><td>elm.value</td></tr><tr><td>INPUT(checkbox)</td><td>onclick()</td><td>elm.forEach(function (el)<br>⇒el.checkedを確認(true or false)</td></tr></tbody></table></figure>
<!-- /wp:table -->

●変更後

|タグ|発火点|取得方法|
|:---|:---|:---|
|SELECT|onchange()|elm.value|
|INPUT(radio)|onclick()|elm.value|
|INPUT(checkbox)|onclick()|elm.forEach(function (el)<br>⇒el.checkedを確認(true or false)|

こちらは画面とにらめっこしないと変換できないかも…

面倒でしたら、スルーしてもいいかもしれません。

codeタグ(インラインのcode)

●変更前

<code>hoge</code>

●変更後

`hoge`

こちらは、そのままにしてもいい気がします。

最後に

以上となります。

修正することで、文字数が半分以下に圧縮できると思います。

また、意外と、誤字脱字や説明不足等を見つける、良い機会となりました。

【Ruby on Rails7】バリデーションだけではなくスキーマも連携させる

この記事では、Ruby on Rails7における、スキーマとバリデーションの連携性を解説します。

バリデーションとスキーマの属性は一致させておく

バリデーションだけではなく、実際のテーブルも合わせておくことが望ましいです。

以降は、実際のシステムに近い例示をしつつ、記入例を紹介していきます。

null: falseと, default: ""とpresence: true

Railsではキーや更新日次など主要な項目以外、NULL抑制やデフォルト値は設定されないのですが、多くのDBでは、NULL抑制やデフォルト値を持つことを推奨されます。

以下はマイグレーションの例です。

add_index :customers, :fax_no, unique: true

この例では、デフォルト値が入るので、バリデーションは不要かと思いますが、もし、入力必須を伴う場合は、モデルは以下となります。

validates :fax_no, presence: true

FAXってまだ使うの?
複合機くらいはあるでしょ?的な議論はさておき、入力必須にしている入力フォームは見かけなくなりましたね。

unique: trueとuniqueness: true

Railsの場合はidというプライマリーキーがデフォルトで付加されます。
しかし、顧客コードや商品コード、社員番号、支店コードなど、idの他に基幹システムのマスターコードを持ちたいというニーズはあると思います。

その場合の、マイグレーションはこうなります。
(例は、ユーザーテーブルに社員番号を持ちたいといったケース。)

add_index :users, :shain_cd, unique: true
change_column :users, :shain_cd , :string, limit: 10, null: false, default: ''

モデルはこうなります。

validates :shain_cd, presence: true, uniqueness: true

ユニークキーを作成するとともに、バリデーションでuniqueness: trueを追加します。
これで社員番号の一意性が確保されます。

可能な限り、コード値の場合は有効桁数を設定しましょう(基幹システムと合わせることが望ましい)。
NULLも抑制し、default: ''も付加します。

limit: nとmaximum: n

maximum: nのみ

マイグレーションはこうなります。

change_column :tables, :short_name , :string, limit: 10

モデルはこうなります。

validates :short_name, length: { maximum: 10 }

上記は0文字〜10文字入力可能ですが、入力必須の場合はminimum: 1をつけるか、presence: trueをつけるか、どちらかになります。
(エラーは排他的なので2行になることはありませんが、1文字以上10文字以下というメッセージが、好ましいか悩むところですね。)

minimum: nとmaximum: n

例えば2文字のみ有効としたい場合、マイグレーションはこうなります。

change_column :tables, :category , :string, limit: 2, null: false

モデルはこうなります。

validates :category, length: { minimum: 2, maximum: 2 }

minimum: nとmaximum: nを指定した場合、必ず2文字入力しないといけなくなるので、presence: trueが余計になると思います。
(あっても構いませんが、エラーメッセージが2行表示されて、ちょっとうるさい印象です。)

stringとnumericality: true

こちらは、(桁数制限以外の)マイグレーションは必要ありません。

文字列カラムなのですが、入力値は数値のみというケースがあると思います。
その場合のモデルのバリデーションは以下となります。

validates :flg, numericality: true, length: { minimum: 1, maximum: 1 }

上記例では1桁の数値のみが入力可能です。

integerで行うという方法もありますが、ちょっとめんどくさい気もします。
stringの方が楽そうですね。

qiita.com

stringでnumericalityなケースは大抵の場合、値がすでに決まっているケースが殆どで、ラジオボタンやSELECT/OPTIONのお世話になると思いますので「数値以外エラー」というバリデーションが活躍するケースは少ない気がします。

【Ruby on Rails7】RSpecおよびshoulda-matchersのインストール

この記事では、Ruby on Rails7にテストプログラムのRSpecと、その簡略化記法shoulda-matchersを導入します。

RSpecおよびshoulda-matchersの導入

Gemfileに追記しインストール

まずはGemfileに追記します。

group :development do
  gem "rspec-rails"
end

gem "shoulda-matchers"

コマンドラインで、インストールします。

bundle install

さらに、RSpecをインストールします。

bundle exec rails g rspec:install

shoulda-matchersの設定

spec/rails_helper.rbの最後に、

Shoulda::Matchers.configure do |config|
  config.integrate do |with|
    with.test_framework :rspec
    with.library :rails
  end
end

を追記します。

使用方法

モデルにRSpecテストプログラムを導入

以下のコマンド実行します。
(Userはモデル名の例。)

bundle exec rails g rspec:model User

次に、spec/models/user_spec.rbの、

pending 'add some examples to (or delete) #{__FILE__}';

を削除します。

shoulda-matchersの一例

先ほど変更したspec/models/user_spec.rbに追記します。

  it { is_expected.to validate_presence_of :shain_no }
  it { should have_db_index(:shain_no).unique }

上記はshoulda-matchersのの記法例です。RSpecでは一つのエラーを実現するだけでも、(ダミーデータを作成するなど)数行かかるのですが、1行で済みます。
テーブルusersには、shain_noがあり、idの他にこの項目でもユニークになっていないといけないとします。
当然ながら入力必須です。

もし、モデルuserに何もせってせず、このまま、下記のコマンドを実行すると、

bundle exec rspec spec/models/user_spec.rb -f d

2件エラーになるはずです。

そこでバリデーションを、app/models/user.rbに追記します。

validates :shain_no, presence: true, uniqueness: true

下記のコマンドを実行すると、

bundle exec rspec spec/models/user_spec.rb -f d

エラーはなくなり、さきほどの2件は成功となるはずです。

shoulda-matchersのできること、できないこと

shoulda-matchersについて

shoulda-matchersはモデル用の省略記法のようですので、単純なエラーの記述の冗長化を防ぐことができます。
RSpecは単純なエラーでも数行かかるので、そうではないエラーとの見分けがつくのは助かります。

こちらを参考にさせていただきました。ありがとうございます。

qiita.com

本家サイト。
下部に使用できるテスト内容が書いていますが、かなり豊富に用意されていますね。

github.com

【Ruby on Rails7】SELECT/OPTIONタグ用のテーブルを作成

この記事では、HTMLタグ(SELECT/OPTION)を作成するためのテーブルをマイグレーションし、ビューで使用する方法を考えてきます。
さらに、cocoonで管理する方法も考えます。

テーブル構造を考える

他の(マシンの)システムで多いのは、コードテーブル、名称テーブルの類を利用する

一般論として、顧客マスターや商品マスター、社員マスターといったマスターでは、顧客マスターや商品コード、社員番号といったユニークキー(プライマリーキー)と、顧客名、商品名、社員名といった名称、さらに住所や販売価格、在庫数といった付随する情報がセットされていると思います。

どんなシステムでもコード値の存在チェックは行いますので、それを利用して、名称を表示しています。

そういったマスター以外に、細かい区分やフラグといったものに名称をつけたいといったニーズがあり(例えば、商品でしたら、1=仕入商品、2=委託商品など)、一般的には、これを名称テーブルあるいはコードテーブルといったファイル名で取り扱うケースが多々あります。

もし、これがすでに基幹システムなどに存在し、夜間バッチ等でRails用のデータベースに差し替えるようなことが可能ならば、それを利用してSELECTのOPTIONタグの表示名称として利用したいのですよね。

コードテーブルのデータ構造について

多くのケースでは、

  • コードヘッダー:コードキー(ユニークキー)と、このコードの名称や使用目的等を記述
  • コード明細:コードキー(ユニークキー)と実際の区分値とその名称や略称を保持

といった親子関係で表現されていることが多いと思います。
画面に表示される桁数には制限があるので、多くの場合は略称を持っています。

Ruby on Rails(Activerecord)の制限をもとにしたテーブル構成

もし、Railsでこれをインポートするには、以下の構造になるはずです。

  • コードヘッダー:idとコードキー(ユニークキー)と、このコードの名称や使用目的等を記述
  • コード明細:*idと、ヘッダーテーブルのid**、実際の区分値とその名称や略称を保持

Railsのプライマリーキーはidになりますので、子にはヘッダーテーブルのidを持つことで、親子関係を実現します。

上記例では、子にはコードキー(ユニークキー)は持たないようにしていますが、ActiverecordあるいはSQLの条件として持っても良いかと思います。

こちらをもとにして実装していくことにします。

(参考)夜間バッチで差し替えるには?

参考までに、ロジックを考えてみます。

プログラムその1:コードキー(ユニークキー)が期間。Rails両者に存在する場合
親テーブルをUPDATEし、子のテーブルをDELETE/INSERTします。
その際、親のidを子にセットします。

プログラムその2:コードキー(ユニークキー)がRails側に存在しない場合
親テーブルをINSERTし、子のテーブルをINSERTします。
親テーブルのidは新規に与えられますので、コードキー(ユニークキー)で親のidにを取得し、その親idも含めた形で子をINSERTしてください。

プログラムその3:コードキー(ユニークキー)がRails側にのみ存在する場合
基幹側が削除されたとみなし、親テーブルをDELETEし、子のテーブルもDELETEします。

基幹側以外のデータも管理(入力)したい場合は、別のテーブルを用意したほうが賢明かもしれません。

実装手順

親子のテーブル作成

まずは親のテーブルを作成します。

bundle exec rails g scaffold Codeheader code_key:string code_name:string

code_keyは基幹システムのプライマリーキー、code_nameはこのコード自体の名称を記述します。
のちほど、cocoonでメンテナンス画面を作成するために、g scaffoldにしておきます。

次に、このテーブルを作成します。

bundle exec rails g model Codedetail value_key:string value_name:string short_name:string codeheader:belongs_to

value_keyは区分やフラグの値、value_nameはその名称、short_nameはその略称です。
codeheader:belongs_toで親テーブルのidをセットするカラムが作成されます(MySQLではid同様bigintとなる)。
こちらはscaffoldではなくg modelにしておきます。

モデルに追記する

app/models/codeheader.rbに以下を追記します。

  has_many :codedetails, dependent: :destroy
  #accepts_nested_attributes_for :codedetails, reject_if: :all_blank, allow_destroy: true
  accepts_nested_attributes_for :codedetails, allow_destroy: true

reject_if: :all_blankは親のみ作成される危険性があるため、記述を除去します。

app/models/codedetail.rbに以下を追記します。

  belongs_to :codeheader

ヘルパーにSELECT/OPTION用の配列を求める関数を作成

app/helpers/application_helper.rbに以下を追記します。

  # select/optionを取得 
  def get_select_option(key)
    return Codedetail.joins(:codeheader).select("codeheaders.*, codedetails.*").where("codeheaders.code_key = ?",key)
  end

ビュー側でヘルパーの関数を埋め込む

_form.html.hamlに以下を追記します(追記例)。

   .field.col-md-4.mb-3
      = f.label :category, class: "form-label"
      = f.collection_select :category, get_select_option('category'), :value_key, :value_name, {prompt: "選択してください"}, :class => 'form-control', :id => 'category'

上記はcategoryというコードキーを持つ一覧を取得してOPTIONタグを生成していきます。
value_nameshort_nameに変更すると、略称がセットされると思います。

かなり簡単に実装できましたね。

cocoonを使って、メンテンス画面を作成する

基幹からデータ持ってくるだけでしたら、別段必要はないですが、基幹もなく、イチから用意したい場合は、cocoonを利用した管理画面を用意したほうが良いかと思います。

幸い、cocoonについてのコラムは用意していますが、それをリンクしながら、異なる点などを説明します。

cocoonのインストール

inuinu-tech.hatenablog.jp

modelおよびscaffoldの作成」はこの記事のものを使用してください。
それ以降もProject、project、projectsはCodeheader、codeheader、codeheadersに差し替えてください。
同様に、Task、task、tsaksはCodedetail、codedetail、codedetailsに差し替えてください。

コントローラーの変更」のパラメータは下記のように変更してください。

●app/helpers/application_helper.rb

    # Only allow a list of trusted parameters through.
    def codeheader_params
      #params.require(:codeheader).permit(:code_key, :code_name)
      params.require(:codeheader).permit(:code_key, :code_name, codedetails_attributes: [:id, :value_key, :value_name, :short_name, :_destroy])
    end

cocoonのエラー訂正

inuinu-tech.hatenablog.jp

tasksはcodedetailsに差し替えます。

inuinu-tech.hatenablog.jp

Task、task、tsaksはCodedetail、codedetail、codedetailsに差し替えてください。

inuinu-tech.hatenablog.jp

子の二重登録エラーは、前出のapp/helpers/application_helper.rbのパラメータ変更が行われているのならば、発生しないはずです。

子の0行エラー抑制

inuinu-tech.hatenablog.jp

Project、project、projectsはCodeheader、codeheader、codeheadersに差し替えてください。
同様に、Task、task、tsaksはCodedetail、codedetail、codedetailsに差し替えてください。

子の件数抑制

inuinu-tech.hatenablog.jp

inuinu-tech.hatenablog.jp

Project、project、projectsはCodeheader、codeheader、codeheadersに差し替えてください。
同様に、Task、task、tsaksはCodedetail、codedetail、codedetailsに差し替えてください。
(taskはTASKもありますね。)

行コピー

inuinu-tech.hatenablog.jp

inuinu-tech.hatenablog.jp

inuinu-tech.hatenablog.jp

taskのみcodedetailに読み替えてください。

子の重複チェック

inuinu-tech.hatenablog.jp

projectをcodeheaderに差し替えてください。
同様に、tsaksはcodedetailsに差し替えてください。

以上で、cocoon化は完了するかと思います。

お好みで

お好みで、ページネーションや検索、ソートを実装してみてください。

inuinu-tech.hatenablog.jp

inuinu-tech.hatenablog.jp

時間があれば、日本語化も。

inuinu-tech.hatenablog.jp

これで、ほぼほぼ良い感じになると思います。

【Ruby on Rails7】Rails7を導入する

この記事では、Ruby on Rails7を導入します。
デフォルトではgemは共通領域にインストールされますが、他の環境を汚すリスクがあるので、プロジェクトにあるvendorフォルダ内にgemをインストールするようにします。

バージョン3.0以上のRubyを導入する

Rails7での推奨バージョンを確認する

Ruby 2.7.0以上が必須、Ruby 3.0以上が望ましい

7_0_release_notes

とあるので、最新バージョンに近いバーションをrbenvを通じてインストールします。

rbenvはMacでは使用できますが、Windows環境ですと異なるインストール手段になると思います。

結果3.2.2をインストールしました。

バージョン7.0以上のRubyを導入する

共通のgemエリアにインストールしたくない場合はどうするか?

下記サイトが参考になりました。ありがとうございます!

utano.jp

こちらのサイトの手順に近い方法でインストールしていきます。

ベースディレクトリを作成し、一時ディレクトリ(.bundle)内インストールする

まず、ベースとなるフォルダを作成します。
(このフォルダはプロジェクト名ではないことに注意してください。)

以下のコマンドを実行します。

bundle init

作成されたGemfileを変更します。

gem "rails", '=7.0.8'

7.0.8は執筆段階での最新バージョンになります。
(こちらは適宜変更してください。)

以下のコマンドを実行します。

bundle install --path .bundle/

これで、.bundleというフォルダにRailsその他がインストールされます。

Macの場合はデフォルトでは「.」で始まるファイルやフォルダは表示されません。
「command」+「shift」+「.」で表示させてください。
(Githubで使用する.giticnoreなども表示されるようになります。)

バンドル無しで、Railsのプロジェクトを開始する

以下のコマンドを実行してください。

SQLiteを使用する場合、

bundle exec rails new プロジェクト名 --skip-bundle

MySQLを使用する場合(事前にインストールする必要があります)、

bundle exec rails new プロジェクト名 -d mysql --skip-bundle

Gemfileだけを作成するようなイメージでしょうか。

Gemfileを追記する

プロジェクト名フォルダ内に作られたGemfileに必要なものを追記していきます。
一例として私の評価用のプロジェクトを上げておきます。

追記は必要となった都度でも全然問題はありませんが、erbかhaml でいくかは、先に決めておいたほうが良いかもしれません。

●Gemfile

# Bootstrap5
gem "bootstrap", "~> 5.2.0"
gem "mini_racer", "~> 0.6.2"

# テストプログラム
group :development do
  gem 'rspec-rails'
end
# テストプログラム(簡易書式化)
gem "shoulda-matchers"

# haml
gem 'haml-rails'

# 日本語化
gem "rails-i18n", "~> 7.0.0"

# ページネーション
gem "kaminari"
gem 'bootstrap5-kaminari-views'

# 検索機能
gem "ransack"

# cocoon
gem 'cocoon'

Bootstrap5については、gem以外にもいくつかの手順があります。下記を参照してください。

inuinu-tech.hatenablog.jp

テストプログラムでRSpecを使用したい場合は追記します。
shoulda-matchersは、RSpecの簡略記法となります。

hamlはgemのみでインストールは完結しますが、導入時点では、Rilas7でのレイアウトがerbとは異なっています。
詳細はこちらを参考にしてください。

inuinu-tech.hatenablog.jp

国際化パッケージ(=日本語化パッケージ)のi18nは、gemインストール以後も導入作業があります。下記を参照してください。

inuinu-tech.hatenablog.jp

ページネーションおよび検索機能(ソート機能含む)については、下記をご参照ください。
(両者は相性がいいので一括して導入したいということもあるかと思います。)

inuinu-tech.hatenablog.jp

inuinu-tech.hatenablog.jp

親子関係にあるテーブルを一括で入力する場合、明細追加や削除等のアクションを簡略化できるパッケージcocoonの導入および問題解決、関連する機能の追加については、下記のコラムシリーズをチェックしてみてください(関連するコラムは10以上あります)。

inuinu-tech.hatenablog.jp

ベースフォルダのgemを削除する

次に、ベースフォルダー直下にある.bundleフォルダとGemfileを削除します。

プロジェクト内のgemをvender内にインストールする

以下のコマンドを実行してください。

bundle install --path vendor/bundle/

(MySQLのみ)データベースを作成する

SQLiteの場合は、すでにデータベースファイルが作成されていると思いますが、MySQLの場合はデータベースに接続するためconfig/database.ymlを変更します。

  password: (パスワードを入力)

さらに、以下のコマンドを実行します。

rake db:create

MySQL Workbench等のユーティリティソフトで、データベースの作成を確認してください。

プロジェクト名_developmentプロジェクト名testの2つのデータベースが作成されているはずです。

Ruby on Railsでは、このようにDB・テーブル操作もRails上で行います。

動作確認

以下のコマンド実行してください。

bundle exec rails s

さらに、下記のURLをクリックしてください。

http://localhost:3000/

Railsのトップページが表示されたら、動作確認の完了です!

【Ruby on Rails7】Rails7にBootstrap5をNode.jsなしで導入する

Rails7ではNode.jsと決別しましたが、Bootstrap5はNode.jsのV8エンジンに依存しているようです。
この記事では、Node.jsを使用せず、mini_racerを含めて導入します。
さらに、他の選択肢はないのか?も考えていきます。

導入時の環境について

導入時の環境

導入時の環境は以下の通り。

前提条件としてNode.jsをインストールしていないこと

これは重要な点ですが、Node.jsをインストールしていないことが前提条件になります。
もし、すでにNode.jsを導入済みの場合で、アンインストールできない場合は、導入していないマシンを選択する等を行ってください。

BootstrapをNode.jsレス(Webpackerレス?)で導入しました!といった記事を見かけますが、JQueryベースのものを導入していたり、V8エンジンを別途導入なしで導入…といったものを見かけます。
特にV8エンジン無しで導入は、Node.jsがもともと導入されているのでは?といった、ステルス的な要素ないか?が気になるところです。

導入手順

emfileに追記しインストール

まずはGemfileにBootstrapを追加します。

# Bootstrap5
gem "bootstrap", "~> 5.2.0"
# please switch to Node.js (V8) or mini_racer (V8)が出た場合
gem "mini_racer", "~> 0.6.2"

思い切って最新版を入れちゃいましょう。
mini_racerについては、後ほど説明します。

さらに、

bundle install

でインストールします。

importmapのインストール

binディレクトリにimportmapがない場合は、下記コマンドを実行します。

bundle exec rails importmap:install

bin/importmapおよびconfig/importmap.rbが作成されます。

さらに、以下のコマンドを実行します。

bin/importmap pin bootstrap

Javascriptファイルの変更

app/javascript/application.jsに下記を追記します。

import 'bootstrap'

cssをscssに変更

まず、app/assets/stylesheets/application.cssの名前をaplication.scssに変更します。

その、app/assets/stylesheets/application.scssの中身を(すでにカスタマイズしていない場合)全て消して、以下を追記します。

@import "bootstrap";

設定は以上となります。

動作確認用のページを作成する

どこに何を作成するか?

動作確認については、Bootstrap+Javascriptですと、プルダウンメニューの例が良いと思います。<

メニューバー等が用意されていない場合は、下記参考サイトのビューを適当に移植してみてください。

getbootstrap.jp

qiita.com

プルダウンメニュー(dropdown)が動作していれば、Javascript含め、問題なくインストールされています。

mini_racerについて

Node.jsまたはmini_racerが導入されていないと、Railsがエラーを吐き出す

もし、Node.jsまたはmini_racerが導入されていないと、Railsがエラーを表示します。

please switch to Node.js (V8) or mini_racer (V8)

V8エンジンが必要だというエラーメッセージですが、どうやら、動作確認をNode.jsとmini_racerで行っているようです。
ここが、mini_racerをインストールする理由になっています。

環境によってインストールできるバージョンが違う(っぽい)

私の環境(Intel Mac)だけかもしれませんが、最新版のmini_racerでは動作しませんでした。
ですので、意図的にバージョンを下げています。

mini_racerはイマイチだという意見もネットには転がっているようですが、これが要因なのでしょうか?
以前のバージョンでも、とりあえず問題なさそうなので使用しています。

動作確認後Bootstrapを拡張したデザインテンプレートを導入するには?

CDNがあれば、importmapのpinを変更する

config/importmap.rbのpin以降のURLを変更します。

# config/importmap.rb
pin "bootstrap", to: "CDNのURL"

あるいは、Javascriptファイルをダウンロードして、サーバー内のパスを指定しても良いかと思います。

importmapはJavascriptのみ、CSSはどうするか?

ダウンロードし、app/assets/stylesheetsディレクトリに起きます。

さらに、app/assets/stylesheets/application.scssを変更します。
(下記はbootstrap.min.cssの場合。)

#@import "bootstrap";
@import 'bootstrap.min';

JQueryベースのBootstrapを使用し続ける場合

無理にBootstrapのバージョンを上げずに、別途JQueryをいれてしまったほうが良い

動作が安定しているのであれば、クライアントからデザイン変更等のリクエストがない限り、無理に変える必要は無いのでは?と思います。

以下に、Rails7での導入例を説明いたします。
まず、下記のコマンドを実行します。

bin/importmap pin jquery

config/importmap.rbを確認し、挿入されていない場合は、追記してください。

# config/importmap.rb
pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.7.1/dist/jquery.js"

上記のJQueryのが新しすぎるのであれば、適当に変更したほうが良いかと思います。

さらに、app/javascript/application.jsの中を確認し、下記の行がない場合は、追記してください。

// app/javascript/application.js
import jquery from "jquery"
window.$ = jquery

他の選択肢はないのか?

CDNあるいはダウンロードしたファイルでBootstrapを導入したほうが楽?

調べたところ、CDNを直接app/views/layout/application.html.erbに書く手法もあるようですね。

qiita.com

gem経由で受けられる恩恵もあるかと思いますので、kaminari用のbootstrapが動作するかは確認したほうが良いかもしれません。

こちらでも、プルダウンメニューが動作するのであれば、mini_racerの出番はないかもしれませんね。<

Node.jsレスあるいはJavascriptレスのCSSを採用したほうが楽?

Node.jsレスあるいはJavascriptレスのCSSを採用するという手法もあります。
TailwindCSSですとか、Yahoo!が開発したPure.cssもそうだったと思います。

techracho.bpsinc.jp

purecss.io

【Ruby on Rails7】Rails7にi18nを導入する

Ruby on Railsはバリデーションエラーが簡単に実現できますが、デフォルトは英語です。
この記事では、Ruby on Rails7に国際化ライブラリーi18nを導入し、日本語化を図ります。

i18nとは?

i18nは正式には国際化ライブラリーの総称です。
フルスペルは「internationalization」なのですが、長すぎるのでi18nと表示しているようです。

これにより、アプリを多言語対応できます。

Railsのメッセージは英語なのですが、i18n用のgemをインストールし、翻訳用のja.ymlを定義した後、ビュー及びコントローラーをi18nを呼び出すように変更していくことで、日本語化することができます。

日本語をビューやコントローラーにベタに書くことも当然できますが、ja.ymlに定義しておけば、クライアントの急な(気まぐれな)表記変更に素早く対応できます。

18nのインストール

Gemfileに追記しインストール

まずはGemfilei18nを追加します。

# 日本語化
gem "rails-i18n", "~> 7.0.0"

さらに、

bundle install

でインストールします。

configに定義を追加

config/application.rbに、

module WagagunMy
  class Application < Rails::Application
(中略)
    # 日本語化
    config.i18n.available_locales = %i[ja en]
    config.i18n.default_locale = :ja

    # 複数のロケールファイルが読み込まれるようpathを通す
    config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

  end
end

を追記します。

localesディレクトリを作成し、テンプレート的なファイルを置く

localesディレクトリを作成し、以下のファイルを置いておきます。
(役に立つのかは不明ですが。)

https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/en.yml

https://raw.githubusercontent.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml

ja.ymlを記述

2つのディレクトリを作成し、ja.ymlを記述

あとは任意のディレクトリを作成し、そこにja.ymlを作成しますが、私はいくつかの参考サイトをもとにactiverecordviewsの2つのディレクトリを作成し、それぞれにja.ymlを記述しました。

activerecordディレクトリのja.yml

config/locales/activerecord/ja.ymlは次の内容になっています。

ja:
  activerecord:
    models:
      project: 'プロジェクト'
      task: 'タスク'
    successful:
      messages:
        created: "%{model}に1件登録されました。"
        updated: "%{model}の情報が変更されました。"
        destroyed: "%{model}から1件削除されました。"
    attributes:
      task:
        description: '詳細'
        done: "完了"
      projet/tasks: 
        description: "詳細"
        done: "完了"
      projet: 
        tasks: 'タスク'

viewsディレクトリのja.yml

config/locales/views/ja.ymlは次の内容になっています。

zja:
  defaults:
    show: "詳細"
    back: "戻る"
    delete: "削除"
    edit: "変更"
    new: "新規作成"
    cancel: "キャンセル"
    save: "登録"
  views:
    pagination:
      first: '最初'
      last: '最後'
      previous: '前'
      next: '次'
      truncate: '...'
  projects:
    index:
      title: 'プロジェクト一覧'
    new:
      title: 'プロジェクト登録'
    edit:
      title: 'プロジェクト変更'
    show:
      title: 'プロジェクト詳細'

pagination:はkaminari(ページネーション用のgem)ですね。

inuinu-tech.hatenablog.jp

ビューを記述

削除ガイダンスをヘルパーに記述

app/helpers/application_helper.rbにビューの削除ボタンのガイダンスに指定するconfirm_deleteを定義します。

module ApplicationHelper
  
  # 削除の確認メッセージ
  def confirm_delete
    return '本当に削除しますか?'
  end

index.html.haml

%h2= t ".title"

%p#notice.text-success
  = notice
(中略)
= link_to (t "defaults.new"), new_project_path, class: 'btn btn-primary'
(中略)
  %tbody
    - @projects.each do |project|
(中略)
          = link_to (t "defaults.show"), project, class: 'btn btn-info'
          = link_to (t "defaults.edit"), edit_project_path(project), class: 'btn btn-primary'
          = button_to (t "defaults.delete"), project method: :delete, data: { turbo_confirm: confirm_delete }, class: 'btn btn-danger'

「t」で始まる箇所(タイトル、リンク、ボタン)が、日本語に置き換わります。
削除完了メッセージを表示するために、3〜4行目を追加します。
削除ボタンには、先程作成したconfirm_deleteを埋め込みます。

show.html.haml

%h2= t ".title"
(中略)
= link_to (t "defaults.edit"), edit_project_path(@project), class: 'btn btn-primary'
\|
= link_to (t "defaults.back"), projects_path, class: 'btn btn-secondary'

new.html.haml

%h2= t ".title"

= render 'form'

= link_to (t "defaults.back"), projects_path, class: 'btn btn-secondary'

「t」で始まる箇所(タイトル、リンク、ボタン)が、日本語に置き換わります。

edit.html.haml

%h2= t ".title"

= render 'form'

= link_to (t "defaults.show"), @project, class: 'btn btn-info'
\|
= link_to (t "defaults.back"), projects_path, class: 'btn btn-secondary'

「t」で始まる箇所(タイトル、リンク、ボタン)が、日本語に置き換わります。

.html.haml

= form_for @project do |f|
  - if @project.errors.any?
    #error_explanation
      %h4= t("errors.template.header", model: @project.model_name.human, count: @project.errors.count)
      %ul
        - @project.errors.full_messages.each do |message|
          %li= message

「t」で始まるバリデーションエラーメッセージが、日本語に置き換わります。

コントローラーを記述

app/controllers/projects_controller.rb

  # POST /projects or /projects.json
  def create
    @project = Project.new(project_params)

    respond_to do |format|
      if @project.save
        #format.html { redirect_to project_url(@project), notice: "Project was successfully created." }
        format.html { redirect_to project_url(@project), :notice => "#{t('activerecord.successful.messages.created',:model => @project.model_name.human)}" }
        format.json { render :show, status: :created, location: @project }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /projects/1 or /projects/1.json
  def update
    respond_to do |format|
      if @project.update(project_params)
        #format.html { redirect_to project_url(@project), notice: "Project was successfully updated." }
        format.html { redirect_to project_url(@project), :notice => "#{t('activerecord.successful.messages.updated',:model => @project.model_name.human)}" }
        format.json { render :show, status: :ok, location: @project }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @project.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /projects/1 or /projects/1.json
  def destroy
    @project.destroy

    respond_to do |format|
      #format.html { redirect_to projects_url, notice: "Project was successfully destroyed." }
      format.html { redirect_to projects_url, :notice => "#{t('activerecord.successful.messages.destroyed',:model => @project.model_name.human)}" }
      format.json { head :no_content }
    end
  end

「#{t(」で始まる各更新完了メッセージが、日本語に置き換わります。
(コメントになっている行は変更前の内容です。)<

最後に

とりあえず、これで、日本語表示ができるようになります。