Last active
December 14, 2019 10:17
-
-
Save ktakase00/deda0d425151970b873878d064742b2a to your computer and use it in GitHub Desktop.
テーブルの継承をしていてもFactoryBotを使いたい
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Majikiri | |
extend ActiveSupport::Concern | |
# テーブルが分割される側のモデルを記憶する | |
@@majikiri_objects_that_are_divided = {} | |
# テーブルを分割する側のモデルを記憶する | |
@@majikiri_subjects_that_divide_others = {} | |
included do | |
# 他のモデルに分割されるテーブルの設定 | |
# @param subject_name [Symbol|String] 分割する側のモデルの名前 | |
# @param attr_name [Symbol|String] 分割時にテーブル名に付加される文字列を持つ属性 | |
# | |
def self.majikiri_divided_by(subject_name, attr_name: nil) | |
return if !Rails.configuration.x.majikiri.auto_divide | |
cls_key = majikiri_make_cls_key | |
@@majikiri_objects_that_are_divided[cls_key] = { | |
model_name: subject_name.to_sym, | |
attr_name: attr_name.nil? ? :type : attr_name.to_sym | |
} | |
end | |
# 他のモデルのテーブルを分割する設定 | |
# @param object_name [Symbol|String] 分割される側のモデルの名前 | |
# @param attr_name [Symbol|String] 分割時にテーブル名に付加される文字列を持つ属性 | |
# | |
def self.majikiri_divide(object_name, attr_name: nil) | |
return if !Rails.configuration.x.majikiri.auto_divide | |
cls_key = majikiri_make_cls_key | |
if !@@majikiri_subjects_that_divide_others.has_key?(cls_key) | |
@@majikiri_subjects_that_divide_others[cls_key] = {} | |
end | |
@@majikiri_subjects_that_divide_others[cls_key][ object_name.to_sym ] = { | |
attr_name: attr_name.nil? ? :type : attr_name.to_sym | |
} | |
# レコード登録時のコールバックを定義 | |
after_create :majikiri_inherit_table | |
def majikiri_inherit_table | |
self.class.majikiri_subjects_that_divide_others.each do |key, val| | |
# 子テーブルのテーブル名を決定 | |
model_class = key.to_s.classify.constantize | |
inherit_prefix = self.public_send(val[:attr_name]) | |
inherit_table_name = "#{inherit_prefix}_#{model_class.table_name}" | |
# 子テーブルを作成 | |
sql = <<-SQL | |
CREATE TABLE IF NOT EXISTS #{inherit_table_name} ( | |
LIKE #{model_class.table_name} INCLUDING ALL | |
) INHERITS (#{model_class.table_name}); | |
SQL | |
self.class.find_by_sql([sql]) | |
end | |
end | |
end | |
# 子テーブルにレコードを追加する | |
# @return [Boolean] 登録に成功した場合はtrue | |
# | |
def majikiri_save | |
return false if !Rails.configuration.x.majikiri.auto_divide | |
majikiri_create.present? | |
end | |
# 子テーブルにレコードを追加する | |
# @return [ApplicationRecord] 登録した結果を持つインスタンス | |
# | |
def majikiri_create | |
return if !Rails.configuration.x.majikiri.auto_divide | |
# バリデーション | |
return if self.invalid? | |
sql = majikiri_make_insert_sql | |
values = majikiri_param_values | |
res = self.class.find_by_sql([sql, values]).first | |
return if res.nil? | |
self.attributes = res.attributes | |
self | |
end | |
def self.majikiri_objects_that_are_divided | |
@@majikiri_objects_that_are_divided[majikiri_make_cls_key] | |
end | |
def self.majikiri_subjects_that_divide_others | |
@@majikiri_subjects_that_divide_others[majikiri_make_cls_key] | |
end | |
def self.majikiri_make_cls_key | |
self.name.underscore.to_sym | |
end | |
# レコード登録用のSQLを生成 | |
# @return [String] レコード登録用のSQL | |
# | |
def majikiri_make_insert_sql | |
# レコード登録先のテーブル名を決定 | |
inherit_table_name = majikiri_make_inherit_table_name | |
# 登録するデータの属性から主キーを取り除く | |
column_name_ary = self.class.columns.map { |it| it.name } | |
attr_name_ary = column_name_ary.select { |it| it != self.class.primary_key } | |
# 登録するカラム、プレイスホルダー、戻り値 | |
attr_name_str = attr_name_ary.join(',') | |
placeholder_str = attr_name_ary.map { |it| ":#{it}" }.join(',') | |
column_name_str = column_name_ary.join(',') | |
<<-SQL | |
INSERT INTO #{inherit_table_name} ( | |
#{attr_name_str} | |
) VALUES ( | |
#{placeholder_str} | |
) RETURNING | |
#{column_name_str} | |
SQL | |
end | |
# 子テーブルのテーブル名を生成 | |
# @return [String] 子テーブルのテーブル名 | |
# | |
def majikiri_make_inherit_table_name | |
# 分割する側のモデルの情報を取得 | |
devided_by = self.class.majikiri_objects_that_are_divided | |
subject_name = devided_by[:model_name] | |
attr_name = devided_by[:attr_name] | |
# レコード登録先のテーブル名を決定 | |
subject = self.public_send(subject_name) | |
inherit_prefix = subject.public_send(attr_name) | |
"#{inherit_prefix}_#{self.class.table_name}" | |
end | |
# SQLに与える値を生成 | |
# @return [Hash<Symbol, Object>] find_by_sqlに与える値を表すハッシュ | |
# | |
def majikiri_param_values | |
default_key_ary = majikiri_default_values.keys | |
values = self.attributes.symbolize_keys.map { |key, val| | |
param_value = case | |
when default_key_ary.include?(key) | |
# デフォルト値を与える | |
val.nil? ? majikiri_default_values[key] : val | |
when val.is_a?(Time) | |
# 日時はタイムゾーンまで引き渡されるようにする | |
val.to_s | |
else | |
val | |
end | |
[ key, param_value ] | |
}.to_h | |
end | |
# デフォルト値の定義 | |
# @return [Hash<Symbol, Object>] デフォルト値を指定する属性の名前と値 | |
# | |
def majikiri_default_values | |
{ | |
created_at: 'now()', | |
updated_at: 'now()', | |
} | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment