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

2011年2月27日日曜日

[Rails3] whenever

whenever】はRubyとCronの橋渡しをしてくれるようなgemで、DSLで処理を記述しておくだけで crontab に書き込んでくれるという嬉しい代物です。bash経由で実行するようです。

Railsでももちろん使えて、rakeやrunnerなどを実行できますが、runnerの場合、うちの環境では bash のオプションに -l (--login) がついているとうまく動作しませんでした。

ので、手動で crontab -e して -l を削除しましたとさ、という報告でした。


2011年2月26日土曜日

Ruby on Rails と Thread

Rails2.2 の頃からスレッドセーフだとか何とか騒がれていましたがボクは知りません。知らないので、ネイティブスレッドではないRubyの Thread クラスを Rails で使っちゃおうという計画。delayed_jobではちょっと力不足だったのです。

やりたいことはただひとつ。
  • サーバで起こっている作業の途中経過をAjaxで取得したい。
コレ。簡単そうに思えましたが、ところがどっこい苦労しまくって体調崩すほどでした。誰か他にいい方法があったら教えてください。

つまりですよ。
  1. クライアントがファイルをアップロードする。
  2. サーバはレスポンスを返す。
  3. と同時に、サーバは新しく Thread.new して非同期に処理を開始し、処理をしながら進捗情報をどこかに保存しておく。
  4. クライアントは setInterval とかでサーバにリクエストを送信する。
  5. サーバは保存されている進捗情報を返す。
こんなことがしたかったんですね。

超問題になったのが「進捗情報の保存場所」。ぶっちゃけると 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

次は 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]
)

としたんですが、どうも「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 で解決です。

2011年2月14日月曜日

[Rails3] [will_paginate] Array インスタンスを WillPaginate::Collection に変換

SomeKindOfActiveRecordModel.paginate からではなくて、何らかの読み出しで Array オブジェクトになってしまった SomeKindOfActiveRecordModel を動的に WillPaginate::Collection 型(ActiveRecord::Base.paginateで返ってくるやつ)に変換するには、Array#paginateを単に呼び出してやればいいらしい。

ちゃんとスーパークラスをRailsプラグインらしくバリバリ拡張してくれているのは大助かりですね。
--

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

こんな感じ。

で、ハマりどころは、

  • コントローラや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 テーブルには事前に定義した条件があってもいいような悪いような。

で、メソッドチェインできるのは

  1. where
  2. having
  3. select
  4. group
  5. order
  6. limit
  7. offset
  8. joins
  9. includes
  10. lock
  11. readonly
  12. from

の12個。

チェインを繋げるとデフォルトで(というか普通に) AND になるようだけど、 OR にする方法ないのかな…。Arel使えってことか。

押してねっ→BlogPeople「趣味の世界」ブログランキング

2011年1月11日火曜日

[Rails3] link_to do

link_to にブロックが渡せるなんてどこに書いてあったんだ…知らなかった。

例えば Rails で

    画像
リンク文字列

のようなレイアウトにしたい場合にブロックが使える。例えばHaml:

= link_to @user do
  = image_tag "#{@user.name}.png"
  %br
  = @user.name

マクロとレキシカルスコープがあればRuby最強なのになと思う信者でした。

押してねっ→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「趣味の世界」ブログランキング

[Ruby][Rails] CamelCase と snake_case

結構どこを探してもなかったんよね。
ヘルパーのAPIドキュメントにも無かったっぽいし。

つまりリフレクションしたいときとかにクラス名(CamelCase)をメソッド名(snake_case)に変換して __send__ したい場合があるじゃないのよさ。そんなときに一発変換できないかなと思って探してみたら、こんなメソッドが出てきました。

"CamelCase".underscore     # -> "camel_case"
"snake_case".camelize      # -> "SnakeCase"
"snake_case".pluralize     # -> "snake_cases"
"snake_cases".singularlize # -> "snake_case"
"snake_cases".classify     #  = "snake_cases".singularlize.camelize

classify メソッドなんてものもあった。で、こういうことがわかると検索したときに「あ、こんなに情報あるんじゃん」という事態になってしまう何かの法則。

あると便利だと思うので、Rails3からバラして使うのも良しですね。

押してねっ→BlogPeople「趣味の世界」ブログランキング

2010年12月21日火曜日

Rails3 で Exception Notification

Rails 0.9.x のときに Exception Notifier (エラーをメールで通知するやつ)が便利だなーと思っていて、最近の事情はどうなのかしらと思っていたら、やっぱり例によってプラグインになっていたのね。プラグイン形式は便利かも知れないけど、メンテナが…フォーク数が…という事態になりがちで、活発なのはいいことなんだけれど、Rails3がリリースされた今「デファクトスタンダード」が無いのも辛いなと思う今日この頃。

今回のプラグインには https://github.com/michallo/exception_notification こちらを採用させてもらいました。とりあえず railsmaster から 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とは何の関係も無さそうなライブラリが大活躍。まずは配置。



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'

_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

Gemfilegem 'haml' と書くだけでも使えるけど、それだと scaffold のときに not found とか言われてしまうので、 gem 'haml-rails' と指定しなきゃいけないみたい。

それから、 haml をデフォルトのレンダリングエンジンにするためには、 config/application.rb

config.generators do |g|
  g.template_engine :haml
end

と書けばよろしいらしい。

出納帳できちゃいました

講座を書くのも早々に飽きて出納帳が完成してしまいました。
もともと「これまでの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であれば、たった一行で防止することができます。ビュー、コントローラに続いて、今回はモデルについて掘り下げてみましょう。



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



Ruby on Rails3で出納帳を作ろう・その参

前回scaffold を使ってレコードの CRUD インターフェイスを実現させるところまで行きました。しかし、不満を覚えた人も多いのではないでしょうか。例えば…
  • 英語じゃん
  • 作成したあとにいちいち Back するのが面倒くさいじゃん
今回はまず、日本語化してみましょう。 MVC (Model View Controller) のうち View を扱っていきます。



2010年12月13日月曜日

Ruby on Rails3で出納帳を作ろう・その弍

前回はRails3を使ってアプリケーションを新規に作成(rails new receipt)し、バンドル(bundle)するところまでで終わってしまいました。今回はデータベース(SQLite3を採用)の設計から scaffold という便利な機能を使って CRUD (Create Read Update Delete) のインターフェイスを見る、いじるところまで行くつもりです。

必要になりそうなテーブルは:
  • Receipts: 日付と時刻・月(暗黙的参照: Accounts)・分類(参照: Categories)・概要・詳細(無記入可)・収支
  • Categories: 名前
  • Accounts: 月(ユニーク)・金額
こんなところでしょうか。

流れとしては:
  1. (自動)今月のベースとなる0円のAccounts行を作成。
  2. Categories行をひとつ以上作成。
  3. 収支に応じてReceipts行を作成していく。
  4. (自動)Receiptsをリスト表示するときは、今月分のAccounts行からReceiptsの差分を表示。
  5. (自動)月が変わったらその月のAccounts行を作成し、先月のAccountsおよびReceiptsのsumを保存。
  6. 基本的に2から5を繰り返す。
  7. 先月以前のReceipts行が削除された場合、変化した計算結果をその月以降のAccountsに適用する。
おおむねこんな感じですか。まぁ…はっきり言って雑ですね。RDBMSの特性を真に活かしきれていない気がしますので、「こういう構造のほうが楽ですよ」という意見などあればぜひ教えていただきたいです。

それにしても、こうして書いてみるとデータベースに慣れていない人には何やら難解そうに見えます。ボク自身にも難しそうに見えるというか、よほど集中しないと何が書いてあるのか意味がわかりません。が、Railsでは直感的に作業ができる上にコード量も驚くほど少なく済むので、多分ヒマな人なら一日かからずに作り終えることができるでしょう。実際、この講座に書いてあるのは文章量こそ多かれど実際にタイプするのはその10分の1以下のはずです。

では、まずはデータベースおよびテーブルの作成からですね。