Hatena::ブログ(Diary)

130単位

2013-11-24

Rails3.2 -> Rails4.0 アップグレード

2ヶ月遅れくらいで記事書いてます。実行時はRails4.0.0でしたが、4.0系であれば同様の手順でいけると思います。エラーで修正必須か非推奨警告のみかは曖昧だったりします。

Gemfile

#gem 'rails', '~> 3.2'
gem 'rails', '~> 4.0'
gem 'activerecord-session_store'

Rails

rake rails:update
  • bin 以下にrailsなどの実行ファイル
  • config以下は上書きされるので diff して必要な設定を書き戻し
  • config/initializers/filter_parameter_logging.rb 追加
  • config/initializers/secret_token.rb
    • secret_token -> secret_key_base
rake rails:update 以外のファイル
  • update
  • add
    • README.rdoc
    • app/controllers/concerns
    • app/models/concerns
  • remove
ApplicationController
  • protect_from_forgery
    • generateされるものは with: :exception になっており、通常はこのオプションがよい
    • :with オプションを指定しない場合は :null_session
Routing
  • :via 指定なしの match をなくす
  • put -> patch
    • 変更した場合はViewでlink_toの:method指定も :patch にする
正規表現
  • 意図しないマッチを避けるため ^ よりも \A、$ よりも \z か \Z を使う
ProtectedAttributes
  • Model から attr_accessible 削除
  • save/create の without_protection 削除
StrongParameters
  • 利用したほうがよいが、一時的に無効化する場合の設定
    • config.action_controller.permit_all_parameters = true
Scope / Conditioned associations
  • whereなどの条件をlambdaの中で指定
ActiveRecord::Base.all
  • Model.scoped -> Model.all
    • all が AR::Relation を返すようになった
Where condition
  • whereで関連先の条件を文字列で指定する場合は references(:table_name) が必要
Associationの細かい仕様変更
@blog.posts.size #=> 0
@blog.posts.build
@blog.posts.pop
@blog.posts.size #=> 0(Rails3) 1(Rails4)
Data attributes in view
= submit_tag 'submit', confirm: 'OK?', disable_with: 'processing...'
= submit_tag 'submit', data: { confirm: 'OK?', disable_with: 'processing...' }
Exception name

Gems

  • MetaSearch -> Ransack
    • MetaSearchはRails4で動かない
    • 依存度により移行はそれなりに大変で、そのうち別記事で書くかも
  • Spork -> Spring
    • 流行り/設定簡略化
  • DatabaseCleaner -> DatabaseRewinder
    • 流行り/速度改善 (※Ruby2.0のみ)

More information


4774155071
WEB+DB PRESS Vol.73

2013-10-15

Rails3.0 -> Rails3.2 アップグレード

今さら需要があるかわからない3.2へのアップグレード情報です。半年前にはほぼ準備完了していたんですが、諸事情あった末にようやく調整ができたので、先日アップグレードしました。ちなみにRails4リリース後のセキュリティメンテナンス対象は3.2系のみであるため、3.1系以前を運用されている方はできるだけアップグレードしたほうがいいと思います。

Gitのlogを見ながら書き起こしているため、エラーか非推奨警告かは曖昧な上に、もしかしたらバージョンアップ関係ない点もあるかもしれませんがご了承ください。

Gemfile

#gem 'rails', '~> 3.0'
gem 'rails', '~> 3.2'
#gem 'mysql2', '< 0.3'
gem 'mysql2'
gem 'devise-encryptable'
#gem 'jpmobile', '< 2.0'
gem 'jpmobile'
#gem 'spork', '> 0.9.0.rc'
#gem 'guard', '~> 0.8.8'
  • 古いバージョン指定を除外(コメントアウト分が3.0用)
  • DeviseでRestful Authentication互換のEncryptorを利用しているのでDevise Encyptable追加

Rails

rake rails:update
  • config以下が3.2のデフォルトで上書きされる
  • git diff で3.0の内容と比較しながら設定
config.assets.enabled = false
  • asset pipeline をオフにして3.0時代のasset管理を継続
has_many_association#clear
# has_many :posts
has_many :posts, :dependent => :delete_all
  • 3.0は @user.posts.clear = @user.posts.delete_all だった
  • 3.2は :dependent を指定しないと 関連idを NULL で UPDATE する
    • 関連idがNULL不可の場合、:dependent を :delete_all (または :destroy) にする
Rails.cache with specific instances
config.eager_load_paths += %W[#{config.root}/lib]
config.after_initialize do
  require_dependency 'my_class' unless defined? MyClass
end
MimeType
  • config/initializers/mime_types.rb でPDF追加してたけど不要になった

Gems

Devise
<%#= confirmation_url(@resource, :confirmation_token => @resource.confirmation_token) %>
<%= confirmation_url(@resource, :confirmation_token => @token) %>
#User.__send__(:generate_token, 'encrypted_password').slice(0, 8)
Devise.friendly_token.slice(0, 8)
  • ランダム文字列生成
RSpec(Guard)
#guard 'rspec', :version => 2, :cli => '--fail-fast --drb' do
guard 'rspec', :cli => '--fail-fast --drb' do
  • :version 指定不要
Capybara
  • git mv spec/requests spec/features
  • Capybara2系の仕様により、Capybara::DSL を使うspecをfeaturesへ移動
#select @datetime.month.to_s, :from => 'created_at_2i'
select "#{@datetime.month}", :from => 'created_at_2i'
#select @datetime.hour.to_s, :from => 'created_at_4i'
select '%02d' % @datetime.hour, :from => 'created_at_4i'
  • optionのテキスト検出が厳格になった
#click_button 'resource_submit'
click_button '登録する'
  • FormBuilder で submit の id が付かなくなった
Fabrication
  • resource!{ ... } -> resource{ ... }
  • sequence はブロック化して遅延評価
  • :from => Resource -> :class_name => :resource

感想

そもそもアップグレードが遅れたのは、非エンジニアがpublic以下を編集することが多いアプリで、asset pipeline対応ができなかったのが原因でした。ただ対応しないという判断で割り切ってしまえば、2系から3系よりは、対象項目が少なかった印象です。またテストを書いていたため、(テスト自体の修正もあったものの)比較的安心して進めることができました。Model.pluckが使えるようになったのが地味にうれしいですね。


4844331566
改訂新版 基礎Ruby on Rails (IMPRESS KISO SERIES)

関連記事

2012-10-18

jQuery クエリパラメータを全リンクに付加

やりたいこと

  • URLのクエリパラメータをページ内の全リンク(内部リンク)に付加してサイト内で引き継ぐ

コード

jQuery、URL Parserというライブラリを利用します。

<script type='text/javascript' src='/javascripts/jquery.js'></script>
<script type='text/javascript' src='/javascripts/purl.js'></script>
$(function(){
  if (location.search == '') {
    return;
  }
  var req_params = $.url(location.href).param();
  $('a').each(function(){
    if (/^(#|https?:|\/\/)/.test($(this).attr('href'))) {
      return;
    }
    var parsed = $(this).url();
    var old_params = (parsed.attr('query') == '') ? {} : parsed.param();
    var new_path = parsed.attr('path') + '?' + $.param($.extend(old_params, req_params));
    $(this).attr('href', new_path);
  });
});

仕様

  • リクエストURLにクエリパラメータがなければ何もしない
  • リクエストURLのクエリパラメータをハッシュ化
  • 全リンクを走査
    • href要素がフラグメントのみもしくは絶対URLであればスキップ
      • 内部リンクでも絶対URLの可能性がるためホスト名を検証したほうがいいかもしれない
    • リンクのクエリパラメータをハッシュ化
      • URL Parser のparam()メソッドはクエリパラメータがない場合 { : ''} が返るのでその回避
        • ただしマージ後文字列化した時不自然な=が入るだけで実害はないと思われる
    • リクエストURLとリンクのクエリパラメータハッシュをマージして文字列
    • 新たに生成したパスでhref要素を上書き

※パラメータに配列が含まれていたり、リンクに同一パラメータが存在するかもしれないので、ハッシュ化してマージする仕様にした

留意点とか

  • GETメソッドのフォームにはactionにパラメータ追加しても効かない
    • hidden要素を追加する必要がある
  • Flashでのページ遷移があると効かない
  • そもそもJavaScriptオフだと効かない

※結局実運用せず見送った

2011-12-04

Rails 擬似配列型カラムと複数選択チェックボックス

多対多の関連をレコード内の1つのカラムで表現したいことがあると思います。片方が少ない種類でほぼ決まっており、テーブルにするまでもないような場合です。

ALBUMS = {'Perfume〜Complete Best〜' => 1, 'GAME' => 2, 'Δ' => 3, 'JPN' => 4}
  • ユーザーが好きなアルバムを選択できる (複数可)

f:id:deeeki:20111204084241p:image

こんな入力フォームの実装についてまとめてみます。Railからはずれているのでめんどくさいです。

環境

  • Ruby 1.9.2p290
  • Rails 3.0.10
  • SimpleForm 1.5.2

擬似配列型カラム

class User < ActiveRecord::Base
  serialize :fav_albums, Array
end

serializeクラスマクロを使います。第二引数にクラスを指定するとそれ以外を受け付けなくなります。MySQLでのカラムはTEXT型です。YAML化した文字列で格納されます。

FormTagHelper

FormBuilderのcheck_boxは真偽値のカラム用になっているため、check_box_tagを使います。

check_box_tag(name, value = "1", checked = false, options = {})

ActionView::Helpers::FormTagHelper
<%= form_for @user do |f| %>
  <%= hidden_field_tag 'user[fav_albums][]' %>
  <% ALBUMS.each do |label, value| %>
    <%= check_box_tag 'user[fav_albums][]', value, @user.fav_albums.include?(value), :id => "user_fav_albums_#{value}" %>
    <%= label %>
  <% end %>
<% end %>
  • 配列で受け取るためのモデル名つきname属性を自力で指定
  • checkedの判定を自力で指定
  • id属性は書かないと重複してしまうため優しさで指定
  • hidden要素は全てチェックなしでの更新をするために必要

FormBuilder

FormBuilderでやろうとするとどうなるか。

check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")

ActionView::Helpers::FormHelper
<%= form_for @user do |f| %>
  <% ALBUMS.each do |label, value| %>
    <%= f.check_box :fav_albums, {:name => "user[fav_albums][]", :id => "user_fav_albums_#{value}"}, value, nil %>
    <%= label %>
  <% end %>
<% end %>
  • checkedの判定は不要
  • hidden要素は不要
    • しかしチェックボックス毎に無駄にできる
  • デフォルトだとname属性がuser[fav_albums]になってしまうので明示的に指定
  • id属性は優しさで(ry

そんなわけで、あまりFormBuilderの恩恵は受けられません。

SimpleForm

そこでSimpleFormというgemを利用すると、簡潔に書けます。

<%= simple_form_for @user do |f| %>
  <%= f.input :fav_albums, :collection => ALBUMS, :as => :check_boxes %>
<% end %>
  • name属性もid属性もchecked判定もうまいことやってくれる
    • さらにラベル表示やエラー表示まで完璧にこなす
  • ただしhidden要素は複数できる
  • 改行入れたりとか細かいカスタマイズはしにくい

アクセサメソッド

上記いづれかでHTMLはできましたが、データのやり取りに問題があります。

  • パラメータで渡されるのは文字列なので数値に変えないといけない
  • 空白を除去しないといけない

これらの解決のためにモデルにアクセサメソッドを追加します。

class User < ActiveRecord::Base
  serialize :fav_albums, Array

  def fav_albums=(value)
    write_attribute(:fav_albums, Array.wrap(value).reject{|v| v.blank? }.map{|v| v.to_i })
  end

  def fav_albums
    read_attribute(:fav_albums) || []
  end
end

ゲッターでは値がない場合に空の配列を返すようにしました。

これで書き込み/読み込みが期待通りに動きます。

参考リンク

4274068668
RailsによるアジャイルWebアプリケーション開発 第4版

2011-08-24

DropboxをインストールせずにDropboxにバックアップ

コマンドラインからDropboxへのアップロードを可能にするシェルスクリプトです。通常Dropboxは(Linuxでも)インストールして使うものですが、このツールはファイル1つのみで動作します。

こちらの記事で知りました。現在はバージョンが上がっていたり少しつまずいたりしたので、そのメモです。(※バージョン0.7.1で検証)

使いかた

sh dropbox_uploader.sh -u account@example.com -p password -f targetfile_or_dir -d /path/to/uploaddir -v
ハードコーディング
LOGIN_EMAIL="account@example.com"
LOGIN_PASSWD="password"

dropbox_uploader.shに直接記述すればアカウントとパスワードのオプションは不要になります。

正しいのにログインできない場合

アカウントまたはパスワードの記号がURLエンコードされないのが原因の可能性があります。スクリプトではcURLの通信を行っていおり、パラメータの生成時にURLエンコードが必要です。Gmailでエイリアス('+'記号)を使っていたりするとアウトです。

回避するにはアカウントおよびパスワードにURLエンコード済みの文字列を指定する必要があります。

スクリプト内でURLエンコード

ファイルにパスワード書きたくない&URLエンコード面倒な場合は以下のようにします。

function urlencode
{
	echo $1 | nkf -MQ | tr = %
}

curl -s -i -c $COOKIE_FILE -o $RESPONSE_FILE --data "login_email=`urlencode $LOGIN_EMAIL`&login_password=`urlencode $LOGIN_PASSWD`&t=$TOKEN" "$LOGIN_URL"
  • nkfをインストール
  • urlencode関数を追記
  • 232行目あたりを編集

その他

  • アップロード先はディレクトリしか指定できない
    • よってアップロード時にファイル名の変更はできない
  • ディレクトリがない場合は作成される
  • Mac(Snow Leopard)で試すとアップロードにやたら時間かかる
    • cURLとかの問題かもしれない

PR

4798123722
Dropbox WORKING

関連記事