model.create / model.save は成功するのに model.all.count = 0になる on Rspec + FactoryGirl
SQLもWebアプリも難しいですね。
しょうもない事でハマってしまいました。
model単体のテストをRspec + FactoryGirlで書いていたのですが、model.create / model.save は成功する(trueが返ってきてレコードがちゃんとできる)のに、model.all.countはいつも0で、DBには保存されていないように見えるという不思議な現象に悩んでおりました。
で、原因はmodelの定義で設定していたdefault_scopeでした。
関連している他のモデルの属性を使ってソートするdefault_scopeを設定してたのですが、今回のテストには関連先まで定義していなかったため、どんなにレコードを登録しても検索条件には引っかからずmodel.allは常に0という事が起こっていたのです orz。
で、モデル単体のテストときは全てのスコープを無効にするようにしたら、ちゃんと登録済レコードが見えるようになりました。
こんな感じ。
it {
Model.unscopded {
テストコード
}
}
でも、これだとまた忘れた頃に同じ事ではまりそうなので、結局default_scopeで設定するのをやめて、名前付きスコープで定義するようにしましたです。
ではでは。
Deviseでconfirmableを設定しているときのFixtureの書き方
Deviseでconfirmableを設定していると、普通にemailとpasswordだけでFixtureを用意してもうまく行きませんでした。
user.createの時にconfirmation mailを送るため、それでこけているようです。
createの前にskip_confirmation!を設定しても良いのですが、下記方法でconfirmedなUserと認識してくれたので、これで行こうと思います。
[FactoryGirlの例]
FactoryGirl.define do factory :user do email "test@test.org" password "testtest" confirmed_at Time.now # これでConfirmedユーザー end end
ではでは。
ポリモーフィック関連のfactoryの書き方 on FactoryGirl
こんな感じで、polymorphic関連を設定しているとします。
# Photo model class Photo < ActiveRecord::Base belongs_to :category, :polymorphic => true end # Mountain model class Mountain < ActiveRecord::Base has_one :photo, :as => :category end # River model class River < ActiveRecord::Base has_one :photo, :as => :category end
そんでもって、こんなfactoryを書きます。
FactoryGirl.define do factory :mountain_photo, class: "Photo" do association :category, factory: :mountain end factory :river_photo, class: "Photo" do association :category, factory: :river end factory :moutain do name "Mt.hoge" end factory :river do name "Riv.fuga" end end
これで "FactoryGirl.create(:mountain_photo)" とやると、:categoryに:mountainが入ったレコードが、"FactoryGirl.create(:river_photo)" とやれば、:categoryに:riverが入ったレコードが生成されるようになります。
ではでは。
Dragonflyの画像ファイルを持つphotoモデルをRspec + FactoryGirl で testする
Dragonflyで画像ファイルを管理しているのですが、test方法のメモです。
テスト環境は Rspec + FactoryGirl ですが、Unit testとFixtureでも同じだと思います。
Dragonflyはtest時以下ディレクトリからファイルを探します
public\system\dragonfly\test\YYYY\MM
登録しようとする画像ファイル名が"IMG_0001.jpg"だとすると、Dragonflyの生成するuidはYYYY/MM/DD_HH_MM_SS_MLS_IMG_0001.jpgになるので、上記ディレクトリに "DD_HH_MM_SS_MLS_IMG_0001.jpg"という名前で画像ファイルを置けばよいです。
photo_spec.rbはこんな感じです。
# -*- coding: utf-8 -*- require 'spec_helper' describe Photo do describe "画像ファイル" do before do @jpg = create(:photo, photo_image_name: "OK.jpg", photo_image_uid: "2013/08/01_00_00_00_001_OK.jpg") end it { @jpg.should_not be_new_record } end end
上記specだと "public\system\dragonfly\test\2013\08\01_00_00_00_001_OK.jpg" というファイルを準備しておけば良いです。
参考までにphotoモデルはこんな感じ。
class Photo < ActiveRecord::Base attr_accessible :photo_image # For dragonfly image_accessor :photo_image end
ではでは。
limitの値によってクエリの結果が異なる
先日、"関連付けたモデルの属性で、関連元モデルをソートする"というエントリを書きましたが、これ、一見うまくいっているように見えるだけで、所望の結果が得られない場合があることがわかりました。
Model.allで全レコードを取得したときにしか正しい結果が得られません。
paginationやlimitを使って取得レコード数を制限している場合は、レコードの内容によっては所望の結果が得られない場合があります。
結論から先に言うと、:include ではなく :joinsを使用するというのが正解のようです。
こんな感じ。
default_scope :joins => :report, :group=> "spots.id", :order => "reports.created_at DESC"
SQLを直接発行してうまくいくクエリをActivRecordに書きなおしただけですが、これでうまくいきました。
さて、うまくいかない状況をもう少し詳しく説明します。
先日のエントリで紹介した設定は下記の通り。
class Spot < ActiveRecord::Base has_many :report default_scope :include => :report, :order => "reports.created_at DESC" end class Report < ActiveRecord::Base belongs_to :spot end
これで、spot.all すると、新しいReportを持っている順にspotがsortされて取得できます。
結果も正しい。
でも spot.limit(8)とかやると、spot.allのときと順番が違ったり、取得できるレコードまで違ったりするのです。
生成されたクエリを直接SQLで発行すると、limitが付いている場合でも正しい結果が取得できたりと、わけがわかりません。
色々と実験したのですが、:includeの場合は、limitで指定したレコード数分のspotレコードに紐付いているreportだけをロードしてきて、その中でソートをかけるという動作をしているように見えます。
なので、allじゃないと正しい結果にならない場合があるようです。
SQLちゃんと勉強していればわかる内容なのでしょうが、なんでこんなことになるのかよくわかっておりません。。。
navbarからmodal windowを呼び出すと、操作できないmodal windowが描画される on Twitter Bootstrap
何を言っているのかわからないと思いますので、まずは画像をどうぞ。
画像上部にNavigation barがあって、右端の Modal A / Modal B リンクをクリックすることでModal windowが描画されます。
画像は、Modal Aをクリックした状態です。
Modal window ボタンA/BというのがあるWindowがModal windowなのですが、全部後ろに描画されてしまっていて操作ができません。
調べてみると ".navbar .navbar-fixed-top" の z-indexが1030であるのに対し、半透明の黒いレイヤーである ".modal-backdrop" の z-index が1040なので、これが原因のようです。
cssでz-indexを1030より小さい値でオーバライドしてみます。
.modal-backdrop { z-index: 1020; }
治りました。
でも、画像の上部を見るとわかるように navbar も有効になってしまっているので、厳密にはmodal windowではありません。
例の様にnavbarに modal windowへのリンクを2種類持っている場合は、どちらもクリックできてしまうので2つのmodal windowが同時に描画されてしまいます。
これをさけるために、modal windowの toggleはjavascriptを使う事にしました。
<script type="text/javascript"> function clickModalA() { $("#modalA").modal('hide'); $("#modalB").modal('show'); } function clickModalB() { $("#modalB").modal('hide'); $("#modalA").modal('show'); } </script> <ul class="nav pull-right"> <li><%= link_to "Modal A", "#", {"onclick"=>"clickModalA()"} %></li> <li><%= link_to "Modal B", "#", {"onclick"=>"clickModalB()"} %></li> </ul>
よくわからないのが、この問題、ブラウザをフルウィンドウで表示しているときにだけ発生します。
ブラウザを少しでもリサイズしている場合は、z-indexをオーバライドしなくても、全部後ろに描画されることも navbar が有効になることもなく、問題無く動くのです。
[ブラウザサイズをリサイズしてある場合。z-indexのオーバライドはしていない]
media typeで色々とCSSを書き換えている(しかも適当に)ので、それが原因しているかもしれないのですが、とりあえずこれでしのぐことにします。
サムネイルが2段目からずれる on Twitter Bootstrap その2 (IE対応)
Bootstrapのサムネイルが2段目からずれる件、cssのnth-childを使用して対応したエントリを先日書きましたが、IEだとnth-childプロパティが効かない事がわかりました。。。
調べてみると、jqueryにもnth-childというメソッドがあるようでしたので、こんな感じで試してみます。
$(document).ready(function(){ $(function() { $("ul.thumbnails li.span3:nth-child(4n+1)").css("margin-left", "0px") }) })
IEでもずれなくなりました。
span3 と nth-cildの中身(4n+1)の関係はこちらを参照くださませ。
クロスブラウザ対応って大変なんですね。