Rails 3.1: Engine vs. Aplikasi yang Dapat Dipasang

120

Bisakah seseorang membantu saya memahami perbedaan antara Rails Engine dan aplikasi Mountable? Di Rails 3.1, Anda dapat membuat salah satunya dengan perintah "rails new plugin _ __ ".

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

Kapan Anda ingin menggunakan yang satu versus yang lain? Saya tahu Anda dapat mengemas Engine sebagai permata, untuk satu. Bukankah itu kasus Aplikasi Mountable? Apa perbedaan lain yang ada?

Jeremy Raines
sumber

Jawaban:

143

Saya telah memperhatikan yang berikut:

Mesin Penuh

Dengan mesin penuh, aplikasi induk mewarisi rute dari mesin. Tidak perlu menentukan apa pun di parent_app/config/routes.rb. Menentukan gem di Gemfile sudah cukup bagi aplikasi induk untuk mewarisi model, rute, dll. Rute mesin ditetapkan sebagai:

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

Tidak ada namespacing model, pengontrol, dll. Ini segera dapat diakses oleh aplikasi induk.

Mesin yang Dapat Dipasang

Namespace mesin diisolasi secara default:

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

Dengan mesin yang dapat dipasang, rute diberi ruang nama dan aplikasi induk dapat memaketkan fungsionalitas ini dalam satu rute:

# 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 

Model, pengontrol, dll diisolasi dari aplikasi induk - meskipun pembantu dapat dibagikan dengan mudah.

Inilah perbedaan utama yang saya lihat. Mungkin ada yang lain? Saya telah bertanya di sini , tetapi belum menerima tanggapan.

Kesan saya adalah karena mesin penuh tidak mengisolasi dirinya dari aplikasi induk, paling baik digunakan sebagai aplikasi mandiri yang berdekatan dengan aplikasi induk. Saya yakin bentrokan nama bisa terjadi.

Mesin yang dapat dipasang dapat digunakan dalam situasi di mana Anda ingin menghindari konflik nama dan menggabungkan mesin di bawah satu rute tertentu di aplikasi induk. Misalnya, saya sedang membangun mesin pertama saya yang dirancang untuk layanan pelanggan. Aplikasi induk dapat menggabungkan fungsionalitasnya dalam satu rute seperti:

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

Jika asumsi saya salah, seseorang harap beri tahu saya dan saya akan memperbaiki tanggapan ini. Saya telah membuat artikel kecil tentang subjek di sini Cheers!

astjohn
sumber
1
Bisakah mesin yang dapat dipasang dirutekan / dipasang di root aplikasi induk?
Slick23
3
@Justin Anda bisa mencoba mount MyEngine::Engine => "/". Ini berfungsi untuk sumber daya, mungkin itu juga kasus untuk mesin.
Benoit Garret
2
@astjohn Ringkasan yang bagus dari blog Anda. Tapi bukankah sebaliknya? Apakah Full Engine akan "tidak lengkap" dan memerlukan aplikasi induk agar berfungsi, sedangkan Mountable Engine dapat bekerja sendiri, karena "terisolasi" dari aplikasi induk?
Theo Scholiadis
39

Kedua opsi tersebut akan menghasilkan mesin . Perbedaannya adalah itu --mountableakan membuat mesin dalam ruang nama yang terisolasi, sedangkan --fullakan membuat mesin yang berbagi ruang nama dari aplikasi utama.

Perbedaan tersebut akan diwujudkan dalam 3 cara:

1) File kelas mesin akan memanggil 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) File mesin config/routes.rbakan memiliki namespace:

Mesin penuh:

Rails.application.routes.draw do
end

Mesin terpasang:

MyMountableEngine::Engine.routes.draw do
end

3) Struktur file untuk pengontrol, pembantu, tampilan, dan aset akan diberi spasi nama:

buat app / controllers / my_mountable_engine /application_controller.rb
buat app / helpers / my_mountable_engine /application_helper.rb
buat app / mailer buat aplikasi / model
buat app / views / layouts / my_mountable_engine /application.html.erb
buat app / assets / images / my_mountable_engine
buat app / assets / stylesheets / my_mountable_engine /application.css
buat app / assets / javascripts / my_mountable_engine /application.js
buat config / routes.rb buat lib / my_mountable_engine.rb
buat lib / tugas / my_mountable_engine.rake
buat lib / my_mountable_engine.rake buat lib / my_mountable_engine .rb
buat lib / my_mountable_engine / engine.rb


Penjelasan

Kasus penggunaan untuk --fullopsi tersebut tampaknya sangat terbatas. Secara pribadi saya tidak dapat memikirkan alasan bagus mengapa Anda ingin memisahkan kode Anda ke dalam mesin tanpa mengisolasi namespace juga- Ini pada dasarnya hanya akan memberi Anda dua aplikasi yang digabungkan erat yang berbagi struktur file yang identik dan semua konflik dan kebocoran kode itu memerlukan.

Setiap bagian dari dokumentasi yang pernah saya lihat menunjukkan --mountableopsinya, dan memang panduan tepi saat ini sangat mendorong Anda untuk memasukkannya isolate namespace- yang sama dengan mengatakan penggunaan --mountableberulang --full.

Akhirnya ada kebingungan terminologi: Sayangnya rails plugin -hmenunjukkan deskripsi berikut:

[--full] # Hasilkan mesin rel dengan aplikasi Rails yang dibundel untuk pengujian
[--mountable] # Hasilkan aplikasi terisolasi yang dapat dipasang

Ini memberi kesan yang Anda gunakan --fulluntuk membuat "mesin" dan --mountableuntuk membuat sesuatu yang lain yang disebut "aplikasi yang dapat dipasang", padahal sebenarnya keduanya adalah mesin - satu namespace dan satu tidak. Itu pasti akan menimbulkan kebingungan karena pengguna yang ingin membuat mesin kemungkinan akan berasumsi bahwa itu --fulladalah opsi yang lebih relevan.

Kesimpulan

  • rails plugin new something --full= Mesin di ruang nama aplikasi Anda. (Mengapa kamu akan?)
  • rails plugin new something --mountable= Mesin dengan namespace itu sendiri. (Luar Biasa)

Referensi

Yarin
sumber
9
Ada satu alasan bagus untuk menggunakan --full: jika Anda memiliki bagian dari situs web rel, Anda ingin tetap terintegrasi (bukan di ruang nama yang terisolasi) dan masih berbagi di antara proyek rel yang berbeda. Juga bisa lebih sederhana dari itu: mungkin permata Anda tidak menambahkan sebanyak itu, tetapi Anda ingin bisa mengaitkannya dengan benar.
nathanvda
2
@nathanvda - Benar, tapi saya pikir jika Anda berbagi sesuatu di banyak proyek, itu benar - benar harus menggunakan namespace, karena pada dasarnya Anda menggunakannya sebagai plugin
Yarin
Saya pikir Anda mungkin ingin menggunakan --full jika Anda ingin mengisolasi file Anda, namespace situs panggilan Anda ketika Anda melakukannya Admin::AdminService.some_actiontetapi tidak harus mengubah rute Anda jika aplikasi sisi klien lain seperti aplikasi Ember menggunakan rute yang terkait dengan kode Anda ingin mengisolasi. --full sepertinya merupakan langkah perantara yang mungkin lebih mudah diterapkan.
Jwan622
Saat ini saya sedang mengerjakan aplikasi internasional yang perlu menangani peraturan khusus negara, tetapi masih memperlihatkan antarmuka yang sama ke dunia. Saya punya satu contoh "Inti" per negara, tidak perlu menangani semuanya sekaligus. "Mesin negara" sendiri tidak masuk akal, jadi penggabungan dengan aplikasi "inti" tidak menjadi masalah. Namun, saya tidak ingin mereka berada di namespace mereka sendiri karena aplikasi inti tidak boleh tahu di negara mana aplikasi tersebut beroperasi. Saya merasa mesin "penuh" lebih seperti mengatur file dan kelas Anda secara modular, tetapi tetap mempertahankan "monolit" Anda.
Mankalas
17

saya bertanya-tanya hal yang sama dan, karenanya, berakhir di sini. menurut saya jawaban sebelumnya pada dasarnya mencakup pertanyaan, tetapi saya pikir yang berikut mungkin juga membantu:

# 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

yang menarik (bagi saya) adalah kenyataan bahwa tidak ada perbedaan antara

rails plugin new test-plugin -T --mountable

dan

rails plugin new test-plugin -T --full --mountable
Corey Innis
sumber
Mungkin itu karena --fulllebih diutamakan --mountable?
Mankalas
8

Pemahaman saya tentang perbedaannya adalah bahwa mesin itu seperti plugin, dan menambahkan fungsionalitas ke aplikasi yang ada. Sementara aplikasi yang dapat dipasang pada dasarnya adalah sebuah aplikasi, dan dapat berdiri sendiri.

Jadi, jika Anda ingin menjalankannya sendiri atau dalam aplikasi lain, Anda akan membuat aplikasi yang dapat dipasang. Jika Anda bermaksud menjadikannya sebagai tambahan untuk aplikasi yang sudah ada, tetapi tidak berjalan dengan sendirinya, Anda akan menjadikannya sebagai mesin.

JDutil
sumber
2

Perbedaannya, saya yakin, adalah bahwa aplikasi yang dapat dipasang diisolasi dari aplikasi host, sehingga mereka tidak dapat berbagi kelas - model, pembantu, dll. Ini karena aplikasi yang dapat dipasang adalah titik akhir Rack (yaitu aplikasi Rack dengan sendirinya ).

Penafian: Saya, seperti kebanyakan, baru saja mulai bermain-main dengan Rails 3.1.

Keris
sumber
Sepakat. Satu hal yang tampak aneh adalah bahwa secara default, Engine memberi Anda folder "model", tetapi Aplikasi yang Dapat Dipasang tidak. Saya ingin tahu apakah "praktik terbaik" adalah memiliki generator yang membuat model untuk aplikasi yang disertakan, karena sepertinya Anda tidak ingin melakukan migrasi apa pun di engine / moutable
Jeremy Raines