2010-08-09 18 views
7

Trong mã sau đây bao gồm mô-đun được sử dụng. Cách tôi nhìn thấy nó nếu bao gồm mô-đun được gỡ bỏ sau đó cũng là một phương pháp dụ sẽ được tạo ra. Sau đó, tại sao người dùng bao gồm mô-đun?lý do tại sao sử dụng bao gồm mô-đun khi class_eval của chính nó sẽ đủ

http://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations.rb#L1416

include Module.new { 
      class_eval <<-RUBY, __FILE__, __LINE__ + 1 
      def destroy      # def destroy 
       super       # super 
       #{reflection.name}.clear  # posts.clear 
      end        # end 
      RUBY 
     } 
+0

có thể ping các nhân vật cốt lõi của đường ray? – rogerdpack

Trả lời

11

Trước hết hãy làm một điều rõ ràng. Khi họ gọi super bên trong class_eval - điều đó hoàn toàn không liên quan gì đến lý do họ sử dụng điều include Module.new {}. Trên thực tế, super được gọi bên trong phương thức destroy hoàn toàn không liên quan để trả lời câu hỏi của bạn. Có thể có bất kỳ mã tùy ý nào bên trong phương thức hủy đó.

Bây giờ chúng tôi đã loại bỏ nó, đây là những gì đang diễn ra.

Trong ruby, nếu bạn chỉ định nghĩa một phương thức lớp, và sau đó xác định lại nó trong cùng một lớp, bạn sẽ không thể gọi super để truy cập phương thức trước đó.

Ví dụ:

class Foo 
    def foo 
    'foo' 
    end 

    def foo 
    super + 'bar' 
    end 
end 

Foo.new.foo # => NoMethodError: super: no superclass method `foo' for #<Foo:0x101358098> 

này có ý nghĩa, bởi vì foo đầu tiên không được định nghĩa trong một số lớp cha, hoặc bất cứ nơi nào trong chuỗi tra cứu (đó là nơi super điểm). Tuy nhiên, bạn có thể xác định foo đầu tiên theo cách sao cho sau này bạn ghi đè lên - nó sẽ có sẵn bằng cách gọi super. Đây là chính xác những gì họ muốn đạt được với mô-đun bao gồm.

class Foo 
    include Module.new { class_eval "def foo; 'foo' end" } 

    def foo 
    super + 'bar' 
    end 
end 

Foo.new.foo # => "foobar" 

Điều này có tác dụng, bởi vì khi bạn bao gồm một mô-đun, ruby ​​sẽ chèn nó vào chuỗi tra cứu. Bằng cách này, bạn có thể gọi super theo phương pháp thứ hai và mong đợi phương thức được bao gồm được gọi. Tuyệt quá.

Tuy nhiên, bạn có thể thắc mắc, tại sao không chỉ đơn giản bao gồm một mô-đun mà không có tất cả các thủ thuật? Tại sao họ sử dụng cú pháp khối? Chúng tôi biết rằng ví dụ trên của tôi giống hệt như sau:

module A 
    def foo 
    'foo' 
    end 
end 

class Foo 
    include A 

    def foo 
    super + 'bar' 
    end 
end 

Foo.new.foo # => "foobar" 

Vậy tại sao họ không làm điều đó? Câu trả lời là - gọi tới số reflection.Họ cần nắm bắt biến (hoặc phương pháp) có sẵn trong ngữ cảnh hiện tại, là reflection.

Vì chúng đang xác định mô-đun mới sử dụng cú pháp khối, tất cả các biến bên ngoài khối có sẵn để sử dụng bên trong khối. Tiện lợi.

Chỉ để minh họa.

class Foo 
    def self.add_foo_to_lookup_chain_which_returns(something) 
    # notice how I can use variable something in the class_eval string 
    include Module.new { class_eval "def foo; '#{something}' end" } 
    end 
end 

# so somewhere else I can do 

class Foo 
    add_foo_to_lookup_chain_which_returns("hello") 

    def foo 
    super + " world" 
    end 
end 

Foo.new.foo # => "hello world" 

Gọn gàng, huh?

Bây giờ, hãy để tôi nhấn mạnh lại. Cuộc gọi tới super bên trong phương thức destroy trong ví dụ của bạn không liên quan gì đến bất kỳ điều nào ở trên. Họ gọi nó vì những lý do riêng của họ, bởi vì có thể lớp học mà điều này đang xảy ra là phân lớp một lớp khác đã được định nghĩa là destroy.

Tôi hy vọng điều này đã rõ ràng.

+0

câu trả lời tuyệt vời .. cảm ơn :) – Orlando

0

Tôi đoán nhưng ... họ không muốn ghi đè lên các "tiêu diệt" phương pháp, và muốn để lại cho nó có sẵn để bị quá tải bởi một số người dùng cuối (bạn hoặc tôi), mà không cần xóa chức năng "reflection.clear" này.

Vì vậy - bằng cách bao gồm nó làm mô-đun, họ có thể gọi "siêu" sẽ gọi phá hủy ban đầu hoặc phiên bản quá tải (được viết bởi người dùng cuối).

0

Nhờ phương thức include, destroy không được ghi đè. Nó hạ cánh trong lớp ma mà lớp thực tế xuất phát từ. Bằng cách đó, khi một người gọi destroy trên đối tượng AR, một bản gốc sẽ được gọi và super sẽ gọi một từ mô-đun ẩn danh (sau đó gọi gốcdestroy từ lớp bắt nguồn từ đó).

Một chút khó khăn, thực sự.

Các vấn đề liên quan