haml2slim でハマった点とその解決方法

MFクラウド給与の開発に携わっているエンジニアの増山(@nyangryy)です。

普段Railsを使った開発の中でハマった内容や、調査の過程を紹介していきたいと思います。
今回は、MFクラウド給与の開発中における、Railsのテンプレートエンジンを haml から slim へ変換する際ハマった点と、それを解決するために行なった方法を紹介します。
 

動作確認環境

MacOSX 10.10.2

Ruby 2.1.5
Rails 4.1.8
haml2slim 0.4.7

zsh 5.0.5 (x86_64-apple-darwin13.0.0)
ag 0.26.0
sed (GNU sed) 4.2.2

 

なぜ slim に変換したか

まず、端的に haml から slim への変換を行なった理由を述べると以下の3点となります。

  • 表示速度を少しでも速くして、UXを向上させたい
  • erb2haml のように haml2slim でもスムーズに変換が可能だと考えていた
  • 社内へのフィードバック

表示速度を少しでも速くして、UXを向上させたい

haml から slim への変換に対して、最も強く期待しているのはこの理由です。
MFクラウド給与はオンライン上で給与関連業務を完結させることを目指していますが、インストール型のアプリケーションと比べ、まだまだ操作性、体感速度等、改善すべき課題があります。

既存のアプリケーションの操作感に慣れ親しんでいるユーザにとって、操作に手間がかかったり、体感速度が低下してしまうことは、WEBアプリケーションが提供できるメリット以上に、大きなデメリットとなってしまいます。

なので、少しでも体感速度を向上させるために、取りうる選択肢はどんどん試していこうという意気込みでサービスを開発しています。

erb2haml のように haml2slim でもスムーズに変換が可能だと考えていた

当初MFクラウド給与の開発は、 haml で行なっていたのですが、これは haml がマネーフォワードのRailsプロジェクトで使用されており、まずは動くものを作るという点において、一番自分たちがパフォーマンスを発揮できると考えていたためです。

ではなぜ、 haml の資産が積み上がってきた途中から slim への変換を行なったかというと、MFクラウド給与の開発指針を定める際、将来的なレンダリングのパフォーマンスを重視して、できればリリース前のタイミングで、 haml から slim に変換しようと決めていたためです。

その際 erb2haml のように haml2slim でもスムーズに変換が可能だろうと、楽観視していたのですが、実際のところ、そんなうまい話はありませんでした・・。

ここはもう少し予備調査を行うべきだったのですが、工数が限られていた中で、まずは haml で動くものを作ることを重視していて、もし工数が足りなければ最悪 haml のままで進めるという選択肢もあったので、ここの調査がおざなりになってしまいました。

社内へのフィードバック

今回 haml から slim へと変換を行なったのは、MFクラウド給与 という3月30日にβ版の提供が開始されたばかりのサービスです。

このMFクラウド給与はマネーフォワードの中で最も新しいRailsプロジェクトとなるので、既存のRailsプロジェクトで導入したかった設計や技術をどんどん盛り込んでいこうと決めていて、また、その導入過程で得られた知見を社内にフィードバックすることで、少しでも社内の開発効率の向上に寄与したいという狙いがありました。

slim の導入もその1つで、正直なところ、ここはスムーズに haml から変換できると考えていたのですが、結果として haml で構築された既存のRailsプロジェクトを slim に変換したい意識が高まった際は、今回の知見が役に立つのではないかと期待しています(苦笑)
 

ハマった点とその解決方法

前置きが長くなってしまいましたが、ここからは実際に haml2slim を使って一括変換を行なった後に、エラーをなくすために行なったこと、ソースコードの乱れをなおすために行なったこと、細かい注意点等を紹介したいと思います。

また、ここから行う手順は主に正規表現による置換処理なのですが、1つの手順ごとに Git のコミットを行なっておくことを強く推奨します。
git diff した際の変化点がわかりやすくなりますし、revert も手順単位で可能なのでオススメです。
 

エラー退治編

haml2slim による一括変換後、サーバを起動して適当なページにアクセスすると、とにかくエラーが出まくります。
まずはこのエラーを潰していきます。

Ruby のハッシュシンタックスを 1.9 => 1.8 に変換する

slim がシンボル形式のハッシュを許容しておらず、この置換処理が一番変換後のエラーを減らせた気がします。
今回は新しいプロジェクトということもあり、ほとんどシンボル形式に統一していたのでそこら中のページで落ちていました・・

REGEX_PATTERN='(\w+):\s?'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/:\1 => /g"

ハッシュシンタックスの変換でゴミができたのでその修正

1つ前の手順で、置換対象となった行の末尾にコロンがついてしまった。

ruby:
  class => 'hogehoge',:
  data => {:
REGEX_PATTERN='(\w+\s=>\s.*[^\s]+):$'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/:\1/g"

Data属性の記述を修正する

div.dropdown  'data-toggle' => 'dropdown'
REGEX_PATTERN="  'data-toggle' => 'dropdown'"; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/*{'data-toggle' => 'dropdown'}/g"

 

cosme編

エラーは起きないけどコード上記綺麗にしておきたいところ。

単語間が無駄に2スペース開いている部分を1スペースに

col  width=('3%' )
REGEX_PATTERN='(\w+)[ ]{2}(\w+)'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/\1 \2/g"

パーレンの内側にスペースを入れる

例1 左側だけスペースが足りない。

col width=('3%' )
REGEX_PATTERN='(=\()(\S)'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/\1 \2/g"

例2 右側だけスペースが足りない。

col width=( '3%')
REGEX_PATTERN='(\(\s.+\S)\)'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/\1 )/g

Ruby コード部分はハッシュを新シンタックスに戻す

REGEX_PATTERN=':(\S+)\s=>\s'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN/\1: /g"

行末のスペース削除

REGEX_PATTERN=' *$'; ag $REGEX_PATTERN app/views -l | xargs sed -i '' -r -e "s/$REGEX_PATTERN//g"

 

slim を使用する上での注意点

ここまでの手順で大方エラーは潰し終わり、ソースコードの気になる部分もなくしました。
続いて、 slim を使い始めてからわかった点を紹介したいと思います。

この辺りはエラーとしてページが落ちるわけではないけど、ハマりやすい部分が多いと思います。

表示が崩れる!

これは slim の記述(特に data 属性辺り)が間違っていて、崩れていることが多かったのですが、
その辺りを潰しても、どうしても謎の表示崩れが発生してしまっていて、
production モードでサーバを起動すると直ることから、解決策を見つけることができた問題です。

結論としては、development モードでも production モードと同様の設定にしておく必要があります。

config/environments/development.rb に以下の記述を追加します。

Slim::Engine.set_options pretty: false

この設定を false にすると何が起きるかというと、タグの間にインデントを挿入する処理を無効にします。
この設定が true になっていると、自動的にHTMLにインデントが付与され、そのインデントが表示崩れを引き起こします・・

コメントエラー

以下のような、行末コメントアウトはシンタックスエラーとなります。

= raw display_meta_tags # hoge

属性の展開に気をつける

slim だと、 以下のようにして属性を展開しても、

class=*{ data: { hoge: true } }

data-html とだけ出力されます。

こういったケースは以下のようにダブルクォーテーションで true を囲むことで、正常に出力されます。

class=*{ data: { hoge: "true" } }

空白・改行の扱いに注意する

haml と違って slim にはタグ間の空白、改行を削除する手段がありません・・

逆にスペースを差し込みたい場合は<>を使います。
これは haml と全く逆の挙動なので注意が必要です。

meta tag を出力するGemがうまく動かない

今回対象となったRailsプロジェクトでは、meta tag を出力してくれるGemを使っていたのですが、どうもうまくHTMLが生成されずに困っていましたが、これは生HTMLを吐き出す slim のメソッドを利用することで解決出来ました。

具体的には、以下のように raw メソッドを利用します。

-= breadcrumbs style: :ul, class: 'pa-breadcrumbs', link_current: true, current_class: nil, semantic: true
+= raw breadcrumbs style: :ul, class: 'pa-breadcrumbs', link_current: true, current_class: nil, semantic: true

 

cells Gem を使っている場合は注意する

cells を使っている場合、app/cells 以下にファイルを配置しているかと思いますが、 haml2slim や今回紹介してきた変換手順では、app/views 以下を対象としているので変換処理が抜けてしまわないように注意してください。
 

まとめ

もっと予備調査を行うべきだったのですが、 haml2slim が思った以上にスムーズに変換できずにハマってしまいました。
とはいえ、 slim に移行したことで、
今後の機能追加等によるパーシャルの追加が行われても、レンダリングのパフォーマンス悪化は防げそうです。

ちなみにローカル環境(Macbook + Webrick)で簡単なベンチマークを行なったところ、パーシャル数が40程度のレンダーを行うページだと、development モードでは、 haml のほうが少し速いという面白い結果になっています。(production モードではほぼ同等)

まだまだMFクラウド給与はプロジェクト自体がそこまで大きくないので、
目に見えて体感速度の向上!とはなりませんでしたが、
slim はレンダーするパーシャル数が多くなるほど効果を発揮すると思うので、今後の開発が楽しみです 🙂

・・・実は今回の変換を行なった数日後に hamlit というGemが公開され、 haml でも slim と同等のパフォーマンスを出すことが可能になったようです!

この苦労は一体何だったんだ・・・と肩を落としていたのですが、
%の入力を減らせる=指にやさしいのでは というポジティブ意見を社内から頂きました 🙂

haml のパフォーマンスを向上させるGem

エンジニア募集中!

マネーフォワードでは、積極的に新しい技術や取り組みに挑戦し、ユーザのためになる開発を行いたいエンジニアを募集しています!
みなさまのご応募お待ちしております!
 
【採用サイト】
『マネーフォワード採用サイト』 https://recruit.moneyforward.com/
『Wantedly』 https://www.wantedly.com/companies/moneyforward
 
【プロダクト一覧】
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 https://moneyforward.com/
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 iPhone,iPad
家計簿アプリ・クラウド家計簿ソフト『マネーフォワード』 Android
クラウド型会計ソフト『MFクラウド会計』 https://biz.moneyforward.com/
クラウド型請求書管理ソフト『MFクラウド請求書』 https://invoice.moneyforward.com/
クラウド型給与計算ソフト『MFクラウド給与』 https://payroll.moneyforward.com/

Pocket