Redmine カスタムクエリが500 ActionView::Template::Error ("XXX" from ASCII-8BIT to UTF-8) になるときの対処方法

# 発生した環境
- Bitnami Redmine Stack (Windows版) 1.4 から 3.1.1 へのアップグレード後
- 通常のIssue作成、閲覧は問題ない
- カスタムクエリだけ 500 Internal Error


# 原因
- production.log によるとVIEWの表示で ASCII-8BIT から UTF-8 へ変換できないとのこと。

```ruby:production.log
Started GET "/redmine/projects/a/issues?query_id=1" for 192.168.XX.XX at 2015-XX-XX XX:XX:XX +0900
Processing by IssuesController#index as HTML
  Parameters: {"query_id"=>"1", "project_id"=>"a"}
  Current user: User (id=1)
  Rendered queries/_filters.html.erb (10.0ms)
  Rendered issues/index.html.erb within layouts/base (16.0ms)
Completed 500 Internal Server Error in 261ms (ActiveRecord: 101.0ms)

ActionView::Template::Error ("\xE4" from ASCII-8BIT to UTF-8):
     7: $(document).ready(function(){
     8:   initFilters();
     9:   <% query.filters.each do |field, options| %>
    10:   addFilter("<%= field %>", <%= raw_json query.operator_for(field) %>, <%= raw_json query.values_for(field) %>);
    11:   <% end %>
    12: });
    13: <% end %>
  app/helpers/application_helper.rb:1076:in `raw_json'
  app/views/queries/_filters.html.erb:10:in `block (2 levels) in _app_views_queries__filters_html_erb__85224075_77320944'
  app/views/queries/_filters.html.erb:9:in `each'
  app/views/queries/_filters.html.erb:9:in `block in _app_views_queries__filters_html_erb__85224075_77320944'
  app/views/queries/_filters.html.erb:1:in `_app_views_queries__filters_html_erb__85224075_77320944'
  app/views/issues/index.html.erb:19:in `block in _app_views_issues_index_html_erb___72240600_77564676'
  app/views/issues/index.html.erb:11:in `_app_views_issues_index_html_erb___72240600_77564676'
  app/controllers/issues_controller.rb:77:in `block (2 levels) in index'
  app/controllers/issues_controller.rb:76:in `index'
  lib/redmine/sudo_mode.rb:63:in `sudo_mode'

```

app/helpers/application_helper.rb にある raw_json が問題を起こしている様子。

```ruby:app/helpers/application_helper.rb
  # Helper to render JSON in views
  def raw_json(arg)
    arg.to_json.to_s.gsub('/', '\/').html_safe
  end
```

`arg.inspect` してみるとちゃんと UTF-8 だと判定されたりASCII-8BIT (例えば"\xE4")と判定されたり混ざっていた。rubyでは「文字コードの違うものどうしを結合できない」とエラーになってしまう。

# 対処（とりあえず）
呼び出し元で対処するか、このraw_jsonで対応すべきか正直迷ったのですが。
来たものを片っ端からUTF-8にしてしまうことで逃げる。

```ruby:app/helpers/application_helper.rb
  # Helper to render JSON in views
  def raw_json(arg)
    arg = force_utf8_strings(arg)      # add
    arg.to_json.to_s.gsub('/', '\/').html_safe
  end
```

ファイルの末（Private メソッドの並び）

```ruby:app/helpers/application_helper.rb
private

# (略)

# add
  def force_utf8_strings(arg)
    if arg.is_a?(String)
      arg.dup.force_encoding('UTF-8')
    elsif arg.is_a?(Array)
      arg.map do |a|
        force_utf8_strings(a)
      end
    elsif arg.is_a?(Hash)
      arg = arg.dup
      arg.each do |k,v|
        arg[k] = force_utf8_strings(v)
      end
      arg
    else
      arg
    end
  end
```

これでエラーは出なくなりました。
実は force_utf8_strings は app/models/setting.rb にあるPrivateメソッドをコピーしたもの。
同じコードが二箇所にあることになるので、原則的に良い方法じゃないですよね・・・。
こういった場合はどこに配置するのがいいのでしょう。


※2018/07/05 追記
こちらの方が対応されたように、もう一か所修正が必要なようです。
https://qiita.com/sugasaki/items/296a2e1f8101826ce588

なお最新版のRedmineでも同様の事象が起きる場合の対処ですが
上記パッチ対応後にカスタムクエリーを修正・保存しなおすと、カスタムクエリ上の条件文字列データがUTF-8で保存しなおされるので次からエラーがでなくなることを確認しました。