Rails4でトークン認証のアクションに対してCSRFを無効にする

Railsはprotect_from_forgeryって書くだけでCSRF対策が有効になってマジ便利なわけだけど、セッションで認証するんじゃなくてiOSから呼ぶAPIとかでトークン使って認証する場合はCSRFの対策いらないので無効にしたい。

その場合は

protect_from_forgery with: :null_session

ってすればいいらしい。これはCSRF Tokenが一致しなかった場合に例外を投げるんじゃなくてセッションを空にするという動作になる。

ちなみにprotect_from_forgeryのデフォルトは:null_sessionなので

protect_from_forgery

でもよさそう(Rails 4.0.0現在)

ただし最初は以下のようになってるので明示的に変更する必要はある。

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
end

RubyMotionでブロック中のblock_given?の値が変わる

これではまった。
https://gist.github.com/ainame/5651453

yieldも同じようにダメっぽい。

def callback_caller
  callback_caller2 do
    yield
  end
end
 
def callback_caller2(&block)
  block.call
end
 
callback_caller do
  p 'origin'
end
# CRuby 1.9.3
$ ruby sample.rb
"origin"

# ruby motion 2.5
$ /Library/RubyMotion/bin/ruby sample.rb 
2013-07-25 22:59:58.924 ruby[89777:707] /Users/hokamura/Works/tmp/sample.rb:3:in `block': no block given (LocalJumpError)
        from /Users/hokamura/Works/tmp/sample.rb:8:in `callback_caller2'
        from /Users/hokamura/Works/tmp/sample.rb:2:in `callback_caller'
        from /Users/hokamura/Works/tmp/sample.rb:11:in `<main>'
/Users/hokamura/Works/tmp/sample.rb:3:in `block': no block given (LocalJumpError)
        from /Users/hokamura/Works/tmp/sample.rb:8:in `callback_caller2'
        from /Users/hokamura/Works/tmp/sample.rb:2:in `callback_caller'
        from /Users/hokamura/Works/tmp/sample.rb:11:in `<main>'

ちなみにcallback_caller2の処理をyieldにするとRubyMotionでもいける。

def callback_caller
  callback_caller2 do
    yield
  end
end
 
def callback_caller2
  yield
end
 
callback_caller do
  p 'origin'
end
# ruby motion 2.5
$ /Library/RubyMotion/bin/ruby sample.rb
"origin" 

うーむ・・。

delegateでclickイベントを拾う場合のtap-highlight-color

iOSとかでリンクをクリックするときにグレイにハイライトされるけど、clickイベントでも特定の条件を満たせばハイライトされる(対象の要素がリフローしない場合とかだった気がするけど詳しくは覚えてない)。

ハイライトされるのはいいとして、delegate次のようにイベント貼ったとして

<div class="box">
  <span class="btn">click</span>
</div>
$('.box').delegate('.btn', 'click', function() {
  console.log('clicked!');
});

.btnをクリックすると.boxの領域がハイライトされて嬉しくない。

ハイライト出さなくてよければCSS

.box {
  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}

とか指定すればいいんだけど、ハイライトは出したいけどクリックされた要素にちゃんとハイライト出したいという場合はこれじゃダメなので、delegateせずに.btnにclickイベント貼ることでしか解決しないかな。

$('.box .btn').click(function() {
  console.log('clicked!');
});

Backbone.Viewのeventsとかだと全部delegateになるし、けっこう困る。

PostgreSQLでwhereの値にダブルクォートでエラる

Railsで以下のようなコード書いてて

entries.where('url != ""')

MySQLSQLiteでは動いてたんだけどPostgreSQLに移行したらエラって動かなくなった。

MySQL uses ' or " to quote values (i.e. WHERE name = "John"). This is not the ANSI standard for databases. PostgreSQL uses only single quotes for this (i.e. WHERE name = 'John'). Double quotes are used to quote system identifiers; field names, table names, etc. (i.e. WHERE "last name" = 'Smith').

http://wiki.postgresql.org/wiki/Things_to_find_out_about_when_moving_from_MySQL_to_PostgreSQL

ということらしい。

とりあえずこれで直った。

entries.where("url != ''")

apt-get install node

ubuntu

apt-get install node

でインストールしたんですけどNode.jsが動きません!と言われてなんでかよくわからなかったんだけど、「node」だとこれが入るらしい
http://packages.ubuntu.com/lucid/hamradio/node

ただしくは

$ apt-get install nodejs

node のほうでもインストールは成功し、インストールされた node コマンド実行しても何も起きない(エラーすらでない)のでこれは知らないとはまる。

Node.jsでロードパスをプログラム内から追加する

その昔はrequire.pathsというのがあってプログラム内からロードパスを設定できたんだけど、いつしかrequire.pathsは廃止され、NODE_PATHで設定するしかなくなった。

でもNODE_PATHだとプログラムの中からロードパスを変更することができない。

// libにパスを通して./lib/foo.jsをrequire('foo')で読み込みたい
process.env['NODE_PATH'] = __dirname + '/lib';
require('foo'); // Error

とやっても ./lib/foo.js は読み込めない。でも環境変数いちいち指定するのめんどいよ!ってこともある。

そこでソース読んでたらModule._initPathsってのを呼べばNODE_PATHを見てパスを再設定してくれそうだなーと思ってやってみたらできた。

process.env['NODE_PATH'] = __dirname + '/lib';
require('module')._initPaths();
require('foo'); // Success

現状の最新の安定バージョン(v0.10.7)で動くのを確認した。

ただしアンドキュメントだし全くもって正攻法ではないので使う場合はその辺をわかった上で使いましょう。むしろ使わないにこしたことはない。

RailsでModelからselectボックスをつくる

collection_selectを使うといい感じにできる。例えば、あるグループに所属しているユーザーをプルダウンで選択させたい場合はこんな感じ。

<%= f.collection_select :user_id, @group.users.all, :id, :name %>

ってやるとuser.idがoptionのvalue、user.nameがoptionの中身になる。結果はこんな感じ。

<select id="user_id" name="task[user_id]">
  <option value="1">user1</option>
  <option value="2">user2</option>
</select>

デフォルトのテキストも表示したい場合はinclude_blankオプションを設定する。

<%= f.collection_select :assigned_user_id, @group.users.all, :id, :name, include_blank: '選択してね!' %>