Rails でのデバッグの基本2007年11月10日 12時57分17秒

さて、Rails を起動するのは、server/script だ。

% script/server 
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options


と Rails で書かれたウェブアプリケーションが起動する。

それに引き替え、script/console の記述自体を見かけることはない。console を走らせて、実験した結果を表示しているのは、多々見るが。これは、Rails を使う者にとっては、周知の事実であり、述べなくても皆、知っていると言うことなのだろうか。

実は、その存在を Rails の実験を始めてから、何ヵ月も経つまで知らなかった。それ以前は、全てのコマンドを、rhtml なり コントローラになり書いて、ブラウザを用いて実験を行なっていた。console を使うと、それに比べると格段に色々なコマンドが簡単に実験できる。


% script/console 
Loading development emnvironment.
>> Todo.find(:all).count
NoMethodError: undefined method `count' for #<Array:0x8f4aff8>
        from (irb):1
>> Todo.find(:all).size
=> 6
>> 

Seamonkey と FireFox を同時に使う2007年11月07日 13時35分05秒

Rails で user_engine を使うと不便なことがある。各ユーザのセッションがブラウザ単位なのだ。まあ、これは user_engine や Rails に限ったことではないが。

そのため、ユーザの設定を変えるために、アドミンでログインし、一般ユーザで入り直すという作業を繰り返していた。設定を変えて試す程度だったら、そんなに頻繁では無いので、そのまま何も考えずに出入りして、やっていた。

ところが、ユーザ間の相互交流を試し始めると途端に複雑になる。いちいち、ログインし直していられないのだ。そこで、ふと、FireFox も立ち上げてみた。普段は、Seamonkey 派なのだ。そうしたら、試験がとても簡単になった。二人のユーザで、ログインし続けられる。

以前は、余計なブラウザを ports の依存関係を満たすためだけに、入れていた。Mozilla 系のコンパイルするのに、半日程度かかっていたので、苦痛だったのだ。始めて、複数のブラウザがあるのに感謝した瞬間だった。とは、言ったものの別に珍しい手法でも無いと思ったりもする。

Model に Application を挟むと…2007年11月02日 13時52分47秒

Rails では、既にコントローラには空の ApplicationController が作られ、目的のコントローラはそれを継承する形で自動生成される。

% cat app/controllers/todo.rb
class TodoController < ApplicationController

end

それに引き替え、モデルはそうなっていない。Rails の ActiveRecord を継承する形になっている。


% cat app/models/todo.rb
class Todo < ActiveRecord::Base

end

同じ、似たようなデータベース処理を複数のデータベースで行なう場合は、是非、基幹クラスに関数として実装し、あちらこちらで使い回してみたくなる。ところが、モデルでそのような事を出来るといった記述が見つからない。しかし、出来ないとの情報もない。そこで、試した。


% cat app/models/todo.rb
class Todo < Application

end
% cat app/models/application.rb
class Application < ActiveRecord::Base

end


使えなかった。Todo.find などとやっても、Application というテーブルにデータを探しにいってしまう。違うテーブルに行なえる同一処理を、一つの基幹クラスの関数にする目論見はつい果てた。

表の行数2007年11月01日 13時00分58秒

とても平素な表であれば、Ruby on Rails では簡単に作成できる。表の行数も増えてくると、せめて通し番号ぐらい付けたくなる。しかし、通し番号はちょっとしたコツが必要だ。


<table>
  <tr>
    <th></th>
  <% for column in Todo.content_columns %>
    <th><%= column.human_name %></th>
  <% end %>
  </tr>

<% count = 0 %>
<% for todo in Todo.find(:all) %>
  <tr>
    <td><% count = count + 1 %><%= count %></td>
  <% for column in Todo.content_columns %>
    <td><%=h todo.send(column.name) %></td>
  <% end %>
    <td><%= link_to 'Show', :action => 'show', :id => todo %></td>
    <td><%= link_to 'Edit', :action => 'edit', :id => todo %></td>
    <td><%= link_if_authorized 'Destroy', { :action => 'destroy', :id => todo },
      :confirm => 'Are you sure?', :post => true %></td>
  </tr>
<% end %>
</table>

  1. 繰り返しの前に、count をゼロで初期化する。
  2. 一つ増やした後に、表示する。
一般的には一から数えるので、こうなる。また、ゼロで初期化している為、繰り返しの後に行数が残る。一で初期化して、後置で増やすと、一つずれる事になる。

また、一気に<= ++count > で表示とインクリメントをやろうとしたが、Rails が処理に失敗し、使えなかった。

一寸先は闇2007年10月31日 14時05分52秒

Ruby on Rails を試用してみて、感じる不思議な閉塞感。何かやろうとすると、たった一つの事をする方法を見つけるのに何時間もかかってしまうのだ。やりたいことと、やりたいことを成し遂げるための手順は、分かっているのだが、それを Rails でどの様に記述するのかが分からない。そのあたりが、Rails を使っていて感じることだ。一歩先に答えがあるのに、見つからない。

Ruby on Rails は当初から作業効率の良さで騒がれていた。また、この考え方は、自分の考え方ととても似ていて、とても直観的に出来る。また、痒いところに手が届くモデルだとも、よく目にした。むしろ、賛同する声は良く見かけても、批評は見たことがない。

一つの言語が、全ての人に適しているわけではない。また、個人的にデータベースや、Ruby に長けている方ではないことを差し置いても、たった一つの事をこなすのに時間がかかった事はない。

コンピュータ系の技術職の方々は、参考書などの書籍をリファレンスように買い置く人が多いと聞く。昔は、自身もその傾向があったが、ある時、その目的で買った本はほとんど目を通すこともないのに気が付いて、買い置くのを止めた。時折、あの本があったなと思い出して引っ張ってみると、既に古典になっているからだ。古くてあまり役に立たない。書籍を買い置きしなくなって、随分と経つが特に困ったことはない。本はなくとも、コンピュータ語は体得できる。ただ、今回のように、色々と試行錯誤にやたらと時間がかかる場合は、やはり何か欠けているのではと思ってしまう。秘密の如く、何処かの書籍にしか、こっそりと書かれていない、自分の知らない何かがあるのではないかと。

Rails を否定するつもりはない。事実、ActiveRecord など、実に面白い形で、SQL を発行する。あちこちで、この様な使い方をすると、この様に凄いことが出来るとも良く載っている。どうも、そこから一歩踏み出すのに大量の時間を費やしてしまう様だ。それこそ、一寸先は闇。Rails で感じる閉塞感だ。

TodoList: ユーザ認証のプラグイン2006年12月05日 11時10分25秒

TodoList を複数の人達で、使えるようにしたい。それには認証も必要になる。

以前に試した login_engine 前編後編user_engine の試用 を元に作業する。

yes | ruby script/plugin discover を実行して、プラグインを使えるようにする。その後、engines、login_engine、user_engine をインストールする。


% ruby script/plugin install engines
+ ./engines/CHANGELOG
+ ./engines/MIT-LICENSE
+ ./engines/README
+ ./engines/generators/engine/USAGE
+ ./engines/generators/engine/engine_generator.rb
+ ./engines/generators/engine/templates/README
+ ./engines/generators/engine/templates/init_engine.erb
+ ./engines/generators/engine/templates/install.erb
+ ./engines/generators/engine/templates/lib/engine.erb
+ ./engines/generators/engine/templates/licenses/GPL
+ ./engines/generators/engine/templates/licenses/LGPL
+ ./engines/generators/engine/templates/licenses/MIT
+ ./engines/generators/engine/templates/licenses/None
+ ./engines/generators/engine/templates/public/javascripts/engine.js
+ ./engines/generators/engine/templates/public/stylesheets/engine.css
+ ./engines/generators/engine/templates/tasks/engine.rake
+ ./engines/generators/engine/templates/test/test_helper.erb
+ ./engines/init.rb
+ ./engines/lib/bundles/require_resource.rb
+ ./engines/lib/bundles.rb
+ ./engines/lib/engines/action_mailer_extensions.rb
+ ./engines/lib/engines/action_view_extensions.rb
+ ./engines/lib/engines/active_record_extensions.rb
+ ./engines/lib/engines/dependencies_extensions.rb
+ ./engines/lib/engines/migration_extensions.rb
+ ./engines/lib/engines/routing_extensions.rb
+ ./engines/lib/engines/ruby_extensions.rb
+ ./engines/lib/engines/testing_extensions.rb
+ ./engines/lib/engines.rb
+ ./engines/tasks/deprecated_engines.rake
+ ./engines/tasks/engines.rake
+ ./engines/test/action_view_extensions_test.rb
+ ./engines/test/ruby_extensions_test.rb
% ruby script/plugin install login_engine
+ ./login_engine/CHANGELOG
+ ./login_engine/README
+ ./login_engine/app/controllers/user_controller.rb
+ ./login_engine/app/helpers/user_helper.rb
+ ./login_engine/app/models/user.rb
+ ./login_engine/app/models/user_notify.rb
+ ./login_engine/app/views/user/_edit.rhtml
+ ./login_engine/app/views/user/_password.rhtml
+ ./login_engine/app/views/user/change_password.rhtml
+ ./login_engine/app/views/user/edit.rhtml
+ ./login_engine/app/views/user/forgot_password.rhtml
+ ./login_engine/app/views/user/home.rhtml
+ ./login_engine/app/views/user/login.rhtml
+ ./login_engine/app/views/user/logout.rhtml
+ ./login_engine/app/views/user/signup.rhtml
+ ./login_engine/app/views/user_notify/change_password.rhtml
+ ./login_engine/app/views/user_notify/delete.rhtml
+ ./login_engine/app/views/user_notify/forgot_password.rhtml
+ ./login_engine/app/views/user_notify/pending_delete.rhtml
+ ./login_engine/app/views/user_notify/signup.rhtml
+ ./login_engine/db/migrate/001_initial_schema.rb
+ ./login_engine/install.rb
+ ./login_engine/lib/login_engine/authenticated_system.rb
+ ./login_engine/lib/login_engine/authenticated_user.rb
+ ./login_engine/lib/login_engine.rb
+ ./login_engine/public/stylesheets/login_engine.css
+ ./login_engine/test/fixtures/users.yml
+ ./login_engine/test/functional/user_controller_test.rb
+ ./login_engine/test/mocks/mail.rb
+ ./login_engine/test/mocks/time.rb
+ ./login_engine/test/test_helper.rb
+ ./login_engine/test/unit/user_test.rb
% ruby script/plugin install user_engine
+ ./user_engine/README
+ ./user_engine/app/controllers/permission_controller.rb
+ ./user_engine/app/controllers/role_controller.rb
+ ./user_engine/app/controllers/user_controller.rb
+ ./user_engine/app/helpers/permission_helper.rb
+ ./user_engine/app/helpers/role_helper.rb
+ ./user_engine/app/models/permission.rb
+ ./user_engine/app/models/role.rb
+ ./user_engine/app/models/user.rb
+ ./user_engine/app/views/permission/_form.rhtml
+ ./user_engine/app/views/permission/edit.rhtml
+ ./user_engine/app/views/permission/list.rhtml
+ ./user_engine/app/views/role/new.rhtml
+ ./user_engine/app/views/role/show.rhtml
+ ./user_engine/app/views/user/_roles.rhtml
+ ./user_engine/app/views/user/edit_user.rhtml
+ ./user_engine/app/views/user/list.rhtml
+ ./user_engine/app/views/user/new.rhtml
+ ./user_engine/app/views/user/show.rhtml
+ ./user_engine/db/migrate/001_initial_schema.rb
+ ./user_engine/db/migrate/002_added_omnipotent_flag_to_roles.rb
+ ./user_engine/db/migrate/003_added_system_roles.rb
+ ./user_engine/db/schema.rb
+ ./user_engine/init_engine.rb
+ ./user_engine/lib/user_engine/authorized_system.rb
+ ./user_engine/lib/user_engine/authorized_user.rb
+ ./user_engine/lib/user_engine.rb
+ ./user_engine/public/javascripts/user_engine.js
+ ./user_engine/public/stylesheets/user_engine.css
+ ./user_engine/tasks/user_engine.rake
+ ./user_engine/test/fixtures/permissions.yml
+ ./user_engine/test/fixtures/permissions_roles.yml
+ ./user_engine/test/fixtures/roles.yml
+ ./user_engine/test/fixtures/users.yml
+ ./user_engine/test/fixtures/users_roles.yml
+ ./user_engine/test/functional/permission_controller_test.rb
+ ./user_engine/test/functional/role_controller_test.rb
+ ./user_engine/test/functional/user_controller_test.rb
+ ./user_engine/test/test_helper.rb
+ ./user_engine/test/unit/permission_test.rb
+ ./user_engine/test/unit/role_test.rb
+ ./user_engine/test/unit/user_test.rb

とても出力が長くなってしまった。しかし、ある程度、rails で試行錯誤してみた後だと、これらのファイル群が何をするのかも、微妙に見当が付く。そう言った意味でも、今回はここに全て写してみた。

前回次回

TodoList: rails で初期化2006年12月04日 05時41分36秒

TodoList をやり直す事にした。コマンドラインと、その出力を極力写したいと思っている。色々と実験していたが、何をどう変更したのかを忘れてしまう事がある。また、試行錯誤用に、表示や扱うデータを最小限にするのにも TodoList は向いている。

先ずは、rails の一歩。


% rails Todo
      create  
      create  app/controllers
      create  app/helpers
      create  app/models
      create  app/views/layouts
      create  config/environments
      create  components
      create  db
...
      create  public/javascripts/application.js
      create  doc/README_FOR_APP
      create  log/server.log
      create  log/production.log
      create  log/development.log
      create  log/test.log
% cd Todo

database.yml を編集する。development のデータベースを指定する。


% vi config/database.yml
development:
  adapter: mysql
  database: todos
  username: root
  password:
  host: localhost

モデルとコントローラを生成する。


% ruby script/generate model Todo
      exists  app/models/
      exists  test/unit/
      exists  test/fixtures/
      create  app/models/todo.rb
      create  test/unit/todo_test.rb
      create  test/fixtures/todos.yml
      create  db/migrate
      create  db/migrate/001_create_todos.rb
% ruby script/generate controller todo
      exists  app/controllers/
      exists  app/helpers/
      create  app/views/todo
      exists  test/functional/
      create  app/controllers/todo_controller.rb
      create  test/functional/todo_controller_test.rb
      create  app/helpers/todo_helper.rb

model :todo をコントローラの中に書くと説明があったが、無くても動くみたいだ。scaffold を追加する。


% vi app/controllers/todo_controller.rb
class TodoController < ApplicationController
  scaffold :todo
end

見栄えはよくないが、一応 TodoList の基本は完成した。


% script/server
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options

を実行して WEBrick を起動する。http://localhost:3000/todo にブラウザでアクセスできる。

前回次回

Rails: user_engine の試用2006年11月14日 11時33分39秒

user_engine を設定する上での覚書。user_engine を使うためのプラグインをインストール。

% rails test
...
% cd test
% ruby script/plugin install engines
...
+ ./engines/tasks/deprecated_engines.rake
+ ./engines/tasks/engines.rake
+ ./engines/test/action_view_extensions_test.rb
+ ./engines/test/ruby_extensions_test.rb
% ruby script/plugin install login_engine
...
+ ./login_engine/test/mocks/mail.rb
+ ./login_engine/test/mocks/time.rb
+ ./login_engine/test/test_helper.rb
+ ./login_engine/test/unit/user_test.rb
% ruby script/plugin install user_engine
...
+ ./user_engine/README
+ ./user_engine/app/controllers/permission_controller.rb
+ ./user_engine/app/controllers/role_controller.rb

user_engine を使うための設定。いくつかのファイルは編集が必要。メールでの通知は一時停止した。config/environment.rb は末尾に以下を追加。


% cat >> config/environment.rb
module LoginEngine
  config :salt, "your-salt-here"
  config :user_email_notification, false
end

Engines.start :login
Engines.start :user

更にいくつかの変更。app/controllers/application.rb と app/helpers/application_helper.rb はほとんど空だった。


% cat > app/controllers/application.rb
# Filters added to this controller will be run for all controllers in the applic
ation.
# Likewise, all the methods added will be available for all controllers.

require 'login_engine'

class ApplicationController < ActionController::Base
  include LoginEngine
  include userEngine
  helper :user
  model :user
  #before_filter :login_required
  before_filter :authorize_action
end
^D
% cat > app/helpers/application_helper.rb
# Methods added to this helper will be available to all templates in the applica
tion.
module ApplicationHelper
  include LoginEngine
  include UserEngine
end
^D

config/environment.rb に管理者の設定を追加。


% vi config/environment.rb
module UserEngine
   config :admin_login, "the login name for your administrator user"
   config :admin_email, "an email address for your administrator"
   config :admin_password, "your initial admin password"
end

データベースを作成。ここでテーブルに必要事項が追加される。デフォルトでは users で、テーブルは空でなければいけないそうだ。


% rake db:migrate:engines:user_engine
(in /export/home/uyota/test)
Migrating engine 'user_engine'
== InitialSchema: migrating ===================================================
-- create_table("permissions", {:force=>true})
   -> 1.0751s
-- create_table("permissions_roles", {:force=>true, :id=>false})
   -> 0.0948s
-- create_table("users_roles", {:force=>true, :id=>false})
   -> 0.0210s
-- create_table("roles", {:force=>true})
   -> 0.0723s
== InitialSchema: migrated (1.2719s) ==========================================

== AddedOmnipotentFlagToRoles: migrating ======================================
-- add_column(:roles, :omnipotent, :boolean, {:null=>false, :default=>false})
   -> 0.9985s
Couldn't find the admin role.
If this is not a migration or test, your authorization data may be corrupt.
== AddedOmnipotentFlagToRoles: migrated (1.1167s) =============================

== AddedSystemRoles: migrating ================================================
-- add_column(:roles, :system_role, :boolean, {:null=>false, :default=>false})
   -> 0.0440s
== AddedSystemRoles: migrated (0.0519s) =======================================
% rake bootstrap
(in /export/home/uyota/test)
Creating guest role 'Guest'
Creating admin role 'Admin'
Creating user role 'User'
Creating admin user 'uyota'

script/server で起動し http://localhost:3000/user/login にアクセス。成功していれば、ログイン画面が出てくる。

前回次回

Rails: LoginEngine の続き2006年10月02日 09時08分18秒

Rails で LoginEngine を用いての認証からの続き。

使っているのは rubygem-rails-1.1.6 なので、rake は以下の形式。


% rake db:migrate:engines ENGINE=login
(in /export/home/uyota/rails/login)
Migrating engine 'login_engine'
== InitialSchema: migrating ===================================================
-- create_table("users", {:force=>true})
   -> 0.1808s
== InitialSchema: migrated (0.1842s) ==========================================

config/environment.rb に Engines.start :login を書くのを忘れたら「Couldn't find an engine called 'login 」とエラーが出た。このエラーでは、何が原因なのか全然見当がつかなかった。最初から全て設定を見直して、間違いに気が付いた。

どこに users というデータベースが出来るかを見つけるのに少々時間が掛かった。データベースも個人的には、ほとんど使わないので。


mysql> use db;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+--------------------+
| Tables_in_seven    |
+--------------------+
| engine_schema_info |
| schema_info        |
| users              |
+--------------------+
3 rows in set (0.03 sec)

mysql> describe users;
+-----------------+-------------+------+-----+---------+----------------+
| Field           | Type        | Null | Key | Default | Extra          |
+-----------------+-------------+------+-----+---------+----------------+
| id              | int(11)     | NO   | PRI | NULL    | auto_increment |
| login           | varchar(80) | NO   |     |         |                |
| salted_password | varchar(40) | NO   |     |         |                |
| email           | varchar(60) | NO   |     |         |                |
| firstname       | varchar(40) | YES  |     | NULL    |                |
| lastname        | varchar(40) | YES  |     | NULL    |                |
| salt            | varchar(40) | NO   |     |         |                |
| verified        | int(11)     | YES  |     | 0       |                |
| role            | varchar(40) | YES  |     | NULL    |                |
| security_token  | varchar(40) | YES  |     | NULL    |                |
| token_expiry    | datetime    | YES  |     | NULL    |                |
| created_at      | datetime    | YES  |     | NULL    |                |
| updated_at      | datetime    | YES  |     | NULL    |                |
| logged_in_at    | datetime    | YES  |     | NULL    |                |
| deleted         | int(11)     | YES  |     | 0       |                |
| delete_after    | datetime    | YES  |     | NULL    |                |
+-----------------+-------------+------+-----+---------+----------------+
16 rows in set (0.06 sec)

さて、サーバを起動する。


% ruby scrip/server
=> Booting WEBrick...
=> Rails application started on http://0.0.0.0:3000
=> Ctrl-C to shutdown server; call with --help for options

そして、http://localhost:3000/user/login に接続すればよい。http://localhost:3000/usr/login に接続して、全然繋がらずに悩んだのはもちろん秘密だ。

簡単だが、以下のような画面が出た。


Please Login


Login ID: 	
Password: 	
Register for an account | Forgot my password

LoginEngineを使ってみるが一番、一般的なようだ。 rails1.1.2覚書:login_engine には画面のスクリーンショットなどもあって分かりやすい。

前回次回

Rails: LoginEngine を用いての認証2006年09月30日 12時09分25秒

LoginEngineを使ってみるを参考に設定。なお、以下のものは個人的な作業記録と防備録。

定型的な下準備。


% rails login
...
      create  log/development.log
      create  log/test.log
% cd login

plugin というスクリプトがある。既に実行可能にされているので ruby の指定は必須ではない。また、プラグインは、vendor/plugins というディレクトリに配置されるようだ。

% ruby script/plugin
Unknown command:
Usage: plugin [OPTIONS] command
Rails plugin manager.
...
% ls vendor/plugins/
%

始めて discover をやるとソースの配付元の指定するのに大量のプロンプトが出るので yes で省略。


% yes | ruby script/plugin discover
...
http://www.nickm.org/svn/repos/rails/plugins/? [Y/n]
Add http://cubesix.net/rails/plugins/? [Y/n]
Add http://svn.rtra.in/public/plugins/? [Y/n]
Add http://svn.superruby.com/svn/plugins/? [Y/n]

login_engine のプラグインをインストール。依存関係も解決してくれるようだ。


% ruby script/plugin install login_engine
+ ./login_engine/CHANGELOG
+ ./login_engine/README
+ ./login_engine/app/controllers/user_controller.rb
...
+ ./engines/lib/engines.rb
+ ./engines/tasks/deprecated_engines.rake
+ ./engines/tasks/engines.rake
+ ./engines/test/action_view_extensions_test.rb
+ ./engines/test/ruby_extensions_test.rb
% ruby script/plugin install engines
already installed: engines (http://svn.rails-engines.org/plugins/engines/).  pass --force to reinstall
% ls vendor/plugins/
engines         login_engine

LoginEngine の設定。

config/environment.rb の末尾に以下のような行を追加します。

module LoginEngine
  config :salt, "my-salt"
  config :email_from, "webmaster@example.com"
  config :admin_email, "admin@example.com"
  config :app_name, "MyApp"
end

Engines.start :login

... その他の config パラメータの意味については vendor/plugin/login_engine/lib/login_engine.rb を見てください。
とあった。example.com のまま試しても問題ないのだろうか。一応、自分の物を使おうと思う。

Ruby on Rails は初めてで、普段は Ruby を使う機会もない。あちこちの説明を読みながら、メモを取りながら実験するので時間が掛かる。十五分で blog を完成とはいかないようだ。

前回次回