💡
この記事は Middleman 時代に書いた古いものです。記録のため、astro-notion-blog に移行していますが、あまり参考にしないでください。
model_spec.rb の設置
昨日手動で設置した model の spec を生成するためのテンプレートを設置する.
-
lib/templates/rspec/model フォルダを作成する.
mkdir -p lib/templates/rspec/model
-
lib/teamplates/rspec/model/model_spec.rb を以下のように記載する. model 名で変更になる部分は erb で記述されている. class_name はクラス名(先頭が大文字),singular_table_name はテーブル名の小文字,plural_table_name はテーブル名の小文字の複数形である.
<%= class_name %>FactoryHash = { one: %w[], two: %w[], three: %w[] } # @param [Symbol, String] key オブジェクトを一意に決定するキー # @return [<%= class_name %>] <%= class_name %> FactoryGirl オブジェクト def <%= singular_table_name %>_factory(key) n, c = <%= class_name %>FactoryHash[key.to_sym] FactoryBot.find_or_create( :<%= singular_table_name %>, name: n, code: c, sort_order: <%= class_name %>.sort_orders[key.to_sym] ) if n end # @param [Array<Symbol, String>] keys オブジェクトを一意に決定するキーの配列 # @return [Array<<%= class_name %>>] <%= class_name %> FactoryGirl オブジェクトの配列 def <%= singular_table_name %>_factories(keys) keys.map { |k| <%= singular_table_name %>_factory(k) } end ##### ↑ write to spec/support/create_factories/<%= singular_table_name %>_factory.rb require 'rails_helper' RSpec.describe <%= class_name %>, type: :model do context 'common validation check' do subject { <%= singular_table_name %>_factory :<%= singular_table_name %> } #it_behaves_like :presence_validates, %i[] #it_behaves_like :unique_validates, %i[], -> { <%= singular_table_name %>_factory :other } #it_behaves_like :plural_unique_validates, %i[], -> { <%= singular_table_name %>_factory :other } #it_behaves_like :destroy_validates #it_behaves_like :reject_destroy_validates #it_behaves_like :belongs_to, :<%= singular_table_name %>, has_many: %i[], has_one: %i[], children: :optional, child: :optional #it_behaves_like :dependent_destroy, :<%= singular_table_name %>, %i[has_many has_one] #it_behaves_like :reject_destroy_for_relations, :<%= singular_table_name %>, %i[has_many has_one] #it_behaves_like :destroy_nullify_for_relations, :<%= singular_table_name %>, %i[has_many has_one] end context 'after some <%= plural_table_name %> are registrered' do model_keys = %i[] let!(:targets) { <%= singular_table_name %>_factories model_keys } describe '<%= class_name %> class' do subject { <%= class_name %> } #it_behaves_like :mst_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end # #it_behaves_like :msta_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end #it_behaves_like :mst, :METHOD1, -> { [v1, <%= singular_table_name %>_factories(model_keys.values_at()), v2, <%= singular_table_name %>_factories(model_keys.values_at())] } #it_behaves_like :msta, :METHOD1, -> { [v1, <%= singular_table_name %>_factories(model_keys.values_at()), v2, <%= singular_table_name %>_factories(model_keys.values_at())] } end context '<%= class_name %> instances' do subject { targets } #it_behaves_like :amst_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end # #it_behaves_like :amsta_block, -> t do # { # method1: [v1, a1, v2, a2, ...], # method2: [v1, a1, v2, a2, ...], # } #end #it_behaves_like :amst, :METHOD1, -> { [v1, a1, v2, a2] } #it_behaves_like :amsta, :METHOD1, -> { [v1, a1, v2, a2] } end end end
Kurasu モデルの作成
-
generator で Kurasu モデルを作成する.Kurasu は teacher_id, name, oboslete を属性と持ち,Teacher と Kurasu は 1 対多の関係となる. 設計当初から obsolete 属性を追加した( アプリの設計 - 不定期刊 Rails App を作る(1) は修正した). 年度更新した際に運用上古いクラスを見えなくするためのフラグである. obsolete が true の時,そのクラスは未使用とする.
$ bin/rails g model kurasu name:string obsolete:boolean teacher_id:integer Running via Spring preloader in process 51112 invoke active_record create db/migrate/20181114124228_create_kurasus.rb create app/models/kurasu.rb invoke rspec create spec/models/kurasu_spec.rb invoke factory_bot create spec/factories/kurasus.rb
-
teacher_id および name については nil が入ることが許されない. Rails 側でも validate で制御できるが,データベース側でも null を許可しないように変更した. plural_uniqueness の説明もしたいので,teacher_id と name の属性はセットで unique である制約をつけてみた. すなわち,一人の教員は同じ名前のクラスを設定することができなくなる(他の教員が同じ名前のクラスを持つことはできる). これを実現するには,add_index に属性の配列を渡し,unique: true オプションをつければよい. この時,teacher_id だけで絞り込みをすることが多いことを考え,teacher_id, name の順番とした. これは,複数インデックスの場合には,左から順に少ない項目でもインデックスが利用可能となるためである(teacher_id だけの場合もこの index が利用可能). これらを踏まえ,db/migrate/20181114124228_create_kurasus.rb は以下のように変更した.
class CreateKurasus < ActiveRecord::Migration[5.2] def change create_table :kurasus do |t| t.string :name, null: false # オプションを追加 t.boolean :obsolete, null: false # オプションを追加 t.integer :teacher_id, null: false # オプションを追加 t.timestamps end add_index :kurasus, %i[teacher_id name], unique: true end end
-
データベースの migrate を実行する(2行目以降は結果).
$ bin/rails db:migrate == 20181114124228 CreateKurasus: migrating ==================================== -- create_table(:kurasus) -> 0.1361s -- add_index(:kurasus, [:teacher_id, :name], {:unique=>true}) -> 0.0090s == 20181114124228 CreateKurasus: migrated (0.1452s) ===========================
-
念のため migration が redo できることも確認しておく(2行目以降は結果).
$ bin/rails db:migrate:redo == 20181114124228 CreateKurasus: reverting ==================================== -- remove_index(:kurasus, {:column=>[:teacher_id, :name]}) -> 0.0649s -- drop_table(:kurasus) -> 0.0143s == 20181114124228 CreateKurasus: reverted (0.1147s) =========================== == 20181114124228 CreateKurasus: migrating ==================================== -- create_table(:kurasus) -> 0.0508s -- add_index(:kurasus, [:teacher_id, :name], {:unique=>true}) -> 0.0043s == 20181114124228 CreateKurasus: migrated (0.0554s) ===========================
長くなったので今日はここまで