Rails에서 메타 프로그래밍(블랙 매직)이라 불리는 send 방법을 활용해봤어요

개시하다


내가 이 기사를 쓰려는 목적은 센스 방법을 활용하지 않은 인상을 가진 사람에게 Rails에서 이렇게 사용하는 장면이 있다는 것을 소개하기 위해서다.
먼저send방법을 사용하는 것이 가장 좋은지 아닌지...웃다 웃다

원 프로그래밍은?


우선 원 프로그램 설계의 의미를 간단하게 소개하다.
Rails 자습서에서 이렇게 설명했습니다.
한마디로 원 프로그래밍은'프로그래밍으로 프로그래밍'이다.메타 프로그램 설계는 루비가 가지고 있는 매우 강력한 기능이다. 라일스의 언뜻 보면 마법 같은 기능('흑마술'이라고도 부른다)은 대부분 루비의 메타 프로그램 설계를 통해 이루어진다.
프로그램으로 프로그램을 만듭니까?이 설명도 살짝만 읽어도 잘 모르겠네요, 웃음.
Rails 튜토리얼에서 실제로는 send 방법을 이용하여 메타 프로그램을 만들었는데 그걸 보면 더 인상적일 수 있어요.
※ 이후 실제 사례로 설명

send 방법이 뭐예요?


루비에게 센드라는 방법이 있어.
간단한 예로 어떻게 사용하는지 설명하다.
예를 들어 upcase 문자열을 대문자로 출력하는 방법을 예로 들자.
string = "ruby"  #=> "ruby"
string.upcase  #=> "RUBY"
string.send(:upcase)  #=> "RUBY"
string.send("upcase")  #=> "RUBY"
이렇게 send 방법의 매개 변수를 호출하고자 하는 방법 이름의 기호나 문자열을 전달하면 일반적으로 방법의 호출이 발생한다.
그럼 다음에 파라미터가 있는 방법을 사용하고 싶을 때 어떻게 해야 하나요? split 방법의 예를 들어보세요.
Rails 튜토리얼에서는 여기까지 설명하지 않았지만 의외로 간단했다.
split 방법은 문자열 등을 매개 변수에 건네주고 나누어 배열하는 것입니다.
string = "aaaaaxbbbbbxccccc"  #=> "aaaaaxbbbbbxccccc"
string.split("x")  #=> ["aaaaa", "bbbbb", "ccccc"]
string.send(:split, "x")  #=> ["aaaaa", "bbbbb", "ccccc"]
string.send("split", "x")  #=> ["aaaaa", "bbbbb", "ccccc"]
매개 변수를 방법에 전달하려면send 방법의 두 번째 매개 변수 다음에 전달합니다.

Rails send 사용 방법


그럼 본론으로 들어가겠습니다.
Rails에서 EC 사이트를 제작할 때 3개의 모델(Artist, Label,Genre)의 데이터를 한눈에 볼 수 있는view는 기본적으로 같기 때문에 나는 그것을 국부화했다.
먼저 간단한 모형을 놓고 어떤 배치인지 보자.(상당히 지저분하니 신경 쓰지 마시오 웃음)

이동





응, 이것은 단지 표시된 데이터일 뿐이야.이걸 국부화하지 않고 뭘 해요?그래서 국부화를 진행했다.

컨트롤러


app/controllers/artists_controller.rb
# 中略
  def index
    @artists = Artist.page(params[:page])
    @artist = Artist.new
  end
# 中略
app/controllers/genres_controller.rb
# 中略
  def index
    @genres = Genre.page(params[:page])
    @genre = Genre.new
  end
# 中略
app/controllers/labels_controller.rb
# 中略
  def index
    @labels = Label.page(params[:page])
    @label = Label.new
  end
# 中略


app/views/artists/index.html.erb
<h2>アーティスト一覧</h2>
<%= render 'layouts/object_list', objects: @artists, object: @artist %>
app/views/genres/index.html.erb
<h2>ジャンル一覧</h2>
<%= render 'layouts/object_list', objects: @genres, object: @genre %>
app/views/labels/index.html.erb
<h2>レーベル一覧</h2>
<%= render 'layouts/object_list', objects: @labels, object: @label %>
app/views/layouts/_object_list.html.erb
<div class="row">
  <div class="col-sm-5">
    <%= render 'layouts/error', object: object %>
    <%= form_with model: object, local: true do |f|  %>
      <%= f.label :name %>
      <%= f.text_field :name %>
      <%= f.submit "登録" %>
    <% end %>
  </div>
  <div class="col-sm-7">
    <div class="index-wrapper">
      <% object_name = object.class.to_s.downcase  # それぞれのオブジェクトのクラス名を変数に代入 %>
      <% objects.each do |data| %>
        <% edit_path = self.send("edit_#{object_name}_path", data)  ### ここで使用!!### %>
        <% destroy_path = self.send("#{object_name}_path", data)  ### ここで使用!!### %>
        <%= data.name %>
        <%= link_to "編集", edit_path %>
        <%= link_to "削除", destroy_path, method: :delete, data: { "confirm" => "本当に削除しますか?" } %>
      <% end %>
    </div>
    <%= paginate objects, class: "pagination" %>
  </div>
</div>
각자index.html.erb로 작성한 부분 템플릿의 호출은 문제없습니다.(모르시는 분들은 구글로 보내주세요)_object_list.html.erb의 코드로 이번 주제의send 방법과 관련된 코드만 설명합니다.

해설


먼저 각 대상의 클래스 이름 (모든 소문자) 을 변수에 대입합니다.
나중에 왜 그런지 알게 될 거야.
<% object_name = object.class.to_s.downcase  # それぞれのオブジェクトのクラス名を変数に代入 %>
하나하나 설명하면.class - 호출된 대상의 클래스를 가져옵니다..to_s - 문자열로 변환.downcase - 문자열을 소문자로 변환
그래서 정리를 해볼게요.
# 渡ってきた元のインスタンス変数 → object_nameに入る文字列
@artist  "artist"
@genre  "genre"
@label  "label"
.
다음은 주제의send 방법의 코드입니다.
<% edit_path = self.send("edit_#{object_name}_path", data)  ### ここで使用!!### %>
<% destroy_path = self.send("#{object_name}_path", data)  ### ここで使用!!### %>
여기서 하고 싶은 것은 edit_path,destroy_path 각각 each 방법에 따라 추출한 대상 + 국부 호출을 통해 전달된 대상의 종류(artist나genre 또는 label) 동적 생성 경로를 대입하는 것이다.
즉, 동적 결정 호출 방법이다.이것은 루비의 메타 프로그램이다.
send 방법의 집행 결과는 다음과 같다.
# artist
edit_path = edit_artist_path(data)
destroy_path = artist_path(data)
# genre
edit_path = edit_genre_path(data)
destroy_path = genre_path(data)
# label
edit_path = edit_label_path(data)
destroy_path = label_path(data)
여기 있다, 응?self.가 뭐예요?그나저나 레일스~_path가 방법인가요?방법은 ***.メソッド의 느낌으로 어떤 대상에게 호출되는 것이 아닌가?
그렇게 생각할지도 몰라요.특히 다른 언어(JavaScript, PHP 등)를 처음 배우는 사람은 함수 아닌가?그렇게 생각할 수도 있어, 이게 방법이야!!
아니면 루비에 함수가 없거나.
이 말을 하면 길어지니까 조금 생략하겠지만 루비 전부 대상~_path도 모종의 대상인 방법입니다.그리고 자기 방법을 쓸 때.~_path의 느낌으로 쓰다.그러나 이것은 생략self.メソッド할 수 있고 방법명만 사용하면 호출할 수 있다.
self.는 평소 생략~_path하고 실제로self.도 불러낼 수 있다는 것이다.
그럼 send 방법을 사용할 때도 생략self.~_path할 수 있습니까?
그렇다니까.위에서 소개한 코드는 좀 더 짧게 할 수 있다.
<% edit_path = send("edit_#{object_name}_path", data)  ### ここで使用!!### %>
<% destroy_path = send("#{object_name}_path", data)  ### ここで使用!!### %>
이번에는 설명하기 편하게 처음엔 가산self. 상태였는데 짧게 쓰면 과를 완성한 것이다.

총결산


실제로send방법을 사용하지 않아도 더욱 간단하게 진행할 수 있다.
prefixself.를 사용하지 않고 상대 경로를 직접 쓰면 상대 경로에 변수를 넣는 것이 그리 어렵지 않겠죠.
하지만 난 그저 흑마술이라는 방법을 사용하고 싶을 뿐이야.웃다 웃다
여러분도 이루기 어려운 게임 감각을 선택해 보세요. 재미있을 거예요.

좋은 웹페이지 즐겨찾기