2010-06-03 8 views
14

Ho un motore che definisce alcuni modelli e controller. Voglio essere in grado di estendere la funzionalità di alcuni modelli/controller nella mia applicazione (ad esempio aggiungendo metodi) senza perdere la funzionalità del modello/controller originale dal motore. Ovunque legga, devi semplicemente definire un controller con lo stesso nome nella tua applicazione e Rails le unirà automaticamente, tuttavia non funziona per me e il controller nel motore viene semplicemente ignorato (non penso nemmeno che sia caricato).Rails engine estendendo le funzionalità

risposta

2

Proprio se qualcun altro si imbatte in stesso problema qualche tempo in futuro, questo è il codice che ho scritto che risolto il mio problema:

module ActiveSupport::Dependencies 
    alias_method :require_or_load_without_multiple, :require_or_load 
    def require_or_load(file_name, const_path = nil) 
    if file_name.starts_with?(RAILS_ROOT + '/app') 
     relative_name = file_name.gsub(RAILS_ROOT, '') 
     @engine_paths ||= Rails::Initializer.new(Rails.configuration).plugin_loader.engines.collect {|plugin| plugin.directory } 
     @engine_paths.each do |path| 
     engine_file = File.join(path, relative_name) 
     require_or_load_without_multiple(engine_file, const_path) if File.file?(engine_file) 
     end 
    end 
    require_or_load_without_multiple(file_name, const_path) 
    end 
end 

Ciò richiederà automaticamente i file dal motore prima di richiedere dall'applicazione se il percorso del file inizia con 'app'.

+0

Questa soluzione funziona per Rails 3, vedere http://stackoverflow.com/questions/5045068/extending-controllers-of-a-rails-3-engine-in-the -main-app – Andrei

+0

Ora funziona anche per Rails 3. – Andrei

+0

Ho creato una gemma da questa risposta circa un anno fa ma ho dimenticato di postarla qui. Funziona bene per noi: https://github.com/EPI-USE-Labs/activesupport-decorators –

1

Questo è vero. Verrà utilizzato il controller che viene trovato per primo.

Quindi, per farlo funzionare si potrebbe avere due opzioni:

  • creare una copia locale del controller, e modificare il metodo è necessario
  • se si ha il controllo del plug-in, è possibile creare un Modulo contenente il codice e include il codice in entrambi i controller, solo sovrascrivendo il metodo nel controller locale. Secondo me, poiché non c'è eredità multipla, questa è l'unica via.

Spero che questo aiuti.

0

non ho mai usato Motori di prima, ma non si può definire un nuovo controller che eredita dal controller fornita dal motore

+0

Non se hanno lo stesso nome. – Andrius

+0

Che dire se si trovano in uno spazio dei nomi separato –

7

È possibile aggiungere queste righe a voi file di modulo del motore nella directory principale lib:

def self.root 
    File.expand_path(File.dirname(File.dirname(__FILE__))) 
end 

def self.models_dir 
    "#{root}/app/models" 
end 

def self.controllers_dir 
    "#{root}/app/controllers" 
end 

Quindi si avrà la possibilità nel ricorso principale (l'rendendo app uso del motore) per richiedere i file necessari dal motore. Questo è bello perché si mantiene la funzionalità predefinita di Rails Engines e si ha anche uno strumento semplice per fare uso dell'ereditarietà di ruby ​​normale, senza bisogno di patch.

EX:

#ENGINE Model - 

class User < ActiveRecord::Base 
    def testing_engine 
    puts "Engine Method" 
    end 
end 

#MAIN APP Model - 

require "#{MyEngine.models_dir}/user" 
class User 
    def testing_main_app 
    puts "Main App Method" 
    end 
end 

#From the Main apps console 

user = User.new 

puts user.testing_engine #=> "Engine Method" 

puts user.tesing_main_app #=> "Main App Method" 
+0

qual è il vantaggio di farlo in questo modo rispetto al modello APP principale che eredita dal modello ENGINE? – westonplatter

+0

Se un file model.rb è presente nell'APP PRINCIPALE e nell'APP ENGINE ma l'APP MOTORE .rb non è richiesta nell'API principale .rb, Rails ignora l'APP MOTORE .rb. L'app ENGINE to MAIN non segue la normale ereditarietà di ruby, almeno quando ho scritto questa risposta. – joshmckin

+0

Mi piace questa soluzione! La mia domanda è: cosa succede se l'utente nel motore in namespace? – user3281384

16
require MyEngine::Engine.root.join('app', 'models', 'my_engine', 'my_model') 

prima della definizione della classe del modello nell'applicazione.

1

È possibile modificare l'ordine di caricamento del motore per evitare il fabbisogno su ciascun modello.

in config/application.rb aggiungere questa riga:

module MyApp 
    class Application 
    config.railties_order = [MyEngine::Engine, :main_app, :all] 
    end 
end 

Questo farà sì che i modelli da MyEngine vengono caricati prima MyApp

+0

Per quanto ne so, l'autoloading di Rails trova la prima definizione (il modello del motore nel tuo caso) e la usa, non passa attraverso gli altri percorsi per caricare possibili altre definizioni dello stesso modello. – linkyndy

Problemi correlati