2011-07-08 50 views
7

Tôi đang chơi với các tính năng lập trình meta của ruby ​​và tôi đang tìm kiếm một chút lông. Tôi đang cố gắng để bọc một cuộc gọi phương thức bằng cách sử dụng một mô-đun. Hiện nay, tôi đang làm điều này:Mở rộng một phương thức lớp học trong một mô-đun

module Bar 
    module ClassMethods 
    def wrap(method) 
     class_eval do 
     old_method = "wrapped_#{method}".to_sym 
     unless respond_to? old_method 
      alias_method old_method, method 

      define_method method do |*args| 
      send old_method, *args 
      end 
     end 
     end 
    end 
    end 

    def self.included(base) 
    base.extend ClassMethods 
    end 
end 

class Foo 
    include Bar 

    def bar(arg = 'foo') 
    puts arg 
    end 

    wrap :bar 
end 

Ba câu hỏi:

  1. Có cách nào để làm điều này mà không cần đổi tên phương pháp, để cho phép việc sử dụng các super? Hoặc một cái gì đó sạch hơn/ngắn hơn?

  2. Có cách nào để đặt giá trị mặc định không?

  3. Có phương tiện để di chuyển cuộc gọi thêm wrap :bar không?

+0

Trong cuộc sống thực, tôi sẽ sử dụng kế thừa lớp cho điều đó. –

+0

Tôi cũng thế, nhưng tôi đang cố gắng để hiểu được điều này. :-) –

+0

Liên quan: http://stackoverflow.com/questions/6631182/difference-between-class-eval-and-instance-eval-in-a-module –

Trả lời

6

1) Cleaner/ngắn

module ClassMethods 
    def wrap(method) 
    old = "_#{method}".to_sym 
    alias_method old, method 
    define_method method do |*args| 
     send(old, *args) 
    end 
    end 
end 

class Foo 
    extend ClassMethods 

    def bar(arg = 'foo') 
    puts arg 
    end 

    wrap :bar 
end 

Theo như tôi biết không có cách nào để đạt được điều này mà không cần đổi tên. Bạn có thể gọi tới số super bên trong khối define_method. Nhưng trước hết, một cuộc gọi đến super từ trong một define_method sẽ chỉ thành công nếu bạn chỉ định đối số một cách rõ ràng, nếu không bạn sẽ nhận được lỗi. Nhưng ngay cả khi bạn gọi, ví dụ: super(*args), self trong ngữ cảnh đó sẽ là một phiên bản của Foo. Vì vậy, một cuộc gọi đến bar sẽ chuyển đến các lớp siêu của Foo, không thể tìm thấy và cuối cùng dẫn đến lỗi.

2) Vâng, như vậy

define_method method do |def_val='foo', *rest| 
    send(old, def_val, *rest) 
end 

Tuy nhiên, trong Ruby 1,8 nó là not possible sử dụng một khối trong define_method, nhưng điều này đã được cố định cho 1,9. Nếu bạn đang sử dụng 1,9, bạn cũng có thể sử dụng số này

define_method method do |def_val='foo', *rest, &block| 
    send(old, def_val, *rest, &block) 
end 

3) Không, thật không may. alias_method yêu cầu sự tồn tại của các phương thức mà nó lấy làm đầu vào. Khi các phương thức Ruby tồn tại khi chúng được phân tích cú pháp, lệnh gọi wrap phải được đặt sau định nghĩa bar nếu không alias_method sẽ làm tăng ngoại lệ.

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