2011-06-16 24 views
8

Tôi có một vài module mở rộng phương pháp mất tích:Làm thế nào để soạn module chứa method_missing trong ruby ​​

module SaysHello 
    def respond_to?(method) 
     super.respond_to?(method) || !!(method.to_s =~ /^hello/) 
    end 
    def method_missing(method, *args, &block) 
     if (method.to_s =~ /^hello/) 
      puts "Hello, #{method}" 
     else 
      super.method_missing(method, *args, &block) 
     end 
    end 
end 

module SaysGoodbye 
    def respond_to?(method) 
     super.respond_to?(method) || !!(method.to_s =~ /^goodbye/) 
    end 
    def method_missing(method, *args, &block) 
     if (method.to_s =~ /^goodbye/) 
      puts "Goodbye, #{method}" 
     else 
      super.method_missing(method, *args, &block) 
     end 
    end 
end 

class ObjectA 
    include SaysHello 
end 

class ObjectB 
    include SaysGoodbye 
end 

này tất cả hoạt động tốt, ví dụ như ObjectA.new.hello_there đầu ra "Hello, hello_there". Tương tự, ObjectB.new.goodbye_xxx kết quả đầu ra "Goodbye, xxx". respond_to? cũng hoạt động, ví dụ: ObjectA.new.respond_to? :hello_there trả về true.

Tuy nhiên, điều này không làm việc rất tốt khi bạn muốn sử dụng cả hai SaysHelloSaysGoodbye:

class ObjectC 
    include SaysHello 
    include SaysGoodbye 
end 

Trong khi ObjectC.new.goodbye_aaa tác phẩm một cách chính xác, ObjectC.new.hello_a hành động kỳ lạ:

> ObjectC.new.hello_aaa 
Hello, hello_aaa 
NoMethodError: private method `method_missing' called for nil:NilClass 
    from test.rb:22:in `method_missing' (line 22 was the super.method_missing line in the SaysGoodbye module) 

Nó ra một cách chính xác, sau đó ném một lỗi. Ngoài ra respond_to? không chính xác, ObjectC.new.respond_to? :hello_a trả về false.

Cuối cùng, thêm lớp này:

class ObjectD 
    include SaysHello 
    include SaysGoodbye 

    def respond_to?(method) 
     super.respond_to?(method) || !!(method.to_s =~ /^lol/) 
    end 


    def method_missing(method, *args, &block) 
     if (method.to_s =~ /^lol/) 
      puts "Haha, #{method}" 
     else 
      super.method_missing(method, *args, &block) 
     end 
    end 
end 

Ngoài ra hoạt động kỳ lạ. ObjectD.new.lol_zzz hoạt động, tuy nhiên ObjectD.new.hello_a and ObjectD.new.goodbye_t đều ném ngoại lệ tên sau khi xuất chuỗi chính xác. respond_to? cũng không thành công cho các phương thức chào và tạm biệt.

Có cách nào để điều này hoạt động chính xác không? Giải thích về cách method_missing, Mô-đun và super tương tác cũng sẽ thực sự hữu ích.

EDIT: coreyward đã giải quyết được sự cố, nếu tôi sử dụng siêu thay vì super.<method-name>(args...) trong tất cả các phương pháp tôi xác định, chương trình hoạt động chính xác. Tôi không hiểu tại sao điều này là mặc dù, vì vậy tôi hỏi một câu hỏi về điều này tại What does super.<method-name> do in ruby?

+0

module bao gồm được bổ sung vào chuỗi thừa kế; họ không ghi đè hoặc thay thế các phương pháp. Vì vậy, nếu mỗi method_missing gọi super, cuối cùng chúng sẽ được gọi. Xem câu trả lời của tôi dưới đây. – ChrisPhoenix

Trả lời

5

Khi bạn xác định lại một phương pháp, bạn xác định lại một phương pháp; giai đoạn.

Những gì bạn đang làm khi bạn đưa mô-đun thứ hai bằng cách xác định phương thức method_missing là ghi đè số được xác định trước method_missing. Bạn có thể giữ nó xung quanh bằng cách đánh dấu nó trước khi bạn xác định lại nó, nhưng bạn có thể muốn xem ra với điều đó.

Ngoài ra, tôi không biết lý do bạn gọi số super.method_missing. Khi định nghĩa method_missing của bạn nằm ngoài thủ thuật, bạn nên cho Ruby biết nó có thể tiếp tục chuỗi tìm kiếm cách xử lý cuộc gọi, tất cả chỉ bằng cách gọi super (không cần phải chuyển đối số hoặc chỉ định tên phương thức).

Về Siêu (cập nhật)

Khi bạn gọi super Ruby nói tiếp tiếp ngược dòng chuỗi thừa kế tìm kiếm các định nghĩa tiếp theo của phương pháp gọi, và nếu nó tìm thấy một nó gọi nó và trả về các phản ứng. Khi bạn gọi super.method_missing, bạn gọi phương thức method_missing theo câu trả lời cho super().

Đi này (hơi ngớ ngẩn) ví dụ:

class Sauce 
    def flavor 
    "Teriyaki" 
    end 
end 

# yes, noodles inherit from sauce: 
# warmth, texture, flavor, and more! ;) 
class Noodle < Sauce 
    def flavor 
    sauce_flavor = super 
    "Noodles with #{sauce_flavor} sauce" 
    end 
end 

dinner = Noodle.new 
puts dinner.flavor  #=> "Noodles with Teriyaki sauce" 

Bạn có thể thấy siêu mà là một phương pháp giống như bất kỳ khác, nó chỉ hiện một số kỳ diệu đằng sau hậu trường. Nếu bạn gọi super.class ở đây bạn sẽ thấy String, vì "Teriyaki" là một chuỗi.

Bây giờ có ý nghĩa?

+0

Nhưng việc nhập mô-đun thứ hai không ghi đè lên phương thức của phương thức module_missing đầu tiên, nếu không ObjectC.new.hello_a sẽ không xuất ra Hello, hello_a (cộng với một NameError). Sử dụng siêu thay vì super.method_missing (...) thực sự đã giải quyết được sự cố, tất cả cuộc gọi method_missing và respond_to? cuộc gọi hiện hoạt động chính xác. Tôi không hiểu tại sao. –

+0

@nanothief: Tôi đã cập nhật câu hỏi (hy vọng) giải thích thêm về cách 'super' hoạt động trong Ruby. Tôi nghĩ rằng nó sẽ xóa mọi thứ cho bạn. – coreyward

+0

cảm ơn, đó là vấn đề tôi gặp phải với siêu. Tôi đã giả định super.method đã gọi phương thức trên lớp cha, không gọi phương thức trên kết quả gọi phương thức hiện hành trên lớp cha. –

1

http://www.perfectline.ee/blog/activerecord-method-missing-with-multiple-inheritance

Bài viết này giải thích một cách chính xác cách hoạt động: Mỗi module mới không ghi đè lên hoặc thay thế các phương pháp - thay vào đó, phương pháp của nó được chèn vào chuỗi thừa kế. Đó là lý do tại sao gọi super từ mỗi method_missing cuối cùng gọi tất cả các phương thức của method_missing.

Lớp học vẫn còn thấp nhất trong chuỗi thừa kế và mô-đun được thêm gần nhất tiếp giáp với lớp học.

Vì vậy:

class Foo 
    include A 
    include B 
end 

kết quả trong Kernel -> A -> B -> Foo

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