カテゴリー別アーカイブ: ruby on rails

guradでspring rspecを実行した時にsimplecovを実行せず、bundle exec rspecを実行した時のみ実行する方法

ことの発端はApartmentとsimplecov+guardの相性が悪いということでした。

しばらくsimplecovを使っていなかったのですが、お金関係の実装をしていてさすがにテストのカバレッジが気になったのでsimplecovを再度入れてみたところ、不可解な現象が生じました。

一部のクラスだけカバレッジが反映されていない。。

本質的な理由はよくわからないのですが、どうやらApartmentのinitializerでexcluded_modelsを設定しているクラスに関してカバレッジが反映されないということがわかりました。githubでは同様の現象をApartmentの開発者に問い合わせている痕跡を見つけることができたものの、「なぜ?」という部分に関しては謎なままissueがcloseに。

いろいろと調べた結果、どうやらApartmentがinitializerですでにクラスをロードしていることが原因と考えられます。そしてこれはguardでspringを使ってrspecを走らせた場合に限ると。つまりguardでspringを走らせた場合は
1.Apartment.initializer
2.excluded_modelsのロード
3.simplecovのスタート
となり、bundle exec guardで走らせた場合は
1.simplecovのスタート
2.Apartment.initializer
3.excluded_modelsのロード
となるわけです。
simplecovはスタートした時点で監視を始めるので、その前にロードされたものに関しては無視するということではないか?というのが仮説ですが、真相はまだわかりません。

最低限やりたいことの条件としては

1.guardを使ってrspecを常に走らせることはしたい。
2.guardを使ってrspecを走らせている時はいちいちカバレッジは更新されなくてもよい。

ということでspec_helperの頭には

[code lang=”ruby”]
  # bundle exec rspec specの時だけsimplecovを動かす
# bundle execで直接rspecを実行した場合
# この時点ではrails_envが設定されていないためこれを利用する
unless ENV[‘RAILS_ENV’]
require ‘simplecov’
SimpleCov.start ‘rails’do
add_filter ‘app/exceptions/’
add_group ‘Validators’, ‘app/validators’
end
end[/code]

rails_helper側の頭は

[code lang=”ruby”]
NV[‘RAILS_ENV’] ||= ‘test’

require ‘spec_helper’
require File.expand_path(‘../../config/environment’, __FILE__)
require ‘rspec/rails’

[/code]

とすると、bundle exec rspecを実行した時のみcoverageを動かすことができる。

wkhtmltopdfをmac osx(marvericks)にインストールする

railsにpdf生成のgem ‘wicked_pdf’をインストールしたのですが、うまく行かず。
なぜかというと公式では同時にwkhtmltopdf-binaryというgemをインストールする事で根本のライブラリであるwkhtmltopdfが入るのですが、このバージョンが低いのです。

gemでいれると0.9系です(2014年10月1日現在)が最新では0.12系でした。

Homebrewを使おうという事にはなるのですがこれも皆無。

結局こちらの記事通りにやって解決しました。

http://seankibler.com/installing-wkhtmltopdf-on-mbp-osx-mavericks-109

1:

git clone --recursive https://github.com/wkhtmltopdf/wkhtmltopdf.git

2:

$ ./build_osx.sh

このコンパイルがすごく長いです。1時間くらいかかったかもしれません。

3:

$ sudo make install

これでインストール完了です。

$ which wkhtmltopdf
/Users/username/.rvm/gems/ruby-2.1.3/bin/wkhtmltopdf

あれ?まだgem見てるな。

$ bundle exec gem uninstall wkthmltopdf-binary
##Gemfileのwkhtmltopdf-binaryを消す
#gem 'wkhtmltopdf-binary' < delete

 

$ wkhtmltopdf -V
Name:
  wkhtmltopdf 0.12.1 (with patched qt)

ちゃんと 0.12.1を見るようになりました。

 

 

rails4でacts_as_listをアップデートしたらdefaultのpositionが設定され、topが1になっていた件

acts_as_listをrails3で使っていたのですが、rails4にアップデートする作業中にacts_as_listもアップデートしました。

rspecがエラーを出しまくりましたが、原因はdefaultのpositionが設定され、1になっていたことです。

0を指定すると1より小さいということで強制的に1にされてしまいます。

0にしたかったのでコードを見ていくとConfigurationが。

下記のようにトップのpositionを設定できます。

acts_as_list :scope => :something, top_of_list: 0

 

refinery overrideでzshのエラーが出る件

こちらのサイトrefinery-cmsをつかっているのですが、
突然refinery override で下記のようなエラーがでました。

zsh: no matches found: controller=refinery/hogehoge/*

調べてみるとzshが引っかかっている様子。

zshの設定を.zshrcに下記の記述を加えて変更すればエラーがでなくなりました。

#~/.zshrc
setopt nonomatch

で読み込んでから
source ~/.zshrc

実行

rrefinery:override controller=refinery/hogehoge/*

 

Ruby on Rails bundle install 失敗。原因はmavericsのインストールとXcodeのアップデート

Agreeing to the Xcode/iOS license requires admin privileges, please re-run as root via sudo.

ちょっとRailsの新しいプロジェクト(テストプロジェクト)を
作ろうと思ってbundle installを実行したら上記のようなエラーがでました。

原因はXcodeのアップデート後にアプリケーション上で「I agree」を実行しなかったこと。

XcodeをGUIで起動してI agreeしたらエラーがでなくなりました。

情報はこちら

http://blog.tomhennigan.co.uk/post/62238548037/agreeing-to-the-xcode-license-from-the-command-line

I love green!

PostgreSQLがOSXで動かなくなった件

Ruby on Rails環境をOSXのローカル上に構築していたのですが、
OSをアップデートしたらPostgreSQLが動かなくなりました。。

直接OSのアップデートが原因なのかは不明。

で、下記の記事を参考にpostmaster.pidを削除して
再起動したら動きました。

http://stackoverflow.com/questions/13755965/after-osx-10-8-upgrade-postgres-can-not-connect-to-the-server

このトピックを書いた人は動いてないようですが、
私のマックには効果ありでした。

OSXはapp版を使うのがいいんですかねー?

Rails3.2 プロフィール画像をアップロードしてDBに格納。サムネイルも。

Rails 画像 アップロード
で検索するとプラグインがいろいろとでてきます。

とりあえずCarrierWaveをつかってみました。が、結局使うのをやめました。

CarrierWaveは優れたプラグインでした。
実装は非常に簡単でした。
しかし「SQLアンチパターン」という本を読み、
画像をファイルでなくDBにバイナリとして保存することを決意。

CarrierWaveでも実装できなくもなさそうでしたが、
恩恵があまり受けられないということで、
使用をやめ、独自に実装することにしました。

ただversionという感じで、いくつかのサイズを選べるという機能は気に入っていたので、
なるべく画像を使用している部分は変更のないような実装を目指しました。

Personクラスに16px, 32px, 64px, 200pxのアバター画像を保存します。

まず、マイグレーションでカラムを追加。

avatar_data16
avatar_data32
avatar_data64
avatar_data200
avatar_content_type:string

つぎに、routesを設定。
/config/routes.rb

match ‘avatar/:version’, :via => :get, :action => ‘avatar’, :as => ‘avatar’,  :on => :member

次にcontrollerを設定。(RMagickを使っています)

#作成
def set_avatar
if params[:person][:avatar_file].present?
file = params[:person][:avatar_file]
params[:person].delete(:avatar_file)
end
if file.present?
img = Magick::Image.from_blob(file.read).shift
img_16 = img.resize_to_fill(16, 16)
img_32 = img.resize_to_fill(32, 32)
img_64 = img.resize_to_fill(64, 64)
img_200 = img.resize_to_fill(200, 200)
params[:person][:avatar_data16] = img_16.to_blob
params[:person][:avatar_data32] = img_32.to_blob
params[:person][:avatar_data64] = img_64.to_blob
params[:person][:avatar_data200] = img_200.to_blob
params[:person][:avatar_content_type] = file.content_type
end
end

#取得する
def avatar
@person = Person.find(params[:id])
version = params[:version]
avatar = case version
when ‘icon’ then @person.avatar_data16
when ‘mini’ then @person.avatar_data32
when ‘thumb’ then @person.avatar_data64
when ‘normal’ then @person.avatar_data200
end
send_data(avatar, :type => @person.avatar_content_type)
end

次はviewです。
アップロード側は

<tr class=”avatar”>
<th class=”label”><%= f.label :avatar_file, ‘顔写真’%></th>
<td class=”data”><span class=”image”><%= image_tag @person.avatar_path(:mini) %></span><span class=”select_button”><%= f.file_field :avatar_file %></span></td>
</tr>

モデルにavatar_pathというメソッドを追加

include ActionDispatch::Routing::UrlFor
include Rails.application.routes.url_helpers
def avatar_path(version = :normal)
if avatar_data16.blank?
path = Person.default_avatar(version)
else
path = avatar_person_path(self, version)
end
end

def self.default_avatar(version = :normal)
case version
when :normal then ‘/assets/weiver-ui/avatar_default.png’
when :thumb then ‘/assets/weiver-ui/avatar_default.png’
when :mini then ‘/assets/weiver-ui/avatar_default_mini.png’
when :icon then ‘/assets/weiver-ui/avatar_default_icon.png’
else ”
end
end

Rails 3.2 partialに関数を渡し、処理してもらう。

繰り返しが必要な部分をpartialにしてしまいたくなるときがあると思います。

<% @products.each do | product | %>
<%= render ‘product’ %>
<% end %>

こうすると partialをproducts分だけ読み込むので、処理が遅くなるときがあります。
ですからこのようなループはpartialの中に入れてしまい、元の配列を渡すようにしています。

<%= render ‘products’ , products: @products %>

そしてpartialの内部で

<% products.each do |product| %>

<% end %>

こうすればpartialの中でループが行われるので、呼び出されるのは一度だけです。
ところがこのループの中で、それぞれのproductに対し処理を入れたいときがあります。

例えば管理者用の製品一覧画面と、ユーザー用の製品一覧画面があったとします。
管理者用の画面にはすべての製品を表示し、
ユーザー用の製品一覧画面にはプロダクトオブジェクトに公開のフラグが立っているもののみを表示したいとします。
※そもそもこの場合はコントローラー+モデルで配列自体を変更すると思いますが、あくまで例です。。

その場合、lambdaを利用してpartialに匿名関数を変数として渡す方法があります。

■管理者用のpartial呼び出し

<%= render ‘products’, products: @products,
lambda_publish: lambda{| product | true }
%>

■ユーザー用
<%= render ‘products’, products: @products,
lambda_publish: lambda{| product | product.publish?}
%>

■partial側

<% products.each do |product| %>
<% if lambda_publish.call(product) %>
..
<% end %>
<% end %>

この方法のメリットとしては、partial側を簡潔にすることができる点にあります。

他にヘルパーを使うことが一般的だと思いますが、そうするとcase-when文が登場したりしてヘルパーが太っていきます。

javascriptでよく関数を引数に渡すことがあると思いますが、それと似たような感じで使えるのでとても重宝しています。

 

Ruby on Rails 3.2 Arel + Postgresql のgroup – having節でエラー

やりたかったのは人物の検索結果を出すことです。
検索条件として、例えば男性で、東京在住であるというような、その人物の「属性」でand検索するというものになります。

いきさつははしょりますが、簡単に言うとsqliteでの開発段階でgroup having節を使っていた部分が、postgresqlに変更してからエラーがでたということが発端になります。

postgresqlでは

must appear in the GROUP BY clause or be used in an aggregate function

というエラーがでて、目的のことをやるには一旦select文を分割せねばならないようでした。

SQLを直接書けばそれ自体は難しくないのですが、なんとかArelをできるだけ活用してできないものかと試行錯誤しました。

ハードルはどのようにサブクエリをarelで組み立てるのかということ。

この問題に関してはこちらの記事が参考になりました。

まず、クラスにたいしarel_tableメソッドでArel::Tableクラスのインスタンスを取り出します。
※pattributes_peopleテーブルに対しPattributePersonクラスです。察しの通り、これはPersonとPattributeクラスの関連クラス

sub = PattributePerson.arel_table

次に条件のひとつとして使用するArel::Attributes::Attributeクラスのインスタンスを作成します
※pattributes は[‘1′,’2’,…] のような属性のIDが入った配列です。
AND検索なのでinにしたいところですが、ここでは一旦in_anyで該当する行をすべて取り出しておきます。

attr = sub[:pattribute_id].in_any(pattributes)

最後にサブクエリの本体となるArel::SelectManagerクラスのインスタンスを取り出します。

select_manager = sub.project(table[:person_id]).where(attr).group(:person_id).having(“COUNT(person_id) >= #{pattributes.length}”)

→pattribute_idがpattributeのリストに一致するものすべてを取り出しておいて、person_idが重複しないように圧縮します。
そのときperson_idがpattributesの数分だけあるもののみ取り出しています。つまり、pattributeが全部該当した者=ANDと同じ。

これを他のArel::Tableでくっつけてひとつの条件(Arel::Attributes::Attributeクラスのインスタンス)を作成します。

person_table = Person.arel_table
attr2 = person_table[:id].in(select_manager)

 あとはこのままpeopleとして取得するなり、他の条件と結合するなりできます。

@result = Person.includes(:user)
@result.where(attr2)

group節がサブクエリになったことでエラーは出なくなりました。

ruby 1.9 hashの組み合わせ

ハッシュに含まれる要素に対しすべての組み合わせを試すコード
例えばA,B,Cというものがあった場合にAB, AC, BC を 作成します。

[ror]
def test
original = {A: ‘a’, B: ‘b’, C: ‘c’}
copied = original.dup
a = []
original.each do | key1, val1 |
copied.delete(key1)
copied.each do | key2, val2 |
a << [key1, key2] #something code
end
end
a
end[/ror]