Synvert で Ruby のコードを構文レベルでリファクタリングする

## <i class='fa fa-question-circle'></i> Synvert とは？
>Synvert = syntax + convert, makes it easy to convert ruby code automatically.

※ [README - xinminlabs/synvert より](https://github.com/xinminlabs/synvert)

Synvert で用意されいている AST の操作の操作や置換を行うDSLを用いて Ruby のコードを変換できます。
Synvert 自体はリファクタリング専用のツールではありませんが、リファクタリングに利用することもできます。

## <i class='fa fa-flask'></i> サンプル1 `String#gsub` to `String#tr`
Synvert の Snippet として用意されている例をみます。
gsub で 1文字の置換を行っている箇所を tr に置きかえる Snippet です。
なぜ gsub を tr に変えるか？についてはパフォーマンスです。詳しくは以下の記事に。

* [gsubよりtr の方が早い - rochefort's blog](http://rochefort.hatenablog.com/entry/2014/10/04/073000)

例えば Rails で以下のような Pull Request がありました。

* [use tr instead of gsub when possible -  rails/rails](https://github.com/rails/rails/commit/56d26d1d9fd82a39c38228759889df5173992db3)

Synvert でひとたび gsub => tr への変換 Snippet を作っておけば
以降はこれを実行するだけでリファクタリングは完了です。

### <i class='fa fa-code'></i> コード
```ruby
Synvert::Rewriter.new 'ruby', 'gsub_to_tr' do
  description <<-EOF
It converts String#gsub to String#tr
    'slug from title'.gsub(' ', '_')
    =>
    'slug from title'.tr(' ', '_')
  EOF

  within_files '**/*.rb' do
    # 'slug from title'.gsub(' ', '_')
    # =>
    # 'slug from title'.tr(' ', '_')
    with_node type: 'send', message: 'gsub', arguments: {size: 2, first: {type: 'str'}, last: {type: 'str'}} do
      if node.arguments.first.to_value.length == 1 && node.arguments.last.to_value.length < 2
        replace_with "{{receiver}}.tr({{arguments}})"
      end
    end
  end
end
```

* [lib/ruby/gsub_to_tr.rb - xinminlabs/synvert-snippets](https://github.com/xinminlabs/synvert-snippets/blob/master/lib/ruby/gsub_to_tr.rb)

## <i class='fa fa-flask'></i> サンプル2 `> 0, < 0` to `Numeric#positive?, negative?`
Ruby 2.3 で導入された Numeric#positive?, negative? 記法へのリファクタリングのための Snippet を作成します。
該当メソッドに関する詳細説明はいつも素晴らしい情報を発信してくださっている伊藤さん( @jnchito )の記事がわかりやすいです。

* [数値の正負を判別する Numeric#positive?, negative? - サンプルコードでわかる！Ruby 2.3の主な新機能 - jnchito - Qiita ](http://qiita.com/jnchito/items/0faac073cb77417d61c7#%E6%95%B0%E5%80%A4%E3%81%AE%E6%AD%A3%E8%B2%A0%E3%82%92%E5%88%A4%E5%88%A5%E3%81%99%E3%82%8B-numericpositive-negative)

### <i class='fa fa-code'></i> 置換対象コード
```ruby
10.times do
  a = rand(-5..5)
  if (a > 0)
    puts "positive"
  elsif (a < 0)
    puts "negative"
  else
    puts "zero"
  end
end
```

### <i class='fa fa-code'></i> Synvert 自作 Snippet
```ruby
Synvert::Rewriter.new "ruby_2_3_new_feature", "nega_posi" do
  description "replace hoge > 0 to hoge.positive?, hoge < 0 to hoge.negative?"

  within_file 'sample_target*.rb' do
    with_node type: 'send', message: '>', arguments: {first: {to_value: 0}} do
      replace_with "{{receiver}}.positive?"
    end

    with_node type: 'send', message: '<', arguments: {first: {to_value: 0}} do
      replace_with "{{receiver}}.negative?"
    end
  end
end
```

### <i class='fa fa-check-circle'></i> 実行
* synvert を実行

```sh
$ synvert --load sample_execute.rb -r ruby_2_3_new_feature/nega_posi
===== ruby_2_3_new_feature/nega_posi started =====
===== ruby_2_3_new_feature/nega_posi done =====
```

* 実行後にリファクタリングされたコードを確認

```ruby
10.times do
  a = rand(-5..5)
  if (a.positive?)
    puts "positive"
  elsif (a.negative?)
    puts "negative"
  else
    puts "zero"
  end
end
```

## <i class='fa fa-pencil'></i> メモ
Synvert は内部で [parser gem](https://github.com/whitequark/parser/) を利用している。


## <i class="fa fa-book" style="font-size:1em;"></i> 外部資料
* [Synvert Official](http://xinminlabs.github.io/synvert/)
* [xinminlabs/synvert - GitHub](https://github.com/xinminlabs/synvert)
* [xinminlabs/synvert-core - GitHub](https://github.com/xinminlabs/synvert-core)
* [xinminlabs/synvert-snippets - GitHub](https://github.com/xinminlabs/synvert-snippets)
* [whitequark/parser - GitHub](https://github.com/whitequark/parser/)
* [AST Format - whitequark/parser - GitHub](https://github.com/whitequark/parser/blob/master/doc/AST_FORMAT.md) 
*