Rails 3.1: Silnik kontra aplikacja do montażu

120

Czy ktoś może mi pomóc zrozumieć różnice między Rails Engine a aplikacją Mountable? W Railsach 3.1 możesz utworzyć jedną z nich za pomocą polecenia "rails new plugin _ __ ".

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

Kiedy chciałbyś użyć jednego w porównaniu z drugim? Wiem, że możesz na przykład zapakować silnik jako klejnot. Czy tak nie jest w przypadku aplikacji do zamontowania? Jakie są inne różnice?

Jeremy Raines
źródło

Odpowiedzi:

143

Zauważyłem, co następuje:

Pełny silnik

W przypadku pełnego silnika aplikacja nadrzędna dziedziczy trasy z silnika. Nie trzeba niczego określać w parent_app/config/routes.rb. Określenie klejnotu w Gemfile wystarczy, aby aplikacja nadrzędna odziedziczyła modele, trasy itp. Trasy silnika są określone jako:

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

Brak przestrzeni nazw modeli, kontrolerów itp. Są one natychmiast dostępne dla aplikacji nadrzędnej.

Zamontowany silnik

Przestrzeń nazw silnika jest domyślnie izolowana:

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

W przypadku silnika, który można montować, trasy są podzielone na przestrzenie nazw, a aplikacja nadrzędna może połączyć tę funkcję w jedną trasę:

# 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 

Modele, kontrolery itp. Są odizolowane od aplikacji nadrzędnej - chociaż pomocników można łatwo udostępniać.

To są główne różnice, które zauważyłem. Może są inni? Pytałem tutaj , ale jeszcze nie otrzymałem odpowiedzi.

Mam wrażenie, że ponieważ pełny silnik nie izoluje się od aplikacji nadrzędnej, najlepiej jest używać go jako samodzielnej aplikacji sąsiadującej z aplikacją nadrzędną. Uważam, że mogą wystąpić starcia nazw.

Mechanizm montowalny może być używany w sytuacjach, w których chcesz uniknąć konfliktów nazw i połączyć go w jedną konkretną trasę w aplikacji nadrzędnej. Na przykład pracuję nad zbudowaniem mojego pierwszego silnika przeznaczonego do obsługi klienta. Aplikacja nadrzędna może połączyć swoją funkcjonalność w ramach jednej trasy, takiej jak:

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

Jeśli moje przypuszczenia są dalekie ode mnie, niech ktoś da mi znać, a naprawię tę odpowiedź. Zrobiłem mały artykuł na ten temat tutaj Cheers!

astjohn
źródło
1
Czy montowalny silnik może być kiedykolwiek skierowany / zamontowany w katalogu głównym aplikacji nadrzędnej?
Slick23
3
@JustinM, możesz spróbować mount MyEngine::Engine => "/". Działa w przypadku zasobów, być może dotyczy to również silników.
Benoit Garret
2
@astjohn Świetne podsumowanie Twoich blogów. Ale czy nie byłoby odwrotnie? Czy pełny silnik byłby „niekompletny” i wymagałby działania aplikacji nadrzędnej, podczas gdy silnik montowalny może działać samodzielnie, ponieważ jest „odizolowany” od aplikacji nadrzędnej?
Theo Scholiadis,
39

Obie opcje wygenerują silnik . Różnica polega na tym, --mountableże utworzy silnik w izolowanej przestrzeni nazw, podczas gdy --fullutworzy silnik, który współużytkuje przestrzeń nazw głównej aplikacji.

Różnice przejawiają się na 3 sposoby:

1) Plik klasy silnika wywoła 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) Plik silnika config/routes.rbbędzie miał przestrzeń nazw:

Pełny silnik:

Rails.application.routes.draw do
end

Zamontowany silnik:

MyMountableEngine::Engine.routes.draw do
end

3) Struktura plików dla kontrolerów, pomocników, widoków i zasobów będzie miała przestrzeń nazw:

create app / controllers / my_mountable_engine /application_controller.rb
create app / helpers / my_mountable_engine /application_helper.rb
create app / mailers create app / models
create app / views / layouts / my_mountable_engine /application.html.erb
create app / asset / images / my_mountable_engine
utwórz aplikację / asset / stylesheets / my_mountable_engine /application.css
utwórz aplikację / asset / javascripts / my_mountable_engine /application.js
utwórz config / Routes.rb
utwórz lib / my_mountable_engine.rb
utwórz lib / jobs / my_mountable_engine.rake utwórz lib / my_mountable_engine .rb
utwórz lib / my_mountable_engine / engine.rb


Wyjaśnienie

Przypadek użycia tej --fullopcji wydaje się być bardzo ograniczony. Osobiście nie mogę wymyślić żadnego dobrego powodu, dla którego chciałbyś podzielić swój kod na silnik bez izolowania przestrzeni nazw - w zasadzie dałoby ci to dwie ściśle powiązane aplikacje, które mają identyczne struktury plików i wszystkie konflikty i wyciek kodu to pociąga za sobą.

Każdy fragment dokumentacji, który widziałem, demonstruje tę --mountableopcję, a obecny przewodnik po krawędziach zdecydowanie zachęca do uwzględnienia isolate namespace- co jest tym samym, co powiedzenie use --mountableover --full.

Wreszcie pojawia się zamieszanie terminologiczne: niestety rails plugin -hpokazuje następujące opisy:

[--full] # Generowanie silnika Railsów z dołączoną aplikacją Rails do testowania
[--mountable] # Generowanie montowanej izolowanej aplikacji

Sprawia to wrażenie, jakbyś --fulltworzył „silnik” i --mountablecoś innego, co nazywa się „montowalną aplikacją”, podczas gdy w rzeczywistości są to oba silniki - jeden z przestrzenią nazw, a drugi nie. To z pewnością prowadzi do nieporozumień, ponieważ użytkownicy, którzy chcą stworzyć silnik, prawdopodobnie uznają, że --fulljest to bardziej odpowiednia opcja.

Wniosek

  • rails plugin new something --full= Silnik w przestrzeni nazw aplikacji. (Dlaczego miałbyś?)
  • rails plugin new something --mountable= Silnik z własną przestrzenią nazw. (Niesamowite)

Bibliografia

Yarin
źródło
9
Jest jeden dobry powód, aby użyć --full: jeśli masz części strony internetowej rails, które chcesz zachować zintegrowane (nie w izolowanej przestrzeni nazw) i nadal udostępniać je innym projektom railsów. Może być też prostsze: może twój klejnot nie dodaje tak dużo, ale chcesz mieć możliwość prawidłowego zaczepienia.
nathanvda
2
@nathanvda - Racja, ale myślę, że jeśli udostępniasz coś w wielu projektach, to naprawdę powinno mieć przestrzeń nazw, ponieważ zasadniczo używasz go jako wtyczki
Yarin
Myślę, że możesz chcieć użyć --full, jeśli chcesz odizolować swoje pliki, nazwij swoje witryny połączeń, kiedy to robisz, Admin::AdminService.some_actionale nie musisz zmieniać tras, jeśli inne aplikacje po stronie klienta, takie jak aplikacja Ember, używają tras związanych z kodem, chcą się odizolować. --full wydaje się być krokiem pośrednim, który może być łatwiejszy do wdrożenia.
Jwan622,
Obecnie pracuję nad aplikacją międzynarodową, która musi obsługiwać przepisy obowiązujące w danym kraju, ale jednocześnie udostępnia ten sam interfejs światu. Mam jedno wystąpienie „Core” na kraj, nie muszę obsługiwać wszystkich naraz. „Lokalne silniki” same w sobie nie mają sensu, więc połączenie z „podstawową” aplikacją nie stanowi problemu. Nie chcę jednak, aby znajdowały się we własnej przestrzeni nazw, ponieważ podstawowa aplikacja nie może wiedzieć, w którym kraju działa. Wydaje mi się, że „pełny” silnik jest bardziej podobny do organizowania plików i klas w sposób modułowy, ale nadal utrzymuje swój „monolit” na miejscu.
Mankalas
17

zastanawiałem się nad tym samym i dlatego wylądowałem tutaj. wydaje mi się, że wcześniejsze odpowiedzi zasadniczo pokrywają pytanie, ale pomyślałem, że poniższe mogą również pomóc:

# 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

szczególnie interesujący (mnie) jest fakt, że nie ma między nimi żadnej różnicy

rails plugin new test-plugin -T --mountable

i

rails plugin new test-plugin -T --full --mountable
Corey Innis
źródło
Może to dlatego, że --fullma pierwszeństwo --mountable?
Mankalas
8

Rozumiem różnicę, że silniki są jak wtyczki i dodają funkcjonalność do istniejących aplikacji. Chociaż aplikacje, które można montować, są zasadniczo aplikacjami i mogą istnieć samodzielnie.

Więc jeśli chcesz mieć możliwość uruchomienia go samodzielnie lub w innej aplikacji, stworzyłbyś aplikację do zamontowania. Jeśli chcesz, aby był on dodatkiem do istniejących aplikacji, ale nie działał samodzielnie, zrobiłbyś z niego silnik.

JDutil
źródło
2

Uważam, że różnica polega na tym, że aplikacje, które można montować, są odizolowane od aplikacji hosta, więc nie mogą udostępniać klas - modeli, pomocników itp. Dzieje się tak, ponieważ aplikacja do montażu jest punktem końcowym Rack (tj. ).

Uwaga: jak większość, dopiero co zacząłem bawić się Railsami 3.1.

Kris
źródło
Zgoda. Jedną rzeczą, która wydaje się dziwna, jest to, że domyślnie silnik udostępnia folder „modele”, ale aplikacja do montażu nie. Zastanawiam się, czy „najlepszą praktyką” byłoby posiadanie generatorów, które tworzą modele dla aplikacji zawierającej, ponieważ wygląda na to, że nie chciałbyś mieć żadnych migracji w silniku / urządzeniu mobilnym
Jeremy Raines