2014年9月30日火曜日

ActiveRecordをRails.cacheに入れてみたときの挙動

ActiveRecordでDBから取得した内容をキャッシュしたかったのでRails.cacheに入れてみました。

開発環境ではcacheが効かないようになっているので有効にする。
config/environments/development.rb
 config.action_controller.perform_caching = true

例としてサービスマスタのModelを作成
mst_service.rb
class MstService < ActiveRecord::Base
end

controllerから以下のようなコードでcacheを利用
    puts "cache_#{Rails.cache.read("mst_service").nil?}"
    mst_service = Rails.cache.fetch("mst_service",expires_in: 1.minutes) do
      mst = MstService.select(:service_id).where(:status => '0')
      mst
    end

4回アクセスした時の標準出力は以下のようになる。
2回目以降はキャッシュから取得できているようです。
cache_true
cache_false
cache_false
cache_false

DBへのアクセスはこんな感じ。
アクセス回数分、DBにselectが投げられている。。。
MstService Load (0.3ms)  SELECT `mst_services`.`service_id` FROM `mst_services`  WHERE `mst_services`.`status` = '0'
MstService Load (0.4ms)  SELECT `mst_services`.`service_id` FROM `mst_services`  WHERE `mst_services`.`status` = '0'
MstService Load (0.2ms)  SELECT `mst_services`.`service_id` FROM `mst_services`  WHERE `mst_services`.`status` = '0'
MstService Load (0.2ms)  SELECT `mst_services`.`service_id` FROM `mst_services`  WHERE `mst_services`.`status` = '0'

cacheには入っているようですが、DBへのアクセスが減っておらず、
やりたいことはDBへのアクセスを減らすことなので、意図したキャッシュの処理にはなっていません。
毎回SQLが発行されていることから、ActiveRecordの状態がキャッシュされていて、
データがキャッシュされる訳ではなさそうです。

DBから取得した内容をhashに入れてからキャッシュするように変えてみました。
    puts "cache_#{Rails.cache.read("mst_service").nil?}"
    mst_service = Rails.cache.fetch("mst_service",expires_in: 1.minutes) do
      mst_data = MstService.select(:service_id).where(:status => '0')
      mst = {}
      mst_data.each do |data|
        mst.store(data.service_id,data.service_id)
      end
      mst
    end

4回アクセスした時の標準出力は以下のようになりました。
上のと内容は同じです。
cache_true
cache_false
cache_false
cache_false

DBへのアクセスを確認したところ、以下のように1回だけに減っていました。
MstService Load (0.2ms)  SELECT `mst_services`.`service_id` FROM `mst_services`  WHERE `mst_services`.`status` = '0'

DBから取得した値をRails.cacheに入れるのであれば、ActiveRecordをそのまま入れるのではなく
hashに移し替えてから格納するのがよさそうですね。

2014年9月22日月曜日

rspecとfactory_girlのテストでトランザクション効かせる

railsのmodelのテストをrspecで作成していて、factory_girlで作ったデータでテストをした際に、
テストが完了したらテスト用データも削除されるようにしたいので、そのときにやった対応です。

テスト対象テーブルはこんな感じです。
mysql> select * from seq_sometest4s;
+----+--------+
| id | seq_id |
+----+--------+
|  1 |      1 |
+----+--------+
1 row in set (0.00 sec)

テストしたいクラスです。
$ cat app/models/concerns/seq_table.rb 
module SeqTable
 
  def seq_incrment(klazz)
    base = Object.const_get(klazz)
    base.transaction do
    tbl = base.lock.find(1)
 
    tbl.increment!(:seq_id)
    tbl.seq_id
  end
  rescue => e
    raise e
  end
end
 
$ cat app/models/seq_sometest4.rb 
class SeqSometest4 < ActiveRecord::Base
end

$controller抜粋
seq_id = seq_incrment("SeqSometest4")

spec/rails_helper.rbに設定
config.use_transactional_fixtures = true

まずテストを書いてみました。
require 'rails_helper'
 
describe "SeqTable" do
  include SeqTable
 
  seq = FactoryGirl.create(:seq_sometest4)
  context "#seq_incrment" do
    it {
      res = seq_incrment("SeqSometest4")
      expect(res).to eq (seq.seq_id + 1)
    }
  end
end

これだと、テスト終了後にデータが残ってしまいました。
mysql> select * from seq_sometest4s;
+----+--------+
| id | seq_id |
+----+--------+
|  1 |      1 |
+----+--------+
1 row in set (0.00 sec)

FactoryGirlの処理をit{}の中に持ってきてみました。
require 'rails_helper'
 
describe "SeqTable" do
  include SeqTable
 
  context "#seq_incrment" do
    it {
      seq = FactoryGirl.create(:seq_sometest4)
      res = seq_incrment("SeqSometest4")
      expect(res).to eq (seq.seq_id + 1)
    }
  end
end

mysql> select * from seq_sometest4s;
Empty set (0.00 sec)

itの中に書くことでテスト内での操作ってことになって、トランザクション内での処理ってことになるのかなと。


参考URL
https://relishapp.com/rspec/rspec-rails/docs/transactions

2014年9月17日水曜日

Travis CI Meetup Tokyoに参加してきました

本日開催されたTravis CI Meetup Tokyoに参加してきました。
Travis CIは名前と概要はなんとなく知ってたのですが、使ったことはなかったです。
今日話を聞いてきた限りはかなり便利でぜひ使いたいなと思いました。
jenkinsでもできそうだけど、実行するたびに毎回vm立ち上げてやってくれるのは
環境の変な影響とか受けなくてよさそうだなと感じました。

以下メモしたやつを載せてます。

・Asariさん
主にTravis入門のデモという感じでした。
デモに使われたリポジトリはこちら
https://github.com/BanzaiMan/travis-intro-tokyo
https://travis-ci.org/BanzaiMan/travis-intro-tokyo

Travisを使う上で、どういう流れで動いているかを理解することは大事。
setup/install/buildに関してはtravisの責任で実行する。
travisでsudo使いたい時はメールしてくれるとよいそうです。

matrix: allow_failures:
に書いた設定は失敗してもよい設定になる。
#今動かしてるバージョンより新しいバージョンでテストするときとか便利そう。

rubyだとrvmのバージョン指定で
rvm - ruby-head
で最新版のテストできる。

servicesでredisやelasticsearchを使うことができる

addonsでpostgresqlのバージョン9.1,9.2,9.3を使うことができる


・Joshさん
当然、英語の発表でした。。

サービスの説明
サーバー台数とか使っているサービス数とか1日のjob数とか
メモりきれませんでした。。。

使われてる言語の割合
オープンソースだとnode.jsが多い
.com(商用?)だとrubyが多い

B.I.W.O.M.M
but it works on my machine!
みんな同じようなこと言っちゃうよね、だからtravis使うといいよ的な話と受け取りました。

みんなmacで開発するけど、本番はmacで動かさないよね?的なことを言ってました

質疑応答で出てきたのが、
travis_waitでタイムアウトを変更できる。ドキュメントにはない、秘密コマンドらしい。


・pinzoloさん
Travis API

株式会社 空
redmineのプラグインを作ってる
redmine本体が壊れてないかテストできるので便利

過去のビルド情報が取得できる

TravisのAPIをたたくためのgemがある
インストールするとtravisコマンドが利用できる

access tokenはtravis tokenで取得できる
プロフィールページのtokenではない

durationはダウンロードや準備の部分の時間も含まれちゃうので、
フェーズごとのdurationが取れるといいなぁってお願いされてた。

プレゼン資料あがってました
https://speakerdeck.com/pinzolo/travis-ci-api-lt


・岸川さん
Automated releasing iOS app with Travis CI

Ubiregi Inc
申請は手作業だけど、それ以外はtravisでやってる
TestFlightとかxcodebuildでやってる
申請するためのビルドをそのまま使って配布用に使えるようにしている

プレゼン資料あがってました
https://speakerdeck.com/kishikawakatsumi/automated-releasing-ios-app-with-travis-ci


・shigemk2さん
QUnit on Travis CI

自作JSライブラリをQUnitでテストしつつ継続的インテグレーションしてみた
ハンドルネームのmk2はマーク2。ガンダムではなくエルガイムらしい

リポジトリ
https://github.com/shigemk2/regexp-js

ブログあがってました
http://shigemk2.hatenablog.com/entry/2014/09/17/QUnit_on_Travis_CI%E3%81%A8%E3%81%84%E3%81%86%E5%90%8D%E5%89%8D%E3%81%A7LT%E3%82%92%E3%82%84%E3%81%A3%E3%81%9F_%23travisci_jp


・sanematさん
When was the build passing?

githubのreadmeにはってあるbuild passingがいつのやつかぱっとわからない
自分のプルリクで壊したのじゃないのに、failureになっちゃうのとか嫌だよね

http://tachikoma.io/
使うといいよ
定期的にpull requestを送ってくれて、
いつからビルドがこけてるかがわかるようになる

bundle updateしたものをプルリク投げてくれるらしい

travis ciがやれることはtravis ciに任せる

プレゼン資料あがってました
http://sanemat.github.io/talks/20140917-travis-ci-meetup-tachikoma-io/


・yandoさん
Testing your App with Selenium on Travis CI

PHP NZでやってきた話をかいつまんで。
PHP CONFERENCE2014の宣伝あり

僕らの共通言語はtravis.yml
travis.ymlで動くjenkinsのプラグイン作った人がいるらしい
#これかなぁhttps://wiki.jenkins-ci.org/display/JENKINS/Travis+YML+Plugin

seleniumはtravisでやりやすい
travisのvmにはfirefoxとX入ってるしjavaも入ってる
wgetでselenium-server落として来て起動するだけ

プレゼン資料あがってました
https://speakerdeck.com/yandod/testing-your-app-with-selenium-on-travis-ci-1



あ、ステッカーもらいそこねました。。。

2014年9月11日木曜日

2014年9月10日水曜日

centos7に最新のnginxを入れる

centos7にnginxを入れようと思い、入れるんなら最新版がよいなということで調べました。

まずはnginxのyumレポジトリを登録
# yum -y install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm
読み込んだプラグイン:fastestmirror
nginx-release-centos-7-0.el7.ngx.noarch.rpm                                                                                   | 4.6 kB  00:00:00     
/var/tmp/yum-root-_s7dpv/nginx-release-centos-7-0.el7.ngx.noarch.rpm を調べています: nginx-release-centos-7-0.el7.ngx.noarch
/var/tmp/yum-root-_s7dpv/nginx-release-centos-7-0.el7.ngx.noarch.rpm をインストール済みとして設定しています
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ nginx-release-centos.noarch 0:7-0.el7.ngx を インストール
--> 依存性解決を終了しました。

依存性を解決しました

=====================================================================================================================================================
 Package                             アーキテクチャー      バージョン                  リポジトリー                                             容量
=====================================================================================================================================================
インストール中:
 nginx-release-centos                noarch                7-0.el7.ngx                 /nginx-release-centos-7-0.el7.ngx.noarch                1.5 k

トランザクションの要約
=====================================================================================================================================================
インストール  1 パッケージ

合計容量: 1.5 k
インストール容量: 1.5 k
Downloading packages:
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : nginx-release-centos-7-0.el7.ngx.noarch                                                                              1/1 
  検証中                  : nginx-release-centos-7-0.el7.ngx.noarch                                                                              1/1 

インストール:
  nginx-release-centos.noarch 0:7-0.el7.ngx                                                                                                          

完了しました!

このままだと、最新のmainlineではなくstableのバージョンが入ってしまうので、
/etc/yum.repos.d/nginx.repoのbaseurlを変更します。
# cat /etc/yum.repos.d/nginx.repo 
# nginx.repo

[nginx]
name=nginx repo
#baseurl=http://nginx.org/packages/centos/7/$basearch/
baseurl=http://nginx.org/packages/mainline/centos/7/$basearch/
gpgcheck=0
enabled=1

yumでnginxをインストールします。
# yum install nginx.x86_64
読み込んだプラグイン:fastestmirror
nginx                                                                                                                         | 2.9 kB  00:00:00     
nginx/x86_64/primary_db                                                                                                       | 5.0 kB  00:00:00     
Loading mirror speeds from cached hostfile
 * base: ftp.iij.ad.jp
 * extras: ftp.iij.ad.jp
 * updates: ftp.iij.ad.jp
依存性の解決をしています
--> トランザクションの確認を実行しています。
---> パッケージ nginx.x86_64 0:1.7.4-1.el7.ngx を インストール
--> 依存性解決を終了しました。

依存性を解決しました

=====================================================================================================================================================
 Package                         アーキテクチャー                 バージョン                                   リポジトリー                     容量
=====================================================================================================================================================
インストール中:
 nginx                           x86_64                           1.7.4-1.el7.ngx                              nginx                           362 k

トランザクションの要約
=====================================================================================================================================================
インストール  1 パッケージ

総ダウンロード容量: 362 k
インストール容量: 866 k
Is this ok [y/d/N]: y
Downloading packages:
nginx-1.7.4-1.el7.ngx.x86_64.rpm                                                                                              | 362 kB  00:00:05     
Running transaction check
Running transaction test
Transaction test succeeded
Running transaction
  インストール中          : nginx-1.7.4-1.el7.ngx.x86_64                                                                                         1/1 
----------------------------------------------------------------------

Thanks for using nginx!

Please find the official documentation for nginx here:
* http://nginx.org/en/docs/

Commercial subscriptions for nginx are available on:
* http://nginx.com/products/

----------------------------------------------------------------------
  検証中                  : nginx-1.7.4-1.el7.ngx.x86_64                                                                                         1/1 

インストール:
  nginx.x86_64 0:1.7.4-1.el7.ngx                                                                                                                     

完了しました!

最新のnginxがインストールできました。
# nginx -v
nginx version: nginx/1.7.4

2014年9月9日火曜日

rails4でrspecにfactory_girlを使った時のエラー

rails4でrspecでテストを書こうと思って、factory_girlを使ってみたら
エラー出たのでそのときの対応

まずはバージョンを確認
gem list抜粋
factory_girl (4.4.0)
factory_girl_rails (4.4.1)
rails (4.1.4)
rspec (3.0.0)
rspec-core (3.0.4)
rspec-expectations (3.0.4)
rspec-mocks (3.0.4)
rspec-rails (3.0.2)
rspec-support (3.0.4)

テスト対象のテーブル
mysql> desc seq_sometest4s;
+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| id     | int(10)    | NO   | PRI | 0       |       |
| seq_id | bigint(10) | YES  |     | NULL    |       |
+--------+------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

spec_helper.rbにfactory_girlの設定を追加
require 'factory_girl_rails'
RSpec.configure do |config|
  config.fixture_path = "#{::Rails.root}/spec/fixtures"
  config.use_transactional_fixtures = true
  config.order = "random"
  config.include FactoryGirl::Syntax::Methods
  config.before do
    FactoryGirl.reload
  end
end

config/application.rbにもfactory_girlの設定を追加
config.generators do |g|
  g.test_framework :rspec, :fixture => true, :fixture_replacement => :factory_girl
  g.fixture_replacement :factory_girl, :dir => 'spec/factories'
end

テストコード
$ cat spec/models/concerns/seq_table_spec.rb 
require 'spec_helper'
 
describe "SeqTable" do
  describe "seq_incrment" do
    FactoryGirl.build(:seq_sometest4)
  end 
end

テストを実行するとエラーになりました。
$ bundle exec rspec spec/models/concerns/seq_table_spec.rb
seq_sometest4
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/registry.rb:33:in `find': Factory not registered: seq_sometest4 (ArgumentError)
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/decorator.rb:10:in `method_missing'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl.rb:91:in `factory_by_name'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/factory_runner.rb:12:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:6:in `block (2 levels) in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `module_exec'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `subclass'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:227:in `block in define_example_group_method'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:4:in `block in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `module_exec'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `subclass'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:227:in `block in define_example_group_method'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/dsl.rb:41:in `block in expose_example_group_alias'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/dsl.rb:79:in `block (2 levels) in expose_example_group_alias_globally'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:3:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `block in load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:97:in `setup'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:85:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:70:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:38:in `invoke'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/exe/rspec:4:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `
'

ちょっとググってみたところ、spec_helper.rbに以下を追加するとよいとあったので入れてみました。
requireの下に
FactoryGirl.find_definitions

そして再度テストを実行してみたところ、またエラーが出ました。
$ bundle exec rspec spec/models/concerns/seq_table_spec.rb
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:16: warning: File.exists? is a deprecated name, use File.exist? instead
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:16: warning: File.exists? is a deprecated name, use File.exist? instead
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:16: warning: File.exists? is a deprecated name, use File.exist? instead
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/inflector/methods.rb:238:in `const_get': uninitialized constant SeqSometest4 (NameError)
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/inflector/methods.rb:238:in `block in constantize'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/inflector/methods.rb:236:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/inflector/methods.rb:236:in `inject'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/inflector/methods.rb:236:in `constantize'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/core_ext/string/inflections.rb:66:in `constantize'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/factory.rb:26:in `build_class'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/factory.rb:37:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/factory_runner.rb:23:in `block in run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/notifications.rb:161:in `instrument'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/factory_runner.rb:22:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/strategy_syntax_method_registrar.rb:20:in `block in define_singular_strategy_method'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:6:in `block (2 levels) in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `module_exec'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `subclass'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:227:in `block in define_example_group_method'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:4:in `block in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `module_exec'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:331:in `subclass'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/example_group.rb:227:in `block in define_example_group_method'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/dsl.rb:41:in `block in expose_example_group_alias'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/dsl.rb:79:in `block (2 levels) in expose_example_group_alias_globally'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:3:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `block in load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:97:in `setup'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:85:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:70:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:38:in `invoke'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/exe/rspec:4:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `
'

ちょっと原因がわからなかったので、railsコマンドでもう一度modelを作ってみました。
specファイルの中身を見ると、requireの記述が違います。
$ bundle exec rails g model seq_sometest4 id:integer seq_id:integer
      invoke  active_record
      create    db/migrate/20140827015707_create_seq_sometest4s.rb
   identical    app/models/seq_sometest4.rb
      invoke    rspec
      create      spec/models/seq_sometest4_spec.rb
      invoke      factory_girl
   identical        spec/factories/seq_sometest4s.rb
 
$ cat spec/models/seq_sometest4_spec.rb 
require 'rails_helper'
 
RSpec.describe SeqSometest4, :type => :model do
  pending "add some examples to (or delete) #{__FILE__}"
end

require 'rails_helper'に変更して再度テストを実行してみましたが、またエラーになりました。。
$ bundle exec rspec spec/models/concerns/seq_table_spec.rb
/Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/decorator.rb:10:in `method_missing': Factory already registered: seq_sometest4 (FactoryGirl::DuplicateDefinitionError)
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/decorator/disallows_duplicates_registry.rb:6:in `register'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl.rb:85:in `block in register_factory'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl.rb:84:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl.rb:84:in `register_factory'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/syntax/default.rb:20:in `factory'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/factories/seq_sometest4s.rb:4:in `block in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/syntax/default.rb:49:in `instance_eval'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/syntax/default.rb:49:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/syntax/default.rb:7:in `define'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/factories/seq_sometest4s.rb:3:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `block in load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:232:in `load_dependency'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/dependencies.rb:241:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:20:in `block (2 levels) in find_definitions'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:19:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:19:in `block in find_definitions'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:15:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl-4.4.0/lib/factory_girl/find_definitions.rb:15:in `find_definitions'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/factory_girl_rails-4.4.1/lib/factory_girl_rails/railtie.rb:21:in `block in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/lazy_load_hooks.rb:36:in `call'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/lazy_load_hooks.rb:36:in `execute_hook'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/lazy_load_hooks.rb:45:in `block in run_load_hooks'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/lazy_load_hooks.rb:44:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/activesupport-4.1.4/lib/active_support/lazy_load_hooks.rb:44:in `run_load_hooks'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/application/finisher.rb:64:in `block in '
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/initializable.rb:30:in `instance_exec'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/initializable.rb:30:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/initializable.rb:55:in `block in run_initializers'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:226:in `block in tsort_each'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:348:in `block (2 levels) in each_strongly_connected_component'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:427:in `each_strongly_connected_component_from'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:347:in `block in each_strongly_connected_component'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `each'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `call'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:345:in `each_strongly_connected_component'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:224:in `tsort_each'
 from /Users/xxx/.rbenv/versions/2.1.2/lib/ruby/2.1.0/tsort.rb:205:in `tsort_each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/initializable.rb:54:in `run_initializers'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/railties-4.1.4/lib/rails/application.rb:300:in `initialize!'
 from /Users/xxx/Documents/github/test20140812/test-api/config/environment.rb:5:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/rails_helper.rb:4:in `require'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/rails_helper.rb:4:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:1:in `require'
 from /Users/xxx/Documents/github/test20140812/test-api/spec/models/concerns/seq_table_spec.rb:1:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `block in load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `each'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/configuration.rb:1058:in `load_spec_files'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:97:in `setup'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:85:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:70:in `run'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/lib/rspec/core/runner.rb:38:in `invoke'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/gems/rspec-core-3.0.4/exe/rspec:4:in `'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `load'
 from /Users/xxx/Documents/github/test20140812/test-api/vendor/bundle/ruby/2.1.0/bin/rspec:23:in `
'

先ほど付け足したspec_helper.rbのFactoryGirl.find_definitionsを削除して再度実行したら、
今度はエラーにならずに動きました。
テスト自体は書いてないので0件です。
$ bundle exec rspec spec/models/concerns/seq_table_spec.rb
No examples found.
 
Finished in 0.00033 seconds (files took 3.61 seconds to load)
0 examples, 0 failures

require 'rails_helper'が肝だった訳ですが、ちゃんとrailsコマンドで最初から作っていれば
エラーにはまらずに済んでたと思うので、あるものはちゃんと使わないといけないですね。

参考URL
http://dev.classmethod.jp/server-side/ruby-on-rails/ruby-on-rails_factorygirl/
https://github.com/thoughtbot/factory_girl/blob/master/GETTING_STARTED.md
http://thinkit.co.jp/story/2014/07/08/5092/page/0/2
http://stackoverflow.com/questions/9300231/factory-already-registered-user-factorygirlduplicatedefinitionerror
http://stackoverflow.com/questions/8409787/factorygirl-factory-not-registered-user-argumenterror
http://qiita.com/youcune/items/3a1e4ef76e71da43e043
http://j-caw.co.jp/blog/?p=1042
http://source.hatenadiary.jp/entry/2014/02/06/222318

2014年9月5日金曜日

mysql5.6でユーザー作成したらエラーになった

mysql5.6でユーザー作成したらエラーになったので調べてみました。

grant select on test.* to 'test_r'@'localhost' IDENTIFIED BY PASSWORD 'パスワード';
ERROR 1827 (HY000): The password hash doesn't have the expected format. Check if the correct password algorithm is being used with the PASSWORD() function.
どうやらパスワードを生で入れてはだめと言っているようです。
PASSWORD()で暗号化するとよいみたいです。
mysql> select password('パスワード');
+-------------------------------------------+
| password('パスワード')                    |
+-------------------------------------------+
| *CE405B6FE1164FDCD390917220F25F9E97448173 |
+-------------------------------------------+
1 row in set (0.03 sec)
暗号化したパスワードを入れて再度ユーザー作成を行うと、ちゃんと作成できました。
grant select on test.* to 'test_r'@'localhost' IDENTIFIED BY PASSWORD '*CE405B6FE1164FDCD390917220F25F9E97448173';
アプリケーションから接続する時は暗号化する前のパスワードを使う形でよいようです。

参考URL
http://shareolite.blogspot.jp/2014/03/how-to-solve-mysql-error-1827-hy000.html#.U_XEAIB_vGw

2014年9月3日水曜日

rails4のActiveRecordでselect for updateを実行

mysqlでselect for updateを使ったトランザクション処理をしたかったので調べました。

まずはrailsのバージョン
$ bundle exec rails -v
Rails 4.1.4

mysqlのテーブル
mysql> desc seq_sometest3s;
+--------+------------+------+-----+---------+-------+
| Field  | Type       | Null | Key | Default | Extra |
+--------+------------+------+-----+---------+-------+
| id     | int(10)    | NO   | PRI | 0       |       |
| seq_id | bigint(10) | YES  |     | NULL    |       |
+--------+------------+------+-----+---------+-------+
2 rows in set (0.01 sec)
 
mysql> select * from seq_sometest3s;
+----+--------+
| id | seq_id |
+----+--------+
|  1 | 100049 |
+----+--------+
1 row in set (0.00 sec)

まずネットで調べた以下のやり方だとエラーになりました。
tbl = base.find(1, lock: true)
tbl.increment!(:seq_id)
tbl.seq_id
 
   (0.2ms)  BEGIN
  SeqSometest3 Load (0.7ms)  SELECT `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE `seq_sometest3s`.`id` IN (1, '---\n:lock: true\n')
   (0.2ms)  ROLLBACK

次に出てきた以下のやり方だとエラーは出ませんでした。
ただ、プライマリーキーで指定しているのでorder byが不要です、
tbl = base.where("id =1").lock(true).first
tbl.increment!(:seq_id)
tbl.seq_id
 
   (0.1ms)  BEGIN
  SeqSometest3 Load (0.2ms)  SELECT  `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE (id =1)  ORDER BY `seq_sometest3s`.`id` ASC LIMIT 1 FOR UPDATE
  SQL (0.2ms)  UPDATE `seq_sometest3s` SET `seq_id` = 100046 WHERE `seq_sometest3s`.`id` = 1
   (0.4ms)  COMMIT

次のやり方でもエラーは出ませんでしたが、
order byはつかなくなったけど2回selectが実行されてしまいます。
後半のSQLだけでよいのに。
tbl = base.find(1).lock!
tbl.increment!(:seq_id)
tbl.seq_id

   (0.1ms)  BEGIN
  SeqSometest3 Load (0.2ms)  SELECT  `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE `seq_sometest3s`.`id` = 1 LIMIT 1
  SeqSometest3 Load (0.2ms)  SELECT  `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE `seq_sometest3s`.`id` = 1 LIMIT 1 FOR UPDATE
  SQL (0.2ms)  UPDATE `seq_sometest3s` SET `seq_id` = 100047 WHERE `seq_sometest3s`.`id` = 1
   (0.6ms)  COMMIT

このやり方だとselectも1回しか発行されず、order byもつかないSQLが発行できました。
tbl = base.lock.find(1)
tbl.increment!(:seq_id)
tbl.seq_id
 
   (0.1ms)  BEGIN
  SeqSometest3 Load (0.4ms)  SELECT  `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE `seq_sometest3s`.`id` = 1 LIMIT 1 FOR UPDATE
  SQL (0.3ms)  UPDATE `seq_sometest3s` SET `seq_id` = 100049 WHERE `seq_sometest3s`.`id` = 1
   (0.3ms)  COMMIT

ちなみに存在しないレコードにロックをかけようとすると、
RecordNotFoundのエラーが出ました。
tbl = base.lock.find(2)
tbl.increment!(:seq_id)
tbl.seq_id

   (0.2ms)  BEGIN
  SeqSometest3 Load (0.2ms)  SELECT  `seq_sometest3s`.* FROM `seq_sometest3s`  WHERE `seq_sometest3s`.`id` = 2 LIMIT 1 FOR UPDATE
   (0.1ms)  ROLLBACK
ActiveRecord::RecordNotFound (Couldn't find SeqSometest3 with 'id'=2):


参考URL
http://apidock.com/rails/ActiveRecord/Locking/Pessimistic
http://api.rubyonrails.org/classes/ActiveRecord/Locking/Pessimistic.html

2014年9月1日月曜日

rails4のActiveRecordで特定のカラムだけ取得する

ActiveRecordで普通にfindしたりすると、すべてのカラムが取得できますが、
DBの負荷を考えると必要なカラムだけ取得したいので、そのやり方を調べました。

まずはrailsのバージョンを確認
$ bundle exec rails -v
Rails 4.1.4

DBの中身はこんな感じです。
mysql> select * from sometest3s where id=2;
+----+------+--------------------------------------------------------------------------------------------+----------+---------------------+
| id | name | content                                                                                    | view_flg | ins_date            |
+----+------+--------------------------------------------------------------------------------------------+----------+---------------------+
|  2 | john | {"shop_id"=>[100, 200, 300], "item_id"=>[500, 600, 700, 800], "name"=>"jon", "user"=>2000} | 0        | 2014-08-20 02:17:17 |
+----+------+--------------------------------------------------------------------------------------------+----------+---------------------+
1 row in set (0.00 sec)

取得の仕方はこうで、pluckを使うと結果は配列に入ります。
mapss = Sometest3.select(:id,:name).where(:id => 2).pluck(:id,:name)
puts mapss # => 2 john
puts mapss[0] # => 2 john
puts mapss[0][0] # => 2
puts mapss[0][1] # => john 

その際に発行されるSQLがこんな感じです。
   (0.3ms)  SELECT `sometest3s`.`id`, `sometest3s`.`name` FROM `sometest3s`  WHERE `sometest3s`.`id` = 2

ちなみにwhereとselectを入れ替えてみても発行されるSQLは同じでした。
mapss = Sometest3.where(:id => 2).select(:id,:name).pluck(:id,:name)

   (0.3ms)  SELECT `sometest3s`.`id`, `sometest3s`.`name` FROM `sometest3s`  WHERE `sometest3s`.`id` = 2


参考URL
http://6rats.blog62.fc2.com/blog-entry-75.html
http://d.hatena.ne.jp/suginoy/20120605/p3