Skip to content

Instantly share code, notes, and snippets.

@ktakase00
Last active December 14, 2019 10:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ktakase00/deda0d425151970b873878d064742b2a to your computer and use it in GitHub Desktop.
Save ktakase00/deda0d425151970b873878d064742b2a to your computer and use it in GitHub Desktop.
テーブルの継承をしていてもFactoryBotを使いたい
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