RspecのHTML出力時に文字コードを変更する

# 背景
RSpecでhtml出力した時に、生成されるhtmlのencodingがutf8固定なのでそれを任意のencodingを指定できるようにしたくて調査したメモ

# 問題
rspecの結果をHTML出力すると中々きれいな感じのhtmlが生成されるのですが、デフォルトだとエンコーディングがutf8で、specファイル自体をutf8で書かないと文字化けしてしまいます。


下記のようなspecファイルがあったとして

```rb:spec.rb
#coding: Windows-31J

describe 'htmlのエンコーディングテスト' do
  it "Windows-31Jで書かれたテストケース" do
    expect('テスト').to eq 'テスト'
  end
end
```

これをhtml出力すると

```
rspec -fh -o output_utf8.html spec.rb
```

下記のように文字化けしてしまいます。
![utf8.png](https://qiita-image-store.s3.amazonaws.com/0/34692/9a978ab6-f9a4-c00d-092c-3169d1e9fb6d.png)


# 解法
rspecのソース（versionは2.14.7）を確認したところ、RSpec::Core::Formatters::HtmlFormatterクラスがRSpec::Core::Formatters::HtmlPrinterクラスを利用して出力しているようです。

パラメータ的なものでencodingを指定できるようになってないものかと確認してみると
HtmlPrinterクラスで下記のような処理をおこなっていました。

```:rspec-core-2.14.7/lib/rspec/core/formatters/html_printer.rb
require 'erb'

module RSpec
  module Core
    module Formatters
      class HtmlPrinter
        include ERB::Util # for the #h method
        def initialize(output)
          @output = output
        end

        def print_html_start
          @output.puts HTML_HEADER
          @output.puts REPORT_HEADER
        end
#～中略～

        HTML_HEADER = <<-EOF
<!DOCTYPE html>
<html lang='en'>
<head>
  <title>RSpec results</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />#←ここ

#～後略～
```

というように`HTML_HEADER`という定数に`charset=utf-8`がベタ書きされていてそのままでは指定できなさそうでした。

ですので、HtmlPrinterを継承してこの定数をsub!でencodingを書き換えてやれば出来るはずです。
あとはこのHtmlPrinterをrspecにどう使わせるか？ですが、
HtmlFormatterクラスでHtmlPrinterが下記のように使われています。

```:rspec-core-2.14.7/lib/rspec/core/formatters/html_formatter.rb
require 'rspec/core/formatters/base_text_formatter'
require 'rspec/core/formatters/html_printer'

module RSpec
  module Core
    module Formatters
      class HtmlFormatter < BaseTextFormatter

        def initialize(output)
          super(output)
          @example_group_number = 0
          @example_number = 0
          @header_red = nil
          @printer = HtmlPrinter.new(output) #←ここ
        end
```

なので、HtmlFormatterを継承したクラスを作ってそのコンストラクタで`@printer`を上書きすれば行けそうです。というわけで、下記のようなクラスを作り

```rb:my_html_formatter.rb
require 'rspec/core/formatters/html_formatter'
require 'rspec/core/formatters/html_printer'

class MyHtmlFormatter < RSpec::Core::Formatters::HtmlFormatter
  def initialize(output)
    super
    @printer = MyHtmlPrinter.new(output)
  end
end

class MyHtmlPrinter < RSpec::Core::Formatters::HtmlPrinter
  def initialize(output)
    super
    HTML_HEADER.sub!(/charset=utf-8/, "charset=Windows-31J")
  end
end
```

rspec起動時にこのformatterを指定してやれば行けます。

```
rspec -r ./my_html_formatter.rb --format MyHtmlFormatter -o output_windows31j.html spec.rb
```

で、下記のように出力されます。
![windows31j.png](https://qiita-image-store.s3.amazonaws.com/0/34692/cb9f8e1c-cf3c-ce76-592c-94f402ae28d7.png)

# あとがき
HTML_HEADERをsub!で変更したときに初めて知りましたが警告が出ないんですね。

rubyの定数は「定数」といいつつ再代入できますが、警告がでます。
でも、破壊的なメソッドで定数自体を変更しても警告はでないようです。これは知りませんでした。

```rb
irb(main):001:0> Hoge = "aaa"
=> "aaa"
irb(main):002:0> Hoge = "bbb"
(irb):2: warning: already initialized constant Hoge
(irb):1: warning: previous definition of Hoge was here
=> "bbb"
irb(main):003:0> Hoge.sub!(/bbb/, 'ccc')
=> "ccc"
irb(main):004:0> Hoge
=> "ccc"
```