2013-01-16 32 views
7

Vì vậy, tôi đã có tình huống này, nơi tôi muốn xác định một loạt các lớp theo chương trình. Phương pháp tôi đang sử dụng bên dưới hoạt động tốt, ngoại trừ thực tế là lớp bên thứ ba mà tôi kế thừa từ đây không thích thực tế là lớp được định nghĩa ẩn danh (về cơ bản, nó không có thông tin cần thiết, tức là tên lớp, trong móc 'kế thừa', trước khi tôi có cơ hội thiết lập lớp ẩn danh thành hằng số).Định nghĩa lớp động với khối

['one', 'two', 'three'].each do |model| 
    cls = Class.new(ThirdPartyClass) do 
     define_method :model do 
      model 
     end 
    end 
    ThirdPartyClass.const_set(model.capitalize, cls) 
end 

tôi chỉ đơn giản có thể sử dụng một eval và xác định một cái gì đó lớp như:

['one', 'two', 'three'].each do |model| 
    eval "class ThirdPartyClass::#{model.capitalize} < ThirdPartyClass; ...; end" 
end 

nhưng tôi không thích nó vì sau đó nó lộn xộn chuỗi suy. Phương pháp dựa trên khối có vẻ 'đẹp hơn', về mặt thẩm mỹ.

Có cách nào tôi có thể định nghĩa lớp đó không ẩn danh (ví dụ: với tên ngay từ đầu) bằng cú pháp dựa trên khối hoặc tôi có phải ở trong vùng đầu vào chuỗi lộn xộn của eval không?

+0

Thật lạ khi bạn muốn một lớp nằm trong không gian tên và là lớp con của cùng một lớp. – sawa

+0

Đúng, nhưng điều không gian tên là trực giao cho vấn đề cụ thể này (ít nhất là tôi nghĩ vậy). Nếu bạn tò mò, tôi đang sử dụng gem classy_enum và theo quy ước của gem đó, các giá trị enum phải nằm trong không gian tên của lớp enum chính. Nó tạo ra một cú pháp enum hơi tương tự như trong các ngôn ngữ khác. – elsurudo

Trả lời

0

Gọi lại Class#inherited được gọi khi lớp được tạo. Khi instantiating một lớp vô danh, điều này sẽ luôn luôn được trước khi lớp có thể được gán cho hằng số, gán cho nó một tên. Tôi không thể thấy bất kỳ cách nào để giải quyết vấn đề này. Bạn có thể giảm thiểu số lượng mã trong chuỗi eval bằng cách sử dụng eval để tạo một lớp trống bằng cách sử dụng cú pháp lớp đặc biệt làm cho tên hiển thị trước inherited, sau đó theo dõi class_eval ở dạng khối để xác định phương thức của bạn.

class Super 
    def self.inherited(child) 
    puts "#{self.name} inherited by #{child.inspect} named '#{child.name}'" 
    end 
end 

# Your way, inherited can't see name 
['one', 'two', 'three'].each do |model| 
    klassname = model.capitalize 
    klass = Class.new(Super) do 
    puts "defining #{model} inheriting from Super" 
    define_method :model do 
     model 
    end 
    end 
    Super.const_set(klassname, klass) 
end 

# this way inherited sees name because we use special class definition syntax in minimal string eval 
['four', 'five', 'six'].each do |model| 
    klassname = model.capitalize 

    eval %Q{ 
    class Super::#{klassname} < Super 
     puts %Q[defining Super::#{klassname} inheriting from Super] 
    end 
    } 

    Super.const_get(klassname).class_eval do 
    puts "defining methods for Super::#{klassname} inheriting from Super" 
    define_method :model do 
     model 
    end 
    end 
end 

# produces: 
Super inherited by #<Class:0x0000010084c988> named '' 
defining one inheriting from Super 
Super inherited by #<Class:0x0000010084c640> named '' 
defining two inheriting from Super 
Super inherited by #<Class:0x0000010084c320> named '' 
defining three inheriting from Super 
Super inherited by Super::Four named 'Super::Four' 
defining Super::Four inheriting from Super 
defining methods for Super::Four inheriting from Super 
Super inherited by Super::Five named 'Super::Five' 
defining Super::Five inheriting from Super 
defining methods for Super::Five inheriting from Super 
Super inherited by Super::Six named 'Super::Six' 
defining Super::Six inheriting from Super 
defining methods for Super::Six inheriting from Super 
+0

Cảm ơn, đây là giải pháp thay thế đủ. – elsurudo

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