同じにやっても動かない

書いてある通りやっているのに動かないのです

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

何を言っているのかわからないと思いますので、まずは画像をどうぞ。

f:id:nyarugo:20130709233257p:plain


画像上部に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; 
}


治りました。


f:id:nyarugo:20130709233809p:plain


でも、画像の上部を見るとわかるように 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のオーバライドはしていない]
f:id:nyarugo:20130709234405p:plain


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)の関係はこちらを参照くださませ。


クロスブラウザ対応って大変なんですね。