ためすう

大量データを作成する (MySQL)

2020-11-07

やったこと

テストデータで、1000万件のデータを用意する必要があったので、

MySQL だけで完結できるようにやってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

テーブル準備

CREATE TABLE IF NOT EXISTS `bulk_test` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`text_data` varchar(20) NOT NULL,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci
;

INSERT 実行

1件目

INSERT INTO bulk_test values (null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW());

これを欲しい件数まで繰り返します。

INSERT bulk_test (id, text_data, created_at, updated_at)
  SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;

1 -> 2 -> 4 -> 8 … とどんどん投入データが増えていきます。

実行結果

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)
    ->   SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1 row affected (0.01 sec)
Records: 1  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4 rows affected (0.00 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8 rows affected (0.00 sec)
Records: 8  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 16 rows affected (0.00 sec)
Records: 16  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 32 rows affected (0.00 sec)
Records: 32  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 64 rows affected (0.00 sec)
Records: 64  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 128 rows affected (0.00 sec)
Records: 128  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 256 rows affected (0.02 sec)
Records: 256  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 512 rows affected (0.00 sec)
Records: 512  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1024 rows affected (0.01 sec)
Records: 1024  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2048 rows affected (0.01 sec)
Records: 2048  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4096 rows affected (0.03 sec)
Records: 4096  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8192 rows affected (0.10 sec)
Records: 8192  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 16384 rows affected (0.06 sec)
Records: 16384  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 32768 rows affected (0.12 sec)
Records: 32768  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 65536 rows affected (0.22 sec)
Records: 65536  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 131072 rows affected (0.45 sec)
Records: 131072  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 262144 rows affected (1.09 sec)
Records: 262144  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 524288 rows affected (2.05 sec)
Records: 524288  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 1048576 rows affected (4.62 sec)
Records: 1048576  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 2097152 rows affected (9.64 sec)
Records: 2097152  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 4194304 rows affected (22.91 sec)
Records: 4194304  Duplicates: 0  Warnings: 0

mysql> INSERT bulk_test (id, text_data, created_at, updated_at)    SELECT null, CONCAT('dummy text', CEIL(RAND() * 100)), NOW(), NOW() FROM bulk_test;
Query OK, 8388608 rows affected (44.87 sec)
Records: 8388608  Duplicates: 0  Warnings: 0

mysql> select count(*) from bulk_test;
+----------+
| count(*) |
+----------+
| 16777216 |
+----------+
1 row in set (6.51 sec)

16777216 件のレコードが出来上がりました。

unique キー制約、外部キー制約があると、もう少し複雑になるので、

ruby, python などでプログラム書いた方が速いかもしれません。

MySQL でランダムな数値を出力する (MySQL)

2020-11-03

やったこと

MySQL で、テストデータを作成するときに、ランダムな数値を出力したいことがあったので

やってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper

調査

例えば、0 ~ 5 までの数字を出力するとします。

  • RAND (0 <= v < 1.0) を返す
  • ROUND(X) 四捨五入する
  • FLOOR(X) X 以下で最大の整数値を返す

これを組み合わせ使います。

rand の動きチェック

mysql> SELECT RAND();
+---------------------+
| RAND()              |
+---------------------+
| 0.28683608517687403 |
+---------------------+
1 row in set (0.00 sec)

mysql> SELECT RAND();
+--------------------+
| RAND()             |
+--------------------+
| 0.9208513711773337 |
+--------------------+
1 row in set (0.00 sec)

round の動きチェック

mysql> SELECT ROUND(2.4);
+------------+
| ROUND(2.4) |
+------------+
|          2 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(2.5);
+------------+
| ROUND(2.5) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(2.47);
+-------------+
| ROUND(2.47) |
+-------------+
|           2 |
+-------------+
1 row in set (0.00 sec)

floor の動きチェック

mysql> SELECT FLOOR(2.4);
+------------+
| FLOOR(2.4) |
+------------+
|          2 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(3.2);
+------------+
| FLOOR(3.2) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(3.5);
+------------+
| FLOOR(3.5) |
+------------+
|          3 |
+------------+
1 row in set (0.00 sec)

0 ~ 5 までの数字を出力する (ROUND + RAND)

mysql> SELECT ROUND(RAND() * 5);
+-------------------+
| ROUND(RAND() * 5) |
+-------------------+
|                 1 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT ROUND(RAND() * 5);
+-------------------+
| ROUND(RAND() * 5) |
+-------------------+
|                 5 |
+-------------------+
1 row in set (0.00 sec)

これだと

  • 0.0 ~ 0.4 => 0
  • 0.5 ~ 1.4 => 1

となり、端っこの方でばらつくかもしれません。

なので rand() x 6 して、0... ~ 5.9... して切り捨てすればいい感じになりそうです。

0 ~ 5 までの数字を出力する (FLOOR + RAND)

mysql> SELECT FLOOR(RAND() * 6);
+-------------------+
| FLOOR(RAND() * 6) |
+-------------------+
|                 4 |
+-------------------+
1 row in set (0.00 sec)

mysql> SELECT FLOOR(RAND() * 6);
+-------------------+
| FLOOR(RAND() * 6) |
+-------------------+
|                 0 |
+-------------------+
1 row in set (0.00 sec)

参考

mysqldump を使ってみる (MySQL)

2020-11-01

やったこと

mysqldump を使ってみます。

確認環境

$ mysql --version
mysql  Ver 14.14 Distrib 5.6.25, for Linux (x86_64) using  EditLine wrapper
$ mysqldump --version
mysqldump  Ver 10.13 Distrib 5.6.25, for Linux (x86_64)

調査

dump 対象となるテーブル

  • データベース名: test
  • テーブル名: test_table
mysql> select * from test_table;
+----+------+------+------+
| id | col1 | col2 | col3 |
+----+------+------+------+
|  1 | 2    | 3    | 4    |
|  2 | 2    | 3    | 4    |
+----+------+------+------+
2 rows in set (0.00 sec)

mysqldump を実行する

$ mysqldump -h127.0.0.1 -uroot -p test test_table -t --single-transaction

出力結果

Enter password:
-- MySQL dump 10.13  Distrib 5.6.25, for Linux (x86_64)
--
-- Host: 127.0.0.1    Database: test
-- ------------------------------------------------------
-- Server version	5.6.25

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Dumping data for table `test_table`
--

LOCK TABLES `test_table` WRITE;
/*!40000 ALTER TABLE `test_table` DISABLE KEYS */;
INSERT INTO `test_table` VALUES (1,'2','3','4'),(2,'2','3','4');
/*!40000 ALTER TABLE `test_table` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2020-11-01  9:07:35

mysqldump オプションの説明 (mysqldump –helpより抜粋)

-t で、データのみをdumpします。

  -t, --no-create-info
                      Don't write table creation info.

single-transaction

  --single-transaction
                      Creates a consistent snapshot by dumping all tables in a
                      single transaction.

実際に使う場合は、標準出力をファイルに書き出して、使うと思います。

dotenv を使ってみる (gem)

2020-10-31

やったこと

環境変数をファイルで管理する dotenv を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.4.1

調査

install

Gemfile

gem 'dotenv-rails', groups: [:development, :test]
$ bundle install

RAILS_ENV=development用の環境変数をセット

.env.development

HOGE1='hoge1hoge1'
HOGE2='hoge2hoge2'

rails console

$ bundle exec rails c
Running via Spring preloader in process 47643
Loading development environment (Rails 5.2.4.3)
irb(main):001:0> ENV['HOGE1']
=> "hoge1hoge1"
irb(main):002:0> ENV['HOGE3']
=> nil
irb(main):003:0> ENV['HOGE2']
=> "hoge2hoge2"

RAILS_ENV=test用の環境変数をセット

.env.test

HOGE1='test hoge1hoge1'
HOGE2='test hoge2hoge2'

rails console

$ RAILS_ENV=test bundle exec rails c
Running via Spring preloader in process 48051
Loading test environment (Rails 5.2.4.3)
irb(main):001:0> ENV['HOGE1']
=> "test hoge1hoge1"
irb(main):002:0> ENV['HOGE3']
=> nil
irb(main):003:0> ENV['HOGE2']
=> "test hoge2hoge2"

注意!!!

現在、ドキュメントにあるこの gem はなくなってしまったみたいです。

gem 'gem-that-requires-env-variables'

参考

eager_load! を使ってみる (Rails)

2020-10-25

やったこと

eager_load! を使ってみます。

例えば設定で eager_load を使わない Rails.env の時、 ファイルを先にロードしておきたい時などに使います。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.4.1

調査

app/models/book.rb

class Book < ApplicationRecord
  belongs_to :auther, class_name: Author
end

Author クラスは存在していないクラスとします。

config/environments/development.rb

Rails.application.configure do
  # 抜粋
  # Do not eager load code on boot.
  config.eager_load = false
end

出力結果

$ bundle exec rails c
Running via Spring preloader in process 5654
Loading development environment (Rails 5.2.4.1)
irb(main):001:0> Rails.application.eager_load!
Traceback (most recent call last):
        3: from (irb):1
        2: from app/models/book.rb:1:in `<main>'
        1: from app/models/book.rb:3:in `<class:Book>'
NameError (uninitialized constant Book::Author)

eager_load! すると落ちることが確認できます。

こんな感じで、shell 上で rails のコマンドを実行して、 ロード失敗するクラスを検知することも出来るようになります。

$ bundle exec rails runner 'Rails.application.eager_load!'
Running via Spring preloader in process 7687
Please specify a valid ruby command or the path of a script to run.
Run 'bin/rails runner -h' for help.

uninitialized constant Book::Author

Hugo のインストール、セットアップ

2020-10-24

やったこと

Hugo のインストール、セットアップをした時のメモを残します。

確認環境

$ hugo version
Hugo Static Site Generator v0.53/extended darwin/amd64 BuildDate: unknown

hugo インストール

$ brew install hugo

プロジェクトセット

$ hugo new site tech-blog

テーマ適用

ブログのテーマを適用します。

$ git submodule add https://github.com/qqhann/hugo-primer themes/hugo-primer
$ echo 'theme = "hugo-primer"' >> config.toml

記事投稿

$ hugo new post/my-first-post.md

サーバー起動

$ hugo server -D

jekyll から Hugo に移行させたのですが、記事のビルドのスピードが圧倒的に速くなりました。

rails runner を使ってみる (Rails)

2020-09-12

やったこと

runnerコマンドを使うと、非対話的にRailsの文脈でRubyのコードを実行することができます。たとえば次のようになります。

シェルのコマンドで、Rails のメソッドを使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 6.0.3.2

調査

実行するコードをそのまま渡す

$ rails runner 'puts Rails.env'

出力

Running via Spring preloader in process 37027
development

外部ファイルを実行してみる

lib/hoge.rb

class Sample
  def self.hello
    puts 'called: hello'
    puts Rails.env
  end
end

Sample.hello

出力

$ rails runner lib/hoge.rb
Running via Spring preloader in process 38718
called: hello
development

参考

file を使ってみる (Unix)

2020-09-07

やったこと

ファイルの種類を識別するコマンドの file を使ってみます。

man file より抜粋

file tests each argument in an attempt to classify it.

調査

ファイルを用意する

a.php

<?php

echo 'hoge'

b.rb

class Sample
  def hoge
    p 'hoge'
  end
end

Sample.new.hoge

実行結果

$ file a.php
a.php: PHP script text, ASCII text

$ file b.rb
b.rb: Ruby module source text, ASCII text

php, ruby を識別することができました。

rspec で pending を使ってみる

2020-09-04

やったこと

rspec の pending を使ってみます。

確認環境

$ ruby --version
ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin17]

$ rails --version
Rails 5.2.3

$ gem list | grep rspec-rails
rspec-rails (3.8.2)

調査

spec/models/task11_spec.rb

require 'rails_helper'

RSpec.describe Task, type: :model do
  it 'failedケース pendingあり' do
    pending('pending理由を何か書く')

    raise '例外発生1'
  end

  it 'successケース pendingなし' do
    expect(3 + 4).to eq 7
  end

  it 'successケース pendingあり' do
    pending('pending理由を何か書く')
  end
end

出力結果

$ bundle exec rspec spec/models/task11_spec.rb

Task
  failedケース pendingあり (PENDING: pending理由を何か書く)
  successケース pendingなし
  successケース pendingあり (FAILED - 1)

Pending: (Failures listed here are expected and do not affect your suite's status)

  1) Task failedケース pendingあり
     # pending理由を何か書く
     Failure/Error: raise '例外発生1'

     RuntimeError:
       例外発生1
     # ./spec/models/task11_spec.rb:7:in `block (2 levels) in <top (required)>'

Failures:

  1) Task successケース pendingあり FIXED
     Expected pending 'pending理由を何か書く' to fail. No error was raised.
     # ./spec/models/task11_spec.rb:14

Finished in 0.00317 seconds (files took 1.58 seconds to load)
3 examples, 1 failure, 1 pending

Failed examples:

rspec ./spec/models/task11_spec.rb:14 # Task successケース pendingあり

pending メソッドを使った場合、後続の処理でエラーが発生しないと

そのケースは失敗となります。(エラーが発生するのが正しいため)

rails-assets とサヨナラした話

2020-09-01

やったこと

rails-assets を利用しているシステムから、切り離した時の話です。

調査

rails-assets とは

The solution to assets management in Rails

js, css の依存関係について、gem を介して解決します。

しかし、rails-assets は近年のシステムでフロントエンド、バックエンドを明確に分けるという流れもあり、メンテナンスされない事が決まっていました。

そのような背景もあり、高頻度でサービスに繋がらなくなることありました。

GitHub の issues の例 サービスが落ちる度に、issue が立ち上がっています。

16.38.07.png

rails-assets の gem を自前でホスティングするように変更

rails-assets 経由で、ダウンロードできる gem が欲しかったのですが、

このサイトでは調べる事ができないようでした。

rails-assets を Gemfile に書いてインストールされているものを gem にすれば OK と思っていたのですが、それで正しいのか不安でした。

本家の rails-assets をローカルで gem を生成する事ができると分かりました。

なので、下記の2つの差分を見比べて、ソースコードに差異がなければ OK ということにします。

1.rails-assets から gem を生成したもの

2.rails-assets を Gemfile に書いてインストールされたもの

2.rails-assets を Gemfile に書いてインストールされたもの

は、既に持っているので、

1.rails-assets から gem を生成したもの を頑張って作ります。

rails-assets から gem を生成する

tenex/rails-assets: The solution to assets management in Rails

基本的には、上記の README.md を見ながらセットアップすればOKです。

ここで注意があります。

メンテナンスされてないため、Ruby, npm のバージョンが新しいと動きません。

ハマったところを書いておきます。必要なバージョンは頑張ってインストールします。

  • Ruby 2.2.3 (.ruby-version)
  • npm 6.14.4

また foreman と postgresql を動かしておく必要があります。

$ foreman start
$ postgres -D /usr/local/var/postgres

今回は select2 の gem を生成してみましょう。

$ bin/rake 'component:convert[select2,4.0.3]'

これで gem が生成されます。

public/gems/rails-assets-select2-4.0.3.gem

生成した gem を展開します

$ gem unpack public/gems/rails-assets-select2-4.0.3.gem

差分を比較する

差分を確認してみます。

$ diff -r {1. rails-assets から gem を生成したもの} {2. rails-assets を Gemfile に書いてインストールされたもの}

動作確認

  • GitHub のリポジトリを新設
  • bundle install して動作確認

これで rails-assets の gem を使ってる箇所を確認できれば、OKです。

gem を生成した後、ローカルの gem を bundle install できるようにする

ここはおまけなので、別記事で書きます。(多分)

最後に

こういう作業は、もうやりたくないですね。

参考