RailsのテストでCSS適用のスクリーンショットを見る

Capybaraのsave_and_open_pageでcssを反映する方法についてですが、ようやく正解に辿り着いた感じがあります。
save_and_open_pageを実行しても、CSSがうまく当たってないプレーンなページが表示されてしまう問題ですが、私的な解決策は以下でした。

  • spec_helper.rbで、以下を追記する。
    Capybara.asset_host = "http://localhost:3000"
    
  • rails sでWEBrickを立ち上げる。
  • 表示されるスクリーンショットが、WEBrickに対してJavaScript,CSSなどを取得しにいくので、綺麗なスクリーンショットが見れる。

ただし、WEBrickが死んでいたらまたプレーンになるので注意!
常にWEBrickを立ち上げておく必要があるので、開発はpowとかじゃなくてWEBrickを使ったほうがリソースの使用率的にはいいのかもしれないっすねー(知らんけど)。


RailsとAngularJSとの相性について

既存のRailsプロジェクトで、id以外は似たようなソースのCoffeeScriptがいくつかできてしまったので、ここをJavaScriptのMVCですっきり解決できるんじゃないか?と思って、AngularJSを調べてみた。

gemにangularjs-railsというのがあったのでこれを使ってみた。

結論からいうと、既存のRailsプロジェクトにAngularJSを統合するのはかなりの苦行かつ、Rails側のソースとの重複が発生しそうだなということだった。
最初はなかなかよさげかなーと思ったのだが、嫌になったのは、編集のときだった。

http://localhost:3000/users/1/edit などにアクセスがあったら、Railsならば

  1. Controllerで@userができる
  2. Viewのform_for @userでフォームが作られる。
  3. 既存のデータが入力エリアに反映される。

となる。

しかし、ここにAngularJSが絡むと、

  1. Controllerで@userができる
  2. Viewのform_for @userでフォームが作られる。
  3. 既存のデータが入力エリアに反映される。(ただし一瞬)
  4. AngularJSのモデルの初期化が行われる。
  5. 入力エリアに紐づいているng-modelの値が空なので入力エリアが空欄になる!

となってしまい、困る。

これを直すには、各入力エリアに

f.input :name, input_html: {ng-model="user.name" ng-init="user.model='#{@user.name}'"}

のように書かないといけない。

正直、こんなの、入力エリアが大量にあったら書きたくないのである。

既存のRailsプロジェクトでなければ、ユーザーの情報をAjaxで引っ張ってきてAngularJSのモデルにJSONを突っ込めばいいだけなんだろうけれども、既に出来上がっているものに対して、それを壊してまで対応する価値はなさそうだ、という結論に至った。

サーバサイドでガッツリ作り込んでいるプロジェクトに後々適用できるものではなさそうである。
最初っからJavaScriptMVCをやってみるには、よさそうである。

AngularJSを使う場合には、サーバ側は完全にAPIサーバじゃないと向かんかなと。
Railsとの相性は、既存のプロジェクトとはダメ。新規のプロジェクトでやりとりがJSONのみならばOKという気がする。

というかAngularJS自体、GAEで使うために生まれてきたんじゃないの?という気がする。
まぁGoogle製だしね。


AndroidのテストのdependenciesをProvidedにすること

IntellijでAndroidのテストを書こうと思って、テストプロジェクトを作ってから、ライブラリも追加してからテストを実行したら、エラーで全然テスト自身が行われなかった。
どうすればいいのかわからなかったので、コードをいろいろと見ていたのだが、全然わからなかった。そもそもアプリ自体はちゃんと動いているわけだから、何が悪いのかわからんかったのですが、dependenciesのscopeが悪かった。

既にテスト対象側のモジュールにライブラリが含まれているから、CompileじゃなくてProvidedにする必要がある。

Project Structure

まぁ考えてみれば、そりゃそうか…という感じ。
ちなみにKotlinでやってます。


CanCanでmodelと関連のないcontrollerでのアクセスを許可する

モデルと関連のないコントローラーについて、CanCanでどのように設定するのかがわからなかったので、調べてみた。

結果としては、

  • コントローラーでauthorize_resource class: falseに設定する。
  • ability.rbでcanの第2引数にコントローラー名のシンボルを渡す。

という感じだった。

コントローラー側

class HogeController < ApplicationController
  authorize_resource class: false
  # 略
end

Ability側

class Ability
  include CanCan::Ability
  def initialize(user)
    unless user.nil?
      case user.role.code
        when "manager"
          can :manage, :all
        else
          can :manage, :hoge # コントローラー名のシンボルを記述
      end
    else
      cannot :manage, :all
    end
  end
end

なんか空のモデルを作ってる回答例とかもあったけれど、こっちのほうがスマートですわ。


jQueryプラグインのDataTablesがよさげ

ちょっとギョーミーな感じのテーブル(HTMLの)を使っていて、それをソートするのにいいjQueryのプラグインないかなー?と思ってググったところ、ありました。DataTablesプラグイン。かなり多機能で、自分は使ってないけれど、ページングをAjaxでの切り替えなどもできるっぽい。

jQuery Plugin: DataTables

また、Twitter Bootstrapでも使えるようなTipsも本家の記事で紹介されている。

DataTablesをTwitterBootstrapで使うには(英語)

Railsで使う場合は、上記のページにあるDT_bootstrap.jsとDT_bootstrap.cssをダウンロードして、assetsの各フォルダに突っ込んでおくとよい。
DT_bootstrap.cssで、画像のurl指定をしているところを、/assets/sort_both.pngみたいに/assets/なんちゃらに書き換えておく必要がある。
画像はもちろんapp/assets/imagesディレクトリ以下にコピーしておくこと。

至れり尽くせりな感じがするDataTablesなのだが、しかし、こいつはちょっとわかりにくい。
初期化パラメータがsなんちゃらやaなんちゃらみたいになっていて、ちょっとわかりにくいなーと思っていたら、日本語でwikiにまとめられてた。
非常にありがたい。

JavaScript/jQuery/DataTables

また、日本語化に関する記事があった。

多機能テーブルDataTablesを使う

これに従って、似たような感じにしてみた。(ただしソースはcoffeescript)

$ ->
  $("table#example").dataTable({
    "oLanguage": {
      "sLengthMenu" : "表示件数 _MENU_ 件"
      "oPaginate"   : {
        "sNext"     : "次へ"
        "sPrevious" : "前へ"
      }
      "sInfo" : "全_TOTAL_件中 _START_〜_END_件を表示"
      "sSearch" : "検索:"
      "sZeroRecords" : "データが見つかりません"
      "sInfoEmpty" : "データが見つかりません"
      "sInfoFiltered" : "(全_MAX_件より、フィルタリング)"
      "sProcessing" : "しばらくお待ちください…"
    }
    "bPaginate" : true # ページングを表示
    "bProcessing" : true # 処理が長いときに処理中の表示をする
    "aoColumns" : [ # 列毎に設定を行うときに使用
      {"bSortable" : false} # 対象の列をソートできないようにする
      null # デフォルトの設定から変更しない場合はnullを指定
      null
      null
      null
      null
      null
      null
      {"bSortable" : false}
    ]
  })

ページングもかなりいい感じだし、フィルタリングも速いです。
普通の使い方だと、jQuery UIのテーブル風のデザインにするための設定もあったので、いちいち自分で小綺麗な感じにしなくてもよいのが嬉しいところです。
Ajaxでページングするとかスクロールでの自動ロードとかを使うなど、完全に使いこなすにはJavascriptがわからないとダメっぽいですが、普通にソート、フィルタするだけだったら詳しくなくてもできるんじゃないかなと思います。