Schienen 3.1: Motor vs. montierbare App

120

Kann mir jemand helfen, die Unterschiede zwischen einer Rails Engine und einer Mountable App zu verstehen? In Rails 3.1 können Sie beide mit dem Befehl "Rails New Plugin _ __ " erstellen .

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

Wann möchten Sie eines gegen das andere verwenden? Ich weiß, dass Sie einen Motor zum einen als Juwel verpacken können. Ist das bei Mountable Apps nicht der Fall? Welche anderen Unterschiede gibt es?

Jeremy Raines
quelle

Antworten:

143

Mir ist Folgendes aufgefallen:

Voller Motor

Bei einer vollständigen Engine erbt die übergeordnete Anwendung die Routen von der Engine. Es ist nicht erforderlich, etwas in anzugeben parent_app/config/routes.rb. Die Angabe des Edelsteins in Gemfile reicht aus, damit die übergeordnete App die Modelle, Routen usw. erben kann. Die Engine-Routen werden wie folgt angegeben:

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

Kein Namespace von Modellen, Controllern usw. Diese sind für die übergeordnete Anwendung sofort verfügbar.

Montierbarer Motor

Der Namespace der Engine ist standardmäßig isoliert:

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

Mit einer montierbaren Engine haben die Routen einen Namespace und die übergeordnete App kann diese Funktionalität unter einer einzigen Route bündeln:

# 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 

Modelle, Controller usw. sind von der übergeordneten Anwendung isoliert - obwohl Helfer problemlos gemeinsam genutzt werden können.

Dies sind die Hauptunterschiede, die ich entdeckt habe. Vielleicht gibt es noch andere? Ich habe gefragt , über hier , haben aber noch keine Antwort zu erhalten.

Mein Eindruck ist, dass eine vollständige Engine sich nicht von der übergeordneten Anwendung isoliert und daher am besten als eigenständige Anwendung neben der übergeordneten Anwendung verwendet wird. Ich glaube, dass Namenskonflikte auftreten können.

Eine montierbare Engine kann in Situationen verwendet werden, in denen Sie Namenskonflikte vermeiden und die Engine in der übergeordneten Anwendung unter einer bestimmten Route bündeln möchten. Zum Beispiel arbeite ich daran, meinen ersten Motor für den Kundenservice zu bauen. Die übergeordnete Anwendung kann ihre Funktionalität unter einer einzigen Route bündeln, z. B.:

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

Wenn ich in meinen Annahmen weit davon entfernt bin, lassen Sie es mich bitte wissen und ich werde diese Antwort korrigieren. Ich habe einen kleinen Artikel über das Thema gemacht hier Cheers!

Astjohn
quelle
1
Kann eine montierbare Engine jemals im Stammverzeichnis der übergeordneten App geroutet / gemountet werden?
Slick23
3
@ JustinM könnten Sie versuchen mount MyEngine::Engine => "/". Es funktioniert für Ressourcen, vielleicht ist das auch für Motoren der Fall.
Benoit Garret
2
@astjohn Tolle Zusammenfassung Ihrer Blogs. Aber wäre es nicht umgekehrt? Wäre eine vollständige Engine "unvollständig" und müsste die übergeordnete App funktionieren, während die montierbare Engine eigenständig arbeiten kann, da sie von der übergeordneten App "isoliert" ist?
Theo Scholiadis
39

Beide Optionen erzeugen einen Motor . Der Unterschied besteht darin, dass --mountabledie Engine in einem isolierten Namespace erstellt wird, während --fulleine Engine erstellt wird, die den Namespace der Hauptanwendung gemeinsam nutzt.

Die Unterschiede werden auf drei Arten manifestiert:

1) Die Engine-Klassendatei ruft Folgendes auf 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) Die config/routes.rbDatei der Engine wird mit einem Namespace versehen:

Voller Motor:

Rails.application.routes.draw do
end

Montierter Motor:

MyMountableEngine::Engine.routes.draw do
end

3) Die Dateistruktur für Controller, Helfer, Ansichten und Assets wird mit einem Namespace versehen:

App / Controller erstellen / my_mountable_engine /application_controller.rb
App / helpers / my_mountable_engine
erstellen /application_helper.rb App / Mailer erstellen App / Modelle
erstellen App / Ansichten / Layouts erstellen / my_mountable_engine /application.html.erb
App / Assets / Images / my_mountable_eng erstellen
App / Assets / Stylesheets erstellen / my_mountable_engine /application.css
App / Assets / Javascripts erstellen / 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
Erstellen Sie lib / my_mountable_engine .rb


Erläuterung

Der Anwendungsfall für die --fullOption scheint sehr begrenzt zu sein. Persönlich kann ich mir keinen guten Grund vorstellen, warum Sie Ihren Code in eine Engine trennen möchten, ohne auch den Namespace zu isolieren. Im Wesentlichen erhalten Sie nur zwei eng gekoppelte Anwendungen, die identische Dateistrukturen und alle Konflikte und Codeverluste gemeinsam nutzen das bedeutet.

Jede Dokumentation, die ich gesehen habe, demonstriert die --mountableOption, und in der Tat empfiehlt Ihnen die aktuelle Kantenführung dringend, sie einzuschließen isolate namespace- was dasselbe ist, als würde man "Verwendung --mountableüber" sagen --full.

Schließlich gibt es Terminologie-Verwirrung: rails plugin -hZeigt leider die folgenden Beschreibungen:

[--full] # Generieren Sie eine Rails-Engine mit einer gebündelten Rails-Anwendung zum Testen.
[--mountable] # Generieren Sie eine montierbare isolierte Anwendung

Dies erweckt den Eindruck, dass Sie --fulleine "Engine" --mountableerstellen und etwas anderes erstellen, das als "montierbare Anwendung" bezeichnet wird, obwohl es sich tatsächlich um beide Engines handelt - eine mit Namespace und eine ohne. Dies führt zwangsläufig zu Verwirrung, da Benutzer, die eine Engine erstellen möchten, wahrscheinlich davon ausgehen, dass dies --fulldie relevantere Option ist.

Fazit

  • rails plugin new something --full= Engine im Namespace Ihrer App. (Warum würdest du?)
  • rails plugin new something --mountable= Engine mit eigenem Namespace. (Genial)

Verweise

Yarin
quelle
9
Es gibt einen guten Grund für die Verwendung --full: Wenn Sie Teile einer Rails-Website haben, möchten Sie diese integrieren (nicht in einem isolierten Namespace) und dennoch zwischen verschiedenen Rails-Projekten teilen. Es kann auch einfacher sein: Vielleicht fügt Ihr Edelstein nicht so viel hinzu, aber Sie möchten es richtig einhaken können.
Nathanvda
2
@ Nathanvda - Richtig, aber ich denke, wenn Sie etwas über mehrere Projekte hinweg teilen, sollte es wirklich einen Namespace haben, da Sie es im Grunde genommen als Plugin verwenden
Yarin
Ich denke, Sie möchten möglicherweise --full verwenden, wenn Sie Ihre Dateien isolieren und Ihre Anrufseiten benennen möchten, wenn Sie dies tun, Admin::AdminService.some_actionaber Ihre Routen nicht ändern müssen, wenn andere clientseitige Anwendungen wie eine Ember-App die Routen verwenden, die sich auf den von Ihnen verwendeten Code beziehen isolieren wollen. --full scheint ein Zwischenschritt zu sein, der möglicherweise einfacher zu implementieren ist.
Jwan622
Ich arbeite derzeit an einer internationalen Anwendung, die länderspezifische Vorschriften erfüllen muss, aber dennoch dieselbe Schnittstelle zur Welt bietet. Ich habe eine Instanz von "Core" pro Land, die nicht auf einmal bearbeitet werden muss. Die "Country Engines" sind für sich genommen nicht sinnvoll, daher ist die Kopplung mit der "Core" -App kein Problem. Ich möchte jedoch nicht, dass sie sich in ihrem eigenen Namespace befinden, da die Kern-App nicht wissen darf, in welchem ​​Land sie tätig ist. Ich bin der Meinung, dass eine "vollständige" Engine eher dem modularen Organisieren Ihrer Dateien und Klassen gleicht, aber dennoch Ihren "Monolithen" an Ort und Stelle hält.
Mankalas
17

Ich habe mich das gleiche gefragt und bin daher hier gelandet. Es scheint mir, dass die früheren Antworten im Wesentlichen die Frage abdecken, aber ich dachte, dass das Folgende auch helfen könnte:

# 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

Von besonderem Interesse (für mich) ist die Tatsache, dass es keinen Unterschied zwischen gibt

rails plugin new test-plugin -T --mountable

und

rails plugin new test-plugin -T --full --mountable
Corey Innis
quelle
Vielleicht liegt das daran, dass --fullVorrang vor hat --mountable?
Mankalas
8

Ich verstehe den Unterschied darin, dass Engines wie Plugins sind und vorhandenen Anwendungen Funktionen hinzufügen. Mountbare Apps sind im Wesentlichen eine Anwendung und können eigenständig sein.

Wenn Sie es also alleine oder in einer anderen Anwendung ausführen möchten, erstellen Sie eine bereitstellbare App. Wenn Sie beabsichtigen, dass es eine Ergänzung zu vorhandenen Anwendungen ist, aber nicht von selbst ausgeführt wird, würden Sie es zu einer Engine machen.

JDutil
quelle
2

Ich glaube, der Unterschied besteht darin, dass die bereitstellbaren Apps von der Host-App isoliert sind, sodass sie keine Klassen gemeinsam nutzen können - Modelle, Helfer usw. Dies liegt daran, dass eine bereitstellbare App ein Rack-Endpunkt ist (dh eine eigenständige Rack-App) ).

Haftungsausschluss: Ich habe, wie die meisten, gerade erst angefangen, mit Rails 3.1 zu spielen.

Kris
quelle
Einverstanden. Eine Sache, die seltsam erscheint, ist, dass eine Engine Ihnen standardmäßig einen Ordner "Modelle" gibt, eine Mountable App jedoch nicht. Ich frage mich, ob die "beste Vorgehensweise" darin besteht, Generatoren zu haben, die Modelle für die inklusive App erstellen, da Sie anscheinend keine Migrationen in der Engine / moutable haben möchten
Jeremy Raines