Ruboty を使ってみた

Ruboty 作者の r7kamura さんのエントリを読んで使ってみました。

r7kamura.hatenablog.com

動かしてみる

gem install ruboty
ruboty --generate
cd ruboty
bundle install

ここまでで Ruboty が動作する状態になります。

bundle exec ruboty を実行することで、シェル上で動作確認ができます。

bundle exec ruboty
Type `exit` or `quit` to end the session.
>

ruboty help を実行すると コマンド一覧が出てくるので、試しに ruboty ping を実行してみます。

> ruboty help
ruboty /help( me)?(?: (?<filter>.+))?\z/i - Show this help message
ruboty /ping\z/i - Return PONG to PING
ruboty /who am i\?/i - Answer who you are
> ruboty ping
pong
>

pong が返ってくることが確認できました。

拡張してみる

以下のように、ruboty hello を実行すると world!!! が返ってくるように拡張してみます。

Type `exit` or `quit` to end the session.
> ruboty hello
world!!!
>

クラスの追加

Ruboty::Handlers::Base を継承したクラスを作ることで拡張していくことができます。

# ruboty/test.rb

module Ruboty
  module Handlers
    class Test < Base
      on(
        /hello/, # トリガー
        name: 'say_world', # 実行するメソッド
        description: 'return "world" to "hello"' # ruboty help で表示される説明文
      )

      # world!!! を返すメソッド
      def say_world(message)
        message.reply('world!!!')
      end
    end
  end
end

動作確認

-l ファイル名 をつけることで指定したファイルを読み込ませることができます。 ruboty help を実行してみると、先ほどのコマンドが追加されていることが確認できます。

bundle exec ruboty -l test.rb
Type `exit` or `quit` to end the session.
> ruboty help
ruboty /hello/ - return "world" to "hello"
ruboty /help( me)?(?: (?<filter>.+))?\z/i - Show this help message
ruboty /ping\z/i - Return PONG to PING
ruboty /who am i\?/i - Answer who you are
>

ruboty hello を実行すると world!!! が返ってきます。

> ruboty hello
world!!!
>

RSpec の expect_any_instance_of でハマった

class Test
  def say_hoge
    hoge1 = Hoge.new
    hoge2 = Hoge.new
    hoge1.say
    hoge2.say
  end
end

class Hoge
  def say
    puts 'hoge'
  end
end

こんな感じのクラスがあったとして、 Test#say_hogeHoge インスタンスHoge#say を1回ずつ実行していることをテストしたいとなったときに、以下のようなテストを書きました。

describe 'Test' do
  describe '#say_hoge' do
    it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do
      expect_any_instance_of(Hoge).to receive(:say).once
      test = Test.new
      test.say_hoge
    end
  end
end

これを実行してみたところ、落ちてしまいました。


ああ、呼ばれている回数は全インスタンスの合計になるのか、なるほどね!とか思って、以下のように once を twice に変えてみました。

describe 'Test' do
  describe '#say_hoge' do
    it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do
      expect_any_instance_of(Hoge).to receive(:say).twice # 合計2回実行されるので twice に変えてみた
      test = Test.new
      test.say_hoge
    end
  end
end

これを実行してみたところ、またまた落ちてしまいました。


なぜだと思ってエラーを見てみると、「Hoge#say を hoge2 が実行したんだけど、 hoge1 でもう実行されてるよ」 的なことが書いてありました。

  1) Test #say_hoge Hoge instance call Hoge#say once
     Failure/Error: hoge2.say
       The message 'say' was received by #<Hoge:70248456891000 > but has already been received by #<Hoge:0x007fc7fca35518>

ためしに、以下のように hoge2.say を消して、 twice を once に戻してみました。

class Test
  def say_hoge
    hoge1 = Hoge.new
    hoge2 = Hoge.new
    hoge1.say
    # hoge2.say
  end
end
describe 'Test' do
  describe '#say_hoge' do
    it 'Hoge インスタンス は Hoge#say を1回ずつ実行する' do
      expect_any_instance_of(Hoge).to receive(:say).once
      test = Test.new
      test.say_hoge
    end
  end
end

これを実行すると、ちゃんと通りました。

わかったこと

おそらく、 expect_any_instance_of はどのインスタンスも対象にとるが、対象のインスタンスは1つに限るということなのだと思います。

どうするか

そのため、stub を使って Hoge.new でつくられるインスタンスを同じにして、そのインスタンスHoge#say が合計2回呼ばれるということをテストすることにしました。

class Test
  def say_hoge
    hoge1 = Hoge.new
    hoge2 = Hoge.new
    hoge1.say
    hoge2.say
  end
end
describe 'Test' do
  describe '#say_hoge' do
    it 'Hoge インスタンス は Hoge#say を1回ずつ実行する (合計2回) ' do
      hoge = Hoge.new
      allow(Hoge).to receive(:new).and_return(hoge)
      expect(hoge).to receive(:say).twice
      test = Test.new
      test.say_hoge
    end
  end
end

これを実行したところ無事通りました。

Finished in 0.01246 seconds (files took 0.42959 seconds to load)
1 example, 0 failures

入社してから早くも1年

先日は入社式があり、一緒に働く新しい仲間がたくさん増えました。

ということは、自分が入社してから早くも1年が経ったということです。
この1年は、とてもとてもあっという間でした。が、とても濃い1年でした。

ちょっと振り返る

自分が入社した時はどんな気持ちだったかなーと思い出してみると、やってやるぞ!!という気持ち半分、不安半分でした。

社会人としてやっていけるかなという、初めての環境に対する当然の不安もありましたが、そもそもエンジニアとしてやっていけるのかなという不安もありました。
自分は、技術的にとても未熟で未熟すぎて未熟で、自信がありませんでした。
どれくらい未熟だったかというと、社長に「技術的に不安だったよ〜」と直接言っていただけるくらいです。

いざ入社してみると、会社の先輩はもちろんなのですが、同期がとても優秀だったので焦りました。

エンジニア研修で一番最初に、すでに支給されていた PC の開発環境をいろいろ整えといてと言われたのですが、自分はどういったことをすれば良いのか全くわからない中、同期は空き時間に既に終わらせていたのは衝撃は忘れられません。
ちなみにこのときは、研修担当のエンジニアの方に自分だけみっちり教えていただきました。(同期が優秀な話をしようと思ったのに、自分がただただ未熟だったことしか伝わらないですね...)

その後、長い長い充実した研修を通して、技術的なこともたくさん学びましたし、エンジニアのチームでの働き方もたくさん学びました。
そのおかげで、当たり前なのですが、1年前に比べて見違える程になり、現在は先輩エンジニアの方にたくさん教えてもらいながらですが、日々楽しく開発を行えています。

自分で言うのもなんなのですが、めちゃくちゃ成長しました。
本当にこれだけは胸張って言えます。

2年目として

新しく入ったエンジニアの人たちは優秀なので、自分と同じような不安はあまりないとは思いますが、もしそういった不安を抱いているのであれば、

「大丈夫だよ、こんな自分でもちゃんとやっていけているんだから!(良い意味で)」

ということを伝えていけたらなあと思っています。

そんなことを伝えていきつつも、いるだけで成長のできる素晴らしい環境を存分に活かして、これからもどんどん成長していきたいです!

curl のオプション小まとめ

Web API の動作検証をするときに curl を使っているのですが、 そのときに使うオプションについて少しだけ書きます。
本当に少しだけです。

認証ユーザの指定

-u user_name:password

サーバの認証に使用するユーザ名とパスワードを指定できます。

デフォルトでは Basic 認証なのですが、--digest をつけることで Digest 認証にすることもできます。

リクエストメソッドの指定

-X method

リクエストメソッドを指定できます。

デフォルトは GET なので、 POSTDELETE 等を指定したい場合に使用できます。

リクエストパラメータの指定

-d data

リクエストパラメータを渡すことがきます。

このオプションを指定するとリクエストメソッドPOST として扱われるので、 それ以外を指定したい場合は -X で指定する必要があります。

data の指定方法

-d "name=don&age=20"

key=value のように指定します。 & で区切ることで複数のパラメータを渡すことができます。

-d "user[name]=don&user[age]=20"

のように指定することで、入れ子になったパラメータを渡すこともできます。

Rubyのブロックをメソッド間で受け渡す

ブロックとは

ブロックについてはこちらの記事が分かりやすかったです。

qiita.com

ブロックを引数にとるメソッドの定義

定義方法は、ブロックを引数にとることを明示する or しないの2通りがあります。
メソッドを呼び出したときの実行結果は同じです。

明示する

# 定義
def hoge_method(&block)
  block.call # ブロックの実行
end

# 呼び出し
hoge_method do
  puts 'hello world'
end
=> hello world

明示しない

# 定義
def fuga_method
  yield # ブロックの実行
end

# 呼び出し
fuga_method do
  puts 'hello world'
end
=> hello world

以下では明示する方法で記述していきます。

引数をとるブロックを渡す

ブロックを受け取るメソッドの中でいろいろ処理を行い、その結果をブロックで使用したい場合があると思います。
そういったときには、ブロックが引数をとるようにし、ブロックの実行時に処理結果を渡すようにします。

# 定義
def sample_method(&block)
  result = 'hello'   # 処理
  result << ' world' # 処理
  block.call(result) # ブロックに result が渡される
end

# 呼び出し
sample_method do |message|
  puts message # result の中身が message に入る 
end
=> hello world

ブロックを引数にとるメソッドを 別のメソッドで呼び出す

こういったことをしたい場合には以下のようにします。
少し複雑な気がしますが、ブロックを受け取って下へ下へと渡すだけです。

# 定義
def hoge_method(&block) # ブロックを引数にとるメソッドを呼び出すメソッド
  fuga_method(&block)   # ブロックを渡す
end

def fuga_method(&block) # ブロックを引数にとるメソッド
  result = 'hello'
  result << ' world'
  block.call(result)
end

# 呼び出し
hoge_method do |message|
  puts message
end
=> hello world

この場合には yield を使った方法では記述できないと思います。
( hoge_method が受け取ったブロックを fuga_method の呼び出し時に渡すことを明示する必要があるため。 )

間違ってたらごめんなさい。

ちなみに、 &block& がブロックを Proc オブジェクトに変換しているようなので、 fuga_method に Proc オブジェクトを渡すようにしても書けます。

Proc オブジェクトに変換されていることの確認

irb(main):001:0> def hoge_method(&block)
irb(main):002:1>   puts block.class
irb(main):003:1> end
=> :hoge_method
irb(main):004:0> hoge_method {}
Proc
=> nil

Proc オブジェクトを渡すようにする

# 定義
def hoge_method(&block) # Proc オブジェクトを引数にとるメソッドを呼び出すメソッド
  fuga_method(block)    # Proc オブジェクトを渡す
end

def fuga_method(block)  # Proc オブジェクトを引数にとるメソッド
  message = 'hello'
  message << ' world'
  block.call(message)
end

# 呼び出し
hoge_method do |message|
  puts message
end
=> hello world

プルリクのコメントは大事

先日投稿した 大きいプルリクはつらい というエントリで、レビュー側の負担を少なする術を書きましたが、その続編を書きます。これも自戒の意を込めています。

コメントを残す

PR を見ればコードの diff が分かるので、どんな変更を加えられたのか、その変更によりサービスの挙動がどのように変更されたのかが分かります。

ですが、その内容によっては、なぜその変更が必要なのか、その変更により何が幸せになるのか分かりづらいことがあります。一般的な機能の追加等であれば、そりゃ便利になるよねと理解できるかもしれませんが、そうでないことがあることをを意識する必要があります。

また、コードを書いたものにしか分かりづらい情報、例えば、マジックナンバを使用した際には、何故マジックナンバで良いのかといった情報もコメントとして残すべきです。
PR での変更点以外のコードをいろいろ探ったり、PR を出した人に聞けば分かることかもしれませんが、コードの調査には時間がかかりますし、聞く場合も遠距離で開発している場合にはなかなか難しいこともあります。
さらに、分かりづらい PR の場合は、レビュー側も「分かりづらいから時間のあるときにレビューしよう」というように考えてしまうと思うので、開発自体のスピードが低下してしまうかもしれません。

そのため、あらかじめコメントを記述しておくようにしておくべきです。

コミットメッセージには意味を持たせる

コミットメッセージは、変更点の記述にしないようにすべきです。

コミットの diff を見ればどんな変更が加えられたのかは分かるので、XXXの変更 というメッセージは意味のない情報になってしまいます。
こちらも、コメント同様に、なぜその変更をしたのかや、その変更でどうなるのかを記述するようにすべきです。

最近これらができていないことがあったりしたので書きました。
こういったことを意識して、円滑に開発を進めたい!!

大きいプルリクはつらい

チームで開発をする場合、 GitHub 等のサービスを利用してコードを管理することが多いと思います。 既存のコードを変更する際は、GitHub であればプルリクを出して、他の開発者にレビューをしてもらい OK が出ればマージするといったフローが生じます。 レビューにより、複数の視点からコードが吟味され、一人では気づかなかった点に気づくことができます。

つまり、この「他の開発者にレビューをもらい OK が出れば」 を経ることで、コードの品質が悪くなることを防ぎやすくなります。 さらに、レビューを受ける側の成長にもつながります。

そのため、開発においてレビューはとても大事な存在です。

レビューは大変

レビューには時間がかかるし、見落としもしてはいけないので集中力も必要になり、大変と思う方が多いと思います。

小さいプルリクであればまだ良いのですが、大きいプルリクの場合は、変更点が複雑になり全体を把握しづらくなりますし、集中力が持たずにつらくなってしまいます。

そのため、レビューする側の負担が大きくならないようにする工夫が必要です。

小さいプルリクを出す

工夫としては、変更点が小さくなるように気をつけてプルリクを出すことが効果的かと思います。

そのようにプルリクを出すことで、何を変更したかを把握しやすくなりますし、一回のレビューにかかる時間が短くなり負担を抑えることができます。

気をつけることの例としては、以下が挙げられます。

  • ついでの変更をしない。
  • 大きくなりそうな場合は、開発まとめブランチを用意する。
  • レビューにより修正点が多くなる場合は、再度適した粒度でプルリクを出しなおす。

偉そうに述べましたが、自分もできていなかったので自分への戒めとして書きました。