2011-08-16 15 views
24

Chúng tôi vừa tạo một tệp mới trong 'lib' đã sinh ra một loạt các cơn đau đầu liên quan đến lỗi tải.Hành vi "LoadError" không khớp với 'lib' không gian tên/tự động tải

/lib/response_set.rb:

module MyCompany 
    class ResponseSet < Array 
    ... 
    end 
end 

/spec/lib/response_set_spec.rb

require 'spec_helper' 

describe MyCompany::ResponseSet do 
    describe "..." do 
    ... 
    end 
end 

Chạy spec này trong rspec cho chúng ta được lỗi sau khi nó được cho là người đầu tiên ' mô tả ':

/Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:492:in `load_missing_constant': Expected /Users/my_stuff/projects/my_project/lib/response_set.rb to define ResponseSet (LoadError) 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/backward_compatibility.rb:20:in `const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-expectations-2.5.0/lib/rspec/expectations/backward_compatibility.rb:6:in `const_missing' 
    from /Users/my_stuff/projects/my_project/spec/lib/response_set_spec.rb:4:in `<top (required)>' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `block in load_spec_files' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `map' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/configuration.rb:386:in `load_spec_files' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/command_line.rb:18:in `run' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:55:in `run_in_process' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:46:in `run' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/rspec-core-2.5.1/lib/rspec/core/runner.rb:10:in `block in autorun' 

HOWEVER! Chúng tôi đã sử dụng nhiều tệp khác trong một thời gian dài có cấu trúc giống nhau. Ví dụ, đây là một số khác đã được làm việc tốt vì nó được tạo ra:

/lib/smart_set.rb

module MyCompany 
    class SmartSet < Array 
    ... 
    end 
end 

Và /spec/lib/smart_set_spec.rb

require 'spec_helper' 

describe MyCompany::SmartSet do 
    describe "..." do 
    ... 
    end 
end 

này tập tin có cấu trúc giống nhau nhưng không gây ra vấn đề gì cả.

ResponseSet (lớp sự cố) dường như có vấn đề về tải không có lý do rõ ràng. Trong đường ray giao diện điều khiển, lần đầu tiên tôi cố gắng tạo ra một, tôi nhận được một lỗi, nhưng sau đó tôi có thể tạo một sau:

Loading development environment (Rails 3.0.4) 
ruby-1.9.2-p136 :001 > rs = MyCompany::ResponseSet.new 
LoadError: Expected /Users/my_stuff/projects/my_project/lib/response_set.rb to define ResponseSet 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:492:in `load_missing_constant' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:503:in `load_missing_constant' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:183:in `block in const_missing' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `each' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/activesupport-3.0.4/lib/active_support/dependencies.rb:181:in `const_missing' 
    from (irb):1 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands/console.rb:44:in `start' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands/console.rb:8:in `start' 
    from /Users/my_stuff/.rvm/gems/[email protected]_project/gems/railties-3.0.4/lib/rails/commands.rb:23:in `<top (required)>' 
    from script/rails:6:in `require' 
    from script/rails:6:in `<main>' 
ruby-1.9.2-p136 :002 > rs = MyCompany::ResponseSet.new 
=> [] 

Ngoài ra, thêm

require 'response_set' 

ở đầu response_set_spec. rb cho phép những thử nghiệm đó chạy. Nhưng không có điều đó là cần thiết cho smart_set_spec.rb.

Sau đây được đặt ra trong application.rb:

config.autoload_paths += %W(#{config.root}/lib) 
config.autoload_paths += Dir["#{config.root}/lib/**/"] 
config.autoload_paths += Dir["#{config.root}/app/models/**/"] 

Bây giờ, tôi hiểu rằng Rails có một số loại ý kiến ​​về cách cấu trúc tập tin phải phù hợp với cấu trúc không gian tên cho các loại của sự vật, và chúng tôi đã cơ cấu lại các mô-đun và tệp của chúng tôi hướng tới mục tiêu này. SEEMS đã khắc phục vấn đề (mặc dù chúng tôi đã nhìn thấy một số lỗi tải lạ khác trong một thời gian khi chúng tôi chạy bộ kiểm tra đầy đủ - những điều này đã biến mất một cách bí ẩn). Tuy nhiên, tất cả mọi người ở đây đều bối rối và không có chút khó chịu khi Rails quá mâu thuẫn và chúng tôi muốn biết tại sao. Như bạn có thể thấy có hai tệp giống hệt như cách sắp xếp tên tệp và cấu trúc tệp có liên quan, được xử lý hoàn toàn khác nhau. Trong thực tế, chúng tôi có khoảng một chục tệp khác ở cấp cao nhất của 'lib' với các không gian tên tương tự mà chưa bao giờ gây ra bất kỳ vấn đề nào. Bất cứ ai có thể giải thích những gì heck đang xảy ra ở đây?

Trả lời

4

Tôi nhìn vào nguồn ray và có một khoản

if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load 
    require_or_load file_path 
    raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless local_const_defined?(from_mod, const_name) 
    return from_mod.const_get(const_name) 
elsif ... 

trong load_missing_constant phương pháp. Tôi có thể đoán rằng khi require_or_load được gọi trước raise, đây có thể là lý do mà lệnh gọi thứ hai trong ví dụ của bạn không có lỗi ...

Thật thú vị khi thấy một ví dụ tối thiểu trong đó hai tệp có cấu trúc giống nhau hành xử khác nhau. Ở vị trí của bạn, tôi sẽ tạo một bản sao của ứng dụng và sẽ tiếp tục xóa các phần khỏi ứng dụng trong khi hành vi không nhất quán có mặt để xem ví dụ không nhất quán tối thiểu.

P.S. Tôi đã gửi một câu hỏi tương tự ở đây: http://www.ruby-forum.com/topic/2376956

+0

Cảm ơn bạn Alexey. Điều đó có vẻ đáng ngờ. Dường như với tôi rằng việc nâng cao ngoại lệ phụ thuộc vào thời tiết 'require_or_load' đã thành công. Nhưng sau đó một lần nữa tôi không phải rất quen thuộc với mê cung đó là Rails can đảm. –

+0

@Nick, dường như ngoại lệ được nâng lên khi, sau khi tải tệp, hằng số vẫn không được xác định. Trong trường hợp của bạn, có vẻ như tập tin được dự kiến ​​sẽ xác định 'ResponseSet' trong khi nó định nghĩa' MyCompany :: ResponseSet' (đây là cách nó trông với tôi, nhưng tôi không phải là một chuyên gia). Sự mâu thuẫn là điều kỳ lạ nhất. Bạn có chắc chắn rằng bạn đã không đơn giản hóa vấn đề ban đầu? Có lẽ 'MyCompany :: SmartSet' nằm trong'/lib/my_company/smart_set.rb' và không chỉ '/ lib/smart_set.rb'? Tôi đã gửi một [báo cáo lỗi tại GitHub] (https://github.com/rails/rails/issues/2572) về một vấn đề tương tự. – Alexey

+0

Không. Các cấu trúc tệp giống hệt nhau. Chúng tôi đã dành một vài giờ kiểm tra mọi sự khác biệt có thể chúng tôi có thể nghĩ đến. Sự khác biệt duy nhất là với việc thực hiện thực tế của mỗi lớp (tức là thuộc tính và phương pháp) nhưng không có điều nào trong số đó sẽ ảnh hưởng đến cách các tệp được tải! –

27

Chúng tôi gặp vấn đề tương tự, sau khi đào, hóa ra là do những thay đổi trong Rails 3.x và autoload_paths.

Trường hợp của chúng tôi chỉ xuất hiện trong thử nghiệm (RAILS_ENV=test). Khi Rails đang tải các bộ điều khiển, nó tranh giành về việc tìm kiếm mô hình phù hợp của từng bộ điều khiển (do một ActionController :: Base.wrap_parameters được đặt trong một bộ khởi tạo). Cuối cùng nó vết thương xuống phương thức được đề cập ở trên (load_missing_constant). Kể từ khi autoload_path của chúng tôi bao gồm cả lib và lib/**, Rails lấy tất cả các tệp từ tất cả các thư mục con trong lib. Thật không may, nó dường như bỏ qua không gian tên ngụ ý khi tải từ thư mục con. Dự kiến ​​foo/base.rb sẽ xác định Base so với Foo::Base. Đó có vẻ là lỗ hổng cốt lõi: load_missing_constant gọi số search_for_file trả về bất kỳ tệp nào có tên khớp (ví dụ: trong ví dụ của tôi, foo/base.rb được trả về vì nó khớp với base.rb).

Thật khó để nói nếu đây là lỗi trong Rails - ở chỗ nó vi phạm ánh xạ không gian tên tới thư mục được giả định bởi Ruby - hoặc lạm dụng autoload_paths.

Chúng tôi đã làm việc này ngay bây giờ bằng cách xóa lib/** khỏi số autoload_paths và thêm các yêu cầu cần thiết của chúng tôi vào ứng dụng.rb.

+2

Tôi cũng phải làm như vậy. Tôi nghĩ rằng bạn đang đi đúng hướng về vấn đề gốc .... ngạc nhiên không ai khác phàn nàn về điều này. –

+8

Rails đang bắt đầu làm phiền tôi. – Zabba

+0

@ Zabba: Vâng, có vẻ như đang trở thành một chiếc áo khoác thẳng. –

4

Được chỉ định đúng hướng bởi Brendan, hãy kiểm tra autoload_paths của bạn. Tôi đã có một lỗi tương tự và đây là thủ phạm cho tôi trong application.rb:

config.autoload_paths += Dir["#{Rails.root}/app/models/[a-z]*"] 

Ứng dụng của tôi không phải là hạnh phúc khi tôi bắt đầu sử dụng mô-đun cho các mô hình cùng với việc có thư mục con. Tôi đã thay đổi bản thân để tự động tải xuống chỉ các thư mục không phải là mô-đun:

config.autoload_paths += Dir["#{Rails.root}/app/models/aaaaaaaaa"] 
config.autoload_paths += Dir["#{Rails.root}/app/models/bbbbbbbbb"] 
1

Tôi đã gặp phải vấn đề rất giống với các lớp có tên được tự động tải không chính xác. Đây là manh mối để giải quyết nó (từ câu trả lời của Brendan):

Vì autoload_paths của chúng tôi bao gồm cả lib và lib/**, Rails lấy tất cả các tệp từ tất cả các thư mục con trong lib. Thật không may, nó dường như bỏ qua không gian tên ngụ ý khi tải từ thư mục con. Nó dự kiến ​​foo/base.rb để xác định Base vs. Foo :: Base. Điều đó có vẻ là lỗ hổng cốt lõi: load_missing_constant gọi search_for_file trả về bất kỳ tệp nào có tên phù hợp (ví dụ: trong ví dụ của tôi, foo/base.rb được trả về vì nó khớp với base.rb).

Và tôi đã giải quyết nó bằng cách di chuyển tất cả các lớp namespaced của tôi, tên là Events::Something, vào app/components/events/* và tạo ra một tập tin app/components/events.rb với nội dung sau:

%w(file1 file2 ...).each do |file| 
    require "events/#{file}" 
end 

Và trong khi đây là một chút sổ tay nhỏ, nó hoạt động cả cho các ứng dụng, giao diện điều khiển và chế độ thử nghiệm.

0

Một điểm đáng lưu ý khác là bạn có đúng đường dẫn trong thư mục dịch vụ hay không.

Bằng con đường đúng, ý tôi là

ENGINE_NAME/app/dịch vụ/ENGINE_NAME/your_service.rb

Tôi đã vô tình quên ENGINE_NAME thư mục bên trong dịch vụ, và có hành vi kỳ lạ này.

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