Rails 2.0 で in_place_editor_field を使う

Chad Fowler 著「Rails レシピ」の「レシピ 01 同一画面でのフォーム編集」で取り上げられているインプレース編集機能。これは、あるレコードを変更するときに、変更画面に画面遷移することなく、その場で変更ができるという便利な機能だ。もっとも「Ajax らしい」機能の一つではないだろうか?

Rails 1.2 までは標準で in_place_editor_field というメソッドがあって、ビューの中で次のように使うことができた。

<%= javascript_include_tag :defaults %>
<%= in_place_editor_field :book, :name %>

この例では Book というモデルの name 属性をこの場で編集することを想定している。

Rails 2.0 になって、in_place_editor_field はプラグインに追い出された。次のようにインストールする。

% cd RAILS_ROOT
% ruby script/plugin install in_place_editing

さてここで大きな問題がひとつある。Rails 2.0 からは、デフォルトで POST リクエストに対しては認証トークンのチェックを行うようになった。これは CSRF 対策なのだが、どうやら in_place_editing プラグインはこれに対応していないようだ。そこで、InPlaceMacrosHelper#in_place_editor を書き換えて、認証トークンを送るようにしてみた。下のコードをconfig/environment.rb の一番下にコピ&ペーストすれば動くようになるはずだ。あるいは、プラグイン自体を改造してみてもよいだろう。

module InPlaceMacrosHelper
 def in_place_editor(field_id, options = {})
    function =  "new Ajax.InPlaceEditor("
    function << "'#{field_id}', "
    function << "'#{url_for(options[:url])}'"

    js_options = {}
    js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
    js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
    js_options['loadingText'] = %('#{options[:loading_text]}') if options[:loading_text]
    js_options['savingText'] = %('#{options[:saving_text]}') if options[:saving_text]
    js_options['rows'] = options[:rows] if options[:rows]
    js_options['cols'] = options[:cols] if options[:cols]
    js_options['size'] = options[:size] if options[:size]
    js_options['externalControl'] = "'#{options[:external_control]}'" if options[:external_control]
    js_options['loadTextURL'] = "'#{url_for(options[:load_text_url])}'" if options[:load_text_url]        
    js_options['ajaxOptions'] = options[:options] if options[:options]
    js_options['evalScripts'] = options[:script] if options[:script]
    callback = if options[:with]
       "function(form) { var params = #{options[:with]};"
    else
       "function(form) { var params = Form.serialize(form); "
    end
     callback += "params = (params ? params + '&' : '') + '#{request_forgery_protection_token}=' + encodeURIComponent('#{escape_javascript form_authenticity_token}'); return params;}" 
    js_options['callback']   = callback
    js_options['clickToEditText'] = %('#{options[:click_to_edit_text]}') if options[:click_to_edit_text]
    function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
    
    function << ')'

    javascript_tag(function)
  end
end