rbenv の更新をした後 rbenv install がエラーになった話と gcc の更新
大きな開発が一段落したので環境の更新をしておこう、と思い年単位ぶりくらいに
brew update brew upgrade rbenv ruby-build
を叩き、せっかくだから記事執筆時にリリースされたばかりの最新版 Ruby 2.1.5 をインストールしようと
rbenv install 2.1.5
を実行したところ gcc 関連のエラーが出てインストールができず。
Homebrew のログを読んでみると、更新時に削除されたフォーミュラ一覧に
apple-gcc42
の文字を発見。
せっかくなので gcc のバージョンも更新してみようと思い
brew tap homebrew/versions brew install gcc47
と実行。gcc のインストールを行うので、30分程度はかかりました。
インストールが終わると
/usr/local/Cellar/gcc47/4.7.4: 1000 files, 126M, built in 36.7 minutes
のようにインストールされたパスが表示されるので export でコンパイラを指定します。
export CC=/usr/local/Cellar/gcc47/4.7.4/bin/gcc-4.7
終わったら改めて
rbenv install 2.1.5
エラー無くインストールされる事を確認しました。
追記
既存でインストールしていた分の ruby おそらくすべてのバージョンで OpenSSL のエラーが発生していました。
インストール済のバージョンで使うものすべてを再インストールすれば問題ないようです。
rbenv uninstall 2.0.0 rbenv install 2.0.0
wicked_pdf で Rails が送信するメールに PDF を添付する
表題の件ですが、案外はまってしまったので備忘録。
メーラーの準備
元々のコードがこうだったとして
class NotifyMailer < ActionMailer::Base def customer(mail_object, document) @mail_object = mail_object @document = document mail from: @mail_object.from, to: @mail_object.to, subject: @mail_object.title end end
ファイルの添付は次のように行います。
class NotifyMailer < ActionMailer::Base def customer(mail_object, document) @mail_object = mail_object @document = document # ここから attachments["#{@document.title}.pdf"] = WickedPdf.new.pdf_from_string( render_to_string pdf: "#{@document.title}.pdf", template: 'documents/show.pdf.erb', no_background: false ) # ここまで mail from: @mail_object.from, to: @mail_object.to, subject: @mail_object.title end end
no_background オプションは背景画像を表示したい場合に指定してください。
また、ヘルパーメソッドを使いたい時は
add_template_helper ApplicationHelper
を追加するのを忘れないようにします。
Rails の database.yml で collation を指定する
定期的に引っかかるので備忘録として。検証には Rails 4.0.8 及び Mysql 5.6.10 を使用しました。
Rails から MySQL を使う際、何も考えずにデフォルトのまま設定していくと、大体の場合小文字 a と大文字 A が区別されないデータベースが作成されます。具体的にサンプルを示すと
SELECT 'a' = 'A'; +-----------+ | 'a' = 'A' | +-----------+ | 1 | +-----------+
このように 'a' = 'A' が真となってしまいます。主にメールアドレスなどの一貫性を保証したい時に問題になります。
こうした問題を解消するために使われるのが collation の指定です。バージョン 5.1 の記事ですが、公式ドキュメントはこちら。
http://dev.mysql.com/doc/refman/5.1/ja/charset-unicode-sets.html
詳しい解説は省きますが、今回は utf8_bin を collation に指定できれば問題が解決します。
新規作成する場合
config/database.yml に記述を追加します。
development: adapter: mysql2 encoding: utf8 collation: utf8_bin reconnect: false database: some_project_dev username: username password: password host: localhost
後は普段のように rake db:create からスタートすれば問題ありません。
既存のテーブルを何とかする場合
既存のデータベースについて設定を変更するには MySQL のクエリを直接発行する必要があります。
ALTER DATABASE dbname DEFAULT CHARACTER SET utf8 COLLATE utf8_bin;
しかし、この設定が適用されるのは上記クエリの受付後に作られたテーブル等のみで、既存のデータについてはどうしても区別ができないようです。そのような場合、明示的に BINARY オペレータを用いる事で比較を行います。
SELECT BINARY 'a' = 'A'; +-----------+ | 'a' = 'A' | +-----------+ | 0 | +-----------+
ただし、この方法はパフォーマンス的にあまりオススメされないらしいので、必要なのであれば極力最初から大文字小文字(あるいはその他の何か)が区別可能なデータベースを作成するようにした方がいいでしょう。
wicked_pdf を Rails アプリケーションで使うまで
html から簡単に PDF が作れる便利なプラグインですが、割と面倒な行程が必要だったのでメモ。Rails 4.1 で確認しました。
インストール
Gemfile で以下のように記述します。
gem 'wkhtmltopdf-binary' gem 'wicked_pdf'
が、環境によってはこれでは動作しません。具体的に言うとコンソールで
wkhtmltopdf
と叩いてパスが通っていないなら動作しません。
この場合、自分で wkhtmltopdf をインストールした後 config/initializers に wicked_pdf.rb というファイルを作成し、環境にあわせて実行パスをフルパスで設定します。
WickedPdf.config = { :wkhtmltopdf => '/path/to/wkhtmltopdf' }
2014-09-25 追記
上記の設定では、追記現在の最新版では正しく動作しません。
以下のように書き換えてください。
WickedPdf.config = { :exe_path => '/path/to/wkhtmltopdf' }
PDF を表示する
wicked_pdf を使うと Rails の render で :pdf が指定可能になります。
routes.rb で
resources :items
と指定されているとして items_controller の show メソッドを編集します。
def show @item = Item.find(params[:id]) respond_to do |f| f.html f.pdf do render pdf: @item.title, encoding: 'UTF-8', layout: 'pdf.html' end end end
このように指定すると @item.title が PDF のタイトルとして使われます。また encoding オプションは日本語を使いたい場合、レイアウトの指定は PDF の作成時に使うレイアウトを変えたい場合に必要となるので、事実上必須と言っていいでしょう。
あとは http://localhost/items/1.pdf のようにアクセスすると PDF が表示されるはずです(この段階でエラーになる場合、上記インストール部分を再度確認してください)。
レイアウトや CSS を正しく反映させる
レイアウトファイルについては以下のようなテンプレートを使います。これは erb ですが haml の場合も同じように指定してください。
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <%= wicked_pdf_stylesheet_link_tag 'pdf' %> </head> <body> <%= yield %> </body> </html>
wicked_pdf_stylesheet_link_tag を使うのがポイントで、ここで指定した pdf.css を使うようになります。
大体の場合においてブラウザで表示するレイアウトや CSS と PDF にする CSS は別にしたいと考えるはずですが、その場合は app/assets/stylesheets 以下に pdf などのディレクトリを作成し config/environments/production.rb で
config.assets.precompile += %w( pdf/pdf.css )
のようにプリコンパイルさせるファイルを追加して
<%= wicked_pdf_stylesheet_link_tag 'pdf/pdf' %>
と指定すれば問題ありません。
背景の画像を表示する
CSS の background で指定した画像を表示させるのも素直にはいきません。
まず、先ほどのコントローラーを修正します。
def show @item = Item.find(params[:id]) respond_to do |f| f.html f.pdf do render pdf: @item.title, encoding: 'UTF-8', layout: 'pdf.html', , no_background: false end end end
no_background: false を追加していますが、これを指定しないと何をしても背景画像は表示されません。
次に、公式フォーラムによるとバージョンによっては jpg 以外の画像は表示されないようです(私が試した限りでは png も表示されましたが…)。特に問題がないのであれば、念のため jpg にしておいた方がいいでしょう。
最後に CSS の内部で background を指定する箇所ですが app/assets/stylesheets/pdf/pdf.css を pdf.css.erb にリネームした上で、次のように指定しました。
background: url(<%= Rails.root.join('public', 'images', 'hoge.jpg') %>) right center no-repeat;
ここだけは内部のパス、それもフルパスで指定しないと wkhtmltopdf が読めないのでしょうか。ともあれ、これで画像も表示されるはずです。
なお Rails 4.1 では pdf.css.erb と名前をつけたファイルについても config/environments/production.rb で
config.assets.precompile += %w( pdf/pdf.css )
と指定すればプリコンパイルの対象としてくれるようでした。動作がおかしい場合 public/assets 以下を確認し、対象の CSS ファイルが本当に作成されているか等を確認してみてください。
jQuery.ajax() で読んで来るデータをキャッシュさせない
次のコードは正しく動作しますが、サーバやブラウザの設定によってはデータを更新してもキャッシュが読まれてしまいます。
$.ajax({ type: 'GET', dataType: 'json', url: 'hoge.json', success: function(data) { concole.log(data); });
これを避けるには cache オプションを false に設定します。
$.ajax({ type: 'GET', dataType: 'json', cache: false, url: 'hoge.json', success: function(data) { concole.log(data); });
ちなみに jQuery.getJson() や jQuery.get() などのメソッドには cache オプションはバージョン 2.1.1 現在では存在しないようです。
そのようなケースでは、先に jQuery.ajaxSetup() で同様に設定することで対処できるようです。
$.ajaxSetup({ cache: false });
参考
公式ドキュメント
http://api.jquery.com/
ActiveModel::Model で簡単に ActiveModel の機能を利用する
例えばユーザからの問い合わせを受け付けるメールフォームなど、特にデータベースに値を保存したりする必要はないが ActiveModel の機能は使いたいケースで Rails 4 では ActiveModel::Model をインクルードする事で簡単に処理できます。
class MailForm include ActiveModel::Model attr_accessor :name, :email # 属性はこのように定義する end
同じような事を Rails 3 で行う場合に次のような記述をした事があるのですが、これと比較するとかなりすっきりしています。
class MailForm include ActiveModel::Conversion include ActiveModel::Validations include ActiveModel::Validations::Callbacks extend ActiveModel::Naming extend ActiveModel::Translation attr_accessor :name, :email def attributes=(params) params.each do |k, v| send("#{k}=", v.html_safe) end end
もちろんバリデーションもそのまま使えます。
class MailForm include ActiveModel::Model attr_accessor :name, :email # 属性はこのように定義する validates :name, presence: true end
なお API はこちら。
http://api.rubyonrails.org/classes/ActiveModel/Model.html
これによると ActiveModel::Model にインクルードされているのは ActiveModel::Validations と ActiveModel::Conversion との事ですが i18n_generators を用いた i18n にも対応しています。
ただし日本語化する時に config/locales/translation_ja.yml で次のように指定してもうまく翻訳されません。
ja: activerecord: models: mail_form: メールフォーム attributes: mail_form: name: 名前 email: メールアドレス
以下のように指定すると翻訳が行われるようになります。
activemodel: models: mail_form: メールフォーム attributes: mail_form: name: 名前 email: メールアドレス