あきろぐ

いろいろめもするよ🐈🐈🐈

FactoryBotでインスタンスを一気に作成するときに値をユニークにしたい

create_listは、複数のインスタンスを作成するときに便利なメソッドですが、作成されるインスタンスのテストデータは同じになってしまいます。

create_list(:article, 3, title: 'test', description: 'test')

createメソッドで愚直に1つずつインスタンスを作成すれば、以下のように渡す引数をインスタンスごとに変えることはできますが、

create(:article, title: 'test1', description: 'test')
create(:article, title: 'test2', description: 'test')
create(:article, title: 'test3', description: 'test')

でもそれは面倒なのでもっと簡潔に書きたい、できれば一行で...という場合もあると思います。 そこで、一行で書ける解決方法について模索したのでまとめてみます。

1. 配列をループさせてインスタンス作成するときに渡す引数を変える

タイトルをユニークな値にしたいので、ユニークな値を持った配列をループさせてcreateメソッドでインスタンスを作成します。

['test1', 'test2', 'test3'].each { |title| create(:article, title: title, description: 'test') }

2. 先にインスタンスを作成し、後からインスタンスの持つ値を変更する

create_listメソッドで一気にインスタンスを作成し、後からタイトルを変更するパターンです。注意すべきなのは、create_listメソッドで初期データはDBに保存されますが、後で書き換えられたタイトルは保存されていないので、再度saveする必要があります。

create_list(:article, 3, title: 'test', description: 'test') { |v, i| v.title = 'test' + i.to_s }.map(&:save!)

3. そもそもFactoryBotの定義でユニークな値になるようにすればいい

そもそも論かもしれませんが、最初からsequenceを使ってtitleがユニークになるようにしとけばOKかもですね。

FactoryBot.define do
  factory :article do
    sequence(:title) { |n| "test#{n}" }
    description { 'test' }
  end
end

create_list(:article, 3)

github.com

sequenceを使ってtitleがユニークになるように定義していても、テストケースによってはデフォルト値から変更したい場合も発生すると思うので、上記の2パターンが有効な場合もありそうです。

参考

github.com

【Rails】カラムの中身を別のカラムにコピーする3パターン

1つ目

あるテーブルに新しくカラムを追加し、既存のカラムデータを新規カラムにコピーする

ModelName.update_all(new_column = old_column)

2つ目

テーブルサイズが大きい場合、レコードを分割して処理を実行することも可能

ModelName.in_batches(of: 10000) do | model_name|
  model_name.update_all(new_column = old_column)
  sleep 1
end

railsdoc.com

3つ目

既存の複数カラムデータをconcatして新規カラムにコピーする

ModelName.update_all('new_column = CONCAT(IFNULL(old_column1, ""), IFNULL((old_column2, ""))')

コピー元のカラムにnullが含まれている場合、CONCATされないのでIFNULLで回避する必要がある。

PostgreSQLを入れ直したらrails db:prepareできなくなった

何が起きたのか

PostgreSQLが立ち上がらなくなってしまったので、再度インストールし直した後rails db:prepareを実行したところ、以下のようなエラーが発生した。

rails aborted!
LoadError: dlopen(/Users/xxx/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/pg-1.3.1/lib/pg_ext.bundle, 0x0009): Library not loaded: /opt/homebrew/opt/postgresql/lib/libpq.5.dylib
  Referenced from: /Users/xxx/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/pg-1.3.1/lib/pg_ext.bundle
  Reason: tried: '/opt/homebrew/opt/postgresql/lib/libpq.5.dylib' (no such file), '/usr/local/lib/libpq.5.dylib' (no such file), '/usr/lib/libpq.5.dylib' (no such file) - /Users/xxx/.rbenv/versions/3.1.0/lib/ruby/gems/3.1.0/gems/pg-1.3.1/lib/pg_ext.bundle

エラーの内容としては、以下のファイルがディレクトリ内になくて実行できないとのこと

原因

PostgreSQLを再度インストールしたときにバージョンが上がってしまったことにより、PostgreSQLディレクトリのパスが/opt/homebrew/opt/postgresql@14/変更されてしまったこと。

🍒~$ ls /opt/homebrew/opt/postgresql@14/lib/postgresql@14
_int.so             hstore_plperl.so        libpq.dylib         refint.so
adminpack.so            insert_username.so      libpqwalreceiver.so     seg.so
amcheck.so          isn.so              lo.so               sslinfo.so
auth_delay.so           jsonb_plperl.so         ltree.so            tablefunc.so
auto_explain.so         latin2_and_win1250.so       moddatetime.so          tcn.so
autoinc.so          latin_and_mic.so        old_snapshot.so         test_decoding.so
bloom.so            libecpg.6.14.dylib      pageinspect.so          tsm_system_rows.so
bool_plperl.so          libecpg.6.dylib         passwordcheck.so        tsm_system_time.so
btree_gin.so            libecpg.a           pg_buffercache.so       unaccent.so
btree_gist.so           libecpg.dylib           pg_freespacemap.so      utf8_and_big5.so
citext.so           libecpg_compat.3.14.dylib   pg_prewarm.so           utf8_and_cyrillic.so
cube.so             libecpg_compat.3.dylib      pg_stat_statements.so       utf8_and_euc2004.so
cyrillic_and_mic.so     libecpg_compat.a        pg_surgery.so           utf8_and_euc_cn.so
dblink.so           libecpg_compat.dylib        pg_trgm.so          utf8_and_euc_jp.so
dict_int.so         libpgcommon.a           pg_visibility.so        utf8_and_euc_kr.so
dict_snowball.so        libpgcommon_shlib.a     pgcrypto.so         utf8_and_euc_tw.so
dict_xsyn.so            libpgfeutils.a          pgoutput.so         utf8_and_gb18030.so
earthdistance.so        libpgport.a         pgrowlocks.so           utf8_and_gbk.so
euc2004_sjis2004.so     libpgport_shlib.a       pgstattuple.so          utf8_and_iso8859.so
euc_cn_and_mic.so       libpgtypes.3.14.dylib       pgxml.so            utf8_and_iso8859_1.so
euc_jp_and_sjis.so      libpgtypes.3.dylib      pgxs                utf8_and_johab.so
euc_kr_and_mic.so       libpgtypes.a            pkgconfig           utf8_and_sjis.so
euc_tw_and_big5.so      libpgtypes.dylib        plperl.so           utf8_and_sjis2004.so
file_fdw.so         libpq.5.14.dylib        plpgsql.so          utf8_and_uhc.so
fuzzystrmatch.so        libpq.5.dylib           pltcl.so            utf8_and_win.so
hstore.so           libpq.a             postgres_fdw.so         uuid-ossp.so

解決策

pgをアンインストールした後、再度インストールする

gem uninstall pg
bundle install

Google CalendarをCLIで扱えるnpmを作成しました

作ったもの

Google CalendarCLIで扱えるnpmを公開しました。機能はシンプルですが、ターミナル上でGoogle Calendarのイベントを表示、登録、編集、削除することができます。

www.npmjs.com

作成動機

最近Javascriptを勉強していたこともあり、外部APIを使って何かCLIツールとして便利なものを作ってみたいと思ったからです。 他にも色々案は考えていましたが、大体二番煎じになってしまうものしか思いつかず、一番アイディアだしに苦労しました。

結構無難ではあるけど、自分の観測内ではGoogle Calendarをいい感じに扱えるnpmは存在してなさそうだったので、これに決定しました。 Google Calendar APIを使うためにGCPでService accountを作成したりと、普段AWSしか触っていない身としては勉強になりそうだなと思ったのも1つの動機です。

Google Calendar + Javascriptをいい感じに組み合わせてgoogler-jsと命名しました。

悩んだところ

GCPよく分からん

GCP側のOAuth認証の同意画面で何が原因か分からないエラーに遭遇し、数時間時間を溶かしました。 結局アプリ名がよくなかったみたいです、ハイフン使うとだめだったみたい?(エラーメッセージに含めてほしい)

OAuth認証使うかサービスアカウントを使うか

OAuth認証だと期限が切れると毎回認証作業が発生するのがめんどくさいので、サービスアカウント使う方法を調べていたのですが、サンプルには載っていなくて探すのに時間かかりました。

レポジトリのREADMEに記載あったので無事サービスアカウント使った認証方法で実装できました。

github.com

CLIツールとしての使いやすさ

CLIGoogle Calendarを操作できるとはいえ、やっぱり使いにくいとブラウザ上で操作したほうが早いとなってしまい意味がないので、ある程度の操作性が担保されるように実装するのは考えどころでした。

ワンライナーで必要な情報を指定して実行するのもありですが、対話的インターフェースの方が操作しやすいかと思ったのでenquirerを導入し、入力値に不正な値が含まれていたら弾かれるようにバリデーションもしっかり実装しました。

後は、使っていてほっこりするようにネコチャンを召喚させてます。

実行イメージはこちら(途中入力ミスしてますがw)

googler-jsの今後

まだ最低限しか機能がないので、イベントを検索するなどもう少し機能拡張したいなと思っています。