재미있는 메타프로그래밍/6장

13459 단어 Ruby

제6장 금요일: 코드를 설명하는 코드


금요일 일정

  • 와 류홍의 사랑
  • eval을 사용하세요!
  • eval 사용을 중지하세요!
  • 클래스 매크로를 사용하세요!
  • 연결 방법을 사용하세요!
  • 1. 학급홍과의 연애


  • 원래 학급홍은? クラス定義内でselfを明示せずに呼び出せるキーワードのようなメソッド ex) attr_accessor
  • 류홍과의 해후
    거북이: "반 매크로는 방법이야!? 대단해. 나도 반 매크로를 독립적으로 만들 거야!"
    거북이는 요즘 깜빡깜빡해서 자신이 언제 실례 변수를 만들었는지 잊어버렸다.따라서 실례 변수를 산열로 하고 시간 데이터를 저장할 수 있는 클래스 매크로를 실현하고자 한다.
  • 거북이의 희망
  • Class Intern
      attr_with_date :shimatomo
      attr_with_date :kemako
    end
    
    intern = Intern.new
    intern.shimatomo = 'shimada'
    intern.kemako = 'miyatake'
    intern.shimatomo[:val] # => 'shimada'
    intern.shimatomo[:created_at] # => 3行上で作成した時のTime
    

    2. eval을 사용하세요!


    길흉: "그럼 우선 attr accessor를 단순한 방법으로 실현해 봅시다. 마침 여기서 EVAL을 사용해 보세요."
    거북이:
    "eval은 매개 변수로 전달되는 문자열을 코드로 실행합니다. 자세한 내용은 원 프로를 참조하십시오."
    Q1. add_attr_with_date 방법의 행위는 다음과 같이 실현된다
    class Intern; end
    
    add_attr(Intern, :shimatomo)
    intern = Intern.new
    intern.shimatomo = 'shimada'
    puts intern.shimatomo # 'shimada'
    
    def add_attr_with_date(klass, attr)
      eval "
        #code...
      "
    end
    
    # ヒント!
    # hogeと言う名前のattrを受け取った場合に以下の様なメソッドを定義してほしい。
    def hoge=(value)
      @hoge = value
    end
    
    def hoge
      @hoge
    end
    
    A1. add_attr
    def add_attr_with_date(klass, attribute)
      eval "
        class #{klass}
          def #{attribute}
            @#{attribute}
          end
    
          def #{attribute}=(val)
            @#{attribute} = valf
          end
        end
      " 
    end
    
    # 亀ちゃんさんの希望では次の様になる
      def #{attr}=(val)
        @#{attr} = {val: val, created_at: Time.now}
      end
    
    거북이: "eval을 사용하면 평소에 동적으로 설정할 수 없는 클래스 이름, 방법 이름, 실례 변수 이름을 동적으로 정의할 수 있습니다!"
    하지만 강력한 힘에
    거북이&마장 "큰 책임과 함께!"
    그러나 "eval에 전달된 문자열은 input 등 제3자의 입력을 포함할 때 코드에 악의가 있어도 직접 실행됩니다. 기본적으로 eval을 사용할 때 전달된 코드 문자열은 모두 자신의 입력임을 확인하십시오."

    3. eval 사용을 중단하라!


    거북이: "응, eval의 위험을 완전히 없애기는 어려울 것 같아."
    좋습니다. "거북이, 현재 학급 동태변경 방법은 eval 이외에 또 있죠?"
    아, 수요일에 한 거예요.
    따라서 class eval을 사용하여 add attr with date를 변경하십시오.
    A2.
    def add_attr_with_date(klass, attribute)
      klass.class_eval do
        define_method "#{attribute}=" do |value|
          attr_hash = { val: value, created_at: Time.new }
          instance_variable_set("@#{attribute}", attr_hash)
        end
    
        define_method attribute.to_s do
          instance_variable_get("@#{attribute}")
        end
      end
    end
    

    4. 유홍으로 만들어라!


    거북이: "그런데 이런 반은 거시적인 게 아니야!!"
    그래, 알았어, 알았어. 다음에 드디어 반에 가서 거시적으로 하자.
    작은 거북이:"😲 」
    안개: "드디어 반홍을 만들 때가 됐어요. 거북이의 눈이 더 눈부셔요."
    Q3. 다음 변경 사항에 따라 방법을 클래스 매크로로 설정합니다.다음 코드는 예상한 결과입니다.
    클래스를 매개 변수에 명확하게 전달하지 않음
    - 정의 방법의 위치를 변경합니다(모든 클래스를 호출할 수 있음).
    class Intern
      attr_with_date :shimatomo
    end
    
    intern= Intern.new
    intern.shimatomo = 'shimada'
    intern.shimatomo[:val] # => 'shimada'
    intern.shimatomo[:created_at] # => Timeオブジェクト
    
    A3
    class Module # もしくはClassクラス
      def add_attr_with_date(attr)
    
        define_method(attr) do
          instance_variable_get("@#{attr}")
        end
    
        define_method("#{attr}=")do |val|
          instance_variable_set("@#{attr}", {
            val: val,
            created_at: Time.now
          })
        end
      end
    end
    
    거북이: 다 했어!!!
    참, 클라스반이나 모듈반에서 방법을 정의하면 반 매크로로 사용할 수 있어요. 왜 그런지 아세요?
    중산 선생"이것은 목요일 학급 정의에서 한 일이다. 루비 대상 모델에서 방법의 탐색 행위는 수신기의 특이류를 찾기 위해 방법을 호출한 다음에 특이한 슈퍼클래스를 탐색한 다음에 다음 다음 슈퍼클래스를 탐색하는 것이다. 이렇게 계속된다. 이때 주의해야 할 것은 베이스코이다.bject 특별반의 슈퍼클래스는 클래스반이라고 합니다.목요일에 했어요.즉, 모든 학급(Class류의 실례)은 방법 탐색 과정에서 반드시 Class류와 Module류를 통과해야 하며, 결과는 전체 클래스에서self를 명시하지 않고 키워드처럼 Class류와 Module류의 실례 방법을 호출해야 한다.이것이 바로 유홍의 진면목이다.Class클래스와Module클래스에 이어 Object클래스,BasicObject클래스가 마지막입니다. 이것은 전체 클래스뿐만 아니라 클래스 실례 대상을 포함한 모든 대상도 검색하기 때문에 클래스 매크로로서는 hogehoge입니다.
    방법: "멈췄어"

    5. 갈고리 방법을 사용하라!


    길권: "학급홍을 완성한 건 좋은데 이러면 모든 학급이 불러내겠지."
    거북이: "그러지 마! 한정된 대상과 일치하기를 바랄 뿐이야(include 이후)!"
    두루미: "거북이가 좀 어색한 속박벽이 있는 것 같아."
    거북이'이런 거북이를 위해 대상 모델에 다양한 변화가 있다(계승,include,extend)등 이벤트를 처리할 때 특정 처리의 연결 방법을 설정할 수 있다.이렇게 하면 각종 제한과 특수 처리를 설정할 수 있다.최저한도."included의 사용 방법을 이해하기만 하면 다음 문제에 직면할 수 있습니다."
    Q4. MyModule을 만듭니다. 반에서include를 진행할 때 분류 방법을 사용할 수 있습니다
    Class Intern
      include MyModule
    
      add_attr_with_date :shimatomo
    end
    
    obj = Intern.new
    obj.shimatomo = 'shimada'
    obj.shimatomo[:val] # => 'shimada'
    obj.shimatomo[:created_at] # => Timeオブジェクト
    
    A4.
    module MyModule
      def self.included(klass)
        class << klass
          def add_attr_with_date(attr_name)
            self.class_eval do
              define_method "#{attr_name}=" do |value|
                instance_variable_set("@#{attr_name}", { val: value, created_at: Time.now })
              end
              define_method "#{attr_name}" do
                instance_variable_get("@#{attr_name}")
              end
            end
          end
        end
      end
    end
    
    거북이: "self.included방법에서 단순히 방법을 정의하면 실례적인 방법으로만 사용할 수 있지만 class << klass부분에서klass의 특이류를 열고 방법을 정의하여 유홍을 실현하였습니다!"
    알겠습니다. "이 고리 방법의 사용 방법도 실제로 삐베 코드에 나타날 수 있으니 기억하는 것이 좋습니다."
    지자: "이렇게 되면 금요일도 끝나고, 아주 긴 원 프로의 요일도 끝나. 그럼 너도 루비이스트지!"

    좋은 웹페이지 즐겨찾기