ZIPCODA API を使って、郵便番号の逆引きをした

概要

住所の一覧から郵便番号をまとめて逆引きしたかったので、 ZIPCODA API を使って実現してみた。

CSV ファイルから住所を読み込んで、結果も同じく CSV ファイルに書き込むだけ。

github.com

ZIPCODA を使った理由

たぶんサービス等で定常的に利用するなら ケンオール あたりを使うのがいいんだろうけど、一時的な利用だとオーバースペック感があったので、別のを探していたら見つけた。

API はシンプルで使いやすいと思う。

zipcoda.net

補足

最初は GAS で雑に済ませようと思ったけれど、 GAS からのアクセスは制限されているようだったので、プログラムを書いた。

API の過剰な呼び出しを防ぐように wait するようにしているおり、結果として処理は全部同期的に行うような形になった。

API の結果を住所単位でキャッシュするようにはしているけど、実際に役立つのは繰り返し実行したりするときくらいだと思う。

「プロを目指す人のためのRuby入門」を読んだ #ruby #チェリー本

今の会社に出戻ってから Ruby On Rails をやることになり、まずは Ruby について学びたいなと思って手に取った本。

実際に読み終わったのは少し前なのだけれど、備忘のために書いておく。

改訂版だと Ruby 3.0 に対応してくれている。

実践的な入門書

網羅性が高くかつある程度深く解説してくれるので、まさに実務で使う人のための入門書という感じ。

そのためそれなりの分厚さではあるけれど、著者がかなり読み手に寄り添った形で書いていてくれて、読み進めるモチベーションを保てる。

テストと一緒のハンズオン

ただ読むだけでなく、ハンズオン的に手を動かせるようにもなっているので、その意味でも実践的だった。

入門書の類はどうしてもテストに関する事項が薄かったり省かれたりしがちだけど、ハンズオンがテストとセットで進める形になっているところに非常に好感を持った。

まとめ

Ruby は自分が今までメインで慣れ親しんできた言語と毛色の異なる書き方が結構あるけど、この本のお陰で脳内にインデックスを作れたので、コードを読み解く土台になってくれたと思う。

この充実度の書籍が存在してくれるのは Ruby 初心者として非常に助かった。

Ruby で nil の場合に別の値を入れる #ruby

まとめ

ぼっち演算子と or 演算子を使うといい感じに書ける。

result = hoge&.value || "default"

愚直に書く

if hoge== nil
    result = "default"
else
    result = hoge.value
end

nil? を使う

Object クラスにレシーバーが nil の場合に真偽値を返すメソッドがある。

docs.ruby-lang.org

if hoge.nil?
    result = "default"
else
    result = hoge.value
end

三項演算子を使う

単純な条件なら三項演算子で一行にまとめて書いてもよさそう。

result = hoge.nil? ? "default" : hoge.value

nil? を使わない

nil はそもそも if で false に判定されるのでなくてもよい。

result = hoge ? hoge.value : "default"

ぼっち演算子と or 演算子を使う

ぼっち演算子はレシーバーが nil のときはメソッドが呼ばれず nil を返し、 nil でないときはメソッドを呼び出してくれる。

result = hoge&.value || "default"

追記

ridgepole で MySQL の TEXT 型を後から Not Null にするとエラーになる #Rails #MySQL #ridgepole

概要

Rails で DB の構成管理に ridgepole を使っているんだけれど、 MySQL の TEXT 型に後から Not Null 制約を付けるとエラーになってしまう。

github.com

原因

例えば下記の用に定義していたとする。

# 最初は null: true で作成
t.text "my_text", null: true, comment: "テキスト"

しかし null: true にして、実行した場合には最終的に default: nil が不正になって、エラーが発生してしまう。

# Not Null にしてみる
t.text "my_text", null: false, comment: "テキスト"
-- change_column("my_table", "my_text", :text, {:null=>false, :comment=>"テキスト", :default=>nil, :unsigned=>false})
[ERROR] Mysql2::Error: Invalid use of NULL value

では default 値を指定すればいいのかというと、実は MySQL の TEXT 型は default 値を設定できないため、エラーになる。

# default を指定してみる
t.text "my_text", null: false, default: "dummy", comment: "テキスト"
-- change_column("my_table", "my_text", :text, {:null=>false, :comment=>"テキスト", :default=>"dummy", :unsigned=>false})
[ERROR] Mysql2::Error: BLOB, TEXT, GEOMETRY or JSON column 'description' can't have a default value

対応

スマートにやる方法は浮かばなかったので、別名カラムを用意して、最終的に元の名前にリネームするという手段を取ることにした。

データの移行が必要だったりするので、レコード数や値によっては大変かもしれない。

t.text "my_text", null: true, comment: "テキスト"
# 移行先のカラムを別名で用意する
t.text "my_text_tmp", null: false, comment: "テキスト"

移行先のカラムにデータ移行した後、必要であれば Railsalias_attribute を使って、移行先で運用に問題ないか確認できる。

https://railsdoc.com/page/alias_attribute

# 移行先のカラムを参照するように変更できる
alias_attribute :my_text, :my_text_tmp

使っているライブラリによっては alias_attribute だと二重参照になるような場合もある。

自前でメソッドを作って回避する方法もあるが、逆に別名が局所的になるので、一長一短ではある。

# 自前でメソッドを作る手もある
def my_text
  my_text_tmp
end

問題なさそうなら、古いカラムを別名にする。

カラム名を元に戻すタイミングとアプリケーションの反映タイミングがずれるとエラーになる可能性があるため、先に別の名前にしてしまう。

ridgepole でカラム名を変更するときは、 renamed_from を使用する。

# 元のカラムを別名に変更する
t.text "my_text_old", null: true, comment: "テキスト", renamed_from: "my_text"
t.text "my_text_tmp", null: false, comment: "テキスト"

アプリケーション側で再び元のカラムを参照するように変更し、新しいカラムを最終的な名前に変更する。

これでアプリケーションのリリースタイミングによらず、エラーにならなくなる。(更新が走ったりするのであればもっと考えることは増えると思う)

# 元のカラムを参照するように変更する
alias_attribute :my_text, :my_text_old
t.text "my_text_old", null: true, comment: "テキスト"
# 移行先カラムを最終的な名前に変更する
t.text "my_text", null: false, comment: "テキスト" , renamed_from: "my_text_tmp"

アプリケーション側で alias を外し、問題なければ古いカラムを削除する。

# alias は不要になるので削除
# alias_attribute :my_text, :my_text_old
# 古いカラムは削除
# t.text "my_text_old", null: true, comment: "テキスト"
# 移行先カラムを最終的な名前に変更する
t.text "my_text", null: false, comment: "テキスト" , renamed_from: "my_text_tmp"

まとめ

ridgepole に限らず、 MySQL の TEXT 型は後から Not Null 付与しようと思うと色々面倒くさそうなので、初期から慎重になったほうがよい。

参考

importmap で特定のページでだけ使う js を作る #rails

概要

importmap-rails を使っている場合に、特定のページだけで使う js ファイルを作る方法をまとめる。

github.com

js ファイルを用意する

任意のファイル名と実装内容で良いが、ここでは下記のようにする。

  • app/javascript/foo/special.js

import 可能にする

ディレクトリ配下を pin していれば、ファイルを追加しても自動で importable になる(もちろんファイル単体で pin してもよい)。

pin_all_from "app/javascript/foo", under: "foo", to: "foo"

view ファイルで読み込む

呼び込みたいページの view ファイルで読み込む。

head タグ内等、レイアウト側で読み込むことを想定して content_for を使う。

<% content_for :javascripts do %>  
  <%= javascript_import_module_tag "foo/special" %>  
<% end %>

レイアウトファイルで読み込む

head タグ等で yield して、読み込むようにする。

この時、 javascript_importmap_tags より後に読み込む必要があることに注意。

<%= javascript_importmap_tags %>
<%= yield(:javascripts) if content_for?(:javascripts) %>

参考

importmap-rails の README で、 Selectively importing modules あたりを見るとよい。

factory_bot_rails 6.3.0 から PK に対する sequence はエラーになる #rails

概要

factory_bot_rails 6.3.0 から Active Record の PK カラムに対して sequence を指定するとエラーになるようになったようだ。

指定した場合、 FactoryBot::AttributeDefinitionError が発生する。

github.com

FactoryBot.define do  
  factory :hoge do  
    # エラーになる
    sequence(:id) { |n| n }  
  endend

上記のような場合、↓みたいなエラーメッセージが出る。

FactoryBot::AttributeDefinitionError:
    Attribute generates "id" primary key for Hoge"

    Do not define "id". Instead, rely on the database to generate it.

対応

指定するのをやめればいいのだが、設定で無効化することもできる。

config/application.rb とか config/environments あたりに設定を追加すれば OK。

config.factory_bot.reject_primary_key_attributes = false

この辺は README の Active Record Configuration あたりに書いてある。

今のところ一括で無効化することしか出来ないが、個別に設定するような要望は上がっているようだ。

Rubocop に FactoryBot/IdSequence というのもあるので活用してもいいかもしれない。

感想

要望 Issue を見ていると困るユースケースはありそうだなと思いつつ、 Rails (Active Record) 使うなら取り敢えずサロゲートキーを自動採番で降っておくテーブル設計に統一しとくのがいいのかなと思う。 (それはそれとして、ナチュラルキーに制約は貼るけど)

転職し(てい)ました

表題の通りなんですが、3月いっぱいでコネヒトを退職して、4月からエス・エム・エスという会社で働いています。

試用期間が終わったくらいにブログでも書こうかなと思ってたら、半年以上過ぎてるわ年末が目前だわで、時の早さを自分の怠惰の言い訳にしたくなります。

なぜコネヒトを退職したか

自分のやりたいことと価値観を大切にして、中長期1で働くということを考えた場合に違う環境を選ぶことにしました。

正直複合的な理由がいろいろあるので(残るという選択肢もあった)、興味ある人は直接会ったときにでも聞いてください。

なぜエス・エム・エスに入社したか

きっかけ

エス・エム・エスには2016年から3年半ほど在籍していたことがあり、要は出戻りになります。

ただ転職活動自体は普通にしていて、他社の選考も受けていました。

某媒体で転職活動中なのを @sunaot に見つかり(退職後も多少交流はあった)、飲みにいったのがきっかけです。

以前在籍していたこともあり、 変化や現在地点をリアルに想像できたアドバンテージがあるので、他の人の参考にはならない転職エントリです。。

迷った点

自分は職場を選ぶ際「"何" を "なぜ" 作るのか(何をやろうとしているのか)」というのをとても大事にしています。

そういう意味で、(前回入社してる時点で)その点はそれほど問題ではありませんでした。

ただ今回は、技術スタックを以前より重視していました(言語でいうと Go か Java をやりたかった2

サービスにもよりますが、エス・エム・エスは Rubist が多いし、オファーの配属先も Rails を主に使うので正直そこは希望とは異なりました3

ただ最後まで迷いながら自分の考えを突き詰めた結果、エス・エム・エスを選ぶことにしました。

決めた理由

最終的に決め手になったのはオファー面談か何かで言われた「ボトルネックを解消しにいく(ためにやっている)」という言葉でした。

元々ミッションや理念には共感して前回の入社をしているのだけれど、実行の強さみたいなのを感じたというか。

解きたい問題を構造化して、どの変数を重視してどう解釈して(だから)こうやって解こうとしている、をすごく丁寧にやっているなぁと。

それは前回の在職中にも感じていたことだし、外に出てから改めて実感したことでもあります。

そうすると(仮に間違っていたとしても)仮説や施策の精度だったり、再現性が高くなっていくと思っています。

実際に在職していた頃より、ビジョンに対する解像度が上がっているのを話の中で実感して、その先を想像した時にワクワクするものがありました。

技術スタックを重視しようと思っていたけれど、結局最後の決め手がその辺りになったのは自分でも発見だったかもしれません。

その他

もちろん他にも要素はありました。

  • やっぱ一緒に働いたことがあり、かつ価値観まで理解してくれているであろう人に改めて声かけられるのはうれしい(採用はやっぱリファラル強いよね、と自分事ながら痛感した)
  • 選考過程であった人との話が面白かった(今のマネージャーとか)
  • 課題とか不安になる要素を具体的に想像して検討できた(これは出戻りによるチート)
  • etc

今何をやっているか

たくさん事業がある会社なので、その中のまだ小さい数名程度の開発チームで Ruby On Rails 書いてます。

RubyRails を自分が今から触るのがキャリア的にどうなのかなという気持ちは多少あります。

ただ単純に今までやったことがない新しいことをやるという意味では(想像以上に)楽しみながらはやっているし、それでいいかなとも思っています。

マネージャーが Rails に強いので、ただ動くものを作るだけじゃなく、よりよいものを作るために気軽に相談できたりするのにも助けられています。

開発プロセスやチーム運営も気になったりはするけれど、サービスもチームも今の規模やフェーズだと、重視するものが違うよなぁと感じたりはしながら、その辺は少しずつ貢献していければいいなと思っています。


  1. もちろん何が起きるかわからないので、短期になったらその時はその時かなっと思ってる
  2. 技術スタックを理由に選考を辞退した会社もあるので、結果的に言行不一致で申し訳ないという気持ち。。
  3. 実際に、最後まで迷ったオファーをくれた会社は Java がメインだった