SHOYAN BLOG

I am a pragmatic programmer.

Gitのコミッターを集計するGitFindCommitterをつくった

Gitのコミッターを集計するGitFindCommitterをつくったので紹介します。

GitFindCommitterとは

GitFindCommitterとは変更されたファイルを対象としてコミッターを探すツールです。
名前の通り、Gitのコミット履歴からコミッターを探します。
例えば、fixブランチでhoge.rbとfuga.rbを修正したとします。
GitFindCommitterはhoge.rbとfugue.rbを対象にコミッターを探します。

なぜつくったのか

チーム内でコードレビューを行っているのですが、なかなかレビューしてもらえないということが時々あります。
コードレビューの際に詳しいコミッターをレコメンドすればスムーズにレビューしてもらえるのではと考えました。
レビュアーのレコメンド機能はまだ作成中なのですが、そのモジュールの1つとしてGemに機能を切り出しました。

使い方

以下のコマンドでインストールします。

1
$ gem install git_find_committer

searchメソッドにリポジトリとブランチを指定すると、関連するファイルのコミッターを探してきます。

1
2
3
4
require 'git_find_committer'

GitFindCommitter.search(repo: 'balloonbros/sutekki', branch: 'add-ui')
=> [{:name=>"Shohei Yamasaki", :commit_count=>51}, {:name=>"keitakawamoto", :commit_count=>21}]

GitHub Enterpriseにも対応しており、GitHub Enterpriseで利用する場合は、以下の設定が必要です。

1
2
3
4
GitFindCommitter.configure do |c|
  c.url = "https://<hostname>"
  c.access_token = "<your 40 char token>"
end

便利なTips

集計対象のコミッターをフィルターすることができます。

1
2
3
GitFindCommitter.configure do |c|
  c.available_committer_names = %w(shoyan)
end

名前のみ配列で取得します。

1
2
GitFindCommitter.search(repo: 'balloonbros/sutekki', branch: 'add-ui).names(1)
=> ["Shohei Yamasaki"]

CircleCIでお手軽にCI環境をつくる

最近はCIプラットフォームが充実していて、Travice CI、Wercker、Droneなど様々なプラットフォームが開発されています。
最近はCircleCIを使っているのですが、なかなか便利です。

CircleCIの便利な点

CircleCIの便利な点は特別な設定をせずにテストコードさえあればCIができてしまうことです。
CircleCIにアカウントを作って、CIを行うリポジトリを設定すればOKです。
リポジトリにpushするとCIが実行されます。
ソースコードからbundle installなどの必要な前処理をしてくれるので、CIを行うための前準備的な設定が必要ありません。

私はgit_find_commiterphp-syntax-checkでCircleCIを使っています。

注意点

デフォルトのRubyのバージョンが古いので(マニュアルによるとUbuntu 12.04 and Ubuntu 14.04 build imagesのもの)、Ruby1.9から導入されたキーワード引数など比較的新しく導入された機構を使うと構文エラーが発生することがあります。
その場合はcircle.ymlファイルにRubyのバージョンを設定できます。

1
2
3
machine:
  ruby:
    version: 2.3.0

また、bundlerのバージョンでエラーが発生することがありました。
その場合は、以下のようにバージョンを指定することができます。

1
2
3
dependencies:
  pre:
    - gem install bundler -v 1.12.5

circle.ymlの構文についてはドキュメントを参考にしてください。

3年経ってスクラムをふりかえる

一時期のスクラムムーブメントは一体なんだったのか。
最近はスクラムマスターやファシリテーター、振り返りなどのスクラム用語をめっきりと聞かなくなった。

私のチームではスクラムの最後のプラクティスである朝会さえもなくなった。
結果、それで困っているかというとそんなことはない。

スクラムムーブメントを振り返ってみたいと思う。

スクラム黎明期

スクラムが流行り始めたのは2013年頃だったと思う。
次々と新しいスクラム本が出版され、私もSCRUM BOOT CAMP THE BOOKやアジャイルサムライを読んでスクラムの手法について勉強した。
会社ではスクラムの講習が開かれ、スクラムマスターやプロダクトオーナーを決めたりもした。

私のチームでは朝会と夕会を行い、カンバンでタスクを見える化し、プロジェクトの前には見積もりをチームで行い、定期的にふりかえりを行った。
スクラムマスターとプロダクトオーナーを決めて、スクラムのプラクティスを実践していた。

スクラム暗黒時代

うまくいっているように思われたスクラムだが、雲行きが怪しくなってきた。
スクラムのやり方に縛られ、チームの雰囲気がギクシャクしたものになってきたのだ。
定例のふりかえりでは発言が出ず、議論も深まることなく、時間の無駄に思えた。

スクラムとはなんだったのか

わずか3年でスクラムは姿を消してしまった。
誤解のないように言っておくが、スクラム自体がダメではないということである。
実際にスクラムを導入して成果をあげているチームもあるだろう。
実際によい時期もあったと思う。

スクラムとは手法であり、その内容は主に人同士のコミュニケーションを増やすためのものだ。
よって、人同士のコミュニケーションがうまくいかないと当然スクラムもうまくいかない。
また、コミュニケーションが増えるにあたりMTGなどの時間が増えてしまう問題がある。
そのコストは無視できるほど小さくない。

スクラムは悪くない。
しかし、スクラムは難しい。

[Ruby]オブジェクトの設定情報を保持するパターンの紹介

オブジェクトの設定情報を保持するパターンを紹介します。
このパターンを使えば設定情報を管理するクラスに集約することができ、コードの見通しがよくなります。また、初期設定の定義ができるようになります。

名前空間で区切ってConfigurationモデルに設定を持たせるようにします。
そして、トップにconfigurationとconfigureメソッドを定義します。
クラスメソッドで定義するのがポイントです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Hoge
  class Configuration
    def initialize
      @name = nil
    end
    attr_accessor :name

  end

  def self.configuration
    @configuration ||= Hoge::Configuration.new
  end

  def self.configure
    yield configuration if block_given?
  end
end

configureメソッドは以下のように初期設定したいパラメーターを設定するために使います。

1
2
3
4
5
6
7
Hope.configure do |config|
  config.name = 'shoyan'
end

config = Hoge.configuration
config.name
=> "shoyan"

configureメソッドで設定した情報が保持されています。
初期設定はできるようになりました。

次はこの設定情報を内部で参照するようにします。

Hopeクラスのinitializeメソッドで設定情報をインスタンス変数に保存します。

1
2
3
4
5
6
7
8
9
class Hoge
  def initialize
    @config = Hoge.configuration
  end

  def greeting
    puts "Hello #{@config.name}"
  end
end

実行してみます。
Hope::Configurationの値が出力されていますね。

1
2
3
hoge = Hoge.new
hope.greeting
=> Hello shoyan

Hoge.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class Hoge
  class Configuration
    def initialize
      @name = nil
    end
    attr_accessor :name
  end

  def initialize
    @config = Hoge.configuration
  end

  def greeting
    puts "Hello #{@config.name}"
  end

  def self.configuration
    @configuration ||= Hoge::Configuration.new
  end

  def self.configure
    yield configuration if block_given?
  end
end

Rubyのロード機構について

Rubyはロード機構として、以下を備えています。

  • require
  • load
  • autoload
  • require_relative

require

requireは以下の特徴を備えています。

  • ロードパスからファイルを探してくる
  • 拡張ライブラリもロードできる
  • 拡張子 .rb / .so を省略できる
  • 同じファイルは2度以上ロードしない

Rubyのロードパスは $:に入っています。
私の環境では以下のように表示されました。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[1] pry(main)> puts $:
/Users/shoyan/.rbenv/rbenv.d/exec/gem-rehash
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/did_you_mean-1.0.0/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/coderay-1.1.1/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/slop-3.6.0/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/method_source-0.8.2/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/gems/2.3.0/gems/pry-0.10.3/lib
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby/2.3.0/x86_64-darwin14
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/site_ruby
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby/2.3.0/x86_64-darwin14
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/vendor_ruby
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/2.3.0
/Users/shoyan/.rbenv/versions/2.3.0/lib/ruby/2.3.0/x86_64-darwin14

load

loadもrequireと同じくロードパスからファイルを探してロードします。
しかし、requireのように拡張ライブラリはロードできません。
また、ロードできるファイルもRubyプログラムだけです。
requireのように2回以上の読み込みを制御する機構もなく、callされた回数分ファイルは読み込まれます。

autoload

autoloadはrequireと似ていますが、すぐには読み込まれず、必要になった段階でrequrieされます。以下、サンプルコードです。

autoload.rb

1
2
autoload :Outer, './outer'
puts Outer.new

outer.rb

1
2
3
4
class Outer
end

puts 'Outer loaded!'

実行すると、Outerがloadされているのが確認できます。

1
2
3
  ruby autoload.rb
Outer loaded!
#<Outer:0x007fcc14040e60>

autoloadはネストされたクラスやモジュールもloadすることができます。

autoload.rb

1
2
3
autoload :Outer, './outer'
puts Outer.new
puts Outer::Inner.new

outer.rb

1
2
3
4
5
class Outer
  autoload :Inner, './inner'
end

puts 'Outer loaded!'

inner.rb

1
2
3
4
5
6
class Outer
  class Inner
  end
end

puts "Outer::Inner loaded!"

実行すると、Outer::Innerがloadされているのが確認できます。

1
2
3
4
5
  ruby autoload.rb
Outer loaded!
#<Outer:0x007f9fd4054df0>
Outer::Inner loaded!
#<Outer::Inner:0x007f9fd404f990>

require_relative

あまり見かけることがありませんが、require_relative というメソッドがRuby1.9.2から追加されています。
例えばrequireで同じ階層のファイルをロードする場合は、require './filename' のように明示的に./を指定する必要があります。
require_relativeを使えば、require_relative 'filename' のように定義することができます。