yo_waka's blog

418 I'm a teapot

ActiveRecordで日付の範囲指定

ActiveSupportのコア拡張を使うとめちゃ綺麗に書けて感動した。

  • 今日
from = Time.now.at_beginning_of_day
to   = from + 1.day

items = Item.where(created_at: from...to).order(created_at: :desc)
  • 今月
from = Time.now.at_beginning_of_month
to   = from + 1.month

items = Item.where(created_at: from...to).order(created_at: :desc)

RailsのセッションをJSONで

Railsのセッション情報はこちらの記事にあるようにRubyのMarshalでシリアライズされ、Base64エンコードされたものがクッキーなどに保存される。

ただ、Ruby以外では復元が難しいため、Node.jsなど別言語で書かれたサーバーアプリケーションとセッションを共有しづらい。
なので、こちらの記事を参考にJSONで保存できるようにしてみた(というかほとんどそのまま)。

セッション情報の保存先にはredis-storeライブラリを使ってRedisに保存する。
redis-storeは、RubyGemsにあがっているものだとRails4で動かない(Rails3でもredisライブラリのバージョンが新しいと動かない)。
GitHubを見てみたら、forkされたものが最近のRackのバージョンアップ含めてRails4対応していたので、それを使う。

Gemfile

gem 'redis'
gem 'redis-store', github: 'bricker/redis-store'
gem 'redis-rails', github: 'bricker/redis-store'

config/initializer/session_store.rb

require 'action_dispatch/middleware/session/redis_store'

#
# デフォルトだとRubyのMarshalでセッションオブジェクトを保存するため、Node.js側で扱えない
# JSONで保存するためにモンキーパッチをあてる
#
module ActionDispatch
  module Session
    class RedisStore
      # override
      def get_session(env, sid)
        with_lock(env, [nil, {}]) do
          unless sid and session = @pool.get(sid)
            sid, session = generate_sid, {}
            unless /^OK/ =~ @pool.set(sid, JSON.generate(session))
              raise "Session collision on '#{sid.inspect}'"
            end
          end
          session = JSON.parse(session) unless session == {}
          if session.has_key?('flash')
            session['flash'] = ActionDispatch::Flash::FlashHash.new.update(Hash[*session['flash']])
          end
          [sid, session]
        end
      end

      # override
      def set_session(env, session_id, new_session, options)
        with_lock(env, false) do
          jsonize_session = JSON.generate(new_session)
          @pool.set session_id, jsonize_session, options
          session_id
        end
      end
    end
  end
end

# セッションの生存期間は1週間
Sample::Application.config.session_store :redis_store, servers: {
  host: "localhost",
  port: 6379,
  namespace: "_sessions",
  db: 10,
  expire_in: 60 * 60 * 24 * 7
}

redis-store、もう1年くらい更新されてないしissuesもpull reqも溜まってるけど作者の人やる気もうないのかな。。。

Railsでセレクトボックスの出力

selectとかselect_tagとかcollection_selectとかいろいろあってオプションの指定も微妙に違ってたり分かりにくい。
ActiveRecordの配列と普通の配列を同じように扱えて、name属性を自由に定義したい。
いろいろ試してみたところ、select_tagとoptions_for_selectを使うのがよさげな感じがした。

<!-- ActiveRecordの配列を受け取る -->
<%= select_tag "user", options_for_select(@users.map {|u| [u.name, u.id]}), prompt: t('user') %>
<!-- 配列([name, id])を受け取る -->
<%= select_tag "status", options_for_select(@statuses), prompt: t('status') %>

selectとcollection_selectはname属性が"オブジェクト名[プロパティ名]"になる。
promptは未選択時の項目、空欄でよければinclude_blankをオプションに指定する。

デザイン変えた

テーマストア見てたらなんか意欲が湧いてきたのでデザインを弄ってみた。

ブログのテーマデザインを弄くるのは楽しい。
まだプログラムの勉強やり始めてまもない頃、バイトでMTのテーマとかプラグインやら作ってたのを思い出す。
そういえばあのころ、ブログが広まりだしたころって3カラム全盛期だったなあ。
今はあまり見かけないけど、よく調整されたやつは下にスクロールすると1カラムぽく見えるので、2カラムを下にスクロールするのに比べて違和感なくて好きだった。

vagrantとknife-soloで開発環境構築を自動化するやつ

最近vagrantとknife-soloで、ってのが流行ってるみたいなのでやってみた。
waka/auto-dev-env

ベースBoxは、vagrantbox.esからUbuntu12.10 server minimalってのを落とせるけど、knife-soloはOmibus Chef Packagingのものをインストールしてくれるのでchefは入ってる必要なかったり、Boxのディスクサイズが10GBと少なかったので、Veeweeを使って作った。
vagrantはポートフォワーディングの設定が簡単で便利ですね。

とにかく、これでムシャクシャしたとき「sudo rm -rf /」がいつでも出来るようになったぞ!

Opscodeのレシピ見ててもRedhat系かDebian系かで動かなくなりそうなもの多いので、もし作ったレシピを配布して開発環境合わせるとかやろうとすると、実行するマシンのOSと仮想環境のディストリビューションを統一するのが大事なんだろうな。