【whenever】はRubyとCronの橋渡しをしてくれるようなgemで、DSLで処理を記述しておくだけで crontab に書き込んでくれるという嬉しい代物です。bash経由で実行するようです。
Railsでももちろん使えて、rakeやrunnerなどを実行できますが、runnerの場合、うちの環境では bash のオプションに -l (--login) がついているとうまく動作しませんでした。
ので、手動で crontab -e して -l を削除しましたとさ、という報告でした。
2011年2月27日日曜日
2011年2月26日土曜日
Ruby on Rails と Thread
Rails2.2 の頃からスレッドセーフだとか何とか騒がれていましたがボクは知りません。知らないので、ネイティブスレッドではないRubyの Thread クラスを Rails で使っちゃおうという計画。delayed_jobではちょっと力不足だったのです。
やりたいことはただひとつ。
つまりですよ。
超問題になったのが「進捗情報の保存場所」。ぶっちゃけると session は使えませんでした。値が保持されるかと思ったら、スレッドの外と内でスコープが異なるようです。つまり 5 の時点で謎の値が返るわけです。
次に考えたのが app/models/ に新しくクラスを作成して、クラス変数に保持しておく、ということ。でもこれも駄目でした。development環境ではリクエストのたびにクラスがロードされるらしいので、予測不可能な段階でクラス変数が吹っ飛びます→NameError。production環境では動くかも知れないけど、さすがにそんな危ない橋は渡れません。
で、さんざん悩んだ結果、 lib/thread_manager.rb を作る、ということでした。
class ThreadManager
@@progress
def self.progress
@@progress
end
def self.progress= value
@@progress = value
end
end
クラス変数に対するアクセサメソッドはマクロで定義できないようです。そんなに使う機会が無いとはいえ、あればもうちょっとスマートにできましたかね。
ともあれ、これで何とか凌げそうです。苦労はしましたが、得るものも大きかった…とは言い辛いぞ!正直Scala+Liftに乗り換えたいぞ!でもそれって、「隣の芝は…」ってやつですかね…。
やりたいことはただひとつ。
- サーバで起こっている作業の途中経過をAjaxで取得したい。
つまりですよ。
- クライアントがファイルをアップロードする。
- サーバはレスポンスを返す。
- と同時に、サーバは新しく Thread.new して非同期に処理を開始し、処理をしながら進捗情報をどこかに保存しておく。
- クライアントは setInterval とかでサーバにリクエストを送信する。
- サーバは保存されている進捗情報を返す。
超問題になったのが「進捗情報の保存場所」。ぶっちゃけると session は使えませんでした。値が保持されるかと思ったら、スレッドの外と内でスコープが異なるようです。つまり 5 の時点で謎の値が返るわけです。
次に考えたのが app/models/ に新しくクラスを作成して、クラス変数に保持しておく、ということ。でもこれも駄目でした。development環境ではリクエストのたびにクラスがロードされるらしいので、予測不可能な段階でクラス変数が吹っ飛びます→NameError。production環境では動くかも知れないけど、さすがにそんな危ない橋は渡れません。
で、さんざん悩んだ結果、 lib/thread_manager.rb を作る、ということでした。
class ThreadManager
@@progress
def self.progress
@@progress
end
def self.progress= value
@@progress = value
end
end
クラス変数に対するアクセサメソッドはマクロで定義できないようです。そんなに使う機会が無いとはいえ、あればもうちょっとスマートにできましたかね。
ともあれ、これで何とか凌げそうです。苦労はしましたが、得るものも大きかった…とは言い辛いぞ!正直Scala+Liftに乗り換えたいぞ!でもそれって、「隣の芝は…」ってやつですかね…。
2011年2月24日木曜日
ActionDispatch::Session::CookieStore RuntimeError
になって困ってたんですよね。
【前回の記事】で「Flashからのセッション情報を gem で解決する」と記述しましたが、どうもこれがうまく動かないときがあるので、やっぱりRackミドルウェアとして実装することにしたのです。
まずは app/middleware/flash_session_cookie_middleware.rb:
require 'rack/utils'
class FlashSessionCookieMiddleware
def initialize app, session_key="_session_id"
@app = app
@session_key = session_key
end
def call(env)
if env["HTTP_USER_AGENT"] =~ /^(Adobe|Shockwave) Flash/
req = Rack::Request.new(env)
env["HTTP_COOKIE"] = [@session_key, req.params[@session_key]].
join("=").freeze unless req.params[@session_key].nil?
env["HTTP_ACCEPT"] = "#{req.params['_http_accept']}".
freeze unless req.params['_http_accept'].nil?
end
@app.call(env)
end
end
【前回の記事】で「Flashからのセッション情報を gem で解決する」と記述しましたが、どうもこれがうまく動かないときがあるので、やっぱりRackミドルウェアとして実装することにしたのです。
まずは app/middleware/flash_session_cookie_middleware.rb:
require 'rack/utils'
class FlashSessionCookieMiddleware
def initialize app, session_key="_session_id"
@app = app
@session_key = session_key
end
def call(env)
if env["HTTP_USER_AGENT"] =~ /^(Adobe|Shockwave) Flash/
req = Rack::Request.new(env)
env["HTTP_COOKIE"] = [@session_key, req.params[@session_key]].
join("=").freeze unless req.params[@session_key].nil?
env["HTTP_ACCEPT"] = "#{req.params['_http_accept']}".
freeze unless req.params['_http_accept'].nil?
end
@app.call(env)
end
end
次は config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/app/middleware)
最後に config/initializers/session_store.rb:
MyApplication::Application.config.session_store :active_record_store, :key => '_MyApplication_session'
Rails.application.config.middleware.insert_before(
ActionDispatch::Session::CookieStore,
FlashSessionCookieMiddleware,
Rails.application.config.session_options[:key]
)
MyApplication::Application.config.session_store :active_record_store, :key => '_MyApplication_session'
Rails.application.config.middleware.insert_before(
ActionDispatch::Session::CookieStore,
FlashSessionCookieMiddleware,
Rails.application.config.session_options[:key]
)
としたんですが、どうも「ActionDispatch::Session::CookieStoreなんてミドルウェアは知らないよ」と、タイトルにもあるとおりRuntimeErrorを吐かれてサーバが起動できない。散々調べた結果、収穫はゼロ。このエラーに悩まされているのは世界中でボク一人だったようで、なんとも。
で、試しに :active_record_store から :cookie_store に戻してみたんですよね。
MyApplication::Application.config.session_store :cookie_store, :key => '_MyApplication_session'
Rails.application.config.middleware.insert_before(
ActionDispatch::Session::CookieStore,
FlashSessionCookieMiddleware,
Rails.application.config.session_options[:key]
)
動きました。どうやらこいつが原因だったみたいですが…それでも :active_record_store で Uploadify なんかを使っている人は多いと思うんですよね。
新しくアプリケーションを作成したときにこのエラーが発現するのかどうかが気になるところですが、もうなんか疲れたのでこれまで、ということで。気が向いたら RailsTalk に投稿するかも知れませぬ。
2011年2月23日水曜日
Rails3とUploadify
「Railsでアップロードの進捗を表示させたいなぁ…」と思っていたところ、【Uploadify】というものが見付かりました。Flashこそ使えどjQueryのプラグインなので導入も簡単です。と思いきや手間取りまくったのでメモメモ(本当は jQuery-ui のプログレスバーを使うものがあったらいいなと思ったのだけれど、実装する勇気も知識もない)。
まず知らなかったのは、Flash経由でリクエストするとセッション情報が送られない、という事実。常識なのかも知れないけどFlashから遠い位置にいたんだから仕方ないよねっ!
で、作成しているアプリケーションは「ログインしてからでないとファイルをアップロードしちゃだめ」なので、当然セッション情報は無視できないのです。半日Google先生と相談してわかったのは、【flash_cookie_session】という、読んで字のごとくFlashからCookieとかセッション情報を送ってくれる gem があることでした。結論から言うと試したら動いたので、今回はこれを使って Uploadify を設置していきます。
Flashからのセッション情報送信をRackレベルで動作するミドルウェアとして手作業で実装するのが(検索した結果)メジャーのようでしたが、アプリケーションごとに書くのも「わざわざだなぁ」と思ったので、っていうかやってみたんだけど動かなかったので、今回は gem で解決です。
まず知らなかったのは、Flash経由でリクエストするとセッション情報が送られない、という事実。常識なのかも知れないけどFlashから遠い位置にいたんだから仕方ないよねっ!
で、作成しているアプリケーションは「ログインしてからでないとファイルをアップロードしちゃだめ」なので、当然セッション情報は無視できないのです。半日Google先生と相談してわかったのは、【flash_cookie_session】という、読んで字のごとくFlashからCookieとかセッション情報を送ってくれる gem があることでした。結論から言うと試したら動いたので、今回はこれを使って Uploadify を設置していきます。
Flashからのセッション情報送信をRackレベルで動作するミドルウェアとして手作業で実装するのが(検索した結果)メジャーのようでしたが、アプリケーションごとに書くのも「わざわざだなぁ」と思ったので、っていうかやってみたんだけど動かなかったので、今回は gem で解決です。
2011年2月20日日曜日
Rails 3.0.4 と csrf
Rails がセキュリティアップデートされてました。最新版は 3.0.4 です。詳しいことは「こちら」へ。
元記事にもある通り CSRF や SQL Injection など非常にヤバい単語が並べられているので則アップデートしたものの、Ajax系の動作がうまく動かなくなり(link_to :remote でGET以外のリクエストをAjax経由で送るとセッションがクリアされてしまう)、頭を抱えていたところ、はたと rails.js に原因があるのではないかと思い立って jquery-rails をアップデートしてみたところ、ビンゴだったので詳細を記録。
ちなみに CSRF はクロスサイトリクエストフォージェリ(Cross site request forgeries)の略記で、外部からの意図しないリクエストによってWebサービスの動作を改竄する攻撃手段のひとつ。らしいです、Wikipediaによると。
SQL Injection は、フォームの内容に SQL 文を埋め込むことでサーバのデータを改竄する、これも攻撃手段のひとつですね。
Railsでは app/views/layouts/application.html.erb の <head> タグ内に <%= csrf_meta_tag -%> を埋め込むことでワンタイムトークンを生成し、フォームの送信時に透過的にこれを hidden パラメータとして送信することで外部からの無作為なデータの送信を防ぐ(ことを自動的にやってくれる)わけで、トークンが合致しない場合は InvalidAuthenticityToken 例外が投げられていたのですが、今回は Java や Flash などのリクエストで同じことをやろうとしたときに、なぜだか忘れましたがとにかく例外を投げるだけではよくないらしいです。ので、セッションをクリアすることにしたんだそうで。
SQL Injection については、ActiveRecord の limit に脆弱性があったらしいです。「limitなんて数字だけじゃん」とついつい思ってしまいますが、攻撃する側も防御する側もよく気付くなぁ…と感心してしまいます。そんな悠長な事態じゃないんですけど。
で、肝心の対策。
ボクは jquery-rails を使っているので、
$ sudo gem update jquery-rails
$ rails generate jquery:install
こうして jquery.js, jquery.min.js, rails.js を上書きして事無きを得ました。 rails.js だけでいいのかなと思ったけれど、それでまた動かなかったら嫌なので jquery ライブラリも上書きしてしまいますた。多分バージョンが違うだけだと思うけど(「だけ」ってレベルじゃねーですけど)、jQueryで本格的に開発してる人にはオススメしません。あでも、jsUnitとか使ってるのかしら。いいのかしら。
そんなわけでした。
--
元記事にもある通り CSRF や SQL Injection など非常にヤバい単語が並べられているので則アップデートしたものの、Ajax系の動作がうまく動かなくなり(link_to :remote でGET以外のリクエストをAjax経由で送るとセッションがクリアされてしまう)、頭を抱えていたところ、はたと rails.js に原因があるのではないかと思い立って jquery-rails をアップデートしてみたところ、ビンゴだったので詳細を記録。
ちなみに CSRF はクロスサイトリクエストフォージェリ(Cross site request forgeries)の略記で、外部からの意図しないリクエストによってWebサービスの動作を改竄する攻撃手段のひとつ。らしいです、Wikipediaによると。
SQL Injection は、フォームの内容に SQL 文を埋め込むことでサーバのデータを改竄する、これも攻撃手段のひとつですね。
Railsでは app/views/layouts/application.html.erb の <head> タグ内に <%= csrf_meta_tag -%> を埋め込むことでワンタイムトークンを生成し、フォームの送信時に透過的にこれを hidden パラメータとして送信することで外部からの無作為なデータの送信を防ぐ(ことを自動的にやってくれる)わけで、トークンが合致しない場合は InvalidAuthenticityToken 例外が投げられていたのですが、今回は Java や Flash などのリクエストで同じことをやろうとしたときに、なぜだか忘れましたがとにかく例外を投げるだけではよくないらしいです。ので、セッションをクリアすることにしたんだそうで。
SQL Injection については、ActiveRecord の limit に脆弱性があったらしいです。「limitなんて数字だけじゃん」とついつい思ってしまいますが、攻撃する側も防御する側もよく気付くなぁ…と感心してしまいます。そんな悠長な事態じゃないんですけど。
で、肝心の対策。
ボクは jquery-rails を使っているので、
$ sudo gem update jquery-rails
$ rails generate jquery:install
こうして jquery.js, jquery.min.js, rails.js を上書きして事無きを得ました。 rails.js だけでいいのかなと思ったけれど、それでまた動かなかったら嫌なので jquery ライブラリも上書きしてしまいますた。多分バージョンが違うだけだと思うけど(「だけ」ってレベルじゃねーですけど)、jQueryで本格的に開発してる人にはオススメしません。あでも、jsUnitとか使ってるのかしら。いいのかしら。
そんなわけでした。
--
2011年2月14日月曜日
[Rails3] [will_paginate] Array インスタンスを WillPaginate::Collection に変換
SomeKindOfActiveRecordModel.paginate からではなくて、何らかの読み出しで Array オブジェクトになってしまった SomeKindOfActiveRecordModel を動的に WillPaginate::Collection 型(ActiveRecord::Base.paginateで返ってくるやつ)に変換するには、Array#paginateを単に呼び出してやればいいらしい。
ちゃんとスーパークラスをRailsプラグインらしくバリバリ拡張してくれているのは大助かりですね。
--
ちゃんとスーパークラスをRailsプラグインらしくバリバリ拡張してくれているのは大助かりですね。
--
2011年1月16日日曜日
meta_where を使った検索
検索というかなんというか。
u1 = User.new :name => "user1"
u1.comments << Comment.create :message => "hoge hoge"
u1.comments << Comment.create :message => "hoge moge"
u1.save
u2 = User.new :name => "user2"
u2.comments << Comment.create :message => "hoge fuga"
u2.save
u3 = User.create :name => "user3"
こんな感じのデータがあったとして、
User.search("user") # => [u1, u2, u3]
User.search("hoge") # => [u1, u2]
というような結果を求めるとき、 User.search の実装は以下のようになる。
scope :search, lambda {|query|
includes(:comments).group("users.id").where(:name.like => "%#{query}%" | :comments => {:message.like => "%#{query}%"})
}
Comment に複数の属性があるときは :comments => [{:message.like => "%#{query}%"} | {:updated_at.lt => Date.today}] のように渡せばいい。ゴチャゴチャするけど。
ちなみに includes ではなく joins を使うと、検索結果にコメントの設定されていない u3 がヒットせず、 group を付けないと、 "hoge" の検索結果が [u1, u1, u2] になってしまうので、 users.id でグループ化する必要がある。らしい。
押してねっ→BlogPeople「趣味の世界」ブログランキング
u1 = User.new :name => "user1"
u1.comments << Comment.create :message => "hoge hoge"
u1.comments << Comment.create :message => "hoge moge"
u1.save
u2 = User.new :name => "user2"
u2.comments << Comment.create :message => "hoge fuga"
u2.save
u3 = User.create :name => "user3"
こんな感じのデータがあったとして、
User.search("user") # => [u1, u2, u3]
User.search("hoge") # => [u1, u2]
というような結果を求めるとき、 User.search の実装は以下のようになる。
scope :search, lambda {|query|
includes(:comments).group("users.id").where(:name.like => "%#{query}%" | :comments => {:message.like => "%#{query}%"})
}
Comment に複数の属性があるときは :comments => [{:message.like => "%#{query}%"} | {:updated_at.lt => Date.today}] のように渡せばいい。ゴチャゴチャするけど。
ちなみに includes ではなく joins を使うと、検索結果にコメントの設定されていない u3 がヒットせず、 group を付けないと、 "hoge" の検索結果が [u1, u1, u2] になってしまうので、 users.id でグループ化する必要がある。らしい。
押してねっ→BlogPeople「趣味の世界」ブログランキング
2011年1月12日水曜日
[Rails3] meta_whereのコンディションとdelayed_jobのハマりどころ
都合上「何にでもヒットする meta_where のコンディション」が必要になったので、作りかた。
MetaWhere::Condition.new :id, nil, :like
これでおk。
catch_all = MetaWhere::Condition.new :id, nil, :like
Books.where(catch_all | :title.matches % "相振り中飛車で攻")
こんな感じで使える。場合によっては便利…?
それからdelayed_job、これはいろいろと fork されているみたいだけど、今回は有名な https://github.com/collectiveidea/delayed_job こちらを採用させてもらいました。
Mail.delay.deliver
とメソッドチェインするだけで遅延してくれるという優れ物ですが、実用までのメモと自分用のメモ。
$ vi Gemfile
gem "delayed_job"
$ vi config/initializers/delayed_job_config.rb
Delayed::Worker.destroy_failed_jobs = false
$ bundle
$ rails generate delayed_job
$ rake db:migrate
$ ./script/delayed_job start
これで使えるようになる。なお production 環境で使う場合は
$ rake db:migrate RAILS_ENV=production
$ RAILS_ENV=production ./script/delayed_job start
こんな感じ。
で、ハマりどころは、
このふたつ。見事にハマりました。
ま、リソースが足りなきゃいくら遅延しても意味ないけどね。
お名前.comのサーバがまた落ちている…。
押してねっ→BlogPeople「趣味の世界」ブログランキング
MetaWhere::Condition.new :id, nil, :like
これでおk。
catch_all = MetaWhere::Condition.new :id, nil, :like
Books.where(catch_all | :title.matches % "相振り中飛車で攻")
こんな感じで使える。場合によっては便利…?
それからdelayed_job、これはいろいろと fork されているみたいだけど、今回は有名な https://github.com/collectiveidea/delayed_job こちらを採用させてもらいました。
Mail.delay.deliver
とメソッドチェインするだけで遅延してくれるという優れ物ですが、実用までのメモと自分用のメモ。
$ vi Gemfile
gem "delayed_job"
$ vi config/initializers/delayed_job_config.rb
Delayed::Worker.destroy_failed_jobs = false
$ bundle
$ rails generate delayed_job
$ rake db:migrate
$ ./script/delayed_job start
これで使えるようになる。なお production 環境で使う場合は
$ rake db:migrate RAILS_ENV=production
$ RAILS_ENV=production ./script/delayed_job start
こんな感じ。
で、ハマりどころは、
- コントローラやProcインスタンスに対しては使えない。適宜 SomethingHelper::should_be_delayed などを定義して使う。
- delay を呼び出すコードの周り(どの辺までなのかは知らん)が変わったら delayed_job デーモンを再起動すること。
このふたつ。見事にハマりました。
ま、リソースが足りなきゃいくら遅延しても意味ないけどね。
お名前.comのサーバがまた落ちている…。
押してねっ→BlogPeople「趣味の世界」ブログランキング
[Rails3] ActiveRelation
やたら忘れるのでメモ。
RailsCastsの http://media.railscasts.com/videos/239_active_record_relation_walkthrough.mov この動画がとても参考になった。それにしてもRails使いはMacの人が多いなー。やっぱり故郷がMacだと必然的にそうなるのかしら。
えーそれで、リレーションを作成するには
ActiveRecord::Relation.new User, User.arel_table
とかするみたい。引数はモデルと Arel テーブル。この Arel テーブルには事前に定義した条件があってもいいような悪いような。
で、メソッドチェインできるのは
の12個。
チェインを繋げるとデフォルトで(というか普通に) AND になるようだけど、 OR にする方法ないのかな…。Arel使えってことか。
押してねっ→BlogPeople「趣味の世界」ブログランキング
RailsCastsの http://media.railscasts.com/videos/239_active_record_relation_walkthrough.mov この動画がとても参考になった。それにしてもRails使いはMacの人が多いなー。やっぱり故郷がMacだと必然的にそうなるのかしら。
えーそれで、リレーションを作成するには
ActiveRecord::Relation.new User, User.arel_table
とかするみたい。引数はモデルと Arel テーブル。この Arel テーブルには事前に定義した条件があってもいいような悪いような。
で、メソッドチェインできるのは
- where
- having
- select
- group
- order
- limit
- offset
- joins
- includes
- lock
- readonly
- from
の12個。
チェインを繋げるとデフォルトで(というか普通に) AND になるようだけど、 OR にする方法ないのかな…。Arel使えってことか。
押してねっ→BlogPeople「趣味の世界」ブログランキング
2011年1月11日火曜日
[Rails3] prototype_legacy_helper
Rails3になってUJS(UnobtrusiveJavaScript)が推奨されるようになり、 rails.js がコアとして動作するようになったのは時代に乗ってる感じでいいのだけれど、いかんせんドキュメントもサンプルも少ないのが現状なので、以前のヘルパも使いたくなってくるのが人情というもの。
例えば「link_toヘルパに :remote をつけて特定のtext_fieldの内容を送信」という動作をさせるにはどうすればいいのか、いくら検索してもさっぱりわからなかった。Creative Commonsにオライリーから出ているRails3本の中身が載っているらしいけれど、やっぱり英語…ということで腰が引けてしまう。
そこで、過去の遺産を使うには prototype_legacy_helper が便利らしい。
$ rails plugin install https://github.com/rails/prototype_legacy_helper
これで link_to_remote などといった旧来のヘルパを扱うことができるようになる。上記「特定の text_field の中身を送信」は link_to_remote の :with オプションでばっちり。
...
<%= text_field_tag :address_zipcode %>
<%= link_to_remote "AutoComplete", :url => find_zipcode_path, :with => "'zipcode='+$('address_zipcode').value" %>
...
:with オプションについてはネットにいろいろと情報があるけど、個人的に今回初めて使ったので解説を載っけておく。
ようするに "" で括られたJavaScriptらしい。で、JavaScriptの文字列でなければならず、 :with => "'key1=value1&key2=value2'" と指定すると、 params[:key1] == value1 などになる。 & で区切ってハッシュにしてくれるので、いちいちデータをシリアライズしなくてもいいみたい。
DOMオブジェクトを選択して中身を取り出すのはお馴染 $ 関数に value メソッドだけど、あくまで「JavaScriptの文字列」として渡す = '' で括る必要があるので、少し面倒くさい。そのサンプルは上のコードにある通りで、 "'zipcode='+..." とクォーテーションがいかにも読み辛いけど、Emacsの正規表現に比べたらマシと思えばマシ…かも知れない。
押してねっ→BlogPeople「趣味の世界」ブログランキング
例えば「link_toヘルパに :remote をつけて特定のtext_fieldの内容を送信」という動作をさせるにはどうすればいいのか、いくら検索してもさっぱりわからなかった。Creative Commonsにオライリーから出ているRails3本の中身が載っているらしいけれど、やっぱり英語…ということで腰が引けてしまう。
そこで、過去の遺産を使うには prototype_legacy_helper が便利らしい。
$ rails plugin install https://github.com/rails/prototype_legacy_helper
これで link_to_remote などといった旧来のヘルパを扱うことができるようになる。上記「特定の text_field の中身を送信」は link_to_remote の :with オプションでばっちり。
...
<%= text_field_tag :address_zipcode %>
<%= link_to_remote "AutoComplete", :url => find_zipcode_path, :with => "'zipcode='+$('address_zipcode').value" %>
...
:with オプションについてはネットにいろいろと情報があるけど、個人的に今回初めて使ったので解説を載っけておく。
ようするに "" で括られたJavaScriptらしい。で、JavaScriptの文字列でなければならず、 :with => "'key1=value1&key2=value2'" と指定すると、 params[:key1] == value1 などになる。 & で区切ってハッシュにしてくれるので、いちいちデータをシリアライズしなくてもいいみたい。
DOMオブジェクトを選択して中身を取り出すのはお馴染 $ 関数に value メソッドだけど、あくまで「JavaScriptの文字列」として渡す = '' で括る必要があるので、少し面倒くさい。そのサンプルは上のコードにある通りで、 "'zipcode='+..." とクォーテーションがいかにも読み辛いけど、Emacsの正規表現に比べたらマシと思えばマシ…かも知れない。
押してねっ→BlogPeople「趣味の世界」ブログランキング
2011年1月10日月曜日
Rails3 で大量の一括Insert
ができるらしい。
https://github.com/zdennis/activerecord-import
プラグインではないようなので v0.2.4 をダウンロードしてコピー。Wikiに載っているような使い方もできるけれど、個人的には
columns = [:first_name, :family_name]
values = [["John", "Smith"], ["Jane", "Due"], ["Gonbe", "Nanashi"]]
Person.import columns, values
のような使い方のほうがメモリ効率がよさげかなと。
押してねっ→BlogPeople「趣味の世界」ブログランキング
https://github.com/zdennis/activerecord-import
プラグインではないようなので v0.2.4 をダウンロードしてコピー。Wikiに載っているような使い方もできるけれど、個人的には
columns = [:first_name, :family_name]
values = [["John", "Smith"], ["Jane", "Due"], ["Gonbe", "Nanashi"]]
Person.import columns, values
のような使い方のほうがメモリ効率がよさげかなと。
押してねっ→BlogPeople「趣味の世界」ブログランキング
2010年12月21日火曜日
Rails3 で Exception Notification
Rails 0.9.x のときに Exception Notifier (エラーをメールで通知するやつ)が便利だなーと思っていて、最近の事情はどうなのかしらと思っていたら、やっぱり例によってプラグインになっていたのね。プラグイン形式は便利かも知れないけど、メンテナが…フォーク数が…という事態になりがちで、活発なのはいいことなんだけれど、Rails3がリリースされた今「デファクトスタンダード」が無いのも辛いなと思う今日この頃。
今回のプラグインには https://github.com/michallo/exception_notification こちらを採用させてもらいました。とりあえず rails の master から fork を辿って行って、一番新しいところにいたので、という安直な理由。でもしっかり動いてくれました。
ただ、いくつか躓くところがあったので付記。
今回のプラグインには https://github.com/michallo/exception_notification こちらを採用させてもらいました。とりあえず rails の master から fork を辿って行って、一番新しいところにいたので、という安直な理由。でもしっかり動いてくれました。
ただ、いくつか躓くところがあったので付記。
auto completion for rails3 using prototype.js as backend
戦型を追加するときにオートコンプリートを有効にしようとして、 auto_complete_for なんてマクロがあるのか!とググって感動していたら、かなり前にプラグインとして分離されていたみたいで、しかもDHHさん(Railsの作者)によるメンテナンスが2007年止まり…。
代替策はないのかとGoogle先生を酷使するも、 rails3-autocomplete-jquery とかそんなんばっか。
時代はjQueryなのか…Rails3という(地雷かもしれない)最新版を使っておきながらJavaScriptのバックエンドはprototype.jsのほうがいいという保守的な考えのボクには道はないのか…そう思っていたところが!
あまりにも検索トップで荒ぶっているので見逃していた「このサイト」で配布されている autocomplete という、一見Railsとは何の関係も無さそうなライブラリが大活躍。まずは配置。
代替策はないのかとGoogle先生を酷使するも、 rails3-autocomplete-jquery とかそんなんばっか。
時代はjQueryなのか…Rails3という(
あまりにも検索トップで荒ぶっているので見逃していた「このサイト」で配布されている autocomplete という、一見Railsとは何の関係も無さそうなライブラリが大活躍。まずは配置。
2010年12月20日月曜日
rails3 で Twitter のような「もっと読む」をつくる
かなり時間つかったのでメモメモ。
Comment.rb: (モデル)
PerPage = 3
scope :paginate,
lambda { |page, per_page|
page = 1 if not page.present? or page <= 0
per_page = PerPage if not per_page.present? or per_page <= 0
limit = per_page
offset = (page - 1) * limit
limit(limit).offset(offset)
}
CommentsController: (コントローラ)
def index
@comments = Comment.paginate(params[:page].to_i, nil)
if not params[:page].blank?
@morepage = params[:page].to_i + 1
else
@morepage = 2
end
@morepage = nil if Comment.paginate(params[:page].to_i+1, nil).count.zero?
respond_with @comments
end
index.html.haml: (ビュー)
%div#comments
- if @comments.blank?
%p コメントないお。
- else
= render 'comments/comments'
= render 'comments/more'
-- 押してねっ→BlogPeople「趣味の世界」ブログランキング --
Comment.rb: (モデル)
PerPage = 3
scope :paginate,
lambda { |page, per_page|
page = 1 if not page.present? or page <= 0
per_page = PerPage if not per_page.present? or per_page <= 0
limit = per_page
offset = (page - 1) * limit
limit(limit).offset(offset)
}
CommentsController: (コントローラ)
def index
@comments = Comment.paginate(params[:page].to_i, nil)
if not params[:page].blank?
@morepage = params[:page].to_i + 1
else
@morepage = 2
end
@morepage = nil if Comment.paginate(params[:page].to_i+1, nil).count.zero?
respond_with @comments
end
index.html.haml: (ビュー)
%div#comments
- if @comments.blank?
%p コメントないお。
- else
= render 'comments/comments'
= render 'comments/more'
_comments.html.haml:
- @comments.each do |comment|
%ul
%div{:id => "comment_"+comment.id.to_s}
%li= comment.message
_more.html.haml:
- if @morepage
%div#more
%a.more= link_to "もっと読む", {:page => (@morepage)},
:method => :get, :remote => true
index.js.rjs:
page.insert_html(:bottom, "comments", :partial => "comments/comments",
:locals => {:comments => @comments})
@comments.each do |comment|
page["comment_" + comment.id.to_s].visual_effect :highlight
end
page.replace_html :more, :partial => "more"
なんでこんなに長くなるかねぇ…。
実際のコードから抽出したんで間違いなどあるかもですが、自分用なので良しとする!
それからページネーションは Will_paginate を使えばいいんじゃって話もありますが、ボク自身が link_to :remote の記法に慣れておきたかったことと、 RJS の何たるかをちょっとでも理解してから使いたかったからです。でもこの場合 Comment.next_page とかの定義がわからん。おかげでコントローラの中で「もっと読む」を表示するかどうか調べてるし、全体的にダサいのはあれですね、書いたやつのせいですね。
さて、とりあえず push しておきますかね…。
-- 押してねっ→BlogPeople「趣味の世界」ブログランキング --
2010年12月16日木曜日
haml-rails
Gemfile に gem 'haml' と書くだけでも使えるけど、それだと scaffold のときに not found とか言われてしまうので、 gem 'haml-rails' と指定しなきゃいけないみたい。
それから、 haml をデフォルトのレンダリングエンジンにするためには、 config/application.rb に
config.generators do |g|
g.template_engine :haml
end
と書けばよろしいらしい。
それから、 haml をデフォルトのレンダリングエンジンにするためには、 config/application.rb に
config.generators do |g|
g.template_engine :haml
end
と書けばよろしいらしい。
出納帳できちゃいました
講座を書くのも早々に飽きて出納帳が完成してしまいました。
もともと「これまでのRails」についての機能を確認しながら書いていたに過ぎないので、あまりRails3の有効な情報にはならなかったことでしょう。しかし一旦書き始めると、ネットで調べながらRails3の強力な機能を堪能することができました。
-- 押してねっ→BlogPeople「趣味の世界」ブログランキング --
もともと「これまでのRails」についての機能を確認しながら書いていたに過ぎないので、あまりRails3の有効な情報にはならなかったことでしょう。しかし一旦書き始めると、ネットで調べながらRails3の強力な機能を堪能することができました。
- scopeすごい。joinsとエイリアスされたmergeメソッドで究極のDSL&DRY
- Rackから輸入された経路の記述がわかりやすい。でも動かなかった。
- アプリケーションに対して wget すると500が返ってくる。 template is missing …? haml 関係っぽい。
- でもhaml便利。
とりあえずこれからは git や他のプラグインなどを導入して本格的に Rails3 アプリケーションを組み立てていきます。目下の目標は…とりあえず将棋関連ですかねぇ。
-- 押してねっ→BlogPeople「趣味の世界」ブログランキング --
2010年12月14日火曜日
Ruby on Rails3で出納帳を作ろう・その伍
だんだん記事が長くなっているので自重せねばなりませんな。
前回は scaffold が自動生成した CategoriesController をフルスクラッチから書き直し、RESTという考え方や DRY の実践、また、Rails3で追加された新機能を実際に使用したり、モデルからのデータの取り出しや保存、更新、削除などの CRUD を行ないました。
しかし、大きな問題点があります。
これはRails3であれば、たった一行で防止することができます。ビュー、コントローラに続いて、今回はモデルについて掘り下げてみましょう。
前回は scaffold が自動生成した CategoriesController をフルスクラッチから書き直し、RESTという考え方や DRY の実践、また、Rails3で追加された新機能を実際に使用したり、モデルからのデータの取り出しや保存、更新、削除などの CRUD を行ないました。
しかし、大きな問題点があります。
- 名前が空のカテゴリを作成できてしまう
- 同じ名前のカテゴリをいくつも作成できてしまう
これはRails3であれば、たった一行で防止することができます。ビュー、コントローラに続いて、今回はモデルについて掘り下げてみましょう。
Ruby on Rails3で出納帳を作ろう・その肆
前回は scaffold で作成した Category の View 部分(app/views/categories/ ディレクトリ以下)を、一定の水準まで日本語化することができました。しかし、新規作成したときや編集・保存したときの「Category was successfully created/updated.」などのメッセージは、 View のどこにもありませんでした。また、新規作成・編集したときに、さまざまな情報を管理するならまだしも、 name カラムしか持たないレコードをいちいち確認(表示)するのは冗長です。新規作成・編集したあとは、すぐにリスト表示に戻ってもらえないものでしょうか?
これらを解決するのは MVC (Model View Controller) のうち Controller の役割です。 scaffold は、モデル・ビュー・コントローラのすべての土台(と他にもいろいろ)を自動生成してくれるので、これらの要望はわずかな修正をするだけで実現させることができます。
では、いつものようにサーバを起動して作業していきましょう。
$ cd $(PATH_TO_WORK_DIR)/receipt
$ ./script/rails s
=> Booting WEBrick
=> Rails 3.0.3 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-12-14 11:34:13] INFO WEBrick 1.3.1
[2010-12-14 11:34:13] INFO ruby 1.8.7 (2010-06-23) [i686-linux]
[2010-12-14 11:34:18] INFO WEBrick::HTTPServer#start: pid=9997 port=3000
これらを解決するのは MVC (Model View Controller) のうち Controller の役割です。 scaffold は、モデル・ビュー・コントローラのすべての土台(と他にもいろいろ)を自動生成してくれるので、これらの要望はわずかな修正をするだけで実現させることができます。
では、いつものようにサーバを起動して作業していきましょう。
$ cd $(PATH_TO_WORK_DIR)/receipt
$ ./script/rails s
=> Booting WEBrick
=> Rails 3.0.3 application starting in development on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server
[2010-12-14 11:34:13] INFO WEBrick 1.3.1
[2010-12-14 11:34:13] INFO ruby 1.8.7 (2010-06-23) [i686-linux]
[2010-12-14 11:34:18] INFO WEBrick::HTTPServer#start: pid=9997 port=3000
Ruby on Rails3で出納帳を作ろう・その参
前回は scaffold を使ってレコードの CRUD インターフェイスを実現させるところまで行きました。しかし、不満を覚えた人も多いのではないでしょうか。例えば…
- 英語じゃん
- 作成したあとにいちいち Back するのが面倒くさいじゃん
2010年12月13日月曜日
Ruby on Rails3で出納帳を作ろう・その弍
前回はRails3を使ってアプリケーションを新規に作成(rails new receipt)し、バンドル(bundle)するところまでで終わってしまいました。今回はデータベース(SQLite3を採用)の設計から scaffold という便利な機能を使って CRUD (Create Read Update Delete) のインターフェイスを見る、いじるところまで行くつもりです。
必要になりそうなテーブルは:
流れとしては:
それにしても、こうして書いてみるとデータベースに慣れていない人には何やら難解そうに見えます。ボク自身にも難しそうに見えるというか、よほど集中しないと何が書いてあるのか意味がわかりません。が、Railsでは直感的に作業ができる上にコード量も驚くほど少なく済むので、多分ヒマな人なら一日かからずに作り終えることができるでしょう。実際、この講座に書いてあるのは文章量こそ多かれど実際にタイプするのはその10分の1以下のはずです。
では、まずはデータベースおよびテーブルの作成からですね。
必要になりそうなテーブルは:
- Receipts: 日付と時刻・月(暗黙的参照: Accounts)・分類(参照: Categories)・概要・詳細(無記入可)・収支
- Categories: 名前
- Accounts: 月(ユニーク)・金額
流れとしては:
- (自動)今月のベースとなる0円のAccounts行を作成。
- Categories行をひとつ以上作成。
- 収支に応じてReceipts行を作成していく。
- (自動)Receiptsをリスト表示するときは、今月分のAccounts行からReceiptsの差分を表示。
- (自動)月が変わったらその月のAccounts行を作成し、先月のAccountsおよびReceiptsのsumを保存。
- 基本的に2から5を繰り返す。
- 先月以前のReceipts行が削除された場合、変化した計算結果をその月以降のAccountsに適用する。
それにしても、こうして書いてみるとデータベースに慣れていない人には何やら難解そうに見えます。ボク自身にも難しそうに見えるというか、よほど集中しないと何が書いてあるのか意味がわかりません。が、Railsでは直感的に作業ができる上にコード量も驚くほど少なく済むので、多分ヒマな人なら一日かからずに作り終えることができるでしょう。実際、この講座に書いてあるのは文章量こそ多かれど実際にタイプするのはその10分の1以下のはずです。
では、まずはデータベースおよびテーブルの作成からですね。
登録:
投稿 (Atom)