Tallman

技術とか読書とかいろいろ

クラスメソッドをPryのコマンドにできるGemをリリースしました

作ったGemはこれです

github.com

使い方

まず.pryrc内で下のようにクラスをセットします。

PrySingular.make_commands HogeJob, FactoryBot

これでPryの中で設定したクラスの特異メソッドをコマンドとして使うことができます。

pry(main)> perform_now

引数を渡すこともできます(あくまでコマンドなので引数との間はスペースが必要)

pry(main)> create :user

コマンドにしたいメソッドを直接指定することもできます。

PrySingular.make_commands FactoryBot, only: [:build, :attributes_for]

コマンドにしたくないメソッドを指定することもできます。

PrySingular.make_commands FactoryBot, except: :create

コマンドなのでObjectを汚してメソッドを生やすことはありません。

[1] pry(main)> self
=> main
[2] pry(main)> self.respond_to?(:create)
=> false

また、内部ではsingleton_methods(true)を使っているので、classとかrubyのコアな部分まではコマンドを設定しません。

f:id:sasa5740:20190715124804j:plain
超絶怒涛の神issueとPRを立ててくれた@fursichさんありがとうございます!*1

作った理由

FactoryBotのcreateとかActsAsTenantのwithout_tenantとかをいちいちrails cの中で書くのめんどい〜みたいな話が周りで起こり、.pryrcの話がにわかに盛り上がりました。そのとき職場の人がPry::ClassCommandでゴニョゴニョといっていたのを聞いたのが始まりです。
最初はFactoryBotとPryを連携させたいな〜と思って作っていたのですが、書いていくうちに「これクラス設定してもらって全部できるようにしたほうが便利だな」と思って実装してみました。

実装

コアな部分はここです

def import_class_command(klass, options)
  singular_methods = adapt_options_singleton_methods(klass, options)
  set_pry_command do
    singular_methods.each do |klass_method|
      command "#{klass_method}", "#{klass}.#{klass_method}" do
        extend PrySingular::Slop
        klass.class_eval <<-EOS
          #{parse_singular_method_command(Readline::HISTORY.to_a.last)}
        EOS
      end
    end
  end
end

def set_pry_command(&block)
  commands = Pry::CommandSet.new &block
  Pry.config.commands.import(commands)
end
module PrySingular
  module Slop
    def parse_singular_method_command(items)
      method, args = items.split(" ", 2)
      method + ' ' + args.gsub(' ', '')
    end
  end
end

commandはStringオブジェクトをメソッドの名前、メソッドの説明の順に引数に取って、コマンドが呼ばれたらブロックの中身を実行してくれるPryのメソッドです。
仕組みとしてはコマンドが呼ばれたらReadline::HISTORYの最新のものを一度メソッドと引数に分けてから引数の空白を削除し、対象のクラスをレシーバーにclass_evalして直接実行しています。

pry(main)> create :user

と呼ばれたら最終的に

FactoryBot.class_eval <<-EOS
  create :user
EOS

が実行されるといった具合です。 なのでコマンドの実装の中身は全て、クラスにReadlineから受け取った文字列をズルっとわたしているだけです。

Gemを作ってみて

Gem作成は初めてでしたが、RubyGemsのおかげで簡単に公開することができました。
PryのDSLの柔軟性にも助けられましたし、オプションの設定方法もRailsのパクリです。 巨人の肩は高い 。
反省点としてはリリースしてはだめじゃんを繰り返して既にversionが0.1.7なことですね…
まだまだテストもカバレッジ足りないしリファクタもできると思うし、そもそもまだバグもあるかもしれないので、よいGemにできるようやっていきです。
よかったら使ってみてください、バグとかこれがあったら面白いとかあればissueやコメントで教えてくれたら泣いて喜びます。

2019/07/21 追記

インターフェースをmake_commandsにしたり内部のコードいろいろかえました

*1:ブログに乗せることも快く了承いただき本当にありがとうございます