Archive for 1月, 2010

ActiveRecord::Base#find(_by_id)? のお話

最近Ruby on Railsばかりなので、今回もRailsのお話。

何も考えずに特定のIDのレコードを見つける際は、「ActiveRecord::Base#find」という超基本メソッドを使うけれど、同時に、XXXというフィールドに対して「ActiveRecord::Base#find_by_XXX」なるメソッドも定義されているので、「ActiveRecord::Base#find_by_id」でも同じようにidに紐付けて見つけ出せる。

以上は当たり前の話だが、この同じような2つ、重要なところが違う。すなわち、そのIDに紐付くレコードが存在しない場合。

ActiveRecord::Base#find」は例外が生じる。

1
2
3
4
5
6
7
Post.find(1)
#=> ActiveRecord::RecordNotFound: Couldn't find Post with ID=1
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1586:in `find_one'
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:1569:in `find_from_ids'
#	from /usr/lib/ruby/gems/1.8/gems/activerecord-2.3.5/lib/active_record/base.rb:616:in `find'
#	from (irb):6
#	from :0

ActiveRecord::Base#find_by_id」は nil を返す。

1
2
Post.find_by_id(1)
#=> nil

このお話はそれなりに有名なようですが、find_by_idを使えば、例えばこういうコードが書けて気分がいいですね。

Read the rest of this entry »

Railsのdb:migrateは、最新バージョンより古くてもさかのぼる

どういうことかと言うと、

rails(1) でつくる。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[udzura@udzra.jp rails]$ rails -v
Rails 2.3.5
[udzura@udzra.jp rails]$ rails -d mysql migtest
      create
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      create  config/environments
...
      create  log/server.log
      create  log/production.log
      create  log/development.log
      create  log/test.log

マイグレーションを作る。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[udzura@udzra.jp rails]$ cd migtest/
[udzura@udzra.jp migtest]$ script/generate model User name:string
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/user.rb
      create  test/unit/user_test.rb
      create  test/fixtures/users.yml
      create  db/migrate
      create  db/migrate/20100122041512_create_users.rb
[udzura@udzra.jp migtest]$ script/generate model Blog user_id:integer entry:text
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/blog.rb
      create  test/unit/blog_test.rb
      create  test/fixtures/blogs.yml
      exists  db/migrate
      create  db/migrate/20100122041555_create_blogs.rb

2099年とかすっごい未来にリネームする。

1
2
3
4
5
6
[udzura@udzra.jp migtest]$ ls -l db/migrate/
合計 8
-rw-rw-r--  1 udzura udzura 193  122 13:15 20100122041512_create_users.rb
-rw-rw-r--  1 udzura udzura 217  122 13:15 20100122041555_create_blogs.rb
[udzura@udzra.jp migtest]$ mv db/migrate/20100122041555_create_blogs.rb \
  db/migrate/20990122041555_create_blogs.rb

マイグレートする。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
[udzura@udzra.jp migtest]$ rake db:create
(in /var/web-devel/rails/migtest)
[udzura@udzra.jp migtest]$ rake db:migrate
(in /var/web-devel/rails/migtest)
==  CreateUsers: migrating ====================================================
-- create_table(:users)
   -> 0.0038s
==  CreateUsers: migrated (0.0041s) ===========================================
 
==  CreateBlogs: migrating ====================================================
-- create_table(:blogs)
   -> 0.0032s
==  CreateBlogs: migrated (0.0035s) ===========================================
 
[udzura@udzra.jp migtest]$ mysql -uroot -pxxxx -e "SHOW TABLES" migtest_development
+-------------------------------+
| Tables_in_migtest_development |
+-------------------------------+
| blogs                         |
| schema_migrations             |
| users                         |
+-------------------------------+
[udzura@udzra.jp migtest]$ rake db:version
(in /var/web-devel/rails/migtest)
Current version: 20990122041555

マイグレーションを加える。当然、さっき作った二つのマイグレーションの間に入ります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
[udzura@udzra.jp migtest]$ script/generate model UserFriend user_id:integer friend_user_id:integer
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/user_friend.rb
      create  test/unit/user_friend_test.rb
      create  test/fixtures/user_friends.yml
      exists  db/migrate
      create  db/migrate/20100122041954_create_user_friends.rb
[udzura@udzra.jp migtest]$ ls -l db/migrate/
合計 12
-rw-rw-r--  1 udzura udzura 193  122 13:15 20100122041512_create_users.rb
-rw-rw-r--  1 udzura udzura 249  122 13:19 20100122041954_create_user_friends.rb
-rw-rw-r--  1 udzura udzura 217  122 13:15 20990122041555_create_blogs.rb

こういう場合でもさかのぼってマイグレーションを実行してくれます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[udzura@udzra.jp migtest]$ rake db:migrate
(in /var/web-devel/rails/migtest)
==  CreateUserFriends: migrating ==============================================
-- create_table(:user_friends)
   -> 0.0067s
==  CreateUserFriends: migrated (0.0071s) =====================================
 
[udzura@udzra.jp migtest]$ mysql -uroot -pxxxx -e "SHOW TABLES" migtest_development
+-------------------------------+
| Tables_in_migtest_development |
+-------------------------------+
| blogs                         |
| schema_migrations             |
| user_friends                  |
| users                         |
+-------------------------------+

何が嬉しいの?

例えば、実表のマイグレーションを通常の作成時の日次で、ビュー表を作成するマイグレーションを「2099年」の日次で、という風にしておくと、「必ずすべての実表が作られてからビュー表が定義される」ことが保障され、なおかつ、ビュー表の定義を変更した際、作り直しが rake(1) で容易にできます。

ポイントは rake db:migrate:down VERSION=2099XXXXXXXXXX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[udzura@udzra.jp migtest]$ rake db:migrate:down VERSION=20990122041555
(in /var/web-devel/rails/migtest)
==  CreateViewBlogs: reverting ====================================================
-- drop_table(:view_blogs)
   -> 0.0277s
==  CreateViewBlogs: reverted (0.0280s) ===========================================
 
[udzura@udzra.jp migtest]$ rake db:migrate
(in /var/web-devel/rails/migtest)
==  CreateViewBlogs: migrating ====================================================
-- create_table(:view_blogs)
   -> 0.0472s
==  CreateViewBlogs: migrated (0.0475s) ===========================================
 
[udzura@udzra.jp migtest]$

RailsでCreate Viewしたい際は、普通に execute しないといけません。なので、そもそも「RailsでView表使うとかあほか」って方もいると思う。引き出しの一つとして。

開いたWindowの存在を検出

友達が困ってるので書いた。

ポップアップでウィンドウを開く時、二重でオープンしたり、開いてるウィンドウを間違えてリフレッシュしたりしないためにはどうするかということだと思う。

要するに普通にwindow.openで開けばいいのだが、このメソッドの返り値は開いた先のwindowなので、変数に閉じ込めればよい。そうすれば「closed」というプロパティで開いてるか閉じてるかが検出可能。

1
2
3
4
5
6
var myWin = window.open('http://blog.udzura.jp', 'mywindow1');
alert(myWin.closed);
//=> false
//開いたウィンドウを閉じれば
alert(myWin.closed);
//=> true

実際はグローバル変数とか微妙なので、せめてクロージャを使うとスマートだと思う。

やっつけなりに実装してみたソースは以下の如し。サンプルサイトもあるよ。

Read the rest of this entry »