2012-01-23 39 views
5

Tôi có một tập tin bên ngoài: path_to_external_file.rb với một số định nghĩa lớp:tải file bên ngoài trong một lớp học/mô-đun

class A 
    some_definitions 
end 

Và tôi muốn tải mà trong mô-đun B để các lớp A định nghĩa ở trên có thể được gọi là B::A. Tôi cố gắng:

class B 
    load('path_to_external_file.rb') 
end 

nhưng A được định nghĩa trong môi trường chính, không phải trong B:

A #=> A 
B.constants # => [] 

Làm thế nào tôi có thể tải các tập tin bên ngoài trong một số lớp/module?

Sửa Tôi có nên đọc các tập tin bên ngoài như dây đàn, và đánh giá chúng trong Class.new{...}, và include rằng lớp trong B?

+0

đến mức kết thúc? Tại sao bạn không thể sử dụng trực tiếp lớp A?Bạn đang nhận được một số lợi ích từ mô đun hóa nó? 'load' và' require' sẽ không thực sự nạp một lớp vào một mô-đun, chúng chỉ tải mã nguồn, vì vậy các lớp của bạn được định nghĩa chính xác như chúng có trong tệp. Bạn không chắc chắn lý do tại sao bạn muốn làm điều này? – brad

+1

@brad Bởi vì các tệp bên ngoài này sẽ được viết bởi người dùng và có thể được đặt tên tùy ý. Nếu tôi định nghĩa các lớp đó trong môi trường chính, chúng sẽ làm hỏng không gian đặt tên. – sawa

+0

Cảnh giác với việc giả mạo các không gian tên khác thông qua 'ObjectSpace # each_object'. – Reactormonk

Trả lời

4

Bạn không thể. Ít nhất bằng cách sử dụng load hoặc require, các tệp Ruby sẽ luôn được đánh giá trong ngữ cảnh hàng đầu.

Bạn có thể làm việc xung quanh vấn đề mà theo hai cách:

  • Xác định class B::A trực tiếp (nhưng bạn có lẽ cố gắng tránh điều đó)
  • Sử dụng eval(File.read("path_to_external_file.rb")) trong lớp B bạn

Sửa : Có thể, thư viện này rất thú vị đối với bạn: https://github.com/dreamcat4/script/blob/master/intro.txt

3

Nói chung, bạn nên xác định một lớp là "lớp A" nhưng sau đó "kỳ diệu" làm cho nó chứa trong mô-đun B. Nếu bạn muốn tham chiếu đến lớp A là B :: A, bạn nên xác định nó bằng cách:

module B 
    class A 
    # contents 
    end 
end 

hay:

class B::A 
    # contents 
end 

Nếu bất kỳ ai đọc mã của bạn sẽ bị nhầm lẫn. Trong trường hợp này, bạn không đạt được bất kỳ điều gì rõ ràng, ngắn gọn hoặc thuận tiện bằng cách sử dụng "thủ thuật", vì vậy mã đơn giản sẽ tốt hơn. Có một bài học ở đây: các tính năng siêu lập trình của Ruby rất tuyệt vời, nhưng không cần phải sử dụng chúng một cách vô cớ. Chỉ sử dụng chúng khi bạn thực sự đạt được một cái gì đó từ làm như vậy. Nếu không, bạn chỉ cần làm cho mã của bạn khó hiểu.

NHƯNG, đã đọc nhận xét của bạn, có vẻ như thực sự là một lý do chính đáng để làm điều gì đó như thế này trong trường hợp của bạn. Tôi đề nghị giải pháp sau sẽ tốt hơn những gì bạn đang hình dung:

m = Module.new 
m.module_eval("class C; end") 
m.constants 
=> [:C] 
m.const_get(:C) 
=> #<Module:0xfd0da0>::C 

Bạn thấy không? Nếu bạn muốn một không gian tên "bảo đảm duy nhất", bạn có thể sử dụng một mô-đun ẩn danh. Bạn có thể lưu trữ các mô-đun này trong một băm hoặc cấu trúc dữ liệu khác, và kéo các lớp ra khỏi chúng khi cần thiết. Điều này giải quyết được vấn đề bạn đã đề cập, rằng người dùng ứng dụng của bạn sẽ thêm các lớp của riêng họ và bạn không muốn các tên va chạm.

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