今更ながらシリーズ(2) StringInquirer

Rails.env == 'production'

という式は、

Rails.env.production?

と書ける。これは Rails.env が単なる文字列ではなく、ActiveSupport::StringInquirer という String を継承したクラスに変換されているから。

ActiveSupport.StringInquirer.new("foo").foo? #=> true

ちなみに、Rails 3.1 では、String#inquiry で String から StringInquirer を生成できる。

Rails.version      # => "3.1.0.beta1" 
s = "hoge".inquiry # => "hoge" 
s.class            # => ActiveSupport::StringInquirer
s.hoge?            # => true 
s.fuga?            # => false 

Rails3 で routes.rb を分割・追加する

故あって routes.rb を routes/frontend.rb と routes/backend.rb に分割したいとする。

とりあえず、 routes.rb の中で require なり load なりしてもよいが、これだと開発中に routes/frontend.rb を編集しても、再起動しない限り反映されない。

SampleApp::Application.routes.draw do
 # :
 # :
end

require_relative 'routes/frontend.rb'
require_relative 'routes/backend.rb'

そうしないためには、該当のファイルを routes ファイルパス設定に追加する。

config/application.rb の中で以下のように記述する。

module SampleApp
  class Application < Rails::Application
    # :
    # : その他の設定
    # :
    
    config.paths.config.routes.concat Dir[Rails.root.join("config/routes/*.rb")]
  end
end

あるいは追加したいパスが一つならば、 config.paths.config.routes << Rails.root.join("config/routes/aroute.rb") としてもよいようだ。
(config.paths.config.routes は Rails::Paths::Path なのでそのメソッドを参照。)

そして各ファイルに普通に routes.rb のようにルーティングを記述すればよい。

読み込まれる順番に注意。追加する際と 逆順で読み込まれる 。すなわち、先の例であれば以下のような順で load されることになる。

  1. config/routes/frontend.rb
  2. config/routes/backend.rb
  3. config/routes.rb

Pound で Proxy させるとデフォルトではバックエンドよりタイムアウトの設定が短い件

[UserAgent] - Pound - Varnish - AppServer

例えば↑のような HTTP Proxy が連なった構成のとき、AppServer がうっかり固まってると(ここでは Read timeout を想定)、プロキシがそれぞれタイムアウトするわけだけど、 Pound はデフォルト設定では 15 秒後に 500 (!!) レスポンスを返す。このデフォルト値は Varnish (60s) や Nginx (60s), Apache+mod_proxy (300s) のデフォルト値より短く、またそれらが返すステータスコード 503 と異なる。

返すステータスコードはパッチをあてない限り変更できないので、通常はタイムアウト値をそれらの設定より伸ばすしかない。

TimeOut 305

こういうこともあるので、HTTP Proxy のタイムアウト値の設定については、デフォルト値を使わずにちゃんと要件に合わせて設定を書く必要がある。(もちろん、タイムアウトにならないよう AppServer を十分速くするのは前提。)

escape_utils

escape_utils を以前紹介したけど、その時のバージョンはバグがあったので、0.2.1 以上にした方がよいです。特に日本語のリクエストがあった場合に、情報の欠落が発生する可能性があります。


gem "escape_utils", ">= 0.2.1"

あとは実は厳密には Rack の unescape_url とはエンコーディング関係で挙動違うんだけど、今のところそれは問題になっていないはず。

Model.scoped

ActiveRecord で Model の空のリレーションを作成するには、scoped メソッドを使うらしい。(WEB-DB PRESSに載ってたらしいけど引っ張り出せない。)

https://github.com/rails/rails/blob/master/activerecord/lib/active_record/named_scope.rb

ri scoped する。

Returns an anonymous scope.

  posts = Post.scoped
  posts.size # Fires "select count(*) from  posts" and returns the count
  posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects

  fruits = Fruit.scoped
  fruits = fruits.where(:colour => 'red') if options[:red_only]
  fruits = fruits.limit(10) if limited?

Anonymous scopes tend to be useful when procedurally generating complex
queries, where passing intermediate values (scopes) around as first-class
objects is convenient.

こんな感じか。

# model
class Item < ActiveRecord::Base
  # ..
  def self.rel(condition = nil)
    if condition.present?
      where(condition)
    else
      scoped
    end
  end
end
# controller
  cond = { :cond => :somthing }
  @items = Item.rel(cond).paginate(:page => 1) #...

Vim から Evernote にポスト

とりあえず今開いてるバッファの内容を Evernote に送り付けるスクリプト
_vimrc で設定しておけば一発で Evernote 行きだ。

nmap ,n :call WinVimToEver()<CR>

Mac だと全然違うアプローチが必要そうなので Win って接頭辞つけた。

winvimtoever.vim:

" winvimtoever.vim
" vim script, send current buffer to evernote

if !exists("g:winVimToEverUseFilename")
    let g:winVimToEverUseFilename = 0
endif

if !exists("g:winVimToEverPath")
    let g:winVimToEverPath = 'C:\Progra~1\Evernote\Evernote3.5\ENScript.exe'
endif

if !exists("g:winVimToEverNote")
    let g:winVimToEverNote = ""
end

if !exists("g:winVimToEverAttachment")
    let g:winVimToEverAttachment = 0
endif

function! WinVimToEver()

    if g:winVimToEverUseFilename
        let title = expand("%:t")
    else
        let title = iconv(getline(1), &fileencoding, "cp932")
    endif

    " TODO: tag
    "let tag = ...
    let tmpdir = tempname()
    call mkdir(tmpdir)

    let bodyfile = fnamemodify(tempname(), ":p:r").'.txt'
    let lines = getline(1, line("$"))
    let encoded = iconv(join(lines, "\r\n"), &fileencoding, "cp932")
    call writefile(split(encoded, "\r\n", 1), bodyfile, "b")

    let cmd = g:winVimToEverPath.' createNote /s '.shellescape(bodyfile).' /i '.shellescape(title)

    if g:winVimToEverAttachment
        let filename = expand("%:t")
        if filename == ''
            let filename = 'attachment.txt'
        end
        let tmpfile = tmpdir . '\' . filename
        call writefile(split(encoded, "\r\n", 1), tmpfile, "b")
        let cmd = cmd.' /a '.shellescape(tmpfile)
    end

    if g:winVimToEverNote
        let cmd = cmd.' /n '.shellescape(g:winVimToEverNote)
    endif

    let ret = system(cmd)
    " FIXME!
    call system("rmdir /S /Q ".shellescape(tmpdir))
    call delete(bodyfile)
endfunction

https://github.com/holysugar/winvimtoever に置きました。

参考にしたもの