2010-07-02 29 views
20

Theo tài liệu mod.const_get(sym) "Trả về giá trị của hằng số được đặt tên theo mod".Hành vi khó hiểu của const_get trong Ruby?

Tôi cũng biết rằng const_get theo mặc định có thể tra cứu chuỗi kế thừa của người nhận. Vì vậy, các công trình sau đây:

class A; HELLO = :hello; end 
class B < A; end 
B.const_get(:HELLO) #=> :hello 

tôi cũng biết rằng các lớp học trong Ruby lớp con Object, do đó bạn có thể sử dụng const_get nhìn lên hằng 'toàn cầu' mặc dù người nhận là một lớp học bình thường:

class C; end 
C.const_get(:Array) #=> Array 

Tuy nhiên, và đây là nơi tôi đang bối rối - mô-đun không phân lớp Object. Vậy tại sao tôi vẫn có thể tra cứu các hằng số 'toàn cục' từ một mô-đun bằng cách sử dụng const_get? Tại sao công việc sau đây?

module M; end 
M.const_get(:Array) #=> Array 

Nếu tài liệu chính xác - const_get chỉ cần tra cứu hằng số được xác định trong người nhận hoặc siêu lớp của nó. Nhưng trong mã ngay phía trên, Object không phải là siêu lớp của M, vậy tại sao có thể tra cứu Array?

Cảm ơn

+3

Lưu ý rằng điều này không khớp với hành vi của '::'. 'SomeModule :: SomeGlobalConstant' sẽ gây ra lỗi, trong khi' SomeModule.const_get (: SomeGlobalConstant) 'sẽ hoạt động. – sepp2k

Trả lời

11

Bạn là chính xác nên nhầm ... Doc không nêu rõ liệu của Ruby làm cho một trường hợp đặc biệt cho tra cứu các hằng số trong Modules và đã được sửa đổi to state this explicitly. Nếu hằng số không được tìm thấy trong hệ phân cấp bình thường, Ruby sẽ khởi động lại tra cứu từ Object, có thể là found in the source.

Tự động tra cứu liên tục có thể hơi khó hiểu. Lấy ví dụ sau đây:

module M 
    Foo = :bar 
    module N 
    # Accessing Foo here is fine: 
    p Foo # => bar 
    end 
end 
module M::N 
    # Accessing Foo here isn't 
    p Foo # => uninitialized constant M::N::Foo 
end 
p M::N.const_get :Foo # => uninitialized constant M::N::Foo 

Trong cả hai nơi, tuy nhiên, việc tiếp cận Object hằng mức như Array là tốt (Tạ ơn Chúa!). Điều gì đang xảy ra là Ruby duy trì một danh sách các "định nghĩa mô-đun mở". Nếu hằng số có phạm vi rõ ràng, hãy nói LookHereOnly::Foo, sau đó chỉLookHereOnly và các mô-đun đi kèm của nó sẽ được tìm kiếm. Nếu không có phạm vi nào được chỉ định (như Foo trong ví dụ trên), Ruby sẽ xem xét các định nghĩa mô-đun mở để tìm hằng số Foo: M::N, sau đó M và cuối cùng là Object. Định nghĩa mô đun mở trên cùng luôn là Object.

Vì vậy, M::N.const_get :Foo tương đương với truy cập Foo khi các lớp đã mở chỉ là M::NObject, như trong phần cuối của ví dụ của tôi.

Tôi hy vọng tôi có quyền này, coz tôi vẫn đang bối rối bởi tra cứu liên tục bản thân mình :-)

+1

Bất kỳ ý tưởng nào tại sao nó thực hiện điều đó? Trong những trường hợp nào sẽ hữu ích? – sepp2k

+0

@ sepp2k: không chắc chắn nếu nó là _useful_, nhưng tôi đã cố gắng giải thích những gì tôi nghĩ là logic đằng sau nó. –

+0

Nó không trực quan, nhưng vấn đề M :: N là do phạm vi từ vựng. Nếu bạn làm 'module M; module N; kết thúc; kết thúc', nội dung của M nằm trong phạm vi từ vựng của N (M có thể nhìn thấy từ N do lồng nhau). Với 'module M :: N; end', M không hiển thị vì phạm vi chứa là cấp cao nhất. Cá nhân tôi cố gắng tránh việc xác định các lớp và các mô-đun trong biểu mẫu M :: N - cũng bởi vì đó là một lỗi nếu M không tồn tại. – Kelvin

2

tôi đã đưa ra kịch bản sau đây để tải tên hằng số cách nhau:

def load_constant(name) 
    parts = name.split('::') 
    klass = Module.const_get(parts.shift) 
    klass = klass.const_get(parts.shift) until parts.empty? 
    klass 
end 
0

Chừng nào vì chúng tôi không kiểm tra lỗi bạn có thể:

def load_constant(name) 
    name.split('::').inject(Module) do |mod_path, mod_to_find| 
     mod_path.const_get(mod_to_find) 
    end 
end 
Các vấn đề liên quan