[Rails]STI(단일 테이블 상속) 및 메타 프로그래밍 DRY
개요
복제 코드가 증가하기 쉬운 샘플 응용의 디자인을 예로 들다
STI(단일 테이블 상속)와 메타프로그램 설계를 사용하여 DRY(중복 제외)를 시도해 봅니다.
제재
사용자가 유지하고 있는 곡을 분류별로 관리하는 프로그램입니다.
사용자 페이지에서 등록 곡을 카테고리별로 나열할 수 있습니다(더 나아가 CRUD).
그런 인상이네요.
kidachi_さん
あなたの登録曲一覧
Rock
ほげRock
ふがRock
Pops
未登録です。
Jazz
ふーJazz
ばーJazz
아무 생각 없이 하면 록/pops/jazz 각자의 모형, 보기, 컨트롤러에유사한 기술이나 복제품이 늘어날 것 같으십니까?
그렇다면 이를 막기 위해서는 먼저 STI부터 시작해야 한다.
(※ 추서)
사실 상술한 요건일 뿐이라면
user 테이블, 뮤직 테이블, genre 테이블만 준비되어 있습니다.
user has_many genres through musics
의assosiation에서도 실현 가능(원래 록/pops/jazz 모형을 준비할 필요가 없다).
실제로'앞으로 각 genre에 여러 가지 특유의 처리를 추가하고 싶다'고 가정하면
각genre의 개별 모델을 준비하는 것을 전제로 한다.
일이 좀 있는 예가 좋지 않은 것 같아서 죄송합니다.
STI(Single Table Inheritance/단일 테이블 상속)는
상속
수퍼 클래스와 비공통 프로젝트의 하위 클래스를 사용하여
코드의 중복을 방지하는 구조이지만 주의하십시오
STI도db(표) 디자인에 적용됩니다.
단일 테이블 상속 이름은 그대로 유지됩니다.
구체적 예
방금 전의 악곡 관리 응용 탁자를 대략적으로 설계해 보았다
나는 다음과 같은 인상이 있을 것이라고 생각한다.
괜찮은데 모델/테이블에 중복된 메시지가 있어요.
늘어난 것 같은데.
따라서 단일 테이블 상속
이렇게
슈퍼클래스를 준비해서 계승 메커니즘을 사용한다는 개념과 똑같다.
언뜻 보기에는 별다른 것이 없는 것 같지만, 사실은
아무렇지 않은 듯 뮤직 테이블에'type'이라는 열이 추가됐습니다.
록/pops/jazz 같은 애들 정보가 여기 있을 거야.
또 록 테이블에서만 얻을 수 있는 정보를 원한다면
사전에 그것을 뮤직으로 정의했다.
모델 설치
STI를 사용하더라도 모델은 일반적인 상속 형식으로만 이루어진다.
music.rb
class Music < ActiveRecord::Base
belongs_to :user
validates :name, presence: true
validates :artist, presence: true
end
rock.rbclass Rock < Music
end
주의해야 할 것은아이가 베이스가 아니라 부모 뮤직을 물려받았어요.
대충 그렇겠지.
그나저나 부모님이 설정한 관계와validates는 모두
자반으로 물려받을 수도 있고.
STI에 관해서는 여기까지입니다.
결론, STI를 이용하여db를 생성할 때
Cntroller 설치 및 메타프로그램 설계
users/show로 사용자의 곡을 일람하는 방법입니다.
즉 show에 @rock/@pops/@jazz 세 개를 설정하고 싶다는 것이다.
여기서 원 프로그래밍을 활용해 보자.
users_controller.rb
class UsersController < ApplicationController
GENRE = [
'rock',
'pops',
'jazz'
]
def show
@user = User.find(current_user.id)
set_music_by_genre
end
private
def set_music_by_genre
@music_list = Array.new
GENRE.each do |music|
key = "@#{music}"
if @user.send(music).nil? # 1
music = music.gsub(/\b\w/) { |s| s.upcase } # 2
value = self.class.const_get(music).new # 3
else
value = @user.send(music) # 4
end
instance_variable_set(key, value) # 5
@music_list << instance_variable_get(key) # 6
end
end
end
set_music_by_genre 설명
1. if @user.send(music).nil?
send를 사용하여 @user에 대한 메시지를 동적으로 수신하고 있습니다.
즉, @user입니다.rock/@user.pops/@user.동적 생성 jazz
여기, @user rock/pops/jazz 각자의 데이터가 있는지 여부
, 기존 데이터를 저장한 상태에서 호출(#2, 3)
저장하지 않으면 새 객체가 생성됩니다(#4).
2. music.gsub(/\b\w/) { |s| s.upcase }
동적으로 Rock/Pops/Jazz 문자열을 생성합니다.
#3 을 사용합니다.
3. self.class.const_get(music).new
self.class.const_get(str)에서 문자열(str)부터 시작
클래스나 모듈의 상수를 얻을 수 있습니다.
즉, #2를 생성하는 "Rock"/"Pops"/"Jazz"
각각 new인 클래스로 변환(Rock/Pops/Jazz).
4. @user.send(music)
#1과 마찬가지로 동적으로@user 메시지를 보냅니다.
5. instance_variable_set(key, value)
인스턴스 변수를 동적으로 생성합니다.
@rock/@pops/@jazz는 각각 기존 대상이나 새 대상을 설정합니다.
6. @media_list << instance_variable_get(key)
뷰에서 사용하기 위해
[
#<Rock id: nil, type: "Rock", created_at: nil, updated_at: nil>,
#<Pops id: nil, type: "Pops", created_at: nil, updated_at: nil>,
#<Jazz id: nil, type: "Jazz", created_at: nil, updated_at: nil>
]
이 대상을 포함하는 그룹을 준비하십시오.View
views/users/show.html.erb
<% @music_list.each do |music| %>
<% if music.try(:id).nil? %>
<!-- 新規登録フォーム -->
<% else %>
<!-- 登録済み情報の表示 -->
<% end %>
<% end %>
show 설정@music_list을 바탕으로 instancevariable_get 누르기()동적으로 대상을 가져오고 대상에 따라 디스플레이 처리를 합니다.
Controller
새 등록 형식에서 버려진 데이터에 따라create를 하는 곳.
여기도 셀프야.class.const_get ()에 대응하는 하위 클래스
클래스를 동적으로 설정하고 저장합니다(Rock/Pops/Jazz).
music_controller.rb
class MusicController < ApplicationController
before_action :set_music_info
before_action :music_params
def create
const_name = @music_name.gsub(/\b\w/) { |s| s.upcase }
# サブクラスごとのオブジェクトを初期化
@music = self.class.const_get(const_name)
@music.new(music_params)
respond_to do |format|
if @music.save!
~
end
end
end
private
# strong_parameters
def music_params
params.require(@music_name).permit(:user_id, :name, :email, :password)
end
end
rock_controller.rbclass RockController < MusicController
private
set_music_info
@music_name = "rock"
end
end
STI를 사용해서 그런지 뮤직 테이블, 뮤직이 준비되어 있습니다.controller에서모든 하위 클래스(rock/pops/jazz)의 save 처리도 매우 직관적으로 진행된다.
※ 개인적으로 STI는 특별한 구조라기보다는
관계를 계승하고자 하는 모델에 맞는 표 인터페이스
내 생각엔 뭐가 아닐까?
완성
그러면 show 페이지의 악곡 일람과 악곡이 새로 등록된 논리를 사용할 수 있습니다.
이런 금속 골격을 준비해 뮤직의 종류를 늘릴 때
hoge_controller.rb와 GENRE 리스트에 새로운 종류를 추가하면 됩니다.
(당연히 controller/model 파일이나routes.rb 추기를 준비해야 하며, 별도로 준비해야 한다)
classic_controller.rb
class ClassicController < MusicController
private
set_music_info
@music_name = "classic"
end
end
users_controller.rbclass UsersController < ApplicationController
GENRE = [
'rock',
'pops',
'jazz',
'classic'
]
※ GENRE 배열은 설정 파일로 외부화하는 것이 좋습니다이런 느낌.
길어지니까 그만하자
다른 논리 부분도 마찬가지로 복제 코드를 없앨 수 있다.
참고 자료
Rails에서 단일 테이블 상속(Single Table Inheritance)
http://blog.matake.jp/archives/railssingle_table_inherit
const_get (Module)
http://ref.xaio.jp/ruby/classes/module/const_get
instance_variable_set/get (Object)
http://ref.xaio.jp/ruby/classes/object/instance_variable_set
http://ref.xaio.jp/ruby/classes/object/instance_variable_get
최후
효율적이고 더 좋은 디자인/쓰기 방법이 있다면 꼭 가르쳐 주십시오!
Reference
이 문제에 관하여([Rails]STI(단일 테이블 상속) 및 메타 프로그래밍 DRY), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://qiita.com/kidach1/items/789c2e7aebbcfbd2583e텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)