Rubyのワンライナーを使って、HAMLの%img{ }をimage_tagに置換する方法


## このTipsが必要になる背景

- デザイナーさんが画面デザインをHTMLで納品してくれる場合がある。
- RailsプログラマとしてはHAMLで書き直したい。
- 大方のタグは [Html2Haml](http://html2haml.heroku.com/)で自動変換できる。
- しかし、画像はassetsの下に置くのでパスが変わってしまう。
- `%img{ }`は全部`image_tag`メソッドを使って書き直したいが、数が多いので手作業でやるのは面倒。

## やりたいこと

こんなimgタグを

`%img{alt: "顔写真", height: "80", src: "img/face.jpg", width: "100"}`

こう置換したい。

`= image_tag "face.jpg", size: "100x80", alt: "顔写真"`

## 解決策

Rubyのワンライナーで置換してみた。

````bash
$ ruby -pe 'sub(/%img{alt: "([^"]+)", height: "([^"]+)", src: "img\/([-\w.]+)", width: "([^"]+)"}/, "= image_tag \"\\3\", size: \"\\4x\\2\", alt: \"\\1\"")' test.html.haml > replaced.html.haml
````

### 実行例

````test.html.haml
%body
  %img{alt: "顔写真1", height: "80", src: "img/face-1.jpg", width: "100"}
  %hr
  %img{alt: "顔写真2", height: "100", src: "img/face-2.jpg", width: "120"}
  %p
````

````bash
$ ruby -pe 'sub(/%img{alt: "([^"]+)", height: "([^"]+)", src: "img\/([-\w.]+)", width: "([^"]+)"}/, "= image_tag \"\\3\", size: \"\\4x\\2\", alt: \"\\1\"")' test.html.haml > replaced.html.haml
````

````replaced.html.haml
%body
  = image_tag "face-1.jpg", size: "100x80", alt: "顔写真1"
  %hr
  = image_tag "face-2.jpg", size: "120x100", alt: "顔写真2"
  %p
````

## 解説

コマンドがややこしく見えると思うので解説します。

### コマンドの基本形

`ruby -pe 'sub(/正規表現/, "置換後の文字列")' 処理対象ファイルのパス > 置換後のファイルパス` というのがこの処理の基本形。

- `-e`オプションはワンライナーを実行する。
- `-p`オプションはファイルの内容を1行ずつ読み取る。
- `sub`は読み取った行のテキストを置換する。
- `処理対象ファイルのパス`でRubyに食わせるファイルを指定する。
- 最後に、`> 置換後のファイルパス`という形で標準出力の内容を新しいファイルに書き込む。これを省略すると標準出力に置換後の文字列が表示される。動作確認するときに便利。

### subの処理

`sub`の処理については以下の通り。

1. 正規表現で各値をキャプチャする。
 - `/%img{alt: "([^"]+)", height: "([^"]+)", src: "img\/([-\w.]+)", width: "([^"]+)"}/`

1. 各値は以下のような番号でキャプチャされる。(丸括弧がキャプチャ用のメタ文字)
 - alt: `\1`
 - height: `\2`
 - src: `\3` (ただし`img/`は含まない)
 - width: `\4`
1. キャプチャした文字列を利用して、置換後の文字列を組み立てる。

````
"= image_tag \"\\3\", size: \"\\4x\\2\", alt: \"\\1\""
````

## 制限事項＆備考

- あくまで、altやheightなどの属性が同じ順番で並んでいることが前提です。順番が違ったり、存在する属性としない属性がバラバラだったりすると正規表現にマッチしません。
- そこそこの努力で80点ぐらいの結果が出せることを目的としています。
- 一発で完璧にマッチする正規表現を作るのは難しいので、http://rubular.com で試行錯誤した方が近道だったりします。

## 応用

CSS(SCSS)の`url`も`image-url`に置き換えてみましょう。

````test.css.scss
body {
  text-align: center;
  background: url("../img/bg.jpg") no-repeat center top #f8f8f8;
}

#headWrapper {
  background: url("../img/head_bg.jpg") repeat-x right top;
  height: 80px;
}
````

````bash
$ ruby -pe 'sub(/url\(.?\.\.\/img\/([-\w.]+).?\)/, "image-url(\"\\1\")")' test.css.scss > replaced.css.scss
````

````replaced.css.scss
body {
  text-align: center;
  background: image-url("bg.jpg") no-repeat center top #f8f8f8;
}

#headWrapper {
  background: image-url("head_bg.jpg") repeat-x right top;
  height: 80px;
}
````

## 参考文献

- [rubyのワンライナーに見る驚きの省略記法](http://d.hatena.ne.jp/zariganitosh/20131107/ruby_oneliner_omission)
- [Rubyワンライナー入門](http://d.hatena.ne.jp/maeharin/20130113/ruby_oneliner)

## おまけ:「そもそも正規表現がわからないんですけど？」というプログラマさんへ

それはマズいです！！今すぐ勉強してください！
正規表現をマスターすればあなたの生産性は256倍向上します。

僕はオライリーの「フクロウ本」で勉強しました。
一見、難しそうな本に見えますが、基本の「キ」から丁寧に教えてくれるので非常にわかりやすかったです。

<a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873113598/junic05-22/ref=nosim/" name="amazletlink" target="_blank">詳説 正規表現 第3版</a>
<a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873113598/junic05-22/ref=nosim/" name="amazletlink" target="_blank">
![51cRxtwo7IL._SL160_.jpg](https://qiita-image-store.s3.amazonaws.com/0/7465/354cdf88-cb1c-aeb1-e4a5-6dd094f4b903.jpeg "51cRxtwo7IL._SL160_.jpg")
</a>