2011-08-16 13 views
24

Abbiamo appena creato un nuovo file in "lib" che ha generato una serie di mal di testa che comportano errori di caricamento.Comportamento "LoadError" incoerente con namespacing/libagamento "lib"

/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 

L'esecuzione di questo spec in Rspec ci dà il seguente errore quando si arriva al primo ' descrizione:

/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' 

TUTTAVIA! Abbiamo utilizzato molti altri file per molto tempo che hanno una struttura identica. Ad esempio, ecco un altro che sta funzionando benissimo da quando è stato creato:

/lib/smart_set.rb

module MyCompany 
    class SmartSet < Array 
    ... 
    end 
end 

E /spec/lib/smart_set_spec.rb

require 'spec_helper' 

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

Questo il file ha la stessa struttura ma non causa alcun problema.

ResponseSet (la classe problema) ha apparentemente problemi di caricamento per nessun motivo visibile. Nella console rotaie, la prima volta che tenta di creare una, ottengo un errore, ma poi mi può creare un seguito:

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 
=> [] 

Inoltre, l'aggiunta di

require 'response_set' 

in cima response_set_spec. rb consente a quei test di funzionare. Ma nessuna cosa è necessaria per smart_set_spec.rb.

Il seguente è messo in pratica application.rb:

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

Ora, capisco che Rails ha una sorta di parere su come la struttura dei file deve corrispondere la struttura dello spazio dei nomi per questi tipi di cose, e abbiamo ristrutturato i nostri moduli e file verso questa fine. SEMBRA aver risolto il problema (anche se stavamo vedendo alcuni altri strani errori di caricamento per un po 'quando abbiamo eseguito l'intera suite di test - questi sono andati misteriosamente via). Ciò nonostante, tutti qui sono sconcertati e non un po 'infastiditi dal fatto che Rails sia così incoerente e vorremmo sapere perché. Come puoi vedere ci sono due file identici per quanto riguarda il namespacing e la struttura dei file, trattati in modo completamente diverso. In effetti abbiamo una dozzina di altri file nel livello più alto di "lib" con namespaces simile che non ha mai causato problemi. Qualcuno può spiegare cosa diavolo sta succedendo qui?

risposta

4

ho guardato nella fonte rotaie e c'è una clausola di

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 ... 

in load_missing_constant metodo. Posso indovinare che come require_or_load viene chiamato prima di raise, questo potrebbe essere un motivo che nella seconda chiamata nell'esempio non c'è errore ...

Sarebbe interessante vedere un esempio minimo in cui due file con struttura identica comportarsi in modo diverso Al posto dell'utente vorrei fare una copia dell'applicazione e continuerei a rimuovere le parti da esso mentre il comportamento incoerente è presente per vedere l'esempio incoerente minimo.

P.S. Ho inviato una domanda simile qui: http://www.ruby-forum.com/topic/2376956

+0

Grazie Alexey. Sembra sospettoso. Mi sembra che l'aumento dell'eccezione debba dipendere dal fatto che il "require_or_load" del clima ha avuto successo. Ma poi di nuovo non ho molta familiarità con il labirinto che è il coraggio di Rails. –

+0

@Nick, apparentemente l'eccezione viene sollevata quando, dopo aver caricato il file, la costante non è ancora definita. Nel tuo caso, sembra che il file definisca 'ResponseSet' mentre definisce' MyCompany :: ResponseSet' (questo è come mi sembra, ma non sono uno specialista). L'incoerenza è la cosa più strana. Sei sicuro di non aver semplificato il problema originale? Forse 'MyCompany :: SmartSet' è in'/lib/my_company/smart_set.rb' e non solo '/ lib/smart_set.rb'? Ho presentato una [segnalazione di bug a GitHub] (https://github.com/rails/rails/issues/2572) su un problema simile. – Alexey

+0

No. Le strutture dei file erano identiche. Passammo un paio d'ore a controllare ogni possibile differenza che potessimo pensare. Le uniche differenze sono con l'effettiva implementazione di ogni classe (cioè attributi e metodi) ma nessuna di queste dovrebbe influire sul modo in cui i file vengono caricati! –

27

Abbiamo avuto un problema simile che, dopo aver scavato, si è rivelato causato dai cambiamenti in Rails 3.xe autoload_paths.

Il nostro caso è apparso solo in prova (RAILS_ENV=test). Quando Rails stava caricando i controller, ha strapazzato la ricerca del modello di corrispondenza di ciascun controller (a causa di un ActionController :: Base.wrap_parameters impostato in un inizializzatore). Alla fine si è rivelato il metodo sopra menzionato (load_missing_constant). Poiché i nostri percorsi autoload includevano sia lib che lib/**, Rails ha richiamato tutti i file da tutte le sottodirectory sotto lib. Sfortunatamente, sembra ignorare lo spazio dei nomi implicito durante il caricamento da sottodirectory. Si aspettava che foo/base.rb definisse Base rispetto a Foo::Base. Questo sembra essere il difetto principale: load_missing_constant invoca search_for_file che restituisce qualsiasi file il cui nome corrisponde (ad esempio, nel mio esempio, foo/base.rb è stato restituito in quanto corrisponde a base.rb).

È difficile dire se si tratta di un errore in Rails - in quanto viola il mapping di namespace to directory assunto da Ruby - o un uso improprio di autoload_paths.

Abbiamo lavorato su questo per ora rimuovendo lib/** dal nostro autoload_paths e aggiungendo le necessarie istruzioni necessarie a application.rb.

+2

Ho avuto la stessa cosa. Penso che tu sia sulla strada giusta per quanto riguarda il problema alla radice .... sorpreso che nessun altro si lamenti di questo. –

+8

Rails sta iniziando a infastidirmi. – Zabba

+0

@Zabba: Sì, sembra trasformarsi in una giacca dritta. –

4

È stato puntato nella direzione corretta da Brendan, controllare i percorsi autoaggiunti. Ho avuto un errore simile e questo è stato il colpevole per me in application.rb:

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

mia app non era felice, una volta ho iniziato ad usare i moduli per i modelli con avere sotto-directory. Ho cambiato la mia a caricare automaticamente solo le directory non-modulo:

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

ho sofferto fin stesso problema con le classi namespace essere caricate automaticamente in modo non corretto. Questa è la chiave per risolverlo (dalla risposta di Brendan):

Poiché i nostri autoload_paths inclusi sia lib e lib/**, Rails tirato in tutti i file di tutte le sottodirectory lib. Sfortunatamente, sembra ignorare lo spazio dei nomi implicito durante il caricamento da sottodirectory. Si aspettava che foo/base.rb definisse Base vs. Foo :: Base. Questo sembra essere il difetto principale: load_missing_constant richiama search_for_file che restituisce qualsiasi file il cui nome corrisponda (ad esempio, nel mio esempio, foo/base.rb è stato restituito in quanto corrisponde a base.rb).

e ho risolto spostando tutte le mie classi namespace, nome Events::Something, in app/components/events/* e la creazione di un file app/components/events.rb con il seguente contenuto:

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

E mentre questo è un po 'piccolo manuale, Funziona sia per le modalità applicazione, console e test.

0

Un altro punto da notare è che si dispone del percorso corretto nella cartella servizi.

By percorso corretto, intendo

ENGINE_NAME/app/servizi/ENGINE_NAME/your_service.rb

ho accidentalmente dimenticato ENGINE_NAME cartella all'interno dei servizi, ed ha ottenuto questo strano comportamento.

Problemi correlati