Capybaraのsave_and_open_pageでcssを反映する方法

結論?

WEBrickを立ち上げっぱなしにして、そちらからJavaScriptとCSSをDLさせるという方法が一番しっくりきました。
RailsのテストでCSS適用のスクリーンショットを見る

注意!!

この方法を使うと、asset_hostがlocalhostじゃなくなるから、Ajaxが動かなくなるという現象にあたります。やっぱりこの方法はいけてないので、推奨しません。

以下、原文

我々はこのときをずっと待っていた。
Capybaraで保存したページのスクリーンショットはCSSが反映されてなくて、微妙だと思っていた。常々、思っていた。そこで、この問題を解決する方法はないものか?と思ってようやくググった。しかし、capybara save_and_open_page cssとかでググっても、セレクタの情報しか出てこない。当然だ。

ここで、googleさんのサジェストに救われた。
assetsという文字が出てきたのだ。srd!!と思った。

答えはここにあった。
save_and_open_page not working with capybara 2.0?

assets precompleをを実行しないといかんと。さらに、テスト用のassetsフォルダを準備することと、assets_hostを絶対パスに変更すること。なるほど。

まず、spec/spec_helper.rbを編集する。:suiteブロックに書くことで、テストのとき1度だけrake assets:precompileを実行する。

config.before :suite do
  %x[bundle exec rake assets:precompile]
end

次に、config/environments/test.rbを編集する。最後のほうに以下を追加する。

  config.assets.prefix = "assets_test"    # place test assets in public/assets_test directory
  config.action_controller.asset_host = "file://#{::Rails.root}/public"

これで、save_and_open_pageしたときに、cssが適用されたスクリーンショットが取れるようになる。
やったね!!と思ったのだが、このままだと毎回テストを流すたびにrake assets:precompileが実行される。この処理ってすごく遅いので、ストレスになる。

そこで、save_and_open_pageをするときだけ、assetsプリコンパイルするようにすればいいと思った。
もう一度、spec/spec_helper.rbを編集する。

config.before :suite do
  # コメントアウトしとくか、消す
  # %x[bundle exec rake assets:precompile] 
end
#...RSpec.configureブロックの外で、関数を定義する。
# save_and_open_pageは長いので、show_pageにする。
def show_page
  %x[bundle exec rake assets:precompile]
  save_and_open_page
end

これでスクリーンショットを取りたいときに、show_pageを書いたら、うまく動いてくれた。
いける。これでいける。


bootstrapとsimple_formでラジオボタンを横に並べる

すげーハマったのでメモ。
bootstrapとsimple_formでラジオボタンを横方向に並べたかった。しかし、label: {class: “inline”}としても、ラジオボタン自身のlabelではなく、項目のlabel側にclassが書かれるだけだったので、どうしたものか?と思って調べていたら、StackOverflowに辿り着いた。ありがたい。

simple_form_for rails radio button inline

要はここに書かれているようにすればいいだけなのだが、一応ここにも書いておく。

ちなみに、テンプレートエンジンはslimです。
piyoが、横方向に並べたいラジオボタンです。

= simple_form_for @hoge, html: { class: 'form-horizontal' } do |f|
  = f.input :piyo, collection: [["有", true],["無", false]], as: :radio_buttons, item_wrapper_class: "inline"

item_wrapper_classで指定するのねー、なるほど。
なんか本家のサイトで検索しても出てこないのだが…。
とりあえず解決したのでよしとする。


kotlinでnode.ktを動かしてみた。

Node.ktという、Node.JSのkotlinバージョンが作られている模様。
http://jonninja.github.com/node.kt/

英語力の弱い自分が読んだ限りでは、

  • node.jsは素晴らしい!
  • でもnode.jsには不満もあるんだよねー…
  • ひとつはJavascriptは動的型付けで型安全じゃないところ
  • もうひとつはシングルスレッドなところ
  • その点、kotlinはJVM言語であり、楽しい言語だよ!

みたいに読めた。

とりあえず動かしてみるかーと思って、やってみた。
ちなみに私の環境はMacです。他の環境は知りません。

  1. Node.ktをgithubからチェックアウトする。
    git clone git@github.com:jonninja/node.kt.git
    
  2. IntelliJでnode.ktプロジェクトを開く。
  3. kotlin-runtime.jarを入れとく。(ktファイルを開いたら聞かれる。JVMのほう。)
  4. エラーまみれなのを直していく。IntelliJで、プロジェクト上で右クリックして、Open Module Settingsを選ぶ。
  5. おそらく、dependenciesが真っ赤です。
  6. Node.ktはgradleでライブラリの管理を行っている模様(俺は最初、gradle?ナニソレだった)
  7. gradleはgroovy製のライブラリ管理ツールらしいので、GVMを入れる。(gvmは複数のgroovy系のものを管理できるやつ。rubyでいうrvmやrbenvみたいなの)
  8. GVMのサイトを開いてそっちを見ながらやるのが確実だけど開くの面倒なら以下のように。
  9. GVMをインストールする。
    curl -s get.gvmtool.net | bash
    

    sourceで読み込めって最後に言われるから、それをやる。
    ******は自分の環境に合わせてください。

    source "/Users/*******/.gvm/bin/gvm-init.sh"
    
  10. gvm helpでヘルプが見れたらOK。
  11. 次に、groovy 1.8.6を入れる。古いけれど、node.ktのプロジェクトがそれを求めているから。
    新しくても動くかもしれないけれど試してない。

    gvm install groovy 1.8.6
    
  12. 次に、gradleを入れる。gradleのバージョンは1.2にしておく。
    これもnode.ktプロジェクトがそれを指してたから。

    gvm install gradle 1.2
    
  13. ターミナルでnode.ktプロジェクトのcoreディレクトリに移動し、gradle buildと打つ。すると、色々とダウンロードが始まる。
  14. IntelliJでモジュールのdependenciesを開くと、まだ真っ赤だと思うので、jarファイルの位置を編集する。指定する場所は、赤くなっているやつを参考に選ぶ。たぶん、/Users/*****/.gradle/caches/なんちゃら
  15. だいぶ赤いのがなくなるはず。しかし、残っているのがあるとしたら、jarのバージョン違いのやつだったりするので、同じjarは読み込んでいるんならば、赤いほうを消す。
  16. lesscssのjarのやつが残る。こいつを入れるようにgradleに書かれていないようだ…。
  17. build.gradleを開いて、lesscssのjarをとってくるように書く。
    dependenciesだけ抜粋する。最後のlesscssの行を追加。

    // 4 - Kotlin compilation and runtime dependencies.
    dependencies {
        compile "org.jetbrains.kotlin:kotlin-runtime:$kotlinVersion",
                "org.jetbrains.kotlin:kotlin-stdlib:$kotlinVersion",
                "org.jetbrains.kotlin:kotlin-jdk-annotations:$kotlinVersion",
                files( "${ System.getProperty( 'java.home' )}/lib/jce.jar" )
    
        compile 'com.fasterxml.jackson.core:jackson-databind:2.0.6'
        compile 'io.netty:netty:3.5.7.Final'
        compile 'org.apache.velocity:velocity:1.7'
        compile 'org.freemarker:freemarker:2.3.19'
        compile 'commons-codec:commons-codec:1.7'
        compile 'org.apache.httpcomponents:httpclient:4.2.1'
        compile 'commons-io:commons-io:2.4'
        compile 'com.google.code.morphia:morphia:0.99'
        compile 'org.mongodb:mongo-java-driver:2.10.0'
        compile 'com.google.code.morphia:morphia-logging-slf4j:0.99'
        compile 'org.lesscss:lesscss:1.3.0'
    }
    
  18. もう一度ターミナルからgradle buildをする。
  19. これでIntelliJのモジュールのdependenciesから赤いのが消えたはず。
  20. IntelliJ上から、node.kt/examples/helloworld/src/HelloWorld.ktを右クリックしてrunを選択。
  21. ブラウザでhttp:localhost:3000/にアクセスする。
  22. 見事にHello Worldが表示される。
  23. おしまい。

以上。とりあえず動かしてみただけなので、まだ全然わかりません!!
あと画像が全くなくてすみません!サボりました!


Cancanでアクセス権限がないときのメッセージを定義

Cancanで、アクセス制限をやろうと思っているんだけれども、アクセスリストが基本的にDenyなんだが、エラーメッセージ変えたいなーと思ってた。
今までコードでは以下のような感じになっていた。
models/ablity.rb

# coding: utf-8
  include CanCan::Ability

  def initialize(user)
    unless user.nil?
      case user.role.code
        when "admin"
          can :manage, :all
        else
          authorize! :manage, :all, message: I18n.t("cancan.access_deny")
      end
    else
      authorize! :manage, :all, message: I18n.t("cancan.access_deny")
    end
  end
end

これを、user.role.codeがnormalの場合は、とあるデータのアップデートが可能で、後はアクセスできないようにしたかったので、以下のようにしてみた。(長いので抜粋します)

case user.role.code
  when "admin"
    can :manage, :all
  else
    can :manage, Hoge
    authorize! :manage, :all, message: I18n.t("cancan.access_deny")
end

これでいけるだろうと思っていたらダメだった。authorize!で上書きされてしまうみたいで、アクセスできなくなった。そこで、authorize!の行を削除したら、アクセスできるようになったんだけど、許可したくないコントローラーにアクセスしたらアクセス拒否メッセージが英語になってしまった…。

ぐぐったところ、アクセス許可しない場合のエラーメッセージはconfig/locales/**.ymlに定義できるということだったので、ja.ymlに定義したら、アクセスエラーの場合のメッセージを変えられた。
アクション毎にメッセージ変えられるようだけど、そこまでしなくてもよかったので適当にやった。

ちなみに参考にしたURLは本家の説明ページ

で、こうなった。
models/ability.rb

# coding: utf-8
  include CanCan::Ability

  def initialize(user)
    unless user.nil?
      case user.role.code
        when "admin"
          can :manage, :all
        else
          can :update, Hoge
      end
    else
      cannot :manage, :all
    end
  end
end

config/locales/ja.yml

ja:
  unauthorized:
    manage:
      all: アクセス権限がありません

たぶん、これでいい。


deviseとacts_as_paranoidの組み合わせでハマった

deviseとacts_as_paranoidを使ってユーザーの登録と退会処理をしていたら、退会済みのユーザーのメールアドレスで再度登録できないという事態になった。

ググりまくった結果、config/initializers/devise.rbの
config.authentication_key = :email になっているから、
Userモデルのほうでvalidatableがあると、emailでユニークかどうかのチェックが走ってしまうようだということがわかった。

なので、validatableの記述を削除したら、うまく動き出した。
validatableを使わない場合は、自力で検証を書かないといけないので、
validates_uniqueness_of_without_deleted :email
は必須!!(自分の環境では)。