give IT a try

プログラミング、リモートワーク、田舎暮らし、音楽、etc.

Rails3.0から3.1への移行とRSS(Atom)フィードの取得

はじめに

妻がやってるパン屋さんのWebサイトを変更してみました。
変更と言っても、トップページにRSS経由で最新のブログ記事へのリンクを表示する、ただそれだけの変更です。


こんな感じね。↓


妻は週に1〜2回ブログを更新しますが、店のサイトの方は最近放置状態なので、ちょっとでもサイトに変化を与えようと思い、この改造に着手しました。


特別珍しいことをやるわけでもないし、こんなん楽勝、楽勝!!・・・と思ってたら、予想以上に時間がかかってしまいました。(T T)
トータル5〜6時間ぐらい??


終わってみたら「なんだこれだけかいっ!」って思うんですが、その解決策に至るまでの道のりは非常に険しかったです。ま、簡単にいうと「ハマってた」ということなんですが。
もしかするとどこかの誰かの役に立つかもしれないので、技術メモを残しておきます。

Rails3.0から3.1への移行

最初にGoogleで見かけたサンプルはjQueryを使ってRSSフィードを取得していました。
Rails3.0はprototype.jsで、3.1からjQueryがデフォルトになったようなので、この際3.1に移行しようと思いました。(この選択で後で大きく困らされたのですが・・・)

Gemfile

まずはGemfileを変更します。

# Gemfile
-gem 'rails', '3.0.9'	
+gem 'rails', '3.1.3'


「bundle install(updateだったかも)」で、移行終了〜!!・・・というわけにはいきません。
sqliteのバージョンを1.3.3に固定していたのですが、ダメみたいなのでバージョンを「1.3.0以上」に変更します。

# Gemfile
-gem 'sqlite3', '1.3.3'	
+gem 'sqlite3', '~> 1.3.0'
debug_rjs

Ajax周りの仕様が変更されたため、config/environments/development.rbに以下の一行があるとエラーになります。というわけで削除!

# config/environments/development.rb
-config.action_view.debug_rjs = true
Tableless Model

Webサイトには問い合わせフォームがあります。
単純なフォームなのでRailsの便利なバリデータを使いたいのですが、送信情報自体はデータベースに格納しません。
なので、データベーステーブルに依存しないモデルを作成しました。


Rails3.0では以下のようなコードで動いていたのですが、3.1になると「ActiveRecord::ConnectionNotEstablished」というエラーが出ました。

# http://railscasts.com/episodes/193-tableless-model:title
class Inquiry < ActiveRecord::Base
  def self.columns() @columns ||= []; end
   
  def self.column(name, sql_type = nil, default = nil, null = true)
    columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
  end
             
  column :name, :string
  column :email, :string
  column :tel, :string
  column :text, :text
end


これはあれこれ探しまくって、結局以下のような形に落ち着きました。

# http://stackoverflow.com/questions/7275496/tableless-model-in-rails-3-1
class Inquiry
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :name, :email, :tel, :text
  validates :name, :email, :text, presence: true

  def initialize(attributes = {})
    if attributes
      attributes.each do |name, value|
        send("#{name}=", value)
      end
    end
  end

  def persisted?
    false
  end
end
RawOutputHelper => OutputSafetyHelper

問い合わせフォームで入力値エラーがあった際にdivタグではなくspanタグでエラーメッセージを出力したかったので、config/application.rbに以下のようなおまじないを埋め込んでいたのですが、「rawメソッドが見つからない」と怒られてこれも動かなくなりました。

# config/application.rb
# http://www.rabbitcreative.com/2010/09/20/rails-3-still-fucking-up-field_with_errors/
ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
    include ActionView::Helpers::RawOutputHelper
    raw %(<span class="field_with_errors">#{html_tag}</span>)
end


どうやらRawOutputHelperというクラスがなくなったようなので、「ActionView::Helpers::OutputSafetyHelper」という新しいクラスに変更しています。

# config/application.rb
-include ActionView::Helpers::RawOutputHelper
+include ActionView::Helpers::OutputSafetyHelper


ここまでやってようやくローカルでRails3.1が動作するようになりました。

RSS(Atom)の取得

最初はjQueryでやろうと思っていたのですが、ネットのサンプルコードがうまく動かなかったので結局サーバーサイドで実装することにしました。
ということで無理に3.1にしなくても良かった!ということになるんですが・・・。(T T)


それはさておき、あれこれ説明するより、コードを見た方が速いです。
こんなヘルパーメソッドを作って、Viewで呼び出すようにしました。

require 'feed-normalizer'

module PagesHelper
  def load_blog
    source = "http://rss.exblog.jp/rss/exblog/lapin418/atom.xml" 
    feed = FeedNormalizer::FeedNormalizer.parse(open(source), :force_parser => FeedNormalizer::SimpleRssParser).items[0] 
    @blog_title = feed.title.force_encoding('utf-8')
    @blog_date = feed.date_published.strftime(" (%Y/%m/%d)")
    @blog_url = feed.urls[0]
  end
end


そうそう、feed-normalizerも忘れずにインストールしておきましょう。

# Gemfile
+gem 'feed-normalizer'


ところで、最初はこのサイト(RubyRSS)に載ってる方法を試していたんですが、parseしてもnilが返ってきてしまい、動きませんでした。
どうやらRSSとAtomは別物のようで、Atom用のコードを書く必要があるようです。(jQueryのサンプルコードが動かなかったのもたぶんそのせい?)


また、ブログのタイトルを表示しようとすると「incompatible character encodings: UTF-8 and ASCII-8BIT」というエラーが出たため、「feed.title.force_encoding('utf-8')」のようにエンコードを指定する必要がありました。

Herokuへのデプロイ

さあ、ようやくローカルでちゃんと動いた!あとはデプロイしたらおしまい〜!!・・・と思ったら、まだ罠がありました。
Herokuにデプロイするとエラーが発生します。
ログを見ると、またまた「ActiveRecord::ConnectionNotEstablished」とありました。
ググってみると、config/application.rbで読み込むモジュールを個別に指定する必要があるとのことでした。

# config/application.rb
-require 'rails/all'
+# http://www.benjaminoakes.com/2011/09/15/activerecordconnectionnotestablished-in-rails-3-1-on-heroku/
+require "action_controller/railtie"
+require "action_mailer/railtie"
+require "active_resource/railtie"
+require "sprockets/railtie"
+require "rails/test_unit/railtie"
+require "active_record"

タイムゾーンの設定

今度こそOK〜!!・・・と思ったら、今度はブログの更新日がなんか違う。
タイムゾーンがズレているようです。
こちらの記事を参考にして、Herokuのタイムゾーンを設定しました。

heroku config:add TZ=Asia/Tokyo


タイムゾーンを変更しないやり方もあるみたいですが、僕は安易に手っ取り早い解決策を選択しました。^^;

やっと完成!!

ここまでやって、ようやくRails3.1への移行 + 最新ブログ表示機能の完成です!
実際のページはこちらになります。↓


Coupé Baguette クープ バゲット


たったコレだけのために5〜6時間とは・・・悲しいです。
まあRailsに限らず、初めて挑戦することは簡単そうに見えても結構泣かされますもんね。
仕方がない、きっと!!と、自分で自分を励ましておきます。


すでにRailsは3.2が出ていますが、僕と同じように3.0から3.1に移行しようと思っている人は参考にしてみてください。

あわせて読みたい

夫から見たパン屋さんの舞台裏 - give IT a try
僕から見たお店の様子を紹介しています。


「Coupe Baguette」のWebサイトができあがるまで - give IT a try
最初にWebサイトを作った時の技術情報を載せています。


A programmer in Japan: Raw method was moved to OutputSafetyHelper in Rails 3.1 or later
RawOutputHelper から OutputSafetyHelper への変更はググっても明確な情報源がなかったので英語ブログに載せてみました。