2012-02-22 40 views
9

Tôi đang cố tạo một lớp mới, mà không biết tên của lớp cho đến khi nó được tạo ra.Tạo lớp học động

Một cái gì đó như thế này;

variable = "ValidClassName" 

     class variable 

     end 

Test = ValidClassName.new 

Nếu có thể, tôi cũng đánh giá cao những gợi ý som về cách để tự động thêm các thuộc tính (và phương pháp) đến lớp mới này.

tôi sẽ retreiving 'Cài đặt' cho lớp, và họ sẽ giống như thế này:

title :Person 
attribute :name, String 
attribute :age, Fixnum 

Nhưng không phải được thiết kế để chỉ chấp nhận rằng tập tin rõ ràng, các thuộc tính có thể khác nhau cuối cùng số kiểu.

nào cuối cùng sẽ tạo ra một lớp học mà nên tìm kiếm một cái gì đó như:

class Person 
    def initialize(name, age) 

     @name_out = name 
     @age_out = age 
    end 

end 

Trợ giúp?

+1

bạn muốn tạo mã nguồn cho một lớp học? hoặc là bạn muốn tạo ra nguồn và cho ruby ​​để biên dịch/tải lớp trong thời gian chạy? – ardnew

+1

Vì tò mò, bạn đang giải quyết vấn đề gì? Bạn dự định sử dụng các lớp được tạo động này như thế nào? – ctcherry

+0

Lớp học của tôi được cho là hành động như một 'khung', một khi được tạo ra sử dụng để tạo ra objetcs (miễn là chúng đáp ứng các yêu cầu, được thiết lập bởi lớp) từ một tập tin yaml. YAML giữ một nhóm "người", và một số người trong số họ có các thuộc tính phù hợp với yêu cầu. – BSG

Trả lời

23

Một lớp đạt tên của nó khi nó được gán cho một hằng số. Vì vậy, thật dễ dàng để làm theo kiểu chung với const_set.

Ví dụ, giả sử bạn muốn sử dụng Struct để xây dựng một lớp học với một số thuộc tính, bạn có thể:

name = "Person" 
attributes = [:name, :age] 

klass = Object.const_set name, Struct.new(*attributes) 
# Now use klass or Person or const_get(name) to refer to your class: 
Person.new("John Doe", 42) # => #<struct Person name="John Doe", age=42> 

Kế thừa từ một lớp khác, thay thế các Struct.new bởi Class.new(MyBaseClass), nói:

class MyBaseClass; end 

klass = Class.new(MyBaseClass) do 
    ATTRIBUTES = attributes 
    attr_accessor *ATTRIBUTES 
    def initialize(*args) 
    raise ArgumentError, "Too many arguments" if args.size > ATTRIBUTES.size 
    ATTRIBUTES.zip(args) do |attr, val| 
     send "#{attr}=", val 
    end 
    end 
end 
Object.const_set name, klass 
Person.new("John Doe", 42) # => #<Person:0x007f934a975830 @name="John Doe", @age=42> 
+0

Tôi nhận được và báo lỗi khi khai báo klass, tên phải là một hằng số, vì vậy tôi đã viết hoa klass thành Klass trong 2, 3 dòng và giờ nó đang hoạt động. – tebayoso

+0

Ồ, đúng vậy. Ví dụ đã được sửa đổi. –

6

Mã của bạn sẽ giống giống như thế này:

variable = "SomeClassName" 
klass = Class.new(ParentClass) 
# ...maybe evaluate some code in the context of the new, anonymous class 
klass.class_eval { } 
# ...or define some methods 
klass.send(:title, :Person) 
klass.send(:attribute, :name, String) 
# Finally, name that class! 
ParentClass.send(:const_set, variable, klass) 

... hoặc bạn chỉ có thể sử dụng eval:

eval <<DYNAMIC 
    class #{name} 
    title :Person 
    attribute :name, String 
    # ...or substitute other stuff in here. 
    end 
DYNAMIC 
+1

Xin lỗi, tôi thực sự đang ở ngoài chiều sâu của tôi ở đây. Bạn có thể thử giải thích nó như thể tôi đã ba tuổi ..? : p – BSG

+1

Tôi không chắc chắn điều này sẽ dễ dàng hơn nhiều. Trường hợp thứ hai có thể dễ hiểu hơn: 'eval' nhận một chuỗi và đánh giá nó * tại thời điểm cuộc gọi * như thể đó là mã Ruby. Vì vậy, bạn tạo một chuỗi với mã nguồn cho lớp động của bạn trong thời gian chạy, sau đó đánh giá nó. Trường hợp đầu tiên tạo ra một đối tượng lớp mới, thực hiện mọi thứ để tạo ra các phương thức mong muốn, vv, và sau đó đặt cho nó một tên - mà trong Ruby giống như gán đối tượng lớp cho một hằng số. –

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