2010-06-03 10 views
14

Tengo un motor que define algunos modelos y controladores. Quiero poder ampliar la funcionalidad de algunos modelos/controladores en mi aplicación (por ejemplo, agregar métodos) sin perder la funcionalidad del modelo/controlador original del motor. Dondequiera que lea, simplemente necesita definir el controlador con el mismo nombre en su aplicación y Rails los fusionará automáticamente, sin embargo, no funciona para mí y el controlador en el motor simplemente se ignora (no creo que esté siquiera cargado).Motores Rails que amplían la funcionalidad

Respuesta

2

Sólo si alguien más se topa mismo problema algún momento en el futuro, este es el código que he escrito que fija mi 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 

Esto requerirá automáticamente los archivos de motor antes de que requiere de la aplicación si la ruta del archivo comienza con 'app'.

+0

Esta solución funciona para Rails 3, ver http://stackoverflow.com/questions/5045068/extending-controllers-of-a-rails-3-engine-in-the -main-app – Andrei

+0

Ahora también funciona para Rails 3. – Andrei

+0

Creé una joya de esta respuesta hace aproximadamente un año, pero olvidé de publicarla aquí. Funciona bien para nosotros: https://github.com/EPI-USE-Labs/activesupport-decorators –

1

Eso es cierto. El controlador que se encuentra primero se usará.

Así que para que funcione es posible que tenga dos opciones:

  • crear una copia local del controlador, y modificar el método que necesita
  • si tiene control sobre el plugin, se puede crear una Módulo que contiene el código y include el código en ambos controladores, solo reemplaza el método en su controlador local. Según mi opinión, dado que no hay herencia múltiple, esa es la única forma.

Espero que esto ayude.

0

nunca he utilizado los motores antes, pero no se puede definir un nuevo controlador que herede desde el controlador proporcionado por el motor

+0

No si tienen el mismo nombre. – Andrius

+0

¿Qué pasa si están en un Namespace separado? –

7

Puede añadir estas líneas para archivo de módulo de motor en el directorio raíz 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 

Entonces usted tiene la habilidad en la aplicación principal (la toma de aplicación uso del motor) para exigir que los archivos necesarios del motor. Esto es bueno porque mantiene la funcionalidad predeterminada de Rails Engines y también tiene una herramienta fácil para hacer uso de la herencia de Ruby normal, sin la necesidad de parches.

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

¿cuál es el beneficio de hacerlo de esta manera en comparación con el modelo de APP PRINCIPAL que hereda del modelo ENGINE? – westonplatter

+0

Si un archivo model.rb está presente en la APLICACIÓN PRINCIPAL y en la APLICACIÓN DEL MOTOR pero la APLICACIÓN DEL MOTOR .rb no se requiere en la APLICACIÓN PRINCIPAL .rb, Rails ignora la APLICACIÓN DEL MOTOR .rb. La aplicación ENGINE to MAIN no sigue la herencia ruby ​​normal, al menos cuando escribí esta respuesta. – joshmckin

+0

¡Me gusta esta solución! Mi pregunta es: ¿y si el usuario en el motor está en el espacio de nombres? – user3281384

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

antes de la definición de clase del modelo en la aplicación.

1

Puede cambiar el orden de carga del motor para evitar el requerimiento en cada uno de sus modelos.

En config/application.rb añadir esta línea:

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

Esto asegurará que los modelos de MyEngine se cargan antes de MiApl

+0

Por lo que sé, Rails carga automáticamente la primera definición (el modelo del motor en su caso) y la usa, no pasa por las otras rutas para cargar otras posibles definiciones del mismo modelo. – linkyndy

Cuestiones relacionadas