render_to_string の罠

rails 4.1.4

render_to_string(:index, layout: false) # => "文字列"

コンソールでの評価結果やメソッドの名前からは String が返ってくるように見える

でも実際は ActiveSupport::SafeBuffer が返ってくる

render_to_string(:index, layout: false).class # => ActiveSupport::SafeBuffer

さらに to_s で String にならない

render_to_string(:index, layout: false).to_s.class # => ActiveSupport::SafeBuffer

String を得るにはこうする必要があった

render_to_string(:index, layout: false).to_str

ツイートの内容を View に書いておいて Rubytter でツイートしようとしてハマった

text = render_to_string(:index, layout: false)
rubytter.update(text)

# NoMethodError: undefined method `each_byte' for nil:NilClass
# from /usr/local/rvm/rubies/ruby-2.0.0-p353/lib/ruby/2.0.0/uri/common.rb:307:in `block in escape'

各国リーグの昇降格制度

把握していないところがあったのでまとめました。
2014年現在の制度です。

日本

J1の下位3クラブが自動降格。J2の上位2クラブが自動昇格。J2の3位から6位までのクラブでトーナメント形式のプレイオフを実施し勝者がJ1昇格。

J2からJ3へ降格するクラブはJ3の上位がJ2ライセンス保有しているかどうかで変わってくる。

J3の1位2位がJ2ライセンス保有していた場合

J3の1位が自動昇格
J2最下位のクラブはJ3へ自動降格
J2の21位のクラブはJ3の2位との入れ替え戦

J31位のみJ2ライセンス保有していた場合

J3の1位が自動昇格
J2最下位が自動降格

J32位のみJ2ライセンス保有していた場合

J3の2位とJ2の最下位のクラブの入れ替え戦

J3の1位2位がJ2ライセンス保有していなかった場合

昇降格は実施されない

イングランド

プレミアリーグの下位3クラブがチャンピオンシップに自動降格。チャンピオンシップの上位2クラブが自動昇格。
チャンピオンシップの3位から6位までのクラブでプレイオフを行う。プレイオフは3位と6位、4位と5位でホームアンドアウェー形式で戦い、その勝者が一発勝負の昇格決定戦へ。

ドイツ

ブンデスリーガの下位2クラブがツヴァイテリーガに自動降格。ツヴァイテリーガの上位2クラブがブンデスリーガに自動昇格。
ブンデスリーガ16位とツヴァイテリーガ3位のクラブは入れ替え戦へ。入れ替え戦ホームアンドアウェー形式で行われる。

イタリア

セリエAの下位3クラブがセリエBに自動降格。セリエBの上位2クラブがセリエAに自動昇格し、3位から6位までのクラブでプレイオフを行う。ただし3位と4位の勝ち点差が10以上ある場合は3位のクラブが自動昇格する。プレイオフは3位と6位、4位と5位でホームアンドアウェー形式で戦い、イングランドとは異なり決勝もホームアンドアウェー形式。

変更される?
参考: http://ameblo.jp/ovest-tifosi29/entry-11620370558.html

スペイン

プリメーラ・ディビシオン(一部)の下位3クラブが自動降格。セグンダディビシオン(二部)の上位2クラブが自動昇格し、4位から6位までのクラブでプレイオフを行う。プレイオフは3位と4位の勝ち点差による免除以外はイタリアと同じ形式。

フランス

リーグアンの下位3クラブが自動降格し、リーグドゥの上位3クラブが自動昇格というシンプルなシステム。

オランダ

かなり変則的。エールディヴィジの最下位は自動降格し、エールステディヴィジの1位が自動昇格。

レイオフへは、エールディヴィジから16位と17位のクラブが、エールステディヴィジからはレギュラーシーズンの6節ごとに決められるピリオドチャンピオン6クラブと、レギュラーシーズンの上位2クラブが参加する。

レイオフはシーズン順位を元にした組み合わせによるホームアンドアウェー形式。勝ち残った2クラブが昇格。

ポルトガル

スーペルリーガの下位2クラブが自動降格

追記

セリエBの制度が変更されるかもしれないリンクを修正

rails 4 で任意のテーブルのみ YAML で dump する

rails3 で任意のテーブルのみ dump できる fm_yaml_db(0.2.3) は、rails4 に対応していないのか動きません。

rails4 に対応した yaml_db を使います。

gem 'yaml_db', github: 'jetthoughts/yaml_db', branch: 'rails4'

dump するテーブルを返すメソッドをオーバーライド

config/initialize/dump_tables.rb

module SerializationHelper
  class Dump
    def self.tables
      %w(tables1 tables2 tables3)
    end
  end
end

もしくは以下のようなメソッドを用意して

def override_dump_tables(*tables)
  SerializationHelper::Dump.define_singleton_method "tables" do
    tables
  end
end

適宜書き換えるのも良いかもしれません。

namespace "db:data:dump" do
  task :set => :environment do
    override_dump_tables("tables1", "tables2", "tables3")
    Rake::Task["db:data:dump"].invoke
  end
end

ポリモーフィックなコントローラー

Commentクラスはポリモーフィック関連 commentable を持ちます。

class Comment < ActiveRecord::Base
  belongs_to :commentable
end
class Cat < ActiveRecord::Base
  has_many :comments, :as => :commentable
end
class Dog < ActiveRecord::Base
  has_many :comments, :as => :commentable
end

/cats/:id/comments や /dogs/:id/comments に対応する CommentsController では、commentable_type が Cat なのか Dog なのか判定する必要がありますが、request.path や params[:*_id] などからこれを判定するのはいまいち。

そこで考えた。

resources :cats do
  member do
    resources :comments, :defaults => { :type => Cat }
  end
end

resources :dogs do
  member do
    resources :comments, :defaults => { :type => Dog }
  end
end

こうすることでコントローラーではこのように書けます。

class CommentsController < ApplicationController
  def index
    @commentable = params[:type].find(params[:id])
    @comments = @commentable.comments
  end
end

ただし、この routing の設定では /cats/:id/comments/:id のようにcommentable の ID と comments の ID の複数が必要なときに commentable の ID が取得できなくなってしまいます。

resources :dogs do
  resources :comments, :defaults => { :type => Dog }
end

とすれば params[:dog_id] と params[:id](コメントのID)が設定されます。

class CommentsController < ApplicationController
  before_action :commentable_id

  def show
    @commentable = params[:type].find(@commentable_id)
    @comment = Comment.find(params[:id])
  end

  private

  def commentable_id
    @commentable_id = params.find{ |key, value| key =~ /_id$/ }.last
  end
end

FriendlyID 日本語の slug が変

日本語の文字列から slug を作成しようと、User.each(&:save) とすると"-f9f3789a-daec-4156-af1d-fab81aa16ee5" のような意味不明な slug が作成されてしまう。

どうやら slug の生成に parameterize という ActiveSupportのメソッドを噛ませているらしく、日本語にこのメソッドを呼び出すと空文字となってしまう。日本語はすべて空文字となり他のレコードと重複するので、それを回避するためユニークな文字列を付け加えているようだ。

回避するには normalize_friendly_id メソッドをオーバーライドする。

class User < ActiveRecord::Base
  inlucde FriendlyID
  friendly_id :name, :use => :slugged

  def normalize_friendly_id(string)
    # この場合 string は name
    string.gsub(/[\.\s]/, "-")
  end
end

Sqale を使ってみた

heroku の PostgreSQL 1万行制限に苦しんでいました。heroku は月額 $9 で10,000,000行まで増やせるのですが、コスパがちょっと・・・東京リージョンの VPS か PaaS を借りようかと悩んでいたところに登場したのがSqale の無料キャンペーン。さっそく飛びついてみました。

良いところ

すべて日本語。問い合わせも日本語でOKなのが嬉しい。要望には迅速に対応してくれるようです(無料キャンペーンで忙しいのかメールの返信はまだないけど、言ったところは修正されてた)。なので、以下の気になったところも今後改善されるかもしれません。

気になったところ

  • /assets/ 以下のファイルの扱い

gzip圧縮転送できません。またCache-Controleヘッダも吐かない。最初にアクセスしたときに圧縮されていないjs/cssを読み込むことになるので、東京リージョンだしと期待していたよりも速くならなかったのは多分これのせい。

追記
現在はgzip圧縮されるようになりました

ひとつしか設定できないようで、設定外のドメインでアクセスすると 400 Bad Request となってしまう。静的ファイルをサブドメインで扱いたいときや、WWW有り無しをアプリケーションでコントロール(リダイレクト)して統一したいときに困ります。

メモ
http://mizzy.org/blog/2012/11/05/1/

  • Net::SSH で接続できない

これは自分の環境固有の問題かもしれませんが、Capistrano や Net::SSH から Sqale の ssh に接続すると、コンソールが表示されたところで固まってしまいます。いちいち手動で ssh でログインしてアプリ再起動とか面倒・・・

  • heroku db:push/pull みたいなのがない

面倒なのでyaml_dbを使ってhttp経由で更新出来るようにしてしまった。PaaSなのになんか違う・・・


それできるよ!とかの情報があれば是非教えてください。
Sqaleは使ってみて全体的に良い感じなので期待しています。