ラベル Ruby on Rails の投稿を表示しています。 すべての投稿を表示
ラベル Ruby on Rails の投稿を表示しています。 すべての投稿を表示

2020-08-10

CentOS6でActiveRecord::ConnectionNotEstablished: No connection pool with 'primary' found.というエラーが出たとき。

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク

 CentOS6の環境でrailsのrspecの全件試験をしたときに、試験の後半で

「ActiveRecord::ConnectionNotEstablished:No connection pool with 'primary' found.」

というエラーが連発して発生することがありました。

エラーが発生したテストケースを個別に実行するとエラーがでない感じです。

どうも調べていたらCentOS6のulimitの上限がありそうな気配でした。

以下参考です。

CentOS6のプロセス数の上限

こちらを参考に

/etc/security/limits.d/90-nproc.conf

の内容を

*          soft    nproc     1024

から

*          soft    nproc     2048

へ変更したら無事にエラーが消えました。


2019-05-06

一つのDBを複数のrailsアプリから利用する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
RailsにAPIや管理画面やバッチツールなど、いろいろ機能が盛り込まれて大きくなってきて、それぞれの機能を分離したくなってきてみました。
この段階では、
app
という一つのリポジトリがある感じです。

分離自体は、役割が結構ちゃんとわかれていたので、それほど大変ではありませんでした。
この段階では、
app_api
app_admin
app_batch
のような三つのリポジトリがある感じです。

三つに別れましたが、共通のDBを利用する必要があって、分離されたアプリそれぞれにmigrationがあって、それをなんか変わったときに人手で同期させていたのですが、それぞれ独自にカラム追加したりして、だんだん見通しが悪くなってきて、ちょっと面倒になってきました。

dbを独立したリポジトリにしてよい感じで使えないかなぁと調べていたら、
というのがあることを知りました。
Ridgepoleでは、migrationのように変更を積み重ねるのではなく、最終的なテーブル定義を保持していて、dbとテーブル定義の差分を見て、dbを変更していく感じです。
dbの状態の見通しがよくなりそうだし、定義ファイルを独立したリポジトリで管理して、それぞれのリポジトリのsubmoduleとして取り込むとよさげかなと思ってみました。

というわけで、そのような環境を構築してみます。

以下を参考にしています。
https://re-engines.com/2017/05/03/370/#rails_migrateRidgepole-2

まずはGemfileに
gem 'ridgepole'
を追加して
bundle install
します。これでRidgepoleが利用できるようになります。

現在のdb定義からRidgepole用の定義ファイルを生成します。
bundle exec ridgepole -c config/database.yml -E development --split --export -o db/schemas/Schemafile

これで
db/schemas/
以下に定義ファイルが作成されます。

試しに定義ファイルの適用コマンドを試します。
bundle exec ridgepole -c config/database.yml -E development --apply -f db/schemas/Schemafile
当然、変更はありません。
念のため、test環境にも試します。
bundle exec ridgepole -c config/database.yml -E test --apply -f db/schemas/Schemafile
こちらも変更はありません。

ここまでくれば、migrationは不要になるので思い切って削除します。
rm -r db/migrate

あと、rspecのテストのために以下の変更もしたほうがよいようです。
spec/rails_helper.rb
の中の
ActiveRecord::Migration.maintain_test_schema!
の部分をコメントアウトします。
以下のような感じです。

#begin
#  ActiveRecord::Migration.maintain_test_schema!
#rescue ActiveRecord::PendingMigrationError => e
#  puts e.to_s.strip
#  exit 1
#end

とりあえず、ここでmigrationからRidgepoleに切り替えた感じになります。
Ridgepoleをより便利に使えるようにします。
以下を参考にしています。
https://qiita.com/ToqTock/items/c185a38fd4e2011ddd64

まずmigrationファイルを生成されないようにします。Myappの部分は環境にあわせて変えてください。
config/initializers/generator.rb



Ridgepoleのコマンドが長いのでdb:migrateのようにdb:applyみたいな感じを使えるようにします。
lib/tasks/ridgepole_tasks.rake



ここからdbの部分を以下のような感じで独自のリポジトリにします。
cp -pr db ~/app_db
cd ~/app_db
git init
git add *
git commit
git remote add origin app_db用リポジトリ
git push -u origin master
これで
app_api
app_admin
app_batch
app_db
の4つのリポジトリができましたが、この段階では、app_dbと同じ内容が個別にapp_apiなどにある感じです。

app_apiなどの各アプリのdbディレクトリgitのsubmoduleを利用してapp_dbのリポジトリを見るようにします。
git submoduleに関しては、以下が参考になりました。
https://qiita.com/sotarok/items/0d525e568a6088f6f6bb

cd ~/app_api
この中のdbディレクトリを削除してしまいます。
rm -r db
ここで以下のコマンドを実行してapp_dbをsubmoduleにしてdbに結び付けます。
git submodule add app_db用リポジトリ db
これでリポジトリは、以下のような感じになりました。
app_api(dbフォルダはapp_db)
app_admin(dbフォルダはapp_db)
app_batch(dbフォルダはapp_db)
app_db

これでそれぞれのアプリでdbに変更したものは、app_dbを介して、それぞれに配布できるようになりました。


2017-03-24

railsの開発環境のjavascriptがなんだかよくわからないことになった時の対策

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsのdevelopment環境でいろいろいじっているうちに
なぜだかわからないのですが、jqueryの動きがあやしい感じになってしまいました。

xhrで投げた結果を受け取って、それを画面の置き換えるのが、うまく動かなかったり
imageの位置を変な感じで取得しようとして受け取れなかったり
みたいな感じになっていました。

原因は、もうわからなかったのですが、
rake assets:clean
で一回asset関連をクリーンしたら、うまく動くようになりました。

2015-03-12

passengerを利用していてログにpassenger_native_support.soがどうのこのと書いてあったときのこと

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
passenger4以上では、複数のバージョンのrubyを混在させて利用できるようになっています。

passengerでrubyバージョンを混在させる方法は、以下を参照してください。
http://y-ken.hatenablog.com/entry/how-to-use-multiple-ruby-version-in-one-apache-passenger

railsアプリをサブディレクトリ毎にいくつか配置していて、rbenvを利用してそのrailsアプリ毎にrubyのバージョンを変えて使っていたりしています。

で、apacheのerror_logに時折、以下のようなメッセージが表示されていました。

App 7405 stderr:  --> Compiling passenger_native_support.so for the 
current Ruby interpreter...
App 7405 stderr: 
App 7405 stderr:      (set PASSENGER_COMPILE_NATIVE_SUPPORT_BINARY=0 to 
disable)
App 7405 stderr: 
App 7405 stderr:  --> Downloading precompiled passenger_native_support.
so for the current Ruby interpreter...
App 7405 stderr: 
App 7405 stderr:      (set PASSENGER_DOWNLOAD_NATIVE_SUPPORT_BINARY=0 to 
disable)
App 7405 stderr: 
App 7405 stderr:      Could not download https://oss-binaries.phusionpassenger.com/binaries/passenger/by_release/4.0.59/rubyext-ruby-2.1.2-x86_64-linux.tar.gz: The requested URL returned error: 404
App 7405 stderr:      Trying next mirror...
App 7405 stderr:      Could not download https://s3.amazonaws.com/phusion-passenger/binaries/passenger/by_release/4.0.59/rubyext-ruby-2.1.2-x86_64-linux.tar.gz: The requested URL returned error: 403
App 7405 stderr:  --> Continuing without passenger_native_support.so.

別に普通に使えていたので、ほったらかしいたのですが、
ふと/tmpの下を見たら
passenger_native_support-1q5o6tj.log
というファイルができていて、その中身を見たら以下のような内容が出ていました。

# mkdir -p /usr/local/lib/ruby/gems/1.9.1/gems/passenger-4.0.59/buildout/ruby/ruby-2.1.2-x86_64-linux
Encountered permission error, trying a different directory...
-------------------------------
# mkdir -p /var/www/.passenger/native_support/4.0.59/ruby-2.1.2-x86_64-linux
Encountered permission error, but no more directories to try. Giving up.

私の環境では、apacheをapacheユーザで運用していたのですが、
/var/www/
のオーナーがrootになっていました。
そこで
mkdir /var/www/.passenger
chown apache:apache /var/www/.passenger
としてapacheを再起動したら、上記のメッセージが出なくなりました。

そして
/var/www/.passenger/native_support/4.0.59/ruby-2.1.2-x86_64-linux
の中に
passenger_native_support.so
が確かにできていました。

メッセージが出ているだけで、特に動きに支障はなかったのですが、気にはなっていたので解決できてよかったです。

2014-11-24

railsのログに情報を追加する

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsのログに情報を簡単に追加する方法として
config.log_tags
を利用する方法があります。

config/environment/develpment.rb

config/environment/production.rb
に以下のように書くとセッションIDとリモートIPをログに追加できます。
config.log_tags = [ :uuid, :remote_ip ]

ここでふと、このlog_tagsにはどんなものが記載できるのだろう?と思ってみたわけです。

調べてみたところ、log_tagsで記載した内容が、実際にログにタグとして追加する処理は以下のcompute_tagsの部分で行っているようです。
https://github.com/rails/rails/blob/21a71cd5d8f266252278a63316a99cc403a32cad/railties/lib/rails/rack/logger.rb#L57

Sybolとして指定したものは、requestオブジェクトのメソッドとして呼ばれるようです。なのでrequestオブジェクトのメソッドがここで指定できる候補になります。

実際に、以下のようにuser_agentを追加してみたら
config.log_tags = [ :uuid, :remote_ip, :user_agent ]

以下のようなログが出力されました。
[a0b386ac-c108-4820-be22-4fbf8d14a1e8] [10.10.10.10] [Mozilla/5.0(Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36] Processing by WelcomeController#index as HTML

log_tagsで指定できるのは、Symbol以外にProcも指定できて、この場合はProcオブジェクトにrequestオブジェクトを引数として渡されて処理されるようです。
またそれ以外に、文字列を指定した場合は、その文字列が単純にセットされるようです。

ただrequestのメソッドならば、なんでもSymbolで指定したり、Proc内で処理できるわけではなさそうです。sessionなどは処理できませんでした。
どうも、通常は、ログの処理よりもセッションの処理の方が後に行われるようで、sessionを指定してもログの処理の段階ではセッションの処理がまだ行われていないことが原因のようです。

なので、以下のサイトで紹介されているように、処理の順番を入れ替えることでsessionも扱えるようになるようです。
http://threetreeslight.com/post/51883603368/rails-id-ip

2014-03-09

rubyを2.0にあげたらrailsのfixturesで読み込む値が変わってた

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Rails3.2とruby1.9.3で作ったもののrubyを2.0系にあげてみたところ、rake specが失敗するようになってしまいました。

失敗するのは、日時関連の処理だったので、timezone関係のものがおかしいのかなぁと思ったのですが、fixturesでテストデータを読み込んだ動作が1.9と2.0で違っていました。

具体的には、fixturesで
aaa: 2014-01-25 22:57:09
と書いていたところが、おかしかったようです。

さらに調べたらYAM.loadで読み込んだ時点で違っていました。
rails cでコンソールを立ち上げてそれぞれのバージョンで比較した結果は、以下のとおりです。

1.9.3
[1] pry(main)> a = "aaa: 2014-01-25 22:57:09"
=> "aaa: 2014-01-25 22:57:09"
[2] pry(main)> YAML.load(a)
=> {"aaa"=>"2014-01-25 22:57:09"}
[3] pry(main)> YAML.load(a)["aaa"].class
=> String

2.0.0
[1] pry(main)> a = "aaa: 2014-01-25 22:57:09"
=> "aaa: 2014-01-25 22:57:09"
[2] pry(main)> YAML.load(a)
=> {"aaa"=>2014-01-26 07:57:09 +0900}
[3] pry(main)> YAML.load(a)["aaa"].class
=> Time

YAML.loadが定義されているところを調査してみました。

1.9.3
[1] pry(main)> YAML.method(:load)
=> #<Method: Syck.load>

2.0.0
[1] pry(main)> YAML.method(:load)
=> #<Method: Psych.load>

なんかloadが定義されている場所が違いました。

調べてみると以下のようなことでした。
http://qiita.com/udzura/items/ec4c5b83db94363c9cdf
Syckは古いもののようで、2.0.0からはなくなったようです。

1.9.3でもPsychがデフォルトのようなのですが、railsがSyckがあればそちらを優先している気配です。
今回の例は、あくまでもrails cでコンソールを利用している場合で、railsを利用しないでpryで実行すると1.9.3でも2.0.0でも動きはPsychの動きをしていました。

ちなみに以下のように1.9.3でもPsychを指定すれば2.0.0と同じ動きをします。

[1] pry(main)> YAML::ENGINE.yamler = 'psych'
=> "psych"
[2] pry(main)> a = "aaa: 2014-01-25 22:57:09"
=> "aaa: 2014-01-25 22:57:09"
[3] pry(main)> YAML.load(a)
=> {"aaa"=>2014-01-26 07:57:09 +0900}
[4] pry(main)> YAML.load(a)["aaa"].class
=> Time

で、今回は、"2014-01-25 22:57:09"を日本時間のつもりで書いていたのに、違う解釈をされたのが困ったのでした。
これを解決するには、日付は以下のようにすることで1.9.3でも2.0.0でも同じ動きをするようになります。
aaa: 2014-01-25 22:57:09 +09:00

[1] pry(main)> a = "aaa: 2014-01-25 22:57:09 +09:00"
=> "aaa: 2014-01-25 22:57:09 +09:00"
[2] pry(main)> YAML.load(a)
=> {"aaa"=>2014-01-25 22:57:09 +0900}
[3] pry(main)> YAML.load(a)["aaa"].class
=> Time


2014-02-21

LinuxからSQL Serverに接続して時間がかかるクエリーを実行するとセッションが切れてしまうときのこと

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
SQL Serverに接続するLinux上のRailsアプリケーションで、時間がかかるクエリーを実行したら

だいたい40秒後ぐらいに

CONNECTION RETRY: ActiveRecord::ConnectionAdapters::SQLServerAdapter 
retry #0.
TinyTds::Error: DBPROCESS is dead or not enabled: EXEC sp_executesql N'*******; SELECT @@ROWCOUNT AS AffectedRows'

のようなエラー出たのです。

ちなみに接続先は、SQL Server 2005で、接続にはfreetds 0.91を利用しています。
railsからはtiny_tdsとactiverecord_sqlserver_adapterを利用しています。

同じクエリーをwindows上ののSQLServer Management Studioから実行すれば、時間はかかるけどきちんと結果がかえってくるので、タイムアウトの設定かなぁと思って、
database.ymlにtimeoutを記述してみたものの、タイムアウトを短くする分には効くのですが、長く設定してもダメでした。

Railsの方からどうにかすれば何とかなるかなぁと思っていたのですが、
Railsを使わずにlinux上でSQL serverに接続するtsqlを使ってみたら、こちらでもエラーが出るのがわかったので、どうもRailsは関係なさそうです。

こんな感じで接続して
tsql -S server -U uuu -P ppp

クエリーを実行したら、やはり40秒後ぐらい以下のエラーが出てクエリーが失敗したのです。

Error 20004 (severity 9):
        Read from the server failed
        OS error 104, "接続が相手からリセットされました"

freetdsは、接続情報をダンプできるので、ダンプさせたところ以下のように出ました。

(util.c:156):Changed query state from READING to DEAD
(util.c:331):tdserror(0x1585150, 0x15853b0, 20004, 104)
(util.c:361):tdserror: client library returned TDS_INT_CANCEL(2)
(util.c:384):tdserror: returning TDS_INT_CANCEL(2)
(token.c:555):processing result tokens.  marker is  0()
(token.c:122):tds_process_default_tokens() marker is 0()
(token.c:125):leaving tds_process_default_tokens() connection dead

ダンプ内容を見てもさっぱりわからなかったので、tcpdumpを以下のような感じで実行してみました。

tcpdump host server and port 1433 -Xn

すると、SQL Server側からクエリー実行30秒後ぐらいから1秒おきに10回ぐらいパケットがきて、最後にサーバー側から切断要求が来ていることがわかりました。

tsqlで出たエラーメッセージの内容どおりということでした。

でSQL Serverから出ているのはなんだろう?と思って調べていたらkeep aliveでした。

SQL Serverのkeep aliveを調べていたら以下がありました。
http://msdn.microsoft.com/ja-jp/library/ms190771.aspx

Microsoft SQL Server 構成マネージャでkeep aliveの設定を見ると確かに30秒と1秒間隔が設定されていました。

なので、こちらの値を変えれば住むのですが、値を変更したらSQL Serverのサービスの再起動が必要になるようで、ちょっと再起動がすぐにはできないので、この方法は見送ります。


keep aliveに対して、linux側が返答をしないのがいけないので、返答するようにする方法を調べていたら、以下のページを見つけました。
http://permalink.gmane.org/gmane.comp.db.tds.freetds/13296

どうもlinux側のkeep alive設定をどうのこうのとあったので、ためしにSQL Server側のkeep alive設定とあわせるように以下のように設定してみました。

sysctl -w net.ipv4.tcp_keepalive_time=30
sysctl -w net.ipv4.tcp_keepalive_intvl=1

すると切断されないようになりました。
tcpdumpで見てもきちんとkeep aliveに対して返答をかけていました。

ただデフォルト値が
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
なので今回の設定は小さくしすぎな気がします。

SQL Serverだけ見ればよいのですが、他への影響が心配なので、この方法も見送ります。


SQL Serverだけに対してkeep aliveの値を変えたいなぁと思ってたどりついたのが、freetdsのソースを変更することでした。
以下にページでsocketにkeep aliveを設定する方法が書いてあったので、これを真似します。
http://d.hatena.ne.jp/iww/20081030/setsockopt

socketのopen処理は
src/tds/net.c
で書かれているのを見つけました。SO_KEEPALIVEだけはifdefでくくられて記載されていたのでifdefを取って以下のように書き換えてコンパイルしました。

len = 1;
setsockopt(tds->s, SOL_SOCKET, SO_KEEPALIVE, (const void *) &len, sizeof(len));
len = 30;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPIDLE, (const void *) &len, sizeof(len));
len = 1;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPINTVL, (const void *) &len, sizeof(len));
len = 10;
setsockopt(tds->s, IPPROTO_TCP, TCP_KEEPCNT, (const void *) &len, sizeof(len));

これでkeep aliveに対してちゃんと返答を返すようになりました。


といろいろ調べましたが、実際に取った対応は、遅かったクエリーをチューニングして速くする対応を行いました。

遅いSQLに対してはチューニングがするのが正しい対応だと思うのですが、
railsのmigraionをsql serverに対して実行するときに、大きな既存テーブルに対して何かしらの変更を加えるとやはり問題が起こることがあるのがわかったので、そのうちfreetdsの方の変更もしないとダメかなぁと思っています。



2014-02-17

twitter-bootstrap-railsをproduction環境でエラーが出たとき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsでbootstrapを使うために
twitter-bootstrap-rails
を使ってみました。

developmentでは何の問題もなく使えていたのですが、
productionでは、

ActionView::Template::Error (undefined local variable or method `
bootstrap_flash' for #<#<Class:0x00000004abf460>:0x000000051a9690>):

みたいなエラーが出て使えなかったのです。

調べてみると
helperにbootstrap_flashを追加すればよいみたいな感じなのですが
今回使っている
twitter-bootstrap-rails
のバージョンは2.2.8で
twitter-bootstrap-rails-2.2.8/app/helpers/flash_block_helper.rb
にhelperがちゃんと用意されていました。
これをapp/helperにコピーすれば動くのですが、
それでは、いまいちです。

エラーが出た原因ですが、
Gemfileの書き方がいけませんでした。

最初は、
group :assets do
  gem "twitter-bootstrap-rails"
end
とassetsグループの中に入れていたのですが、これがダメでした。

groupにいれずに普通に
gem "twitter-bootstrap-rails"
と書いておけばOKでした。

やれやれです。


2014-01-29

RailsでSQL Serverを使っているときにエンコードエラーが出たとき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
RailsでSQL Serverを使っているときに

incompatible character encodings: ASCII-8BIT and UTF-8 (RuntimeError)

みたいなエラーが出てマジックコメント書いてささっと解決さと思ったのにすでにマジックコメントが書いてあってどういうことだろう・・・と思ったわけです。

こんなエラーが出ていますが、ただ単に利用するテーブルに権限がないだけで、権限を振ればOKだったりすることがあります。

私の場合既存システムのDBのデータをselectしたいなぁと思ったときに、そのテーブルへのselect権を付け忘れていてエラーが出たのでした。

紛らわしいエラーですね。





      
      

2014-01-15

Railsのmigrationにchangeとup/downの両方を書いてみると

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Railsのmigrationにchangeとup/downの両方を書いてみると
changeの方しか実行しませんでした。

そういうものらしいです。

2014-01-14

gemを作成したけどrubygemsに登録を行うrake releaseをしないようにガードする

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
gemを作ったら、gemのディレクトリでrake releaseをするとrubygemsに登録がされます。
(rubygemsにアカウントがなかれば、登録はされません)

とてもお手軽でよいのですが、ローカル用で公開する気がないものをうっかり登録してしまう可能性もなくはないです。

うっかりrake releaseを防止するためにrake releaseで行われる動作を変更しておけば安心です。

手順は以下のとおりです。

gemのディレクトリのRakefileに以下を追加します。

Bundler::GemHelper.install_tasks
#以下を追加
t = Rake.application.lookup('release')
t.clear
t.instance_variable_set('@full_comment', nil)
desc 'Please keep this for internal use only.'
task :release do
    puts "Please keep this for internal use only."
end

これでうっかりrake releaseを行ってもputsされるだけになります。

本当は、rake release自体を削除したかったのですが、やり方がわかりませんでした・・・

2013-12-30

railsプラグインをrubygemsに登録してみた

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsのgemプラグインを作ってみたのでrubygemsに登録をしてみたいと思います。

作ったrailsプラグインに関しては、
http://kingyo-bachi.blogspot.jp/2013/12/rails.html
を見ていただけたらと思います。

rubygemsに登録するためには、rubygemsにアカウントを作成する必要があります。
https://rubygems.org/sign_up
からサインアップしてアカウントを作ります。

アカウントを作成するとAPI Keyが払い出されます。
これを
~/.gem/credentials
に保存するとrubygemsに登録ができるようになります。

API Keyは
https://rubygems.org/profile/edit
にあります。
またこのページには~/.gem/credentialsを作成するコマンドが書いてあるので、それをコピーして実行します。
パスワードが聞かれるので、パスワードを入力すると~/.gem/credentialsが作成されます。

これで登録する準備はOKです。

実際に登録する手順は、
登録するrailsプラグインのディレクトリに移動して
rake release
で登録されちゃいます。

以下の登録されちゃいました。
https://rubygems.org/gems/rails_acclog2db



以下が参考になりました。
http://blog.kyanny.me/entry/2012/02/20/015129

railsのプラグインを作ってみた

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsのプラグインを作ってみました。

実際に作った手順を簡単に記録しておこうと思います。

作ったのは、railsのアクセスログをdbに保存するというものです。
after_filterでlogsテーブルに記録をするというだけのものです。

とりあえず名前はrails_acclog2dbとします。
githubに実際に作ったものを置いてみました。
https://github.com/vivahiraj/rails_acclog2db


まずは雛形を作ります。

rails plugin new rails_acclog2db -T --skip-bundle --dummy-path=spec/dummy

それぞれのオプションは
-T:テスト用ファイルの作成をスキップ(後でrspecを利用するようにします)
--skip-bundle:コマンド直後にbundle installを実行しない
--dummy-path:作ったプラグインを試すためのrails環境の場所を指定

cd rails_acclog2db


続いてrspecの設定を行います。

vi rails_acclog2db.gemspec


rspec-railsとammeterを追加します。
ammeterはgeneratorのspecを作成する際に利用します。

関連するgemをvendor配下にインストールするように設定します。(必須ではないです)

mkdir .bundle
vi .bundle/config

内容は以下のような感じです。
------------------------------
BUNDLE_PATH: vendor/bundle
BUNDLE_DISABLE_SHARED_GEMS: "1"
------------------------------

gemをインストールします。

bundle install

rspec用のファイルを生成します。

cd spec/dummy
ln -s ../../spec
rails generate rspec:install
cd -
cp spec/dummy/.rspec .

spec_helperを修正します。

vi spec/spec_helper.rb




environmentの読込先を変更してammeter/initを追加します。


今回はログの記録先としてlogsテーブルに保存したいので、migrationとmodelをrails generateで実行できるようにするために専用のgeneratorを作成してみます。

まずはgeneratorでコピーするファイルを保存するディレクトリを作成します。
mkdir -p lib/generators/rails_acclog2db/install/templates

コピーするモデルを作成します。

vi lib/generators/rails_acclog2db/install/templates/log.rb

コピーするマイグレーションを作成します。

vi lib/generators/rails_acclog2db/install/templates/create_logs.rb

ジェネレーターを作成します。

vi lib/generators/rails_acclog2db/install/install_generator.rb

だいたいこんな感じがお決まりのようです。

ジェネレータのspecも作成してみます。

mkdir -p spec/generators/rails_acclog2db/install

vi spec/generators/rails_acclog2db/install/install_generator_spec.rb

specが成功することを確認してみます。
bundle exec rspec spec


実際にジェネレートできるかテスト用のrails環境で試してみます。
cd spec/dummy/
rails g rails_acclog2db:install

実際にmigrationもできるか試します。
rake db:migrate
rake db:migrate RAILS_ENV=test

cd ../../

作成されたLogモデルのspecも作ってみます。

mkdir spec/models
vi spec/models/log_spec.rb
specが成功することを確認してみます。
bundle exec rspec spec


つづいてメインの部分でlogを記録する部分です。
controllerにaccess_loggingと書いたら、そのcontrollerへのログを記録するようにしてみます。
sessionに:userが設定されていたら、きっと何かしらの認証がされたと思い込んで、それっぽい情報を保存するようにもします。

vi lib/rails_acclog2db/logging.rb

ActiveSupport::Concernをextendするとモジュールで取り込まれるclassメソッドとかインスタンスメソッドがちょっとすっきりかけるようです。

上記のファイルをrequireできるようにします。

vi lib/rails_acclog2db.rb 



実際にテスト用のrails環境のcontrollerに組み込んでみてspecを作成してみます。

まずはcontrollerを準備します。

cd spec/dummy
rails g controller welcome
touch app/views/welcome/index.html.erb

vi app/controllers/welcome_controller.rb



vi config/routes.rb



次にspecを作成します。
cd ../../

vi spec/controllers/welcome_controller_spec.rb

specが成功することを確認してみます。
bundle exec rspec spec


これが普通な作り方なのかはよくわからないのですが、こんな感じでできました。


以下が参考になりました。
http://guides.rubyonrails.org/v3.2.14/plugins.html
http://d.hatena.ne.jp/kouji0625/20130523/p1
http://d.hatena.ne.jp/kouji0625/20130524/p1

2013-12-09

ActiveRecordでjoinしたテーブルのカラムでwhere条件を指定するとき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
ActiveRecordで他のテーブルをjoinしたときに、joinした側のテーブルにあるカラムを条件として使いたいときに、以下のようにしていました。

Order.joins(:user).where("users.name='viva'")


で、mergeを使って以下のようにも書けるということを知りました。


Order.joins(:user).merge(User.where(:name=>'viva')


以下が参考になりました。
http://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html#method-i-merge


どっちもタイプする長さはあんまり変わらなかったので、そのときの気分で当面はどっちを使うか決めようかと思います。


      
      

2013-10-30

paperclipをSub-URIで使う

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Railsで添付ファイルを扱いたい時は、いまどきはpaperclipのようなので使ってみました。

使い方は、以下が参考になります。
http://cumiki.com/hacks/19

Sub-URIで運用にしているのですが、アップロードした画像のurlを作成する際にSub-URIを考慮してもらえませんでした。

/my_app
の配下にrailsプロジェクトを置いているのですが、/my_appを考慮してもらえなかったのです。

で、考慮してもらえるように
config/application.rb
に以下を追加しました。

module MyApp
  class Application < Rails::Application
    ・・・
    config.paperclip_defaults = {
      :path => ":rails_root/public/system/:class/:attachment/:id_partition/:style/:filename",
      :url  => "#{Rails.application.config.relative_url_root}/system/:class/:attachment/:id_partition/:style/:filename"
    }
  end
end

2013-10-10

Railsで大量データを扱うときはpluckを使ってみるのも一案のようです

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
Railsで大量データを取得してeachでまわすと遅いです。

これは取得したデータすべてにActiveRecordオブジェクトを生成していることが原因でもあるようです。

なので対策として考えられるのは、生成するオブジェクトの数を減らす方法があります。

これにはfind_eachを使うと実現できます。

find_eachは指定した件数づつ(デフォルトでは1000件)データを取得して、その分だけのActiveRecordオブジェクトを生成します。
find_eachに関しては、過去に紹介していますので、そちらをご覧ください。
大量のデータをActiveRecordで処理するとき

find_eachはソート順を指定できないという欠点があります。


もう一つの方法としてActiveRecordオブジェクトを生成せずに配列なりハッシュなり、ActiveRecordよりも軽いオブジェクトを生成する方法があります。

これには、pluckを使えます。
以下のような感じで利用します。

Order.where( amount: 1000 ).pluck( :order_no, :name, :address )
 # => [["O123", "テスト太郎", "トーキョー"],["O456", "テスト花子", "オーサカ"]]

pluckの方では、ソート順は指定できます。

pluckに複数のカラムを指定できるのは、Rails4.0からのようで3.2では一つのカラムしか指定できません。

3.2でpluckで複数のカラムを指定するには、以下の方法でpluck_allという複数カラムを利用できるようなpluckを定義するパッチをあてる方法があります。
http://meltingice.net/2013/06/11/pluck-multiple-columns-rails/

こちらではハッシュのやり方ではハッシュでデータを取得します。

Order.where( amount: 1000 ).pluck_all( :order_no, :name, :address )
 # => [{:order_no => "O123", :name => "テスト太郎", :address => "トーキョー"},
          {:order_no => "O456", :name => "テスト花子", :address => "オーサカ"}]


もう一つの方法でmultipluckというgemを使う方法もあります。
https://github.com/hanzq/multipluck

こちらは私は試していないのですが、Rails4.0と同じ感じ使えそうなので将来のアップデートを見据えるとこちらもよさそうな気がします。


で、find_eachとpluckのどちらが速いかなのですが、私が試した数万件のデータを処理するケースではだいたい同じぐらいでした。
どちらもeachでまわすよりかは、断然早くなっています。
なのでどちらを利用するかはケースバイケースなのかなぁという感じです。


2013-07-05

ActiveScaffoldでセレクトボックスで特定の値を選択させる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
いろいろできちゃうActiveScaffoldのコネタです。

ActiveScaffoldでセレクトボックスを使うときに、デフォルト値として特定の値を選択させておく方法です。

以下は、hogeカラムをセレクトボックスにしていて、tomatoをデフォルトとして選んでおく方法です。

config.columns[:hoge].form_ui            = :select
config.columns[:hoge].options[:options]  = ["potato","tomato","egg"]
config.columns[:hoge].options[:selected] = "tomato"

ちなみに、このブログでactive_scaffoldに関しての情報を他にも乗せています。
ActiveScaffoldの小ネタのまとめ


2013-06-19

railsでmysqlのlongtext型を使う

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
railsでmysqlのlongtext方を使いたなぁと思ったわけです。


migrationでカラムを追加する際に
add_column :hoges, :memo, :text

とするとmysql上ではtext型になります。


longtextにするためには、以下のようにすればOKです。
add_column :hoges, :memo, :text, :limit => 4294967295

ちなみにlimitの値をいじると、対応するmysqlの型は以下のようになるそうです。
           1 ~            255 → TINYTEXT
        256 ~         65535 → TEXT
     65536 ~    16777215 → MEDIUMTEXT
16777216 ~ 4294967295 → LONGTEXT

以下を参考にさせていただきました。

2013-05-24

rails3.2でサブディレクトリにデプロイしたら画像が出なかったとき

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
やっと、少しずつrailsの3.2を使うようになってきました。

assetがまだ慣れていないです。

以下のような感じでスタイルシートで画像を利用するにしてみました。

div.actions a.new {
  background-image: image-url('add.png');
}

開発環境では問題なく利用できていたので
本番環境にデプロイしてみました。

デプロイ先は、
/app
のようにサブディレクトリの下です。

本番環境では利用する際に

bundle exec rake assets:precompile

とassetをプリコンパイルして、いざ試してみたら画像が表示されません。

コンパイルされたスタイルシートをみたら以下のようになっていました。

div.actions a.new {
  background-image:url(/assets/add.png)
}

/appが現れていません!

で調べてみたら、以下のようにRAILS_RELATIVE_URL_ROOTをつけてプリコンパイルすればOKでした!

bundle exec rake assets:precompile RAILS_RELATIVE_URL_ROOT=/app

ちゃんと無事に/appが現れました。

div.actions a.new {
  background-image:url(/app/assets/add.png)
}

2013-05-09

railsでDATAと__END__みたいなやつ使ってみる

このエントリーをブックマークに追加 このエントリーを含むはてなブックマーク
rubyでは一つのファイルにプログラムとデータを一緒に記述できます。

以下のような感じです。

DATA.each_line do |l|
  puts l
end

__END__
aaa
bbb
ccc

__END__以下がデータを記述したファイル扱いになって、DATAで取り出せます。

これをrailsのmigrationでテーブルをcreateしたの時の初期データセットに利用してみようと思ったわけです。

こんな感じです。

class CreateHoges < ActiveRecord::Migration
  def self.up
    create_table :hoges do |t|
      t.column :c1 ,:string
      t.column :c2 ,:string
      t.column :c3 ,:integer
    end
    DATA.each_line do |line|
      d = line.split(",")
      h = Hoge.new
      h.c1 = d[0]
      h.c2 = d[1]
      h.c3 = d[2]
      h.save!
    end
   end

  def self.down
    drop_table :hoges
  end
end

__END__
aaa,bbbb,111
ccc,dddd,222


でも、これを実行しようとしたらDATAなど知らないよ!とエラーが出てしまいました。

明確な理由は、よくわからなかったのですがDATAと__END__を利用するのは一つのファイルで完結するようなものに限ったほうがよさそうです。
以下が参考になりました。
http://d.hatena.ne.jp/yarb/20120927/p1

そして調べていたら、以下のような感じにすればOKでした。

class CreateHoges < ActiveRecord::Migration
  def self.up
    create_table :hoges do |t|
      t.column :c1 ,:string
      t.column :c2 ,:string
      t.column :c3 ,:integer
    end
    data = File.read(__FILE__).split('__END__').last
    data.each_line do |line|
      line.chomp!
      next if line.size == 0
      d = line.split(",")
      h = Hoge.new
      h.c1 = d[0]
      h.c2 = d[1]
      h.c3 = d[2]
      h.save!
    end
   end

  def self.down
    drop_table :hoges
  end
end

__END__
aaa,bbbb,111
ccc,dddd,222


以下が参考になりました。
http://matthewtodd.org/2008/04/22/rails-tip-2-faking-data-in-tests.html

そもそもrailsでテーブルを追加した時の初期データセットってどうするのが普通なのかしらん?