Rails4.2からRails5.0(RC1)に移行する際に修正したポイント
昨年の秋あたりから、お仕事の関係で監視していたRailsの開発状況ですが、
お仕事が関係なくなっても、なんとなく毎日チェックしておりまして
どうしてもβを採用するのは怖いので、(仕様が固まる)RC版を待っていたところ、
先日RC版がリリースされました(`・ω・´)
Riding Rails: This Week In Rails 💯: RailsConf recap & Rails 5.0 RC 1 is out!
ということで、手持ちの「チェンクロパーティーシミュレーター」を、
Rails4.2からRails5.0RC1に移行したわけです
移行する際に書き換えたコードがこれになりますが・・・
Rails5(RC1)に移行 / 入手先検索のバグを修正 / viewのコードを修正 / データの誤りを修正 · parrot-studio/cc-pt-viewer@9f3e715 · GitHub
・・・全体を見直したことによるバグfixも含むとはいえ、
なんとなくどこが変わったのかはつかめるんじゃないかと
以下、引っかかった点や変更点をざっくりと...φ(・ω・`)
modelをApplicationRecordからの継承に変更
以前は「ActiveRecord::Base」から直に継承していたmodelですが、
Rails5から「ApplicationRecord」というアプリ層クラスを経由するようになりました
この変更はとてもいいので、Rails4.2で進めている今のお仕事でも取り込んでいます(`・ω・´) b *1
例えば、複数のmodelを触るトランザクションを張る場合に、以前だと・・・
# Railsのクラスを直に触っている ActiveRecord::Base.transaction do # なにか更新処理 end # これが嫌なら、自分のクラスを経由するけど、なにか気持ち悪い(´-ω-) Arcana.transaction do # 実際はArcana以外のmodelも更新している end
・・・こんな感じで書くしかありませんでした
しかし、一つクラスをはさんだことで・・・
ApplicationRecord.transaction do # 明確な親クラスでトランザクションを使える end
・・・こう書くことができて、とてもすっきりしますヽ(`・ω・´)ノ
トランザクション以外にも、アプリ全体に処理を仕込みたい場合、
全部「ApplicationRecord」に書けばいいので、ルールとしてわかりやすいです
Relationの仕様変更にはまる
Railsのバージョンアップをする際、だいたい鬼門になるのはActiveRecordです
今回も見事に仕様変更の罠を踏み抜きました(lll゚Д゚)
チェンクロのアルカナ情報を表すクラスとしてArcanaクラスを用意して、
その下に細かい情報クラスをぶら下げておりました
# Rails4.2のコード class Arcana < ActiveRecord::Base # 中略 belongs_to :first_skill, class_name: 'Skill' belongs_to :second_skill, class_name: 'Skill' belongs_to :third_skill, class_name: 'Skill' belongs_to :first_ability, class_name: 'Ability' belongs_to :second_ability, class_name: 'Ability' belongs_to :weapon_ability, class_name: 'Ability' # 後略 end
しかし、Rails5に移行したら、データのimport処理で引っかかりまくりまして、
吐き出されたエラーメッセージを読んだところ、
「結びついたデータがないぞ(#゚Д゚)」といったことが書いてありました
以前はこれで動いていたわけで、なにかおかしい・・・と思ったところ、
Relationの存在チェックするバリデーションが、Rails5からデフォルトで有効になっていたようです
関連を定義したのだから、その先の情報もチェックするのが当然・・・という思想はわかるのですが、
全てのアルカナが二つ以上スキルを持っているわけでもなく、
アビリティに至っては持ってないケースもあるので、これは困ります(´-ω-)
そこで、このようにチェックを無効化する必要があったのです
# Rails5のコード class Arcana < ApplicationRecord # 中略 belongs_to :first_skill, class_name: 'Skill' belongs_to :second_skill, class_name: 'Skill', optional: true belongs_to :third_skill, class_name: 'Skill', optional: true belongs_to :first_ability, class_name: 'Ability', optional: true belongs_to :second_ability, class_name: 'Ability', optional: true belongs_to :weapon_ability, class_name: 'Ability', optional: true # 後略 end
これで以前と同じ動作になります(`・ω・´)
(first_skillだけは必ず持っているはずなので、逆にチェックを増やした状態になってます)
schema.rbがすっきり&MariaDB関連のパッチが採用
移行するついでに、migrationを統合してしまおうと思いまして、
schema.rbの内容をそのままコピペしたmigrationを作ろうとしたのですが、
だいぶいろいろ変わっておりました
ActiveRecord::Schema.define(version: 20160507003347) do create_table "abilities", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin" do |t| t.string "name", limit: 100, null: false t.string "explanation", limit: 500 t.string "weapon_name", limit: 100 t.datetime "created_at", null: false t.datetime "updated_at", null: false t.index ["name"], name: "index_abilities_on_name", unique: true, using: :btree end # ... end
まず、create_tableのオプションに、MariaDB(MySQL)固有のオプション情報が付加されています
あと、indexの生成が「add_index」ではなく、「t.index」という形でcreate_tableに統合されています
後者はとてもいいですね(`・ω・´) b
しかも、migration関連の処理がえらい速くなりました
単にmigrationを統合したから、という感じではない速度です
デフォルトAPサーバがpumaに変更されたが・・・
ActionCableが追加され、WebSocketが採用されたために、
Webrickからpumaに変更されたのですが、
これがいろいろ問題を引き起こしているようで・・・(´-ω-)
configレベルで誤りがあって起動しないって場合に、
Webrickなら例外を吐いて止まってくれるのですが、
pumaだと落ちずにずっとフリーズしたままで、他のコンソールからkillしないといけません
特にコンソールに例外を吐いてくれないのが困ります
どこを直せばいいのかわかりませんし(´・ω・`)
他にもreload周りのissueが挙がっているようですし、
まだまだ不安定な感じがしますね・・・
まあ、本番で使うわけではないですし、コードの本体をいじる際には問題ないのですが
ArelでOR文の書き方変更
Rails5より前だと、ActiveRecordでOR文を書く際、
やや面倒な書き方をしなければいけませんでした(´-ω-)
def skill_search(category, cost, sub, ef) return [] if (category.blank? && cost.blank?) arel = SkillEffect.all arel.where!(category: category) unless category.blank? arel.where!(subcategory: sub) unless sub.blank? arel = arel.joins(:skill).where(skills: { cost: cost }) unless cost.blank? unless ef.blank? efs = [ef].flatten.uniq.compact arel.where!( SkillEffect.where( subeffect1: efs ).where( subeffect2: efs ).where( subeffect3: efs ).where( subeffect4: efs ).where( subeffect5: efs ).where_values.reduce(:or) ) end arel.pluck(:skill_id) end
「where_values.reduce(:or)」って書き方は美しくないので、
「or(条件)」という書き方が採用されております(`・ω・´) b
そこまではいいのですが、ANDとORが混在する場合のルールがいまいちよくわからなくて、
whereとorの順番を変えるだけで、いろいろ変わってしまいまして・・・
先にwhereを書いてからorを書くと、whareの条件と混線してしまうので、
orを先に書いてしまってからwhereを書くことでなんとか
def skill_search(category, cost, sub, ef) return [] if (category.blank? && cost.blank?) arel = SkillEffect.all # 先にOR条件を構築 unless ef.blank? efs = [ef].flatten.uniq.compact arel = arel.where(subeffect1: efs) .or(SkillEffect.where(subeffect2: efs)) .or(SkillEffect.where(subeffect3: efs)) .or(SkillEffect.where(subeffect4: efs)) .or(SkillEffect.where(subeffect5: efs)) end # そのあとAND条件を付加 arel = arel.where(category: category) unless category.blank? arel = arel.where(subcategory: sub) unless sub.blank? arel = arel.joins(:skill).where(skills: { cost: cost }) unless cost.blank? arel.pluck(:skill_id) end
このあたりは生成されるSQLを眺めて慎重に進めるしかないのですが、
やはりドキュメントが欲しいですね(´-ω-)
キャッシュの制御がフレームワーク化
処理効率化のため、Rails.cacheを使うってのはよくあるはなしですが、
開発環境では邪魔だし、かといってテストもできないと困るので、
以前は自前のconfigにON/OFFフラグを記述して制御していました
def with_cache(name, &b) return unless (name && b) # configの情報を見て利用の有無を制御 return b.call unless ServerSettings.use_cache? Rails.cache.fetch(name, &b) end
これがRails5では不要になりまして、
「tmp/caching-dev.txt」の有無で制御できるようになってます
(実際の操作は「rails dev:cache」か「rake dev:cache」するだけです)
# config/environments/development.rb # Enable/disable caching. By default caching is disabled. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true config.cache_store = :memory_store config.public_file_server.headers = { 'Cache-Control' => 'public, max-age=172800' } else config.action_controller.perform_caching = false config.cache_store = :null_store end
こういうコードがdevelopment.rbに自動生成されるおかげなので、
他のバージョンでもこれをコピペすれば同じことができます(`・ω・´) b