KazumaLab.

流行りとリラックマと嵐が大好きです。

初めてのRubyGemsを作りました。

かずまです。
今回は色々困っていることがあってGemを作りました。

作ったもの

Haml-lintをAutoCorrectするものです。
今回はHash Attributeのあとにスペースを自動で入れてくれるものです。

既存のプロジェクトにHaml-Lintを入れたのですが、かなりHashのあとにスペースがなかったりしたので、これは手動でやっていたら果てしない時間がかかると思って作りました。

GlobalなRuby Gemsにはしないつもりです。
自分にとって必要だったので作成してローカルビルドして、ローカルインストールして使っています。

github.com

検証2日、実装1日ほどで作成したのでリファクタリング等はできていませんが、きちんと置き換えてくれると思います。

テストのカバレッジを上げていきたいですがmockを使ってテストを書きたかったので若干そこはまだです。

使ってみる

# bundle exec haml_lint_auto_corrector load file_name.html.haml

今回のlintの規約として

%div{ class: "sample" }
  %button{ class: "button" }= "Sample #{@user.name}"

ハッシュの間の値の前後にスペースを一つ入れるようにしないとLintで引っかかる場合に有効です。
上記コードであれば何もせずにそのまま終わります。

%div{class: "sample"}
  %button{class: "button"}= "Sample #{@user.name}"

このコードだとLintに引っかかるので自動で一つ前のコードのようにスペースを入れた形に置換してくれます。

Haml-lintにはAutoCorrectがない

Rubocopのように自動で置換してくれる機能がありません。
issuesにはたまにあがってくるそうなのですが、作者が好まないのか、Closeされるようです。

もちろん、AutoCorrectを使うのはあまりいい手だとは思えませんのでもしそれで作らないというのであればわかります。

ただ、これから作るプロジェクトであれば、作らなければOKなのですが、既存のプロジェクトだとかなりのコード量にもなっているので治すのは一苦労です。
ですので、使う用途としてはhamlファイルをいじるときに面倒なのでこのコマンドを実行して、自動修正して、あとのLintErrorは自分で修正する形がいいと思います。

実装

今回の実装

  • Pathからファイルを取得
  • バックアップを作成
  • nodeによる分割
  • 正規表現でlintに引っかかったらnodeの中身を置換
  • contextでマージ
  • バックアップが不要になったら削除

node一つがファイルの1行になっています。
contextが、置換したあとのデータファイルを持つイメージです。

難しい部分

面倒だったのが、Stringの中にインスタンスを入れて動的に変更する部分です。
上の例のコードであれば"#{@user.name}"です。

マッチしたら変更しない、にしようと思ったのですが、
1行レベルの正規表現マッチだったので、結構面倒な実装になるなーと思ったので、
計算量的には増えますが、一旦Hash全てにスペース入れたあとにマッチした部分だけもとに戻すということをしています。

if is_string_interprolation
  code_line.gsub!(/\#{\s.*\s\}/) do |word|
    word.gsub!(FRONT_REG_SPACE) { |w| "{#{w[2]}" }
    word.gsub!(BUTTOM_REG_SPACE) { |w| "#{w[0]}}" }
  end
end

色々ツッコミどころはありそうなコードですが、
そういった場合...気になったらpullrequestいただければ嬉しいです。
後にリファクタリングする予定ですが。。。

ということで今後もサービスを効率よく進めるために色々作りつつコードの綺麗さを上げて行きたいと思います。頑張ります。