読者です 読者をやめる 読者になる 読者になる
スポンサーリンク

Ruby on Rails 1.2 での開発のはまり所

ruby DB Ruby on Rails 動かないコード

Ruby on Rails 1.2.6 でWebアプリケーションを開発する際,はまった事のまとめ。

既に世の中では Rails 3.x以降のバージョンが出ているが,
この情報はレガシーRailsアプリケーションの保守・メンテに役立つだろう。


※↑ もくじジェネレータ で自動生成


(1)プラグイン関連


(1−1)acts_as_paranoid で論理削除しても,サーバ時刻がずれると未削除とみなされてしまう

最も冷や汗をかいた点はこれ。


deleted_atに記録される値は,「削除フラグ」の働きをしてくれない。

削除「予定」時刻としてふるまう。

ActiveRecord 削除フラグで削除するプラグイン
http://i-am.web777.net/2007/01/active...
削除予約といった使い方もできます。

マイグレーションでマスタデータを編集している場合,マイグレーション内でマスタデータを削除する場合もある。

つまり,論理削除済みのマスタデータを含むようなアプリをリリースしようとしていた。


ここで,もしマイグレーション実行時点で利用サーバの時刻が狂っていたり不確定だったりして,

あとからサーバの時刻がdeleted_atの時刻より前に巻き戻された場合,削除済みのデータは生き返ってしまう

(VMの仮想マシン上での作業だったりすると,サーバ時刻の大幅なずれはよくある事だ。)


結局,それらのデータは論理削除ではなく,物理削除しておくことになった。


(1−2)acts_as_paranoidで削除済みデータを検索するためには,パッチ適用が必要

find時のwith_deletedオプションが正常に動作しない件。

パッチ適用で解決。

find(:all, :with_deleted => true) broken
http://rubyforge.org/tracker/index.ph...


【ruby on rails】acts_as_paranoid
http://gdgdlog.net/log/show/102

  • count_with_deletedにも書き換えが必要

(1−3)authenticated systemでユーザ管理している時,モデルからユーザ情報を参照したくなった

ロガー(モデル)を自前で実装している時,ロギング情報内にユーザ名を含めたい。

でもユーザ情報はセッション(コントローラ)にある。


要は,M層からC層を参照したかった。

Is there a way to access #current_user from inside another model?
http://railsforum.com/viewtopic.php?p...

原理的に無理なので,発想を転換。


Controllerのapplication.rb内に,before_filterで,

特定のモデルのスタティック属性にユーザ情報を保管してやる。


そうすれば,全モデルからユーザ情報を参照できる事になる。


application.rb内:

  before_filter :setup_logger_info

  def setup_logger_info
    if logged_in?
      # ロガーにユーザ名を渡す
      MyLogger.current_user_id = self.current_user.id
      
      # ロガーにアクセス元IPアドレスを渡す
      MyLogger.ip_addr = request.remote_ip
    end
  end


my_logger.rb内:

  # クラス変数のアクセサを定義
  cattr_accessor :current_user_id, :ip_addr


  def self.log
    self.new(
      :current_user_id => current_user_id,
      :ip_addr         => ip_addr
    ).save
  end

ActiveSupportのcattr_accessor
http://blogs.wankuma.com/kazuki/archi...


(1−4)acts_as_paranoid利用時にscaffoldを作ったら,情報の新規登録ができない

scaffoldが生成するページ上では,DateTime系のカラムにはDateTimeの値を入れるようになっている。


そうすると,deleted_atにnilを入れることができない。

そうすると,,,scaffold経由での「情報の新規登録」ができない。必ず削除フラグが立ってしまうからだ。


scaffolding.rbを編集して,createとかupdateではdeleted_atにnilが自動代入されるように変更。

  def create#{suffix}
    @#{singular_name} = #{class_name}.new(params[:#{singular_name}])

    # この行を追加:強制的にnilを代入
    @#{singular_name}.deleted_at = nil

scaffolding.rbのソースコード
http://svn.topfunky.com/podcast/vendo...

(1−5)acts_as_authenticatedがもはや使えなくなっている

このプラグインは,もはや入手できなくなった。

続編の「Restful_authentication」も,1.x系のRailsを使っている限りは無効。

# ruby script/plugin install acts_as_authenticated

./script/../config/boot.rb:20:Warning: Gem::SourceIndex#search support for String patterns is deprecated, use #find_name
Plugin not found: ["acts_as_authenticated"]


# ruby script/plugin install http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated
./script/../config/boot.rb:20:Warning: Gem::SourceIndex#search support for String patterns is deprecated, use #find_name
already installed: acts_as_authenticated (http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated).  pass --force to reinstall


# ruby script/generate authenticated admin auth

→無効

Railsのバージョンを上げるか,認証プラグインを自作するか。

Railsで作るOpenID対応アプリケーション実践(前編)
http://gihyo.jp/dev/feature/01/openid...

  • Rails 1.x系の頃にはacts_as_authenticatedプラグインがよく使われていましたが,Rails 2.xではRestful Authenticationプラグインが使われている


(2)マイグレーション関連


(2−1)マイグレーション実行中に,モデルオブジェクトが古いテーブル構造を保ってしまう

以下のような流れでマイグレーションを実行すると,スキーマ変更後のデータ投入がうまくいかない。

  1. データ投入
  2. テーブル構造を変化
  3. データ投入

古いカラムへの書き込みはできるのだが,
変化したテーブル構造でのデータ投入ができない。

どうやら,Active Recordが,古いテーブル構造をキャッシュしてしまっているような感じ。

仕方ないのでconnection.executeで直接SQLを実行して解決。


(2−2)マイグレーション中で,AR経由でのNOT NULL属性の変更はできない

カラムのNOT NULL属性を除去したかったのだが,Active Recordのカラム操作メソッドでは不可能だった。

change_column で NULL の許可 / 不許可の変更ができない
http://d.hatena.ne.jp/hajimehoshi/200...

  • PostgreSQL の場合はそのようです。 options で :null を指定しても無視
  • 一度 :null => false に設定した場合、二度と :null => true にはできない

これもconnection.executeで直接ALTER文を発行して対処。


(3)テスト関連


(3−1)Test::Unitの実行は,静的なテーブル構造を前提としている。

動的にテーブル構造が変わる場合,rakeタスクからの単体テストが不可能になる。

 rake test:units

による単体テスト実行は,テストメソッドごとのスキーマ初期化を行なわないため。


しょうがないので,下記のような自作ツールに頼るはめに。

Ruby on Railsのfixturesを,Excelから生成しよう (テストデータを管理しやすくするためのマクロ)
http://language-and-engineering.hatenablog.jp/entry/20091215/p1


Railsのモデルを,簡単に単体テストしよう (フィクスチャを使わない,気軽なテストバッチ)
http://language-and-engineering.hatenablog.jp/entry/20101021/p1

しかし一番よいのは,動的なスキーマ変更を行なわないことだ。DB設計の問題。


チーム内に旧世代のエンジニア(例えば汎用機使い)がいる場合,彼らはORMを知らず,自動単体テストの習慣もない。

それで,「なぜ,スキーマ構造を静的にすべきなのか」を彼らに理解してもらうために,かなり労力が必要になったりする。


(3−2)テストデータのfixtureが分割できない。

1モデル=1テスト=1fixture

という,なんとも扱いづらい「レール」が準備されてしまっている。

これではテストしづらい。

[rails]テストデータを分割したい
http://www.pen-chan.jp/pen-chan/20080...


レールに乗っていゆくには - I am Cruby!
http://d.hatena.ne.jp/authorNari/2009...

  • 「fixturesはもうこりごりです」

前項と同じように,自作ツールに頼って解決。


(4)デプロイ関連


(4−1)Webrickが初回起動できない。

environment.rbのRAILS_GEM_VERSION定義部が未評価のままだったりする。

Noob Install Failed 0.0.0.0:3000
http://www.ruby-forum.com/topic/200929

  • <%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING%>' unless defined? RAILS_GEM_VERSION
  • looks like the template for environment.rb was just copied verbatim, without being evaluated.

適当に書き換えて解決。

  RAILS_GEM_VERSION = '1.3.1' unless defined? RAILS_GEM_VERSION


(4−2)Webrickがすぐに落ちる

  ruby script/server

とやれば,すぐにWEBrickが立ち上がってRailsアプリを動かせる。手軽なのはいい。


しかし,すぐに固まる。

リクエストがタイムアウトになり,動作がフリーズする。


Seleniumで回帰テストができないぐらいひどい。

テストの途中で必ずサーバが固まる。テストシナリオの最後まで到達できない。

Mongrel: WEBrickよりだいぶ早いhttpd
https://www.codeblog.org/blog/eto/200...

  • WEBrickは動作実験に激しく役に立つ。
  • 実際に実用する段階では、そのままWEBrickを使い続けるという例はあまり無い
  • WEBrickは、とっても遅い。それはある意味しかたがないことで、全てをRubyで処理しているから


Redmineが異常に遅い
http://groups.google.com/group/redmin...

  • WEBRickのポータビリティは素敵なのですが、連続稼動させると固まったりすることがあったので、cronで夜間にstop/startをしたりして対応しました
  • 私も webrick で動かしていましたが、遅かったためmongrel に移行、その後さらに passenger にしました。


WEBrick、10の秘密
http://slashdot.jp/~keita/journal/69980

  • あああ、なんでWEBrickがこんなに遅いのか分かった。timeoutライブラリがThread.newを繰り返しているからだったのサ。


動作サーバをApache + mod_railsに入れ替えることで対処。

CentOS 5.6上で Apache+Passenger+Ruby on Rails 1.2 を動作させる手順 (仮想マシン上に,レガシーRailsの実運用環境を構築)
http://language-and-engineering.hatenablog.jp/entry/20110814/p1


(4−3)Windows環境でのデプロイ方法が見つからない

機関車本第2版,p578:

***Capistranoによる繰り返し可能な配置

これまでRailsが配置されてきたのは,ほとんどすべてUnixベースのシステムです。

困難を顧みずにWindowsに配置するのであれば,自力でやるしかありません。お勧めはしませんが。

Windowsへのデプロイは,事実上非推奨ということだ。

チェック必須!Rails新時代の到来か?「Passenger(mod_rails for Apache)」
http://www.moongift.jp/r/2008/04/pass...

  • ちなみにWindowsでは動作しないとのこと。その点予めご了承いただきたい。


Deploying Rails on Windows servers
http://weblog.rubyonrails.org/2006/5/...

  • For those working in a Windows environment, deployment can get considerably harder.

仕方ないので,Linux上のデプロイのまま。

よい方法が見つかればよいのだが。


オライリーの書籍「Railsデプロイ」の7章,「Windows環境でのデプロイ」を熟読する必要があるだろう。

そこからの引用:

***7.11.まとめ

Windows環境でのデプロイについて必要以上に恐れる必要はありません。
・・・
より高い処理性能が求められるようになったら,その時にはLinux環境へのデプロイも考慮しましょう。


※追記(2011年9月)

PaaSでデプロイするという選択肢が存在するようになった。

HerokuでWebアプリ開発を始めるなら知っておきたいこと (3)デプロイが簡単
http://d.hatena.ne.jp/ruedap/20110501...

  • PHPのデプロイの簡単さと比較して、Rubyはデプロイが難しいという指摘に対して、まつもとさんはPHP並みに簡単にデプロイできるものとして「Heroku」を挙げています
  • Herokuで採用されているGitを利用したデプロイ方法というのは、一度ちゃんと覚えてしまった時のそれ以降からの効率の良さはものすごい


(4−4)Passengerでデプロイすると,うっかりSingletonが使えない。

Passengerは,仕組み上,1つのSpawnサーバが全リクエストを受け付ける窓口になる。

Spawnサーバは,リクエストを複数の子プロセスに振り分ける。


という事は,複数のリクエスト間でプロセスが異なる,という事になる。(Webアプリならごく当然の話だが)

プロセスが異なるという事は,当然,メモリ上でのアドレス空間も異なる。

という事は,「ソースコード」は共有できても,「オブジェクト」は共有できない。

複数のリクエスト間で1オブジェクトを共有するようなことはできないのだ。

ユーザをまたぐようなSingletonパターンは存在しえない。

Passenger architectural overview
http://www.modrails.com/documentation...

  • mod_rails,passengerの仕組み図解。1個のspawnサーバが窓口。複数のワーカプロセスにリクエスト振分け。各ワーカはmod_railsプラグイン付きhttpd。
  • 全体でソースコードキャッシュを共有。プロセス間ではメモリのアドレス空間が別


mod_passenger, threads and singleton classes
http://stackoverflow.com/questions/22...

  • mod_railsはアプリケーションを複数spawnし,それらのインスタンスは別々のプロセスなので,singletonクラスは共有されない。

(4−5)Webrickに接続できない

Webrickが起動しているのに,外部マシンからブラウザ経由でアクセスできない。

ブラウザ上には「接続がタイムアウトしました」と出る。

しかし,Webrickが動いているマシン上では,ブラウザ上からRailsアプリを閲覧できる。


これは,Webrickの動作しているサーバ上でファイアウォールが邪魔しており,3000番ポートへの通信をブロックしている場合に起こる。

Linuxであれば,iptablesの設定を変更すればOK。

iptables -I INPUT -p tcp -m tcp --dport 3000 --syn -j ACCEPT


問題の切り分けには,下記のコマンドが役立つだろう。

サーバのあるマシン上で以下のコマンドを実行すれば,ちゃんとHTMLが取得できるはず。

wget -q -O - http://localhost:3000/

Railsセットアップ(CentOS on VMWare)
http://d.hatena.ne.jp/qnzm/20090118

  • アクセスしようとしてもタイムアウトになる。iptablesサービス止めたら繋がったので、パケット破棄されているのだと判断。
  • iptablesいじってポート3000を通す


Linux のファイアウォールの設定方法
http://www.astec-x.com/FAQ/iptables.html


(5)環境関連


(5−1)いきなりディスク容量がいっぱいになる

development.logが肥大化するため。

error levelを下げないと,実行した全SQLが記録されてしまう。

Seleniumとか自動テストでガシガシSQLを吐いていた場合は,どんどんログがたまる。


environment.rb中でlog_level を:error あたりにして解決。

HowtoConfigureLogging
http://oldwiki.rubyonrails.org/rails/...
The available log levels are: :debug, :info, :warn, :error, :fatal.


(5−2)RDocをバージョン管理できない。

rake doc:reapp でRDOCを再生成すると,SVNの隠しファイルも含め丸ごと削除されてしまうので。

仕方ないので,docフォルダ内はバージョン管理対象から外した。

RailsアプリのRDoc関連コマンド
http://d.hatena.ne.jp/willnet/2008082...

  • appで新規作成、reappで更新、clobber_appで削除

(5−3)RubyGemsでRails1.2をインストールできない

gemコマンド発行時に,適切にsource URLを指定しないと,Rails1.2を取得できない。

gem install --source http://gems.rubyforge.org rails -v 1.2.6 -y --include-dependencies

リポジトリから削除されたRails 1.2.6をインストールする。
http://chiki2-cq.seesaa.net/article/9...


(6)モデル関連


(6−1)スレッドを分けても,トランザクションは分かれない

DBへの同時アクセスに対処するための自動テストスクリプトを書こうとするとぶつかる壁。

対処法は以下。

Rubyの動かないコード (中級編) Ruby on Railsで,スレッドごとにトランザクションを分離したい
http://language-and-engineering.hatenablog.jp/entry/20101229/p1


(6−2)新規行INSERT時に,モデルにidをセットしてしまうとsaveできない

id属性をモデルのインスタンスに手動でセットしてしまうと,save()に失敗する。


機関車本第2版の273ページ:

新しい行の作成

新しい行のid属性を設定していないことに注目してください。
自動的に一意な値が作成されます。


(6−3)モデルのインスタンスから,所属テーブル名を取得したい

できない。

動的にテーブルが連番で生成され,それらのテーブル上のデータを1つのモデルで授受しているような場合,困る。

after_findでテーブル名をプロパティに持たせるとかして対処。


しかし一番よいのは,前述の通り,動的なテーブル変更を許可しない設計にすることだ。

(6−4)モデルにnewでidをセットできない

6−2とかぶるが・・・

モデルにnewでidをセットしようとしても,それは無視される。

a = AModel.new( :id => 1 )

a.id # => nil

ハッシュで渡す代わりに

a.id = 1

a.id # => 1

のように直接代入すればOKだった。

(6−5):orderを:order_byと書き間違える

findでソートキーを指定する際,いつも書き間違えて,assert validate keyに引っかかる。

(6−6)テーブルがないとモデルはnewできない

動的なテーブル生成が発生したり,単なる便利オブジェクトをActiveRecordとして実装したりする場合。

クラス名に対応するテーブルが存在しないと,インスタンス化できない。


しょうがないので

  • ActiveRecord::Baseを継承しない単なるクラスにする
  • クラスメソッドのみとして,インスタンス化はあきらめる
  • set_table_nameメソッドで,特定の別のテーブルを利用するようにクラスに命令する
  • モデル名に対応するようなダミーテーブルを実際に作る(そしてすぐに消す)

などで対処。

(6−7)ActiveRecordの便利さを,Rubyの仕様と勘違いしてしまう

ActiveRecord::Baseを継承したクラスは,柿のような便利な性質がある・・・。

  • new時に,ハッシュで属性を初期化できる。

で,ついつい,modelフォルダ内にActiveRecordを継承していない普通のオブジェクトがあるときに,これと同じ性質があるものと勘違いしてしまう。


newにハッシュを渡しても何も起こってくれない。

ちゃんとattr_accessorで属性名も定義しているのに,変だな?


と思ったら,これはRubyのnewの仕様ではなく,ActiveRecordのnewの仕様なのだった。

これに気づくまでに時間を取られた・・・。

(6−8)idは飛び飛びになりうる

Railsに限らない,一般的な話なのだが・・・。

この誤解からコーディングをミスり,結構なトラブルになった。


削除済みのデータが存在しないとして,

以下の2つは,同じとは限らない。

# (1)
MyModel.find_all.length # 全件の件数


# (2)
MyModel.find(
  :all,
  :order => "id ASC"
)[ -1 ].id # 最終レコードのid

idは,飛び飛びになりうるのだ。なので,(1)=(2)ではなくなる。

postgresのシーケンスについて
http://okwave.jp/qa/q27807.html

  • シーケンスによる自動採番で,番号が一部飛び飛びになる。>トランザクションを利用可能なDBはこの現象が起こる。

(6−9)モデル名が数字を含む場合,クラス名を間違えやすい

hoge1.rb というモデルクラスのファイルがあったら,クラス名は「Hoge1」。

決して「Hoge_1」ではない。


hoge_2011_fuga.rbというモデルクラスのファイルがあったら,クラス名は「Hoge_2011_Fuga」となる。


(6−10)セッションを経由してアクションをまたぐと,set_table_nameが無効化される。

セッションにモデルのインスタンスを格納して,メソッドをまたいだ後,

あとからデータを取り出そうとすると,set_table_nameの効果が消えている。

なので,save()しようとした時にテーブル定義が読み込めなくなり,空のINSERT文が発行され,保存に失敗する。

  def hoge1
    # テーブル名をセット
    MyModel.set_table_name "users"
    
    # インスタンス生成
    u = MyModel.new({
      :name => "太郎"
    })
    
    # インスタンスをセッションに格納
    session[ :test_user ] = u
    
    # リダイレクト
    redirect_to :action => "hoge2"
  end
  
  
  def hoge2
    # セッションからインスタンスを取り出す
    u = session[ :test_user ]
    
    # 保存しようとする
    u.save
  end


この時,save時に下記のようなエラーになる。

ActiveRecord::StatementInvalid in MyController#hoge2

PGError: ERROR: Syntax error at or near ')'

Line1: INSERT INTO my_models () VALUES() 
                   ^

INSERT INTO my_models () VALUES()


hoge2内で,set_table_nameの効力が切れているのだ。


この問題は,sessionのせいではない。

「メソッドをまたぐ」と発生する。

つまり,下記のコードは正常に動く。

  def hoge3
    # テーブル名をセット
    MyModel.set_table_name "users"
    
    # インスタンス生成
    u = MyModel.new({
      :name => "太郎"
    })
    
    # インスタンスをセッションに格納
    session[ :test_user ] = u
    
    # セッションからインスタンスを取り出す
    u = session[ :test_user ]
    
    # 保存
    u.save
  end

この場合,セッションを経由しても,エラーにはならない。

どうやら「アクションをまたぐと,set_table_nameの効力が切れる」らしい。

session内に保持されているインスタンスオブジェクトは,それ自体,所属テーブル情報を持っていないのだ。



解決策としては,

上記hoge2アクションの u.save の直前に,再度 set_table_nameしてやればよい。

そうすれば保存がうまくいく。


危なさそうなところでは,常に毎回,直前にset_table_nameしよう。

特に,インスタンスオブジェクトに対して send() メソッドを呼び出す場合にも,どうやらこれが必要になるようだ。



なお本件は,下記のエラーとは別物のようだ。

セッションから復元できないモデルオブジェクトに対処する
http://d.hatena.ne.jp/kusakari/200709...


(7)ビュー関連


(7−1)h()メソッドが,シングルクオートをエスケープしない。


下記はNG。

<input value='<%= h str %>'>

文字列strの中にシングルクオートが含まれていると,上記のHTMLは崩壊する。

Rails の便利メソッド h, html_escape は ERB:Util にあった
http://www.metareal.org/2007/06/30/ht...

    def html_escape(s)
      s.to_s.gsub(/&/, "&amp;").gsub(/\"/, """).gsub(/>/, "&gt;").gsub(/</, "&lt;")
    end

対策として,ビュー中のHTMLの属性値は,シングルクオートではなくダブルクオートで囲むことを規約とする。


(7−2)ビュー中では,case文の書き方に注意が必要

「case」と最初の「when」をつなげて書かないといけない。

Railsのビュー(erb)でのcase文は書き方を注意しないとシンタックスエラーになる。
http://blog.digital-squad.net/article...


(7−3)インスタンス変数の扱いが,まるでグローバル変数のように煩雑になる。

インスタンス変数とは,「@data」みたいに@がついた変数。

この変数は,コントローラからビューに「渡す」事ができる。


しかし,ビューがpartialテンプレートやヘルパーでガッチリと構造化されてゆくと,

このインスタンス変数は,まるで「グローバル変数」のように扱いづらくなってしまう。


なぜかというと,インスタンス変数は

  • コントローラ内のどのメソッドからでも,
  • どのビューテンプレートからでも,
  • どのヘルパメソッドからでも,

参照+更新ができてしまうからだ。


これでは,グローバル変数を使っているのと変わりない(という事態が起こりうる)。


インスタンス変数の利用はうまく「制限」しないと,

アプリケーションがスパゲッティコードになってしまう。


規約として,以下のような方針で対処する。

  • コントローラ上では,「変数をビューに渡す処理」を明確化して,インスタンス変数の記述に制限を設ける。
  • ビュー中では,partialテンプレートに渡すべき変数はrenderメソッド内に明示的に記述する。
  • ビューテンプレート中では,インスタンス変数の参照・更新を禁ずる。
  • ヘルパメソッド中では,インスタンス変数の更新を禁ずる。
  • ビューに表示すべき情報を無理してコントローラ側で用意しようとせず,可能ならヘルパ内などでDB参照して,インスタンス変数の出番を減らす。

(7−4)JavaScriptのコード中で h() したら,二重引用符が&quot;と表示されてしまった

見出しの通り。

JavaScriptのコードに対してRuby側から値を渡す時は,注意が必要。

h()ではなく,gsubのほうがいいかも。

(7−5)ActionMailerをユーザによってカスタマイズ可能にしてしまうと,セキュリティホールになる

ActionMailerでメール送信する際,メール本文はビューファイルである。

RailsでGMailを利用したメール送信 (ActionMailer + tlsmailの仕組みを理解しよう)
http://language-and-engineering.hatenablog.jp/entry/20091123/p1

そうすると,メール雛型の中ではERB記法が記述可能,という事になる。

で,アプリがカスタマイズ可能になっていくうちに,ユーザ操作によってこの雛型を自由に変えられるようにしてあげたくなる。


そうすると大変だ。ERB記法で,下記のような文字列がビューファイル中に埋め込まれてしまったら・・・

<%= `rm -rf /` %>

さいわいRailsがOS上でroot権限で実行されてはいないので,OS全体を破壊することはできない。

でも,htpd権限の及ぶ全ファイルが削除される。

少なくとも,該当するRailsアプリケーションは丸ごと削除される。

こんなセキュリティホールを埋め込んだらおおごとだ。


(8)コントローラ関連


(8−1)paramsという変数をコントローラ内のメソッド間で持ちまわれない

コントローラ内のメソッド間で「params」を持ちまわろうとすると,ある時急にnilになっていたりする。

paramsという名前の「変数」を持ちまわってはいけない。(本当は変数ではない)

paramsは,別名の変数に代入した上で別メソッドに渡す。


(8−2)アップロードされたファイルを,直接セッションに格納できない。

機関車本第2番の405ページ:

***Railsのセッション

セッションに格納できるオブジェクトの種類には,いくつか制限があります。
セッション内のオブジェクトはシリアライズされている必要があります。
したがって,例えばセッション内にI/Oオブジェクトなどを格納することはできません。

アップロードされたファイルはストリーム系のオブジェクトなので,直接,session内に格納することはできない。

なので,オブジェクトからファイル名とファイル内容を取り出し,そちらをsessionに格納すればよい。

    # 送信されてきたファイル
    file_obj = params[ "my_file" ]

    # ファイル名
    file_name = file_obj.original_filename

    # ファイル内容
    file_content = file_obj.read


(8−3)セッションが効かない。

Cookieにセッション情報が正確に格納されてくれない。

原因と対処法は下記。

http://blog.fairy-land.jp/2007/02/000...

  • session :session_key => '_<%= app_name %>_session_id'
  • 「app/controllers/application.rb」の「_アプリ名_session_id」を「_session_id」に書き換えてみると、セッションIDが変わらなくなった。

ERBの「半角イコール」が,Cookie格納値のパースに使われていた・・・。


(9)Rails本体関連


(9−1)クラスやモジュールが馬鹿でかくなる

1テーブル=1モデル=1コントローラ=1ヘルパー

という図式が,次第に保ちづらくなる。


対象法としては,クラスやモジュールを小分けのファイルに分割することになる。

moduleのメソッドをクラスメソッドとして追加したい - うなの日記
http://d.hatena.ne.jp/unageanu/200804...

  • moduleのメソッドを、クラスのクラスメソッドとして追加したい場合、Object#extendが利用できます。
  • includeの場合、クラスのインスタンスメソッドとして追加されます。


(9−2)「便利オブジェクト」の置き場がわからない

便利オブジェクトとは,例えば

  • ハッシュや配列を多用するのに疲れたときに作りたくなる便利クラス
  • DAOみたいなDBアクセスクラスをまとめる,親玉クラス

のこと。


modelフォルダ内には,「1テーブル=1モデルクラス」みたいな暗黙の了解があったので,

自由にクラスを増やしづらかった。


個人的にはサービス層をモデル層に混ぜて置いている。(が,できれば置き場を分割したい。)

Ruby on RailsのModel内に記述するメソッドの分類表
http://language-and-engineering.hatenablog.jp/entry/20100901/p1

(9−3)モデルやコントローラをフォルダ分けできない

できなくはないが,問題を起こしやすい。

Railsでフォルダ(モジュール)下に配置したモデルのロードに失敗することがある
http://www.rmake-labo.com/akasata/art...

  • Railsでクラス数が増えてくるとフォルダ分けしたくなることがあります。その場合、Javaのパッケージと同じように、モデルをフォルダ分けして、モジュール下に宣言してやることになります。
  • belongs_toとかhas_manyとかの関連を解決する際に、以下のようなエラーが出て手がつけられなくなることがあります。悪名高きActiveSupportの「XXX is not missing constant YYY」


Ruby on Rails でモデルを階層化する(サブディレクトリにモデルを入れる)方法
http://doruby.kbmj.com/footsteps/2010...

  • Sample::Categoryで呼び出した場合に問題が出る場合がありますので、::Sample::Categoryと先頭に「::」を付けて呼び出すことで問題が直る場合があります

やめておいて,1フォルダ内に多ファイルが置かれることに。

各クラスのネーミングを工夫することで,フォルダ内の俯瞰性を高めて対処。


(9−4)componentsフォルダの使い道が不明

そもそも非推奨なので不要。

partialビューとヘルパがあれば用が済んでしまう。

componentが非推奨になってたのね。
http://d.hatena.ne.jp/yotaropg/200704...
DEPRECATION WARNING


補足(1)

Railsと関係なく,純粋にRubyではまった点もある。

rubyで多次元配列確保 - octech
http://d.hatena.ne.jp/octech/20051014

  • 2次元配列の初期化で,初期化値として配列を渡したらそれは参照渡しだったので全行が同じ配列を指してしまった


Enumerable#map_with_index が欲しい……かも? - diary of a madman
http://d.hatena.ne.jp/macks/20060413/p1

  • map_with_indexが欲しい


Re: Windows版のirbでカットアンドペーストはできますか?
http://blade.nagaokaut.ac.jp/cgi-bin/...

  • タイトルバー左のアイコンをクリックして 編集-貼り付け


http://ruby-gnome2.sourceforge.jp/ja/...

  • WindowsとLinuxでファイルシステム上のファイル名の文字コードが違う
  • get_platform_filename(filename)を参照


http://jp.rubyist.net/magazine/?0006-...

  • reload : リロードします (キャッシュを破棄し、DB から値を取り込みます)。self を返します。
  • find_by_sqlで複雑なSQLでモデルを取得した場合,SELECTしなかった分のカラムの情報が欠けている。しかしid(主キー)カラムの値さえ含まれていれば,reloadメソッドによってモデル内の全カラムの情報を読み込める。
  • MyModel.find_by_sql( 〜 )[0].reload

あと,変数代入文の行末にうっかり半角カンマを書いてしまい,正常な値が代入されてくれなくて,原因が探し出せず困った。


また,主キーのインデックス不整合に対処するために,下記のSQLを頻繁に打った。

SELECT
  setval(
    'my_table_id_seq',
    (
      SELECT
        max(id)
      FROM
        my_table
    )
  )
;


補足(2)

他にも,1.x系のRailsでのはまりどころを紹介しているページがある。

Railsに関するハマったこと
http://d.hatena.ne.jp/yotena/20071201...


もし2.0系以降であれば,以下のような点にはまると思われる。

ActiveRecord findのキャッシュを無効
http://remio.net/word/?p=150
2.0以降でのみ問題になる点。uncachedブロックを使わないとクエリの実行結果が変わってくれない。