5.1.0のbetaがでて、そろそろ4.2もサポート範囲外になりそうなので、4.2から5.0にアップデートすることにしましたので、
色々作業した内容をメモって行きます。
本当は5.1まで一気にあげたい所ですが、まだbetaということで、一旦5.0.2に。
なあに、メジャーバージョンアップに比べればマイナーバージョンアップだし、きっと影響無いに決まってる。(慢心)
Rails 5へのアップグレード手順メモ(Rails 4.2.6 => 5.0.0)を参考に作業
http://qiita.com/ryo511/items/2a387df126268fec8c78 を参照
- gem のアップデート
- Rails update
- ApplicationRecordをActiveRecordのモデルのベースクラスにする
を実施。
rubyは既に2.4に切り替え済み、
ApplicationJobは使ってなかったので放置
gemのアップデート
gemのアップデート時には
Gemfileをまず
gem 'rails', '5.0.2'
に変更してbundle updateをした所、railitesで引っかかった。
一旦引っかかった物を全部コメントアウトしていたが、最終的に
# gem 'quiet_assets'
だけはコメントアウトしっぱなしになった。
同機能がrails5で入った様です。
Rails update
$ bin/rails app:update
rails update時には、実施してその後git diffして色々重要そうな部分を残しました。
config/initializers/assets.rb とか、configに何か独自設定してても削除されるので、ちゃんと残す様にしないといけない。
binの中身もspringが関係なくなるようだったのでを残すように更新した。(その分後述のトラブル発生)
ApplicationRecordをActiveRecordのモデルのベースクラスにする
class ApplicationRecord < ActiveRecord::Base
self.abstract_class = true
end
sed -i -e "s/ActiveRecord::Base/ApplicationRecord/" app/models/*.rb
srpingの関係で"Array values in the parameter to Gem.paths=
are deprecated."が出る
ruby/rails のversionを上げた際に Array values in the parameter to Gem.paths=
are deprecated. エラーが出た時 を参考に
bundle exec spring binstub --remove --all
bundle exec spring binstub --all
を実行。
多分、 bin/rails app:update時に、bin以下をsrping絡まない様にしてればエラーは発生しなかった(その場合、spring binstubを実行する必要あり?)
rspec用にGem追加
rsepcをとりあえず単品で動かしたらエラーがでたので警告通りGemを追加
NoMethodError:
assert_template has been extracted to a gem. To continue using it,
add `gem 'rails-controller-testing'` to your Gemfile.
gem 'rails-controller-testing'
rspecのget,postの書き方を変える
getやpost時にパラメータはparams: {}で渡す必要ができた模様
Deprecated style:
get :show, { id: 1 }, nil, { notice: "This is a flash message" }
New keyword style:
get :show, params: { id: 1 }, flash: { notice: "This is a flash message" },
session: nil # Can safely be omitted.
大雑把に書き換える
sed -i -r -e 's/(get|post)([^,]*,) ?(.*)/\1\2 params: {\3}/' spec/controllers/**/*_spec.rb
sed -i -r -e 's/\{\{(.*)\}\}/{\1}/' spec/controllers/**/*_spec.rb
grep "{{" spec/**/*_spec.rb | vi - # 色々いじる
適当に置換したのですが、receive(:get_api).and_returnとかやってる所も置換されて偉いことに
後、元々
get :show, parameters
としてた所が
get :show, params: {parameters}
となったりしてエラーになったのでvimで :%s/params: {([^:]*)}$/params: \1/gc の様なコマンドかけたり、修正が大変でいsた。
routes.rbの:actionがwarningになっていたので修正
routes.rbで以下の様な警告がでたのでここを参考に修正
:actionでそれぞれに割り振るのが警告出るようになったぽい
Using a dynamic :action segment in a route is deprecated and will be removed in Rails 5.2.
# get "users/:action", controller: "users"
%w(index list).each do | action |
get "users/#{action}", controller: "users", action: action
end
before_filter -> before_action に変更
以下の警告がでたので未だにbefore_filterだった所を置換
DEPRECATION WARNING: before_filter is deprecated and will be removed in Rails 5.1. Use before_action instead.
sed -i -r -e 's/before_filter/before_action/' app/controllers/**/*_controller.rb
gemで使っているライブラリの方のbefore_filterのアラートは残ってる様です……
ActiveRecordのuniq は distinctに
uniqからdistinctになったぽい。
Array#uniqなので同じ名前のが良かったなあ……
DEPRECATION WARNING: uniq is deprecated and will be removed from Rails 5.1 (use distinct instead)
# User.uniq
User.distinct
enumの挙動の違い
rails 4.2から動きが変わっているので、使っている場合は注意。
サンプルとしてrails g model Article status:integer で制作したモデルに対し以下のenumを設定してそれぞれのメソッドの結果
class Article < ActiveRecord::Base
enum status: {draft: 0, published: 1 }
end
> article = Article.new(status: :draft)
=> #<Article id: nil, status: 0, created_at: nil, updated_at: nil> # rails 4.2
=> #<Article id: nil, status: "draft", created_at: nil, updated_at: nil> # rails 5.0
> article.status
=> "draft" # rails 4.2
=> "draft" # rails 5.0
> article.attributes["status"]
=> 0 # raisl 4.2
=> "draft" # rails 5.0
# 元の値を取りたい時は _before_type_cast
# article.status_before_type_cast
> Article.where(status: :draft).to_sql
=> "SELECT \"articles\".* FROM \"articles\" WHERE \"articles\".\"status\" = NULL" #=> rails 4.2
=> "SELECT \"articles\".* FROM \"articles\" WHERE \"articles\".\"status\" = 0" #=> rails 5.0
Rails 4.2 で Arel を使って OR クエリを構築するで使ってたorが動かなくなってた
大変お世話になっていたのですが、ORがデフォルトで動く様になっていたので、変更
where_valuesのメソッドがwhere_clauseになってて挙動が変わっていたので、 修正するべきか悩んでいます。
21箇所だし、Rails5以降標準の方法にそれぞれ置換した方が良さそう
Arelでクエリを書くのはやめた方が良い5つの理由(Rails 5.0以前の場合)という話もあり、共感していたのですが、
RailsのorでSQLにカッコつける方法がわからなかったので、or内をグルーピングできる元の方が良いので、バッドノウハウですが、新しく、Rails 5.0.2でArel を使って OR クエリをカッコつけて構築するライブラリにしました。
手探りなので大分かっこ悪い気がします。バージョンが上がると通用しなくなるでしょうし。
ActiveRecord::QueryMethods::WhereChain.include(Module.new do
def or(*scopes)
where_clause = nil
if scopes.size == 1 && scopes[0].is_a?(Array)
scopes = scopes[0]
end
scopes.each do | scope |
temp_scope = scope.is_a?(Hash) ? @scope.model.where(scope) : scope
grouping_where_clause = Arel::Nodes::Grouping.new(temp_scope.where_clause.ast)
if where_clause.nil?
where_clause = ActiveRecord::Relation::WhereClause.new([grouping_where_clause], temp_scope.where_clause.binds)
else
where_clause = ActiveRecord::Relation::WhereClause.new([where_clause.ast.or(grouping_where_clause)],
where_clause.binds + temp_scope.where_clause.binds)
end
end
if where_clause
@scope.where_clause += where_clause
end
@scope
end
end)
rspecで空のパラメータがnilになる?
未検証ですが、paramsにからのハッシュを入れていたデータがエラーになっていました。
# テスト側
get :index, params: {q: { } }
# 本体側
if params[:q][:abc_to_eq] #=> undefined method `[]' for nil:NilClass
if文の書き方変えて対処だけしたので、深く掘っていません
render :text でDEPRECATION WARNING:
jsonならrender :json、テキストなら render :plainなど、ちゃんとフォーマットに従って返す様にするべきらしい
warning: constant OpenSSL::Cipher::Cipher is deprecated
deprecatedになっているOpenSSL::Cipher::Cipherの代わりにOpenSSL::Cipherを使用するよう修正しています。
置換 s/OpenSSL::Cipher::Cipher/OpenSSL::Cipher/g
ActiveRecord::Relationに<<できなくなってた
他にしてる人は少なそうですが
users = User.where(admin: true)
users << User.new
こんな感じの事ができなくなってました。
users.to_aしてから<<する様にして回避
rspec の get で Rack::Test::UploadedFileのパラメータがstringになった
本来postで書いておくべきだったテストをgetで書いていたミスがあったのですが、エラーがでて発覚しました。
postに書き換えて対応
jquery-ui系のパスが変更されていたので適応させる
couldn't find file 'jquery-ui/datepicker-ja' with type 'application/javascrip
jquery-ui/widgets/datepicker に
//= require jquery-ui/i18n/datepicker-ja 日本語化にこちらも追加
参考: http://stackoverflow.com/questions/19171470/ruby-on-rails-couldnt-find-file-jquery-ui-datepicker
couldn't find file 'jquery-ui/mouse' with type 'application/javascript'
require jquery-ui/widgets/mouse に
参考: https://github.com/jquery-ui-rails/jquery-ui-rails
link_toやurl_foでparamsを引数にするとエラー
検索条件を保持してリンク先を追加する様に、link_toにparamsを追加してた所がエラーになっていました。
Attempting to generate a URL from non-sanitized request parameters! An attacker can inject malicious data into the generated URL, such as changing the host. Whitelist and sanitize passed parameters to be secure.
permitして、許可するパラメータを指定する
<%= link_to "csvダウンロード", params.permit(:date_at).merge({:format => "csv"}) %>
params[:user]の中身はhashクラスを継承してない。
params[:page] の書き方がDEPRECATION WARNING
DEPRECATION WARNING: Method to_param is deprecated and will be removed in Rails 5.1, as
ActionController::Parameters
no longer inherits from hash. Using this deprecated behavior exposes potential security problems. If you continue to use this method you may be creating a security vulnerability in your app that can be exploited. Instead, consider using one of these documented methods which are not deprecated: http://api.rubyonrails.org/v5.0.2/classes/ActionController/Parameters.html
許可する場所を用意して、to_hしてアクセスしてみた(許可しないと値が取れないっぽい)
params.permit(:page)
params.to_h[:page]
params.to_unsafe_h[:page]でアクセスさせて対応しました。非推奨っぽいけど一番楽だった。
rspecのパラメータがActionController::Parameters として渡される
rspecがこれまでコントローラーに渡してきたパラメータはhashでしたが、ActionController::Parametersとして渡される様になっており、以下の様なケースでエラーがでてました。
post :create, record.attributes.merge(body: "")
# => 4.2 だと {"body" => ""}と言うデータが渡る
# => 5.0 だと {"body" => 元のデータ} で渡る
そもそもattributesは文字列なのでシンボルでハッシュを渡してるテストコードの書き方自体が適切ではないのですが、
こういったケースでのrspecのエラーもありました
config.logger = Logger.newしていると、cssへのアクセスでエラーが出る
config/environments/development.rb で
config.logger = ActiveSupport::Logger.new(config.paths["log"].first)
と書いてた事が原因で以下のようなエラーがでていました。
Unexpected error while processing request: super: no superclass method `local_level=' for #Logger:0x007fb77ff27928
Did you mean? local_variables
どうもActiveSupportで拡張時にメソッド追加したのかな?
原因はちゃんと調べてはいません。
ActiveSupport::Loggerにしておくと解決しました。
config.logger = ActiveSupport::Logger.new(config.paths["log"].first)
delete_all(conditions)がDEPRECATION WARNING
delete_allの引数に条件ではなく、whereにかく
Account.delete_all(area_id: 48)
ではなく
Account.where(area_id: 48).delete_all
rspec のxhr :get 〜〜と言う書き方がDEPRECATION WARNING
xhrの引数だったのを、引数にxhr: trueを
# xhr :get, :check, params: {:format => :js, :id => record.id}
get :check, params: {:format => :js, :id => record.id}, xhr: true
turbolinks 5の書き方が全然違うので、 '=2.5.3'に指定した
turbolinks5にバージョンアップされたのですが、jquery-turbolinksが効かなかった為、
gem 'turbolinks', '=2.5.3'
と指定しました。
params[:q] にhashが入ってる時にlink_toにパラメータを渡しても上手く動かない
ActiveRecordのwhereで配列内の配列はTypeError: can't quote Array
Article.where(status: [[0], 1])
# Rails 4.2
DEPRECATION WARNING: Passing a nested array to Active Record finder methods is deprecated and will be removed. Flatten your array before using it for 'IN' conditions. (called from irb_binding at (irb):3)
Article Load (2.4ms) SELECT "articles".* FROM "articles" WHERE "articles"."status" IN (0, 1)
=> #<ActiveRecord::Relation []>
# Rails 5.0
TypeError: can't quote Array
発生してからよく見たらDEPRECATION WARNINGと警告されてました。
flattenして修正
submit_tag がデフォルトでボタンを押すとdisableになる
data-disable-withというデータが自動的に登録されていて、クリックするとdisableになって押せなくなる。
config.action_view.automatically_disable_submit_tag = false
で解決する。
参考: http://y-yagi.hatenablog.com/entry/2015/08/18/062346
日本語で調べたRails5.0の変更点には載ってなかった……。
とりあえず、ここまでの作業で、rspecのエラーは0になりました。
間違っている箇所があればご指摘をいただけると助かります。(プロジェクト的にも)