Hatena::Grouprubyist

うんたらかんたらRuby RSSフィード

2010-03-24textmateのcheat sheet

textmateのcheat sheet

| textmateのcheat sheet - うんたらかんたらRuby を含むブックマーク はてなブックマーク - textmateのcheat sheet - うんたらかんたらRuby

TextMate Cheat Sheet

f:id:rochefort:20100324230739p:image


自分がよく使うやつを纏めてみた。

こちらを元に作成。

tab triggerは山ほどあるけど、ほとんど頭に入ってない。

適当にtab押すと結構あたる。


2010/03/28追記

css sprite使って文字化け治したかったけど

floatカオス過ぎてあきらめた。

とりあえず画像に置き換えたのでwindowsでも見れるようにはなったはず(未確認)。

機種依存文字、CSS、画像編集難しい。


参考

Mike Clark's Weblog

rochefortrochefort2010/03/25 23:08文字化けしてますね。今日は酔っ払ってどうにもこうにもな感じなので、後で直します。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100324

2010-03-23random文字列

random文字列

| random文字列 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - random文字列 - うんたらかんたらRuby

config/initializers/sessoin_store.rb

の:secretとか。


# ワンライナー
ruby -rsecurerandom -e 'puts SecureRandom.hex(16)'

#irb
require 'securerandom'
SecureRandom.hex(n) #default 16

中身はこんな感じ。

/opt/local/lib/ruby/1.8/securerandom.rb

  def self.hex(n=nil)
    random_bytes(n).unpack("H*")[0]
  end


  def self.random_bytes(n=nil)
    n ||= 16
    if defined? OpenSSL::Random
      return OpenSSL::Random.random_bytes(n)
    end
    if !defined?(@has_urandom) || @has_urandom
      @has_urandom = false
      flags = File::RDONLY
      flags |= File::NONBLOCK if defined? File::NONBLOCK
      flags |= File::NOCTTY if defined? File::NOCTTY
      flags |= File::NOFOLLOW if defined? File::NOFOLLOW
      begin
        File.open("/dev/urandom", flags) {|f|
          unless f.stat.chardev?
            raise Errno::ENOENT
          end
          @has_urandom = true
          ret = f.readpartial(n)
          if ret.length != n
            raise NotImplementedError, "Unexpected partial read from random device"
          end
          return ret
        }
      rescue Errno::ENOENT
        raise NotImplementedError, "No random device"
      end
    end
    raise NotImplementedError, "No random device"
  end
トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100323

2010-03-20mongrel_clusterのローテーション

mongrel_clusterのローテーション

| mongrel_clusterのローテーション - うんたらかんたらRuby を含むブックマーク はてなブックマーク - mongrel_clusterのローテーション - うんたらかんたらRuby

mongrel_cluster使うことになったので

ローテーションどうすんだろうと思って調べてみました。


config.loggerでやろうとすると

mongrel_cluster の場合、複数プロセスが同一ログファイルにはくため

ローテートを実行したプロセス以外は、ログファイルが行方不明となり

お亡くなりになるとのこと。


要は

logrotateでやるのがいいみたい。

syslogもありな感じ。

もしくは、バックアップ後にcp /dev/nul でも動く。


参考

Railsログローテートまとめ- まさらっきのプログラマ的メモ帳

Railsアプリケーションのログをsyslogに記録する方法 - Hello, world! - s21g

Barry Hess :: Rolling your Rails logs with logrotate

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100320

2010-03-17habtmとhas_many throw

habtmとhas_many throw

| habtmとhas_many throw - うんたらかんたらRuby を含むブックマーク はてなブックマーク - habtmとhas_many throw - うんたらかんたらRuby

Railscasts - Two Many-to-Many

より


どっち選択すりゃいいの?

ってなったときは

1.do I need to store extra information in the join?

2.do I need to treat the join like its own model?

if you answer "yes" to any of these, you need a has_many :throw association.

Otherwise you "might" want to use has_and_belongs_to_many.

1.join時に他の情報も格納したいか?

2.join時に自分自身のモデルとして扱いたいか?

どちらかyesなら→has_many_thorow

noなら→habtm


参考

Rails(ActiveRecord)の多対多関連 - yuum3のお仕事日記

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100317

2010-03-16urlのidについて

urlのidについて

| urlのidについて - うんたらかんたらRuby を含むブックマーク はてなブックマーク - urlのidについて - うんたらかんたらRuby

Railscasts - Model Name in URL

より


railsurlは通常「model/id」ってなってると思う。

このidは変更可能なんですって。

知らんかった。


railscastsのurlもこの方式でやってるんだ。

http://railscasts.com/episodes/63-model-name-in-url


やりかた

to_paramをoverrideするだけ。

# product.rb
def to_param
  "#{id}-#{permalink}"
end

# controller
@product = Product.find(params[:id])

id以外でやる場合は

# product.rb
def to_param
  permalink
end

# controller
@product = Product.find_by_permalink(params[:id])

やってみた

f:id:rochefort:20100317010624p:image


追記

ちょっとうれしい。

f:id:rochefort:20100317010625p:image


画像をアップロードするのがhatenaグループからだと

今日の一枚しかアップロードできなくなってる。

ん〜、rubyistさびれまくってるし、移転考えようかな。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100316

2010-03-15memcachedをsession storeに使用することについて

memcachedをsession storeに使用することについて

| memcachedをsession storeに使用することについて - うんたらかんたらRuby を含むブックマーク はてなブックマーク - memcachedをsession storeに使用することについて - うんたらかんたらRuby


session storeにはdb(active record)よりmemcachedが早くていいよ

という記事をよく見かける。


確かに実装は楽チン。

config.action_controller.session_store = :mem_cache_store

でも

memcachedってキャッシュなので

session切れて再ログインってのは、やだよなぁと思っていたところ

↓の記事を読んで、結構すっきりした。

Why storing session on memcached? - Bulknews::Subtech - subtech


(まぁ、ポリシー次第だとは思いますが)

基本はsessionは永続化したい。

だけど、rails標準では、write-through cache させていない。



そこで

Repcached - KLablabWiki

ですよ。


サーバ2台のマルチマスタを実現しているので、冗長構成にはピッタリ。


あとは

tokyo cabinet や他のkvsも試してみたい。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100315

2010-03-13action cache with memcached

action cache with memcached

| action cache with memcached - うんたらかんたらRuby を含むブックマーク はてなブックマーク - action cache with memcached - うんたらかんたらRuby

railsのキャッシュには

page / action / fragment

がある。


page cachingはrailsの内部処理が省けるので非常に高速。

ディスクに出力結果を格納する(cache storeを選択できない)。


今回は、memcachedの使用を前提としたので

action / fragment caching のどちらかだが

requestされるurlベースでcachingしたかったので

action cachingを使用してみた。


install

gem install memcache-client
gem install system_timer

書き方

# environments/develop.rb
config.action_controller.perform_caching = true
config.cache_store = :mem_cache_store, 'localhost:11211', { :namespace => 'app' }

# /opt/local/lib/ruby/gems/1.8/gems/memcache-client-1.8.0/lib/memcache.rb
DEFAULT_OPTIONS = {
  :namespace    => nil,
  :readonly     => false,
  :multithread  => true,
  :failover     => true,
  :timeout      => 0.5,
  :logger       => nil,
  :no_reply     => false,
  :check_size   => true,
  :autofix_keys => false,
  :namespace_separator => ':',
}

namespaceは、environmentとかversionとか入れた方がいいかも。


# controller
caches_action :index, :expires_in => 5.seconds

確認方法

telnet localhost 11211
# idを調べる
stats items
STAT items:16:number 1
STAT items:16:age 20936
STAT items:16:evicted 0
STAT items:16:evicted_time 0
STAT items:16:outofmemory 0
STAT items:16:tailrepairs 0
END

#詳細を確認
stats cachedump 16 100
ITEM app:views/localhost:3000/hoges [2388 b; 1268504202 s]
END

もうちっとみやすいツールはないんだろうか。


memcachedがダウンしていると

ログ上は、こんな感じ。

MemCacheError (No connection to server (localhost:11211 DEAD (Errno::ECONNREFUSED: Connection refused - connect(2)), will retry at Sat Mar 13 20:45:12 +0900 2010)): No connection to server (localhost:11211 DEAD (Errno::ECONNREFUSED: Connection refused - connect(2)), will retry at Sat Mar 13 20:45:12 +0900 2010)
Cached fragment miss: views/localhost:3000/hoges (1.3ms)

エラーなく使えるし、memcachedを上げると

再びcacheを利用するようになる。


課題

page / action cachingはクエリストリングス(パラメータ)を認識しないでキャッシュしちゃう。

action caching は、ソースを見たところcache_path を使用すればなんとかなりそう。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100313

2010-03-12will_paginate使用時のurlを固定にする方法

will_paginate使用時のurlを固定にする方法

| will_paginate使用時のurlを固定にする方法 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - will_paginate使用時のurlを固定にする方法 - うんたらかんたらRuby

will_paginate を使用した場合、

生成されるページングのリンクのクエリーストリングが

最初にリクエストしたものと異なってしまう場合がある。


理由は、url_forを使ってhashを展開してるからで

クエリーストリングの順番は保証されない。

なんてこった(大した問題じゃないが)。


request時

http://localhost:3000/test?a=1&b=2


生成されるリンク

http://localhost:3000/test?b=2&a=1&page=2

http://localhost:3000/test?b=2&a=1&page=3

みたいなことになっちゃう可能性がある。


ということで

renderer オプションを使用し

カスタムクラスの中で変更してみた。


強引だけど、一応実現できた。

#view
<%= will_paginate @models, { :renderer => 'CustomLinkRenderer', :request_uri => @_request.request_uri }%>

#environment.rb
class CustomLinkRenderer < WillPaginate::LinkRenderer

  def page_link(page, text, attributes = {})
    #@template.link_to text, url_for(page), attributes
    @template.link_to text, custom_page_link(page) , attributes
  end
  
  def custom_page_link(page)
    uri = (@options[:request_uri] or "")
    if uri.match(/page=\d+$/)
      uri.sub(/page=\d+$/, "page=#{page}")
    else
      if uri.match(/\?/).nil?
        uri << "?page=#{page}"
      else
        uri << "&page=#{page}" 
      end
    end    
  end
end
トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100312

2010-03-11雑記(activerecord)

雑記(activerecord)

| 雑記(activerecord) - うんたらかんたらRuby を含むブックマーク はてなブックマーク - 雑記(activerecord) - うんたらかんたらRuby

named_scopeすごく便利で、

association設定して:includeオプションで関連テーブル一気に読み込むってのは

すごくいいと思う。


でも

:selectオプション使用できるようになんないかなぁ。

:joinsオプションだと可能なんだけど、eager loading(先読み)になんないし。

結局find_by_sqlの方が早かったりする。

せっかくactiverecord使ってんのにsql書いてたりすると残念な気持ちになるなぁ。


プラグインで、あるにはあるんだけど(名前は失念した)、

includeが入れ子になると、エラーになるんだなぁ。


あと

named_scopeを複数chain時に

sqlの条件が被っちゃった場合

重複を排除したSQLを生成して欲しいなぁと思ったり。


↓みたいな場合。

named_scope and duplicate sql conditions -Ruby on Rails: Talk | Google グループ


追記

find_by_sqlで

eager loadingにならんことに気づいた。

というか勘違いしてた。

ん~なんだかなぁ。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100311

2010-03-10Logger!!! (ログにタイムスタンプを追加する方法)

Logger!!!(ログにタイムスタンプを追加する方法)

| Logger!!!(ログにタイムスタンプを追加する方法) - うんたらかんたらRuby を含むブックマーク はてなブックマーク - Logger!!!(ログにタイムスタンプを追加する方法) - うんたらかんたらRuby

Railscasts - The Logger

Logger!!! - うんたらかんたらRuby - Rubyist


ログに日時を追加(簡易版)

/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/logger.rb

コメントに日付フォーマットのみ変更してタイムスタンプを表示する方法が書いてた。

 20 # Extensions to the built in Ruby logger.
 21 #
 22 # If you want to use the default log formatter as defined in the Ruby core, then you
 23 # will need to set the formatter for the logger as in:
 24 #
 25 #   logger.formatter = Formatter.new
 26 #
 27 # You can then specify the datetime format, for example:
 28 #
 29 #   logger.datetime_format = "%Y-%m-%d"
 30 #
 31 # Note: This logger is deprecated in favor of ActiveSupport::BufferedLogger
 32 class Logger

とりあえずミリ秒まで不要であれば、これだけでいけました。

(Loggerのoverride不要)

# environment.rb
  config.logger = Logger.new(config.log_path) 
  config.logger.formatter = Logger::Formatter.new
  config.logger.datetime_format = "%Y-%m-%d %H:%M:%S"
D, [2010-03-08 03:07:33#5394] DEBUG -- :   Memo Load (1.2ms)   SELECT * FROM "memos" LIMIT 10 OFFSET 0
I, [2010-03-08 03:07:33#5394]  INFO -- : Rendering template within layouts/application
I, [2010-03-08 03:07:33#5394]  INFO -- : Rendering memos/index
D, [2010-03-08 03:07:33#5394] DEBUG -- : Rendered memos/_sidebar (0.7ms)
I, [2010-03-08 03:07:33#5394]  INFO -- : Completed in 147ms (View: 140, DB: 1) | 200 OK [http://localhost/memos]


以下、試行錯誤及び、もっとカスタマイズしたい場合の書き方。


Logger::Formatterを追加

apiを見てみると、Formatterというクラスがあるらしい。

早速追加。

# environment.rb
  config.logger = Logger.new(config.log_path) 
  config.logger.formatter = Logger::Formatter.new

Processing MemosController#index (for 127.0.0.1 at 2010-03-08 01:00:46) [GET]
D, [2010-03-08T01:00:46.375185 #74468] DEBUG -- :   Memo Load (0.6ms)   SELECT * FROM "memos" LIMIT 10 OFFSET 0
I, [2010-03-08T01:00:46.376684 #74468]  INFO -- : Rendering template within layouts/application
I, [2010-03-08T01:00:46.376992 #74468]  INFO -- : Rendering memos/index
D, [2010-03-08T01:00:46.393688 #74468] DEBUG -- : Rendered memos/_sidebar (0.1ms)
I, [2010-03-08T01:00:46.394937 #74468]  INFO -- : Completed in 25ms (View: 19, DB: 1) | 200 OK [http://localhost/memos]

おお、それっぽい。


Loggerのソースを見てみる

apiにはcallメソッドがあると記載されていたので、ソースを見てみた。

/opt/local/lib/ruby/gems/1.8/gems/activesupport-2.3.5/lib/active_support/core_ext/logger.rb

/opt/local/lib/ruby/1.8/logger.rb

class Logger
  class Formatter
    Format = "%s, [%s#%d] %5s -- %s: %s\n"


    def call(severity, time, progname, msg)
      Format % [severity[0..0], format_datetime(time), $$, severity, progname,
        msg2str(msg)]
    end

あった、これっぽい。


Railscastsのようにしてみる

# environment.rb
class Logger
  class Formatter
    def call(severity, time, progname, msg)
      "#{time.to_s(:db)} #{severity} -- #{msg}\n"
    end
  end
end
Processing MemosController#index (for 127.0.0.1 at 2010-03-08 01:07:17) [GET]
2010-03-08 01:07:17 DEBUG --   Memo Load (1.1ms)   SELECT * FROM "memos" LIMIT 10 OFFSET 0
2010-03-08 01:07:18 INFO -- Rendering template within layouts/application
2010-03-08 01:07:18 INFO -- Rendering memos/index
2010-03-08 01:07:18 DEBUG -- Rendered memos/_sidebar (0.7ms)
2010-03-08 01:07:18 INFO -- Completed in 135ms (View: 128, DB: 1) | 200 OK [http://localhost/memos]

おおお、おしい。


ようやく完成

やっぱミリ秒出したり、プロセスIDも出力したいので

デフォルトの内容に若干手を入れることにした。

# environment.rb
class Logger
  class Formatter
    def call(severity, time, progname, msg)
      format = "[%s #%d] %5s -- %s: %s\n"
      format % ["#{time.strftime('%Y-%m-%d %H:%M:%S')}.#{'%06d' % time.usec.to_s}",
                  $$, severity, progname, msg2str(msg)]
    end
  end
end

Formatは使用しているので、format変数を別途定義。


Processing MemosController#index (for 127.0.0.1 at 2010-03-08 02:27:12) [GET]
[2010-03-08 02:27:12.854925 #95499] DEBUG -- :   Memo Load (1.1ms)   SELECT * FROM "memos" LIMIT 10 OFFSET 0
[2010-03-08 02:27:12.938415 #95499]  INFO -- : Rendering template within layouts/application
[2010-03-08 02:27:12.938720 #95499]  INFO -- : Rendering memos/index
[2010-03-08 02:27:12.973117 #95499] DEBUG -- : Rendered memos/_sidebar (0.8ms)
[2010-03-08 02:27:12.976412 #95499]  INFO -- : Completed in 128ms (View: 120, DB: 1) | 200 OK [http://localhost/memos]

あと

log4rも試してみたいところ。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100310

2010-03-09Logger!!!

Logger!!!

| Logger!!! - うんたらかんたらRuby を含むブックマーク はてなブックマーク - Logger!!! - うんたらかんたらRuby

Railscasts - The Logger

より


ログのカラーリングをオフ

ansi escape sequenceを非表示にしたい場合

(本番環境では不要という場合など)

#environment.rb
  config.active_record.colorize_logging = false

script/console

script/console時にログを標準出力にする方法。

.irbrc

if ENV.include?('RAILS_ENV') && !Object.const_defined?('RAILS_DEFAULT_LOGGER')
  require 'logger'
  RAILS_DEFAULT_LOGGER = Logger.new(STDOUT)
end

最近script/consoleをよく使用するようになって

SQLのログが掃かれるようにしたいなぁと、思っていたところ。

ありがたい。


ログを空(0bytes)にする

rake log:clear


ログのフォーマットの変更方法

やってみるも、、、
# environment.rb
class Logger
  def format_message(level, time, progname, msg)
    "#{time.to_s(:db)} #{level} -- #{msg}\n"
  end
end

おお、やっぱ簡単にできるんだなと早速やってみたが、、、

変化無し。

どうやらRails1.x時代のやり方らしい。

Processing MemosController#index (for 127.0.0.1 at 2010-03-08 01:06:09) [GET]
  Memo Load (1.1ms)   SELECT * FROM "memos" LIMIT 10 OFFSET 0
Rendering template within layouts/application
Rendering memos/index
Rendered memos/_sidebar (0.7ms)
Completed in 126ms (View: 119, DB: 1) | 200 OK [http://localhost/memos]

いろいろ調べて

フォーマットの変更は思うようにできたけど纏めんの面倒なので

続きは明日。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100309

2010-03-08script/console

script/console

| script/console - うんたらかんたらRuby を含むブックマーク はてなブックマーク - script/console - うんたらかんたらRuby

Railscasts - Console Tricks

より


期待せずに見たが、知らんことだらけだった。


--sandbox

rollbackしてくれると。

$ ruby script/console --help
Usage: console [environment] [options]
    -s, --sandbox                    Rollback database modifications on exit.
        --irb=[irb]                  Invoke a different irb.
        --debugger                   Enable ruby-debugging for the console.


y

なんぞ。yamlに変換してくれるんか。

>> m = Memo.all
=> [#<Memo id: 1, title: "a", body: "", created_at: "2010-02-28 06:56:26", updated_at: "2010-02-28 06:56:26">, #<Memo id: 2, title: "aaa", body: "bbb", created_at: "2010-02-28 06:56:39", updated_at: "2010-02-28 06:56:39">]
?> y m
--- 
- !ruby/object:Memo 
  attributes: 
    created_at: 2010-02-28 06:56:26
    title: a
    body: ""
    updated_at: 2010-02-28 06:56:26
    id: "1"
  attributes_cache: {}

- !ruby/object:Memo 
  attributes: 
    created_at: 2010-02-28 06:56:39
    title: aaa
    body: bbb
    updated_at: 2010-02-28 06:56:39
    id: "2"
  attributes_cache: {}

=> nil

ri y

--------------------------------------------------------------- Kernel#y
     y( object, *objects )
------------------------------------------------------------------------
     Prints any supplied _objects_ out in YAML. Intended as a variation
     on +Kernel::p+.

       S = Struct.new(:name, :state)
       s = S['dave', 'TX']
       y s

     _produces:_

       --- !ruby/struct:S
       name: dave
       state: TX

methods検索

controllerの〜pathってなんだっけって時はgrepも使える。

?> app.class
=> ActionController::Integration::Session

?>app.methods.grep(/_path$/).sort
=> ["edit_memo_path", "edit_polymorphic_path", "formatted_edit_memo_path", "formatted_memo_path", "formatted_memos_path", "formatted_new_memo_path", "formatted_polymorphic_path", "hash_for_edit_memo_path", "hash_for_memo_path", "hash_for_memos_path", "hash_for_new_memo_path", "memo_path", "memos_path", "new_memo_path", "new_polymorphic_path", "polymorphic_path"]

_

前回の実行結果が出力可能

>> y _
--- 
- edit_memo_path
- edit_polymorphic_path
- formatted_edit_memo_path
- formatted_memo_path
- formatted_memos_path
- formatted_new_memo_path
- formatted_polymorphic_path
- hash_for_edit_memo_path
- hash_for_memo_path
- hash_for_memos_path
- hash_for_new_memo_path
- memo_path
- memos_path
- new_memo_path
- new_polymorphic_path
- polymorphic_path
=> nil
トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100308

2010-03-07rescue_action_in_publicを使用したエラーハンドリング

rescue_action_in_publicを使用したエラーハンドリング

| rescue_action_in_publicを使用したエラーハンドリング - うんたらかんたらRuby を含むブックマーク はてなブックマーク - rescue_action_in_publicを使用したエラーハンドリング - うんたらかんたらRuby

Railscasts - Handling Exceptions


rescue_action_in_publicが紹介されていた。

以前、存在しないpathを指定した際に404を出力させる方法 - うんたらかんたらRuby - Rubyist

で書いた方式とは別に、既存のrescue_action_in_publicをoverrideさせることで実現している。


やり方

# controller
  protected
  def local_request?
    false
  end

  def rescue_action_in_public(exception)
    case exception
    when ActiveRecord::RecordNotFound
      render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
    else
      super
    end
  end

よくわからんのでactionpackのソースを見てみた

local_request?って何よ(まぁメソッド名で想像つくけど)。

/opt/local/lib/ruby/gems/1.8/gems/actionpack-2.3.5/lib/action_controller/rescue.rb

rescue_action_in_public
      # Overwrite to implement public exception handling (for requests
      # answering false to <tt>local_request?</tt>).  By default will call
      # render_optional_error_file.  Override this method to provide more
      # user friendly error messages.
      def rescue_action_in_public(exception) #:doc:
        render_optional_error_file response_code_for_rescue(exception)
      end

コメントにも、overrideする際は、local_request?をfalseにしろと。


render_optional_error_file

一応デフォで呼ばれるrender_optional_error_file

      # Attempts to render a static error page based on the
      # <tt>status_code</tt> thrown, or just return headers if no such file
      # exists. At first, it will try to render a localized static page.
      # For example, if a 500 error is being handled Rails and locale is :da,
      # it will first attempt to render the file at <tt>public/500.da.html</tt>
      # then attempt to render <tt>public/500.html</tt>. If none of them exist,
      # the body of the response will be left empty.
      def render_optional_error_file(status_code)
        status = interpret_status(status_code)
        locale_path = "#{Rails.public_path}/#{status[0,3]}.#{I18n.locale}.html" if I18n.locale
        path = "#{Rails.public_path}/#{status[0,3]}.html"

        if locale_path && File.exist?(locale_path)
          render :file => locale_path, :status => status, :content_type => Mime::HTML
        elsif File.exist?(path)
          render :file => path, :status => status, :content_type => Mime::HTML
        else
          head status
        end
      end

噂のlocal_request?
      # True if the request came from localhost, 127.0.0.1. Override this
      # method if you wish to redefine the meaning of a local request to
      # include remote IP addresses or other criteria.
      def local_request? #:doc:
        request.remote_addr == LOCALHOST && request.remote_ip == LOCALHOST
      end

rescue_action_without_handler

local_request?の呼出し元。

      def rescue_action_without_handler(exception)
        log_error(exception) if logger
        erase_results if performed?

        # Let the exception alter the response if it wants.
        # For example, MethodNotAllowed sets the Allow header.
        if exception.respond_to?(:handle_response!)
          exception.handle_response!(response)
        end

        if consider_all_requests_local || local_request?
          rescue_action_locally(exception)
        else
          rescue_action_in_public(exception)
        end
      end

ここでlocal用とそれ以外でエラーのレンダリングを分けている。(動画にも出てくるが一応試してみた。後述。)

どうやら、consider_all_requests_localってのは、

config/environments/development.rb などの各環境で定義している↓らしい。

config.action_controller.consider_all_requests_local = true

つまり、これとlocalhost(127.0.0.1)両方見て判断している。



rescue_action_locally
      # Render detailed diagnostics for unhandled exceptions rescued from
      # a controller action.
      def rescue_action_locally(exception)
        @template.instance_variable_set("@exception", exception)
        @template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
        @template.instance_variable_set("@contents",
          @template.render(:file => template_path_for_local_rescue(exception)))

        response.content_type = Mime::HTML
        render_for_file(rescues_path("layout"),
          response_code_for_rescue(exception))
      end

rescue_action

一応更なる呼出し元。

rescue_action > rescue_action_without_handler > rescue_action_without_handler

      # Exception handler called when the performance of an action raises
      # an exception.
      def rescue_action(exception)
        rescue_with_handler(exception) ||
          rescue_action_without_handler(exception)
      end



個人的には

rescue_action_in_publicは、overrideされることが前提という印象を持った。

local_request?のoverrideも実害無いし、そもそもどのエラーを表示するかっていうのは

environments以下で定義できるので、この方式でいいような気がする。


例外処理なんて当然どこでもやってるんだろうから、ossのコードでもあとで見てみるか。

実践レベルのベストプラクティス欲しいなぁ。既にあるのかな。

あと、気軽にoverrideできるけど、やってokかどうかの判断っつーの難しいなぁ。



おまけ

rescue_action_locallyとrescue_action_in_publicをそれぞれ実行してみた。

local

config.action_controller.consider_all_requests_local = true で実行。

f:id:rochefort:20100307153404j:image


public

config.action_controller.consider_all_requests_local = false かつ

local_request?をoverrideして実行。

f:id:rochefort:20100307153405p:image

2010-03-06named routesメモ

named routesメモ

| named routesメモ - うんたらかんたらRuby を含むブックマーク はてなブックマーク - named routesメモ - うんたらかんたらRuby

Railscasts - Named Routes

Railscasts - Custom REST Actions

より


なんとなくrails使ってると(scaffoldすると自動でmap.resourcesされるから)〜path/〜urlって時々出てくるけど

そこらへんは、routes.rbで設定してたと。


メモ

map.home "", :controller => 'hoge', :action => 'index'
→home_url  #/
  home_path #http://localhost:3000
map.fuga_archive 'tasks/:year/:month', :controller => 'fuga', :action => 'archive'
→fuga_archive_path
  fuga_archive_url
map.resources :projects
→project_path(project)
  edit_project_path(project)
  project_path(project), :method => :delete  
  new_project_path

map.resourcesの書き方。collection/memberを指定。

map.resources :tasks, :collection => { :completed => :get }, :member => { :complete => :put }
→complete_task_path(task)
  completed_tasks_path
トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100306

2010-03-05Ruby on Rails ステップ・バイ・ステップ - STEP12〜14

Ruby on Rails ステップ・バイ・ステップ - STEP12〜14

| Ruby on Rails ステップ・バイ・ステップ - STEP12〜14 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - Ruby on Rails ステップ・バイ・ステップ - STEP12〜14 - うんたらかんたらRuby

STEP 12: 確認画面(2) - Ruby on Rails ステップ・バイ・ステップ - Ruby on Rails with OIAX

STEP 13: 確認画面(3) - Ruby on Rails ステップ・バイ・ステップ - Ruby on Rails with OIAX

STEP 14: 確認画面(4) - Ruby on Rails ステップ・バイ・ステップ - Ruby on Rails with OIAX


今回、個人的には凄く参考になった。


STEP12

確認画面のhidden値設定はこういう書き方もできると。

  <% [ :subject, :due_date, :done, :note].each do |attr| %>
    <%= f.hidden_field attr -%>
  <% end %>

STEP13

確認画面に「戻る」ボタンを追加。

new で post可となるようにroute.rbを修正し

controller#new でpostの時のみhidden値をattributesに設定するようにする。


STEP14

newでやったことをeditでもできるように変更。

基本はSTEP13までの内容と同等。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100305

2010-03-04previewボタンを付ける方法

previewボタンを付ける方法

| previewボタンを付ける方法 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - previewボタンを付ける方法 - うんたらかんたらRuby

Railscasts - Multibutton Form

より。

previewは結構使うんじゃないかな。


やってみた

view

通常のsubmitに加えprevie_buttonというnameで追加。

表示部は、params[:preview_button]がある場合のみ表示ロジックを記載。

<% if params[:preview_button] %>
  <%= textilize @project.description %>
<% end %>
...
<%= submit_tag 'Create' %>
<%= submit_tag 'Preview', :name => 'preview_button' %>

controller

params[:preview_button]がある場合はnewへrender。

def create
  @project = Project.new(params[:project])
  if params[:preview_button] || !@project.save
    render :action => 'new'
  else
    flash[:notice] = "Successfully created project."
    redirect_to project_path(@project)
  end
end

更新(edit/update)も同様の方法で実装可能。

動画ではform_remote_for / submit_to_remote も紹介。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100304

2010-03-03rvmを入れてみた

rvmを入れてみた

| rvmを入れてみた - うんたらかんたらRuby を含むブックマーク はてなブックマーク - rvmを入れてみた - うんたらかんたらRuby

authorNariさんが紹介されていて

気になったのでインストールしてみた。

今まで1.9の動きを確認する際はubuntu上げて確認してたけど、

rvmだどすぐに切り替えれて便利。

まだ、使い込んでないけど、かなりよさげ。


install

rvm: Ruby Version Manager - Installing RVM

いくつかinstall方法があるようですが、これが推奨とのこと。

mkdir -p ~/.rvm/src/ && cd ~/.rvm/src && rm -rf ./rvm/ && git clone --depth 1 git://github.com/wayneeseguin/rvm.git && cd rvm && ./install

.bashrc / .bash_profile に追記

if [[ -s /Users/rochefort/.rvm/scripts/rvm ]] ; then source /Users/rochefort/.rvm/scripts/rvm ; fi


1.9.1のinstall&切り替え

rvm: Ruby Version Manager - RVM Basics

どこにインストールされるかというと

~/.rvm下。

なので環境がcleanダヨ。

$ rvm install 1.9.1; rvm 1.9.1

$ ruby -v
ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-darwin9.8.0]
rvm info
rochefort:~ rochefort$ rvm info
system:
  uname:        "Darwin rochefort-mini.local 9.8.0 Darwin Kernel Version 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386 i386"
  shell:        "bash"
  version:      "3.2.17(1)-release"

ruby:
  interpreter:  "ruby"
  version:      "1.9.1p378"
  date:         "2010-01-10"
  platform:     "i386-darwin9.8.0"
  patchlevel:   "2010-01-10 revision 26273"
  full_version: "ruby 1.9.1p378 (2010-01-10 revision 26273) [i386-darwin9.8.0]"

homes:
  gem:          "/Users/rochefort/.rvm/gems/ruby-1.9.1-p378"
  ruby:         "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378"

binaries:
  ruby:         "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378/bin/ruby"
  irb:          "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378/bin/irb"
  gem:          "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378/bin/gem"
  rake:         "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378/bin/rake"

environment:
  GEM_HOME:     "/Users/rochefort/.rvm/gems/ruby-1.9.1-p378"
  GEM_PATH:     "/Users/rochefort/.rvm/gems/ruby-1.9.1-p378:/Users/rochefort/.rvm/gems/ruby-1.9.1-p378%global"
  BUNDLE_PATH:  "/Users/rochefort/.rvm/gems/ruby-1.9.1-p378"
  MY_RUBY_HOME: "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378"
  IRBRC:        "/Users/rochefort/.rvm/rubies/ruby-1.9.1-p378/.irbrc"

戻すには
$ rvm system
$ ruby -v
ruby 1.8.7 (2009-06-12 patchlevel 174) [i686-darwin9]

他にも

.rvmrcを用意しておけばディレクトリ毎に切り替えも可能らしい。

rvm list known とかやるとinstall可能な一覧が参照できる。

トラックバック - http://rubyist.g.hatena.ne.jp/rochefort/20100303

2010-03-02存在しないpathを指定した際に404を出力させる方法

存在しないpathを指定した際に404を出力させる方法

| 存在しないpathを指定した際に404を出力させる方法 - うんたらかんたらRuby を含むブックマーク はてなブックマーク - 存在しないpathを指定した際に404を出力させる方法 - うんたらかんたらRuby

routing errorやだな、どうやってやるんだろうかと悩んでいたところ

下記を見つけました。

ありがとうございます。


参考

Railsでカスタムな404エラーのページを出力する方法 - YomuKaku Memo


やってみた

route.rb
  map.connect '*path', :controller => 'application', :action => 'error_404'


application_controller

rescue_fromと組み合わせて、404用テンプレートをlayout無しで表示。

  rescue_from ActiveRecord::RecordNotFound, :with => :error_404
  rescue_from ActionController::UnknownAction, :with => :error_404
  def error_404
    render :template => '/shared/error_404', :layout => false, :status => 404
  end

こんな感じ

f:id:rochefort:20100302233340p:image


f:id:rochefort:20100302233608p:image


2010/03/06追記

*pathについて記載されてた

Railscasts - Catch-all Route


2010/03/07追記

rescue_action_in_publicを使用したエラーハンドリング - うんたらかんたらRuby - Rubyistより

こっちでやると、こんな感じ。

config/environments/*rb

config.action_controller.consider_all_requests_local にて制御。

trueだとlocal用、falseだとpublic用。

  protected
  def local_request?
    false
  end

  def rescue_action_in_public(exception)
    case exception
    when ActiveRecord::RecordNotFound, 
      ActionController::RoutingError,
      ActionController::UnknownAction
      render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
      #render :template => '/shared/error_404', :layout => false, :status => 404
    else
      super
    end
  end

2010-03-01Web App Themeを試してみた

Web App Themeを試してみた

| Web App Themeを試してみた - うんたらかんたらRuby を含むブックマーク はてなブックマーク - Web App Themeを試してみた - うんたらかんたらRuby

やっぱ角丸CSSとかrailsアプリに組み込みたいよなぁ、などと思っていたところ

Web App Themeを試していなかったのを思い出したので

やってみた。


pilu's web-app-theme at master - GitHub

Web App Theme

→デモ。右のサイドバーでテーマを変更して確認できる。


install

gemとpluginが用意されているが、とりあえずプロジェクト依存なのでpluginでインストール。

gemの場合はenvironment.rbへversion追記)

sudo gem install web-app-theme -s http://gemcutter.org
script/plugin install git://github.com/pilu/web-app-theme.git

Theme Generator

まぁ、readmeに載ってますが。

layout

第1引数はlayout名。引数無しなら、application.html.erbとなる。

script/generate theme

(application.html.erbとして使用する場合は、各モデル用のlayoutファイルを削除)


theme

テーマ名を指定。

script/generate theme --theme="drastic-dark"

上のデモと同等のhtml

vendor/plugins/web-app-theme/index.html

にあるので、そこでテーマ名が確認できます。(現在12種)

上記コマンドを再度入力することで途中で変更も可能。

public/stylesheets/themes 以下にcssがそれぞれ格納されます。


アプリケーション名
script/generate theme --app_name="My New Application"

Themed Generator

テーマ用のviewに(デモページのように)書き換えてくれます。


model名の複数形をcontroller名にしている場合(scaffoldなど通常のrailsの場合)は

第1引数にcontroller名を設定する。

script/generate themed posts # you have a model named Post and a controller named PostsController

modelとcontrollerが違う場合は

第1引数にcontroller名、第2引数にmodel名を設定する。

script/generate themed items post

will_paginate

なんとwill_paginate用のオプションも用意されています。

script/generate themed items post --with_will_paginate

エラーメッセージをform内にしたい場合

environment.rb

ActionView::Base.field_error_proc = Proc.new do |html_tag, instance| 
  if html_tag =~ /<label/
    %|<div class="fieldWithErrors">#{html_tag} <span class="error">#{[instance.error_message].join(', ')}</span></div>|
  else
    html_tag
  end
end|

など、一部端折りましたが

初期から導入するのがいいんじゃないでしょうか。

こんな感じで。

rails 〜
script/generat scaffold hoge 〜
rake db:migrate
script/plugin install git://github.com/pilu/web-app-theme.git
script/generate theme --app_name="My New Application"
script/generate themed hoges


#以下、pagingが必要であれば
script/generate themed hoges --with_will_paginate
#ってやって、
#後は通常のpaginateの実装にしてやればok。

#environment.rb
require 'will_paginate'

#controller
@hoges = Hoge.paginate(:per_page => 10, :page => params[:page])

参考

Railsのクールなデザインを一瞬で自動生成する「Web App Theme」がステキ - 医者を志す妻を応援する夫の日記