32 votes

Moteur de rendu dans la mise en page de l'application

Contexte :

Je crée une application composée d'un noyau et de plusieurs modules. Les modules sont des moteurs Rails, et fournissent la fonctionnalité réelle car le noyau lui-même ne fait que servir d'hôte. Les moteurs sont hébergés dans /lib et montés à leurs chemins respectifs.

coreApp
lib
    module1
    module2
    etc

Les modules sont ensuite montés de cette manière :

mount Module1::Engine => "/module1", :as => "module1"
mount Module2::Engine => "/module2", :as => "module2"

Le noyau est également responsable de la gestion de la session, bien que la connexion elle-même soit effectuée par un module.

Problème :

Je n'ai pas encore trouvé de bonne manière de partager la mise en page de l'application principale avec les moteurs. Voici comment je mets actuellement la mise en page à disposition des moteurs :

coreApp
 app
     views
         layouts
             application.html.erb
             core.html.erb

Le fichier core.html.erb contient uniquement :

<%= render :template => 'layouts/application' %>

Il est ensuite inclus dans chaque module de cette manière :

module Module1
  class ApplicationController < ActionController::Base
    layout "core"
  end
end

Bien que ce ne soit pas particulièrement élégant, cela fonctionne bien, et le contenu du module est rendu là où se trouve l'instruction yield dans la mise en page de l'application.

Les problèmes sont les suivants :

1. Les feuilles de style spécifiques au module ne sont pas incluses dans l'en-tête

J'ai besoin d'une manière d'inclure les feuilles de style du module actif.

2. L'en-tête a besoin d'accéder aux informations concernant l'utilisateur connecté

L'en-tête contient des informations sur l'utilisateur connecté, comme

Connecté en tant que <%= @user[:realname] %>

Cela provient du home_controller du noyau

def index
  @user = User.find_by_id(session[:user])
end

Mais lorsque j'essaie d'accéder au module, j'obtiens l'erreur suivante

NoMethodError dans Module1/home#index

Vous avez un objet nul lorsque vous ne vous y attendiez pas !
Vous avez peut-être attendu une instance de Array.
L'erreur s'est produite en évaluant nil.[]

Manifestement, cela fait référence à @user.

Question :

Comment résoudre cela de manière aussi élégante et DRY que possible sans trop toucher au côté moteur ?

J'ai beaucoup cherché sur Google, mais je ne parviens pas vraiment à comprendre comment résoudre cela. Il se peut que cela soit dû à un manque total de compréhension du fonctionnement de Rails, donc il y a de fortes chances que cette question ne soit même pas logique pour quelqu'un qui connaît bien Rails.

N'hésitez pas à commenter si quelque chose n'est pas clair ou ambigu, et j'essaierai d'élaborer.

44voto

Theo Scholiadis Points 986

J'ai utilisé avec succès les mises en page de mon application parent dans mes moteurs. Tout d'abord, en fonction de la Section 4.3.2 des Guides Rails (Engines), afin d'accéder aux variables de l'ApplicationController de l'application parent (comme session, comme vous l'utilisez ci-dessus), vous devez remplacer le application_controller.rb du moteur par celui que vous avez actuellement :

module Module1
  class ApplicationController < ActionController::Base
    layout "core"
  end
end

par ceci :

class Module1::ApplicationController < ::ApplicationController

Cela permettra d'hériter de l'ApplicationController de l'application parent, ainsi que de toutes ses variables.

Deuxièmement, vous devrez supprimer le fichier app/views/layouts/application.html.erb de vos vues de moteur, car il ne sera pas nécessaire puisque vous utilisez celui de l'application parent.

Maintenant, lorsque vous affichez une vue de Module1 à partir de l'application parent, la mise en page de l'application parent sera utilisée, et toutes les variables session[] seront évaluées correctement.

N'oubliez pas d'ajouter les mots "main_app." avant chaque lien dans vos mises en page, sinon il tentera de rechercher les chemins dans le moteur au lieu de l'application parent. Par exemple, si la mise en page de l'application parent inclut un lien vers some_path (qui est une vue dans l'application parent), en affichant une vue dans le moteur qui utilise cette mise en page essaiera de rechercher some_path dans le moteur au lieu de l'application parent. Vous devrez modifier le lien en main_app.some_path pour que cela fonctionne.

J'espère que cela vous aidera.

18voto

montrealmike Points 3795

Utilisez layout 'layouts/application'

Et si vous ne voulez pas utiliser main_app.your_path vous pouvez également ajouter:

module YourEngine
  module ApplicationHelper
    def method_missing(method, *args, &block)
      if (method.to_s.end_with?('_path') || method.to_s.end_with?('_url')) && main_app.respond_to?(method)
        main_app.send(method, *args)
      else
        super
      end
    end
  end

9voto

ShadyKiller Points 382

J'ai également fait la même chose dans mon application. Tout ce que j'ai fait était :

  1. Supprimer la mise en page du moteur dans /app/view/layouts/

  2. Changer votre application_controller en

    module EngineModule
      class ApplicationController < ::ApplicationController
        layout 'layouts/application' 
      end
    end
  3. Dans vos vues, si vous voulez faire référence à un chemin tel que login_path, vous pouvez le faire via main_app.login_path

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X