statesmanの状態遷移の定義からmermaidのstateDiagramを書き出す

下書きに入ってたから掘り起こすシリーズ。

github.com

statesmanを使った状態遷移を伴うコードを読む機会があって、眺めてたら図に書き出したくなったのでコードを書いた。

書き出したいStatesmanの状態定義

サンプルとしてStatesmanのREADMEにあった状態定義をそのまま持ってくる。

# order_state_machine.rb
class OrderStateMachine
  include Statesman::Machine

  state :pending, initial: true
  state :checking_out
  state :purchased
  state :shipped
  state :cancelled
  state :failed
  state :refunded

  transition from: :pending,      to: [:checking_out, :cancelled]
  transition from: :checking_out, to: [:purchased, :cancelled]
  transition from: :purchased,    to: [:shipped, :failed]
  transition from: :shipped,      to: :refunded
end

statesmanのメソッド郡が使えるようになるしくみ

statesman を使ってリソースの状態遷移を扱うためには使いたいクラスで Statesman::Machine を include する必要がある。includeすると、そのクラスにクラスメソッドが定義される。

github.com

仕組みをシンプルに書くと以下のとおりとなる。

irb(main):001:1* module Foo
irb(main):002:2*   module Bar
irb(main):003:3*     def self.included(base)
irb(main):004:3*       base.extend(ClassMethod)
irb(main):005:2*     end
irb(main):006:2*
irb(main):007:3*     module ClassMethod
irb(main):008:4*       def baz
irb(main):009:4*         puts "Hello!"
irb(main):010:3*       end
irb(main):011:2*     end
irb(main):012:1*   end
irb(main):013:0> end
=> :baz
irb(main):014:1* class A
irb(main):015:1*   include Foo::Bar
irb(main):016:0> end
=> A
irb(main):017:0> A.baz
Hello!
=> nil

self.included で module Barinclude されたときの処理を記述できる。引数はインクルードしたオブジェクトである。 docs.ruby-lang.org

base.extend(ClassMethod) はつまるところ A.extend(ClassMethod) となる。extendObject#extend で、Object に対して特異メソッドとして引数に渡したモジュールのインスタンスメソッドを定義できる。ここでは ClassMethod#baz が Aクラスの特異メソッドとして定義される。 docs.ruby-lang.org

クラスの特異メソッドということはクラスメソッドとおおよそ同義として扱って良い。 docs.ruby-lang.org

mermaid の状態遷移図を出力する

実装方法がわかったので、 Statesman::Machine をいい感じに再実装することで状態の宣言と状態遷移のルールの記述に対して好きなことができる。あとはその再実装が適用される状態で直接statemachineのファイルをrubyで実行すればよい。ということで、mermaidのstateDiagramを書き出すコードを以下のgistに結果も含めてまとめた。

mermaidjsのstateDiagram

今回はRubyの実装の再定義だったので、中身を調べて上書きすればよかったのだけれども、なんらかのDSLに対してRubyのメソッドを実装して読み込ませてなんかするってのはbuildersconでやってたLTが自分の中で印象に残っている。今回のstatemachineのクラスを眺めてなんかできそうと思ったのもこのLTを思い出したことがきっかけだった。

www.slideshare.net

RSpecについて最近話したことと自分のスタンス

最近話したこと Rails アプリケーションのデバッグ編 - Re: 醤油の一升瓶じゃあ戦えない の続き。ここではRSpecのテスト対象はRailsアプリケーションを指すことにする。また、ソフトウェアテスト技法そのものに関してはここではほとんど触れない。

まず読むもの

テストの書き方

自分はRSpecしぐさの内容にだいたい従っている。

www.slideshare.net

it / specify の使い分けについては基本郷に従うけど、自分で書くなら specify を使う。個人的にテストの結果は期待されることの主語を明確にするために受動態で書きたい気持ちがあるのだけれども、it だと主語がダブりそうなので。受動態で書きたいのは古の記憶がそう呼んでくるから。

この使い分けについては伊藤さんが最近書いた記事も合わせて読むと良さそう。 RSpecでit / example / specifyはどのように使い分けるのか?〜日本語で書くならexampleって本当?〜 - Qiita

実践

Active Jobのテスト

仮にFooJob をテスト対象とする。

FooJobジョブそのものをdescribeする場合は perform_now で呼び出せばよい。

何らかのクラスの処理の中で FooJob.perform_later のように非同期でジョブがエンキューされることを確認したいときは、have_enqueued_job matcher を使うとよい。perform_later に渡された引数は have_enqueued_job(FooJob).with("bar", "baz") のように with が使える。そんなことがドキュメントに書かれている。

何らかのクラスの処理の中で FooJob.perform_later が非同期で起こす副作用を確認したいこともあるかもしれない。そのまま実行しても非同期で実行されたジョブはRSpecがその処理を待ってくれず、期待した結果が返ってこない。その時は perform_enqueued_jobs を使うと良い。RSpecではなく、ActiveJob::TestHelper のメソッドである。

perform_enqueued_jobs do
  # FooJob.perform_later を含む処理
end

このように挟んでおくと perform_later も同期的に実行してくれる。パーフェクトRuby on Rails 2版だと「5-1-7 ジョブのテスト」に記載がある。

Action Mailerのテスト

Railsガイドを読むとだいたい解決しそう。 - 12 メイラーをテストする - Rails テスティングガイド - Railsガイド - ActionMailer::Base.deliveries がテストごとにリセットされなくて、テストの結果がランダムで落ちる現象については大昔に書いてた。大事なことは大体Rails ガイドが教えてくれる - Re: 醤油の一升瓶じゃあ戦えない

おまけ

  • 動くコード書くよりもテスト書いてる時間のほうが多分長くなりますよ
  • キリ悪いときはわざと落ちるテスト書いておくと、あとから再開する時に続きがわかって便利すよ

みたいな話をしたきもする。


眠気覚ましにガッと書いた。合わせて昔買ったソフトウェア技法やRailsの書籍をパラパラ読み返すいい機会になった。気が向いたら見返したり追記したりする。

fishで直前に実行したコマンドをコピーする

特定のコマンドを実行した結果をコミットしたいときにコミットメッセージにそのコマンドが記載されていると、あとから見返す時に便利。ただその実行したコマンドをコピーするのにhistoryで履歴呼び出してから先頭に echo つけてパイプ挟んで pbcopy して〜という作業がだるかったのでコマンド一発で終わらせたくて書いた。 sed 使うのがシンプルっぽい。

history | sed -n 1p | pbcopy

fishのhistoryは1行目の方が新しいので1行目だけ出力されたい。なので 1p で1行目を出力する。ただ、そのままだとsedがパイプで受け取った入力を全部垂れ流すので -n で抑制する。ちゃんと面と向かってsedと付き合ったことなかったので学びの取っ掛かりとしてよかった。

ちなみに、以下のように実行する前にコピーする案のもぱっと思いついたのだけれども、実行結果が大丈夫かわからない状態でコピーしても意味ないなと思ってボツにした。

 function copy-command
   echo $argv | pbcopy
   $argv
 end

sedについてもっと調べたくて、DMMのセールで買ったシェルスクリプト基本リファレンスに書いてないかな〜と思ったけど「詳細はオンラインマニュアルを参照してください」で終わっていてはいとなった。

買ってよかったもの2021

今年も書く。

uvb-76.hatenablog.com

とりあえず。しっかし最近はめちゃくちゃ寒くて参ってる。起きてリビング降りたら11℃て。

Tern Surge

Surge | Tern Bicycles Japan

漕げば進むタイプのミニベロ。乗り心地は硬いけど、進むので満足。今年は多摩川を遡上したり、羽田空港まで足を伸ばしたりした。

パナソニックの冷蔵庫

406L 大容量冷蔵庫 NR-E416V 商品概要 | 冷蔵庫 | Panasonic

冷凍室が大きな冷蔵庫は良い。

かなでものの机

脚の影響もあるだろうけど、とても安定してる。ずっと使えそう。

サンワサプライのケーブルトレ

CB-CT5【ケーブル配線トレー メッシュ 汎用タイプ】デスクに取り付けて、タップやケーブルなどを収納できるケーブル配線トレー。メッシュタイプ。|サンワサプライ株式会社

机にクランプで挟むタイプのケーブルトレー。トレーがメッシュになっていて、ケーブル整理したいときにトレーを外せるのが本当に嬉しい。

BOSCHの電動ドライバー

コードレスドライバー IXO5 | ボッシュ電動工具

ちょこちょこ家具とか組み立てる機会があって買ったらなかなか捗った。小さなドライバーでも十分活躍してくれるので、早く買っとけばよかった。

LGのテレビ

OLED55CXPJA | テレビ | LGエレクトロニクス・ジャパン

なんだかんだででかいテレビは正義。リモコンシンプルで好き。ゆーちゅーぶ見たりとぅいっち見たりゲームしたり劇レみたりと活躍してる。しばらくするとだんだん番組表の読み込みが遅くなったりするのがちょっとつらい。コンセント抜き指ししてやると直るのでどこかでうまく開放できてないみたいなやつが起きてそう。


デカブツ中心で思ったよりも自分の身の回りの小物を買ってないことに気がついた。

ghqとgitconfigのincludeIfでリポジトリサーバーごとに設定を切り替える

この記事はGMOペパボエンジニア Advent Calendar 2021 11日目の記事です。

首の調子が悪くて整体に電話をかけたら「画面を見るな」と言われたので、書きかけの記事を慌てて引っ張り出してネタにします。皆様も良い姿勢で画面と向き合いましょう。


GMOペパボではGitHub Enterprise Server(以下GHES)を使用してコードを管理しています。GHESのアカウントはgithub.comのアカウントとは別になるので、 git configのUser設定を切り替える必要があります。この作業は何もしなければ、git cloneごとに必要な作業で忘れると、意図しないアカウントでコミットが詰まれpushされる可能性があります。

これはめんどくさいので勝手に切り替わるようにします。要件は以下のとおりです。

  • それぞれのリポジトリのuser設定は別にしたい
  • GHESのホスト名をdotfilesの管理下に含めない

前提

コード管理を行なっているGHES のホスト名を ghes.example.com とします。

方法

ghqを使ってリポジトリを管理する

ghqを使うことでリポジトリのクローン先をホストごとに分けることができます。

Songmu/ghq-handbook に詳しい利用方法が記載されています。

ghq get コマンドでリポジトリを取得することによって、 gitconfigの ghq.root に設定されたディレクトリ以下にリポジトリがクローンされるようになります。

gitconfigを設定する

以下の設定を書きます。

~/.gitconfig

これだけdotfilesのコード管理に含めます。

# $ cat ~/.gitconfig
[user]
  name = github_user
  email = github_user@example.com

[include]
  path = ~/.config/git/local.gitconfig

[ghq]
  root =~/src

~/.config/git/ 以下

以下はコード管理下に置きません。

~/.config/git
├── ghes.example.com.gitconfig
└── local.gitconfig
# $ cat ~/.config/git/local.gitconfig
[IncludeIf "gitdir:~/src/ghes.example.com/"]
  path = ~/.config/git/ghes.example.com.gitconfig
# $ cat ~/.config/git/ghes.example.com.gitconfig
[user]
  name = ghes_user
  email = ghes_user@example.com

IncludeIf で特定のディレクトリの下でのみgitconfigを適用できるので、それを利用してuser設定を切り替えます。これにより、 ~/src/ghes.example.com/ の下のディレクトリで ~/.config/git/ghes.example.com.gitconfig の設定を適用させることができます。 local.gitconfig を挟んでいるのは、 IncludeIf の設定でGHESのホスト名を含んだ設定を行う必要があるためです。気にしないのであれば、回りくどいことをせずにgitconfigに直接書くのが良いと思います。また、個人的にはgithub.comのアカウント情報がGHESにコミットされた方がダメージが低いので、globalのuser設定ではgithub.comのアカウント情報を設定し、GHESのリポジトリの上で設定を上書きするようにしています。

参考

VSCode 1.62でRubyのインデントガイドを設定する

code.visualstudio.com 眺めて便利〜ってなったのを書き残すコーナーです。

Improved bracket pair guides

Improved bracket pair guides

editor.guides.bracketPairstrue もしくは active に設定することでかっこのペアを垂直方向のガイドで強調してくれる。これは元々あったみたい。1.62で editor.guides.bracketPairsHorizontal という設定が追加されている。これは水平(行全体)にガイドを引いてくれる設定ぽい。

f:id:uvb_76:20211118133728p:plain
インデントがずれててもガイドだしてくれて便利
bracketPairs, bracketPairsHorizontal 両方 true にしたのが上記。

Customizable bracket pairs

Customizable bracket pairs

上記ガイドを出すかっこの組み合わせは言語ごとに settings.json で設定できる。Rubyだと以下のような設定を追加する1ことでメソッドやキーワードと end のペアでもガイドを出してくれるようになる。Rubyは キーワードと end の対になることが多いからこれにも対応しないかな〜ってリリースログみてたら案の定用意されていて最高だった。

"[ruby]": {
    "editor.language.brackets": [
      ["[", "]"],
      ["(", ")"],
      ["{", "}"]
    ],
    "editor.language.colorizedBracketPairs": [
      ["def", "end"],
      ["do", "end"],
      ["if", "end"],
      ["unless", "end"],
      ["case", "end"],
      ["begin", "end"],
      ["while", "end"],
      ["until", "end"],
      ["for", "end"],
      ["class", "end"],
      ["module", "end"],
      ["[", "]"],
      ["(", ")"],
      ["{", "}"]
    ]
  },

この設定を入れて、試しに lobster.rb の一部を抜粋してみると以下のような感じになる。ちょっと賑やかすぎるかな、と思ったので色々後で調整しようと思う。 rack/lobster.rb at master · rack/rack

f:id:uvb_76:20211118141003p:plain
lobster


  1. colorizedBracketPairs の設定は全て GitHub Copilot を有効にした状態で colorizedBracketPairs の設定を書こうとしたらリコメンドされたもの。便利すぎ。

最近話したこと Rails アプリケーションのデバッグ編

最近Railsのアプリの開発について色々喋ることが多くて、今後も発生しそうだったので書いとく。数ヶ月後の自分用。

前提

  • Docker の上で動いている Ruby on Rails の開発環境がある

デバッグの方法

デバッガとして使えるライブラリは色々あるけど、最近話したアプリケーションでは deivid-rodriguez/pry-byebug を使っているので、その話。 binding.pry というメソッドがブレークポイントとして機能する。止まった後は止まっているコンテキストでオブジェクトの値を確認したり、 step, next, continue を使って処理を動かしたり。最近書かれている binding.pryキホンのキ - SmartHR Tech Blog にも便利情報が載っている。

GitHub - ruby/debug: Debugging functionality for Ruby も最近 binding.break を使うブレークポイントが実装されてそうだった。まだ試してないけど検討はできそう。

pry-byebug の紹介ではないけど、 Rails アプリケーションのデバッグ - Railsガイド にもデバッグの情報はある。

起動している Railsブレークポイントを差して動かすとDocker のコンテナ上で止まるので、コンテナにアタッチする必要がある。RSpecdocker run などのコマンドで実行する場合はコマンドを実行したコンソールで直接 pry-byebug の操作を行える。

RSpec の実行中にアプリケーションログを確認する

テストの実行中はアプリケーションログの出力が抑制されている。config/environments/test.rb の中で config.logger = Logger.new(STDOUT) とか書いとくとアプリケーションログも標準出力に一緒に出てくる。 Request Spec などでテストの中でなに起きてるかわかってない時によく使う。


思い出したら追記する。多分次回はRSpec編。