2016-01-13 16 views
6

tôi có các mô-đun và các lớp học sau:Kiểm tra xem một phương pháp học được gọi là

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

    module ClassMethods 
    attr_reader :config 

    # this method MUST be called by every class which includes MyModule 
    def configure &block 
     @config = {} 
     block.call(@config) if block 
    end 
    end 
end 

class A 
    include MyModule 

    configure do |config| 
    # do sth with the config 
    end 
end 

class B 
    include MyModule 
end 

Có thể kiểm tra, nếu phương pháp configure từ các mô-đun được gọi là? Điều này có nghĩa là A sẽ ổn, nhưng B sẽ phát sinh lỗi vì nó không bao giờ được gọi là configure.

Tôi đã thử nó trong cuộc gọi lại self.included, nhưng sau đó phương thức configure được gọi.

+0

Khi nào ngoại lệ nên được ném? Khi quá trình này thoát? Làm thế nào bạn có thể biết rằng nó sẽ không được gọi là một thời gian trong tương lai? – ndn

+0

Trường hợp ngoại lệ phải được ném, khi lớp B được xác định. Nhưng bạn có một điểm với * Làm thế nào bạn có thể biết rằng nó sẽ không được gọi là một thời gian trong tương lai? * – 23tux

+0

Một lớp học có thể được mở cửa trở lại.Một mô-đun có thể được bao gồm sau khi một lớp được định nghĩa. – ndn

Trả lời

0

Về mặt kỹ thuật, @ndn đúng, nó có thể được gọi sau khi lớp đã được đánh giá. Tuy nhiên, có vẻ như bạn muốn xác nhận rằng phương thức cấu hình đã được gọi ở một số điểm trong định nghĩa lớp cơ thể (điều này cũng sẽ cho phép bất kỳ mô-đun nào được bao gồm, kết thúc đánh giá, vì vậy nếu một mô-đun bao gồm các cuộc gọi cấu hình phương pháp, tất cả đều tốt).

Giải pháp gần nhất tôi đã đưa ra để giải quyết tình trạng này có thể được tìm thấy ở đây:

https://github.com/jasonayre/trax_core/blob/master/lib/trax/core/abstract_methods.rb

Đoạn mã trên là một thực hiện phương pháp trừu tượng cho ruby, mà về mặt kỹ thuật không phải là những gì bạn đang hỏi (bạn đang nói về việc gọi phương thức, các phương thức trừu tượng là về việc kiểm tra xem một lớp con đã định nghĩa nó chưa), nhưng cùng một mẹo mà tôi đã sử dụng ở đó có thể được áp dụng. Về cơ bản, tôi đang sử dụng thư viện điểm dấu vết của ruby ​​để xem kết thúc định nghĩa lớp để đánh, lúc đó nó kích hoạt một sự kiện, tôi kiểm tra xem phương thức đã được xác định chưa, và ném một lỗi nếu không. Vì vậy, miễn là bạn đang gọi cấu hình từ WITHIN các lớp học của bạn, một giải pháp tương tự có thể làm việc cho bạn. Một cái gì đó tương tự (không kiểm tra):

module MustConfigure 
    extend ::ActiveSupport::Concern 

    module ClassMethods 
    def inherited(subklass) 
     super(subklass) 
     subklass.class_attribute :_configured_was_called 
     subklass._configured_was_called = false 

     trace = ::TracePoint.new(:end) do |tracepoint| 
     if tracepoint.self == subklass #modules also trace end we only care about the class end 
      trace.disable 

      raise NotImplementedError.new("Must call configure") unless subklass._configured_was_called 
     end 
     end 

     trace.enable 

     subklass 
    end 

    def configure(&block) 
     self._configured_was_called = true 
     #do your thing 
    end 
    end 
end 

class A 
    include MustConfigure 
end 

class B < A 
    configure do 
    #dowhatever 
    end 
end 

class C < B 
    #will blow up here 
end 

Hoặc, bạn có thể thử sử dụng các mô-đun InheritanceHooks từ thư viện của tôi và bỏ qua việc xử lý tracepoint dẫn sử dụng:

class BaseClass 
    include::Trax::Core::InheritanceHooks 
    after_inherited do 
    raise NotImplementedError unless self._configure_was_called 
    end 
end 

Lưu ý, mặc dù tôi đang sử dụng mô hình này trong sản xuất tại thời điểm này, và mọi thứ hoạt động tốt trên MRI, bởi vì tracepoint là một thư viện được xây dựng để gỡ lỗi, có một số hạn chế khi sử dụng jruby. (ngay bây giờ nó phá vỡ trừ khi bạn vượt qua cờ gỡ lỗi jruby) - Tôi đã mở một vấn đề một lúc trở lại cố gắng để có được tracepoint thêm vào mà không cần phải kích hoạt gỡ lỗi một cách rõ ràng.

https://github.com/jruby/jruby/issues/3096

0

Đây là ví dụ dựa trên cấu trúc của bạn. Nó kiểm tra tại instantiation nếu cấu hình đã được gọi, và sẽ làm việc tự động với bất kỳ lớp học mà bạn prepended MyModule.

Nó kiểm tra ở mọi phiên bản nếu cấu hình đã được gọi, nhưng nó chỉ là kiểm tra một boolean để nó không nên có bất kỳ tác động hiệu suất.

Tôi đã tìm cách xác định phương thức được thêm trước cho một lớp cụ thể nhưng không tìm thấy bất kỳ thứ gì.

module MyModule 
    def self.prepended base 
    base.extend(ClassMethods) 
    end 

    module ClassMethods 
    attr_reader :config 

    def configured? 
     @configured 
    end 

    def configure &block 
     @configured = true 
     @config = {} 
     block.call(@config) if block 
    end 
    end 

    def initialize(*p) 
    klass = self.class 
    if klass.configured? then 
     super 
    else 
     raise "Please run #{klass}.configure before calling #{klass}.new" 
    end 
    end 
end 

class A 
    prepend MyModule 

    configure do |config| 
    config[:a] = true 
    puts "A has been configured with #{config}" 
    end 
end 

class B 
    prepend MyModule 
end 

A.new 
puts "A has been instantiated" 

puts 

B.new 
puts "B has been instantiated" 

# => 
# A has been configured with {:a=>true} 
# A has been instantiated 

# check_module_class.rb:27:in `initialize': Please run B.configure before calling B.new (RuntimeError) 
# from check_module_class.rb:50:in `new' 
# from check_module_class.rb:50:in `<main>' 
Các vấn đề liên quan