Rails 3.1: moteur vs application montable

120

Quelqu'un peut-il m'aider à comprendre les différences entre un moteur Rails et une application montable? Dans Rails 3.1, vous pouvez créer l'un ou l'autre avec la commande "rails new plugin _ __ ".

rails plugin new forum --full        # Engine
rails plugin new forum --mountable   # Mountable App

Quand voudriez-vous utiliser l'un contre l'autre? Je sais que vous pouvez emballer un moteur comme un bijou, pour un. N'est-ce pas le cas pour les applications montables? Quelles sont les autres différences?

Jeremy Raines
la source

Réponses:

143

J'ai remarqué ce qui suit:

Moteur plein

Avec un moteur complet, l'application parente hérite des routes du moteur. Il n'est pas nécessaire de spécifier quoi que ce soit dans parent_app/config/routes.rb. La spécification du gem dans Gemfile suffit pour que l'application parente hérite des modèles, des routes, etc. Les routes du moteur sont spécifiées comme suit:

# my_engine/config/routes.rb 
Rails.application.routes.draw do 
  # whatever 
end 

Pas d'espacement de noms des modèles, des contrôleurs, etc. Ceux-ci sont immédiatement accessibles à l'application parente.

Moteur montable

L'espace de noms du moteur est isolé par défaut:

# my_engine/lib/my_engine/engine.rb
module MyEngine 
  class Engine < Rails::Engine 
    isolate_namespace MyEngine 
  end 
end

Avec un moteur montable, les routes sont espacées de noms et l'application parente peut regrouper cette fonctionnalité sous une seule route:

# my_engine/config/routes.rb 
MyEngine::Engine.routes.draw do 
  #whatever 
end 

# parent_app/config/routes.rb 
ParentApp::Application.routes.draw do 
    mount MyEngine::Engine => "/engine", :as => "namespaced" 
end 

Les modèles, contrôleurs, etc. sont isolés de l'application parente - bien que les aides puissent être partagées facilement.

Ce sont les principales différences que j'ai repérées. Peut-être y en a-t-il d'autres? J'ai posé la question ici , mais je n'ai pas encore reçu de réponse.

Mon impression est que, comme un moteur complet ne s'isole pas de l'application parente, il est préférable de l'utiliser en tant qu'application autonome adjacente à l'application parente. Je pense que des conflits de noms pourraient survenir.

Un moteur montable peut être utilisé dans les situations où vous souhaitez éviter les conflits de noms et regrouper le moteur sous une route spécifique dans l'application parente. Par exemple, je travaille sur la construction de mon premier moteur conçu pour le service client. L'application parente pourrait regrouper ses fonctionnalités sous une seule route telle que:

mount Cornerstone::Engine => "/cornerstone", :as => "help" 

Si je suis loin dans mes hypothèses, quelqu'un s'il vous plaît laissez-moi savoir et je corrigerai cette réponse. J'ai fait un petit article sur le sujet ici Cheers!

Astjohn
la source
1
Un moteur montable peut-il être routé / monté à la racine de l'application parent?
Slick23
3
@JustinM vous pouvez essayer mount MyEngine::Engine => "/". Cela fonctionne pour les ressources, peut-être que c'est aussi le cas pour les moteurs.
Benoit Garret
2
@astjohn Excellent résumé de vos blogs. Mais ne serait-ce pas l'inverse? Un moteur complet serait-il "incomplet" et aurait besoin de l'application parente pour fonctionner, alors que le moteur montable peut fonctionner de manière autonome, car il est "isolé" de l'application parente?
Theo Scholiadis
39

Les deux options généreront un moteur . La différence est que --mountablecela créera le moteur dans un espace de noms isolé, alors que --fullcela créera un moteur qui partage l'espace de noms de l'application principale.

Les différences se manifesteront de 3 manières:

1) Le fichier de classe du moteur appellera isolate_namespace:

lib / my_full_engine / engine.rb:

module MyFullEngine
  class Engine < Rails::Engine
  end
end

lib / my_mountable_engine / engine.rb:

module MyMountableEngine
  class Engine < Rails::Engine
    isolate_namespace MyMountableEngine # --mountable option inserted this line
  end
end

2) Le config/routes.rbfichier du moteur sera espacé:

Moteur complet:

Rails.application.routes.draw do
end

Moteur monté:

MyMountableEngine::Engine.routes.draw do
end

3) La structure des fichiers pour les contrôleurs, les assistants, les vues et les actifs sera nommée:

créer app / controllers / my_mountable_engine /application_controller.rb
créer app / helpers / my_mountable_engine /application_helper.rb
créer une application / mailers créer une application / modèles
créer une application / vues / layouts / my_mountable_engine /application.html.erb
créer une application / assets / images / my_mountable_engine
créer app / assets / stylesheets / my_mountable_engine /application.css
create app / assets / javascripts / my_mountable_engine /application.js
create config / routes.rb create lib / my_mountable_engine.rb
create lib / tasks / my_mountable_engine.rake
create lib / my_mountable_engine .rb
créer lib / my_mountable_engine / engine.rb


Explication

Le cas d'utilisation de l' --fulloption semble être très limité. Personnellement, je ne vois aucune bonne raison pour laquelle vous voudriez séparer votre code en un moteur sans isoler également l'espace de noms - Cela vous donnerait essentiellement deux applications étroitement couplées partageant des structures de fichiers identiques et tous les conflits et les fuites de code cela implique.

Chaque document que j'ai vu démontre l' --mountableoption, et en effet le guide de bord actuel vous encourage fortement à inclure isolate namespace- ce qui revient à dire que l'utilisation est --mountableterminée --full.

Enfin, il y a confusion terminologique: rails plugin -haffiche malheureusement les descriptions suivantes:

[--full] # Générer un moteur de rails avec l'application Rails
fournie pour les tests [--mountable] # Générer une application isolée montable

Cela donne l'impression que vous utilisez --fullpour créer un "moteur" et --mountablepour créer quelque chose d'autre appelé "application montable", alors qu'en fait ils sont tous les deux des moteurs - un avec un espace de nom et l'autre pas. Cela ne peut que prêter à confusion, car les utilisateurs qui cherchent à créer un moteur supposeront probablement que --fullc'est l'option la plus pertinente.

Conclusion

  • rails plugin new something --full= Engine dans l'espace de noms de votre application. (Pourquoi voudrais-tu?)
  • rails plugin new something --mountable= Moteur avec son propre espace de noms. (Impressionnant)

Références

Yarin
la source
9
Il y a une bonne raison à utiliser --full: si vous avez des parties d'un site Web de rails que vous souhaitez conserver intégrées (pas dans un espace de noms isolé) et toujours partager entre différents projets de rails. De plus, cela peut être plus simple que cela: peut-être que votre gemme n'ajoute pas grand chose, mais vous voulez pouvoir l'accrocher correctement.
nathanvda
2
@nathanvda - D'accord, mais je pense que si vous partagez quelque chose sur plusieurs projets, il devrait vraiment avoir un espace de noms, car vous l'utilisez essentiellement comme un plugin
Yarin
Je pense que vous voudrez peut-être utiliser --full si vous souhaitez isoler vos fichiers, l'espace de noms de vos sites d'appels lorsque vous ne Admin::AdminService.some_actiondevez pas modifier vos itinéraires si d'autres applications côté client comme une application Ember utilisent les itinéraires liés au code que vous veulent isoler. --full semble être une étape intermédiaire qui peut être plus facile à implémenter.
Jwan622
Je travaille actuellement sur une application internationale qui doit gérer des réglementations spécifiques à chaque pays, mais qui expose pourtant la même interface avec le monde. J'ai une instance de "Core" par pays, pas besoin de tout gérer en même temps. Les "moteurs de pays" n'ont pas de sens en eux-mêmes, donc le couplage avec l'application "principale" n'est pas un problème. Cependant, je ne veux pas qu'ils soient dans leur propre espace de noms car l'application principale ne doit pas savoir dans quel pays elle opère. Je pense qu'un moteur "complet" ressemble plus à l'organisation de vos fichiers et classes de manière modulaire, tout en gardant votre "monolithe" en place.
Mankalas
17

Je me demandais la même chose et, par conséquent, je me suis retrouvé ici. il me semble que les réponses précédentes couvrent essentiellement la question, mais j'ai pensé que ce qui suit pourrait également aider:

# generate plugins (NOTE: using same name each time to minimize differences)
# -----------------------------------------------------------------------------

$ rails plugin new test-plugin -T
$ mv test-plugin{,.01}

$ rails plugin new test-plugin -T --mountable
$ mv test-plugin{,.02}

$ rails plugin new test-plugin -T --full
$ mv test-plugin{,.03}

$ rails plugin new test-plugin -T --full --mountable
$ mv test-plugin{,.04}




# compare "stock" (01) with "mountable" (02)
# -----------------------------------------------------------------------------

$ diff -r test-plugin.01 test-plugin.02

Only in test-plugin.02: app
Only in test-plugin.02: config
Only in test-plugin.02/lib/test-plugin: engine.rb
diff -r test-plugin.01/lib/test-plugin.rb test-plugin.02/lib/test-plugin.rb
0a1,2
> require "test-plugin/engine"
> 
Only in test-plugin.02: script
diff -r test-plugin.01/test-plugin.gemspec test-plugin.02/test-plugin.gemspec
18a19
>   # s.add_dependency "jquery-rails"




# compare "stock" (01) with "full" (03)
# -----------------------------------------------------------------------------

$ diff -r test-plugin.01 test-plugin.03
Only in test-plugin.03: app
Only in test-plugin.03: config
Only in test-plugin.03/lib/test-plugin: engine.rb
diff -r test-plugin.01/lib/test-plugin.rb test-plugin.03/lib/test-plugin.rb
0a1,2
> require "test-plugin/engine"
> 
Only in test-plugin.03: script
diff -r test-plugin.01/test-plugin.gemspec test-plugin.03/test-plugin.gemspec
18a19
>   # s.add_dependency "jquery-rails"




# compare "mountable" (02) with "full" (03)
# -----------------------------------------------------------------------------

$ diff -r test-plugin.02 test-plugin.03

Only in test-plugin.03/app/assets/javascripts/test-plugin: .gitkeep
Only in test-plugin.02/app/assets/javascripts/test-plugin: application.js
Only in test-plugin.03/app/assets/stylesheets/test-plugin: .gitkeep
Only in test-plugin.02/app/assets/stylesheets/test-plugin: application.css
Only in test-plugin.03/app/controllers: .gitkeep
Only in test-plugin.02/app/controllers: test-plugin
Only in test-plugin.03/app/helpers: .gitkeep
Only in test-plugin.02/app/helpers: test-plugin
Only in test-plugin.03/app/mailers: .gitkeep
Only in test-plugin.03/app/models: .gitkeep
Only in test-plugin.03/app/views: .gitkeep
Only in test-plugin.02/app/views: layouts
diff -r test-plugin.02/config/routes.rb test-plugin.03/config/routes.rb
1c1
< TestPlugin::Engine.routes.draw do
---
> Rails.application.routes.draw do
diff -r test-plugin.02/lib/test-plugin/engine.rb test-plugin.03/lib/test-plugin/engine.rb
3d2
<     isolate_namespace TestPlugin




# compare "mountable" (02) with "full & mountable" (04)
# -----------------------------------------------------------------------------

$ diff -r test-plugin.02 test-plugin.04

<no difference>




# compare "full" (03) with "full & mountable" (04)
# -----------------------------------------------------------------------------

$ diff -r test-plugin.03 test-plugin.04

Only in test-plugin.03/app/assets/javascripts/test-plugin: .gitkeep
Only in test-plugin.04/app/assets/javascripts/test-plugin: application.js
Only in test-plugin.03/app/assets/stylesheets/test-plugin: .gitkeep
Only in test-plugin.04/app/assets/stylesheets/test-plugin: application.css
Only in test-plugin.03/app/controllers: .gitkeep
Only in test-plugin.04/app/controllers: test-plugin
Only in test-plugin.03/app/helpers: .gitkeep
Only in test-plugin.04/app/helpers: test-plugin
Only in test-plugin.03/app/mailers: .gitkeep
Only in test-plugin.03/app/models: .gitkeep
Only in test-plugin.03/app/views: .gitkeep
Only in test-plugin.04/app/views: layouts
diff -r test-plugin.03/config/routes.rb test-plugin.04/config/routes.rb
1c1
< Rails.application.routes.draw do
---
> TestPlugin::Engine.routes.draw do
diff -r test-plugin.03/lib/test-plugin/engine.rb test-plugin.04/lib/test-plugin/engine.rb
2a3
>     isolate_namespace TestPlugin

un intérêt particulier (pour moi) est le fait qu'il n'y a pas de différence entre

rails plugin new test-plugin -T --mountable

et

rails plugin new test-plugin -T --full --mountable
Corey Innis
la source
Peut-être que c'est parce qu'il --fulla préséance sur --mountable?
Mankalas
8

Ma compréhension de la différence est que les moteurs sont comme des plugins et ajoutent des fonctionnalités aux applications existantes. Alors que les applications montables sont essentiellement une application et peuvent être autonomes.

Donc, si vous voulez pouvoir l'exécuter seul ou dans une autre application, vous créerez une application montable. Si vous avez l'intention qu'il s'agisse d'un ajout aux applications existantes, mais qu'il ne soit pas exécuté par lui-même, vous en feriez un moteur.

JDutil
la source
2

La différence, je crois, est qu'une application montable est isolée de l'application hôte, elle ne peut donc pas partager de classes - modèles, aide, etc. C'est parce qu'une application montable est un point de terminaison Rack (c'est-à-dire une application Rack à part entière ).

Avertissement: comme la plupart des autres, je viens juste de commencer à jouer avec Rails 3.1.

Kris
la source
D'accord. Une chose qui semble étrange est que par défaut, un moteur vous donne un dossier "modèles", mais pas une application montable. Je me demande si la "meilleure pratique" serait d'avoir des générateurs qui créent des modèles pour l'application incluse, car il semble que vous ne voudriez pas avoir de migrations dans le moteur / montable
Jeremy Raines