Rails 3.1: Engine vs. Mountable App


120

Ai đó có thể giúp tôi hiểu sự khác biệt giữa Rails Engine và ứng dụng có thể gắn được không? Trong Rails 3.1, bạn có thể tạo một trong hai bằng lệnh "rails new plugin _ __ ".

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

Khi nào bạn muốn sử dụng cái này so với cái kia? Tôi biết bạn có thể đóng gói một Engine như một viên ngọc quý. Đó không phải là trường hợp của Ứng dụng có thể gắn kết? Có những điểm khác biệt nào?

Câu trả lời:


143

Tôi đã nhận thấy những điều sau:

Động cơ đầy đủ

Với một động cơ đầy đủ, ứng dụng mẹ kế thừa các tuyến từ động cơ. Nó không cần thiết phải chỉ định bất cứ điều gì trong parent_app/config/routes.rb. Chỉ định gem trong Gemfile là đủ để ứng dụng mẹ kế thừa các mô hình, tuyến đường, v.v. Các tuyến động cơ được chỉ định như:

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

Không có vùng chứa tên của mô hình, bộ điều khiển, v.v. Chúng có thể truy cập ngay vào ứng dụng mẹ.

Động cơ gắn được

Không gian tên của công cụ được cô lập theo mặc định:

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

Với một công cụ có thể gắn kết, các tuyến được đặt ở vị trí tên và ứng dụng mẹ có thể gói chức năng này theo một tuyến:

# 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 

Mô hình, bộ điều khiển, v.v. được cách ly khỏi ứng dụng mẹ - mặc dù có thể chia sẻ dễ dàng các trình trợ giúp.

Đây là những khác biệt chính mà tôi đã phát hiện ra. Có lẽ có những người khác? Tôi đã hỏi qua đây , nhưng vẫn chưa nhận được phản hồi.

Ấn tượng của tôi là vì một công cụ đầy đủ không tự cô lập chính nó khỏi ứng dụng mẹ, nó tốt nhất được sử dụng như một ứng dụng độc lập liền kề với ứng dụng mẹ. Tôi tin rằng có thể xảy ra đụng độ tên tuổi.

Một công cụ có thể gắn được có thể được sử dụng trong các tình huống mà bạn muốn tránh xung đột tên và gói công cụ theo một tuyến cụ thể trong ứng dụng mẹ. Ví dụ: tôi đang làm việc để xây dựng động cơ đầu tiên được thiết kế cho dịch vụ khách hàng. Ứng dụng gốc có thể gói chức năng của nó theo một tuyến như:

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

Nếu tôi đang chệch hướng trong các giả định của mình, ai đó vui lòng cho tôi biết và tôi sẽ sửa câu trả lời này. Mình có làm một bài viết nhỏ về chủ đề này Cheers!


1
Có thể định tuyến / gắn kết một công cụ có thể gắn ở gốc của ứng dụng mẹ không?
Slick 23

3
@JustinM bạn có thể thử mount MyEngine::Engine => "/". Nó hoạt động cho tài nguyên, có lẽ đó cũng là trường hợp cho động cơ.
Benoit Garret

2
@astjohn Bản tổng hợp tuyệt vời về các blog của bạn. Nhưng sẽ không phải là ngược lại? Liệu một Full Engine có phải là "chưa hoàn thiện" và cần ứng dụng mẹ để hoạt động hay không, trong khi Mountable Engine có thể hoạt động độc lập, vì nó được "cách ly" với ứng dụng mẹ?
Theo Scholiadis

39

Cả hai tùy chọn sẽ tạo ra một động cơ . Sự khác biệt là nó --mountablesẽ tạo engine trong một vùng tên riêng biệt, ngược lại --fullsẽ tạo một engine chia sẻ vùng tên của ứng dụng chính.

Sự khác biệt sẽ được thể hiện theo 3 cách:

1) Tệp lớp công cụ sẽ gọi 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) Tệp của công cụ config/routes.rbsẽ có không gian tên:

Động cơ đầy đủ:

Rails.application.routes.draw do
end

Động cơ gắn:

MyMountableEngine::Engine.routes.draw do
end

3) Cấu trúc tệp cho bộ điều khiển, trình trợ giúp, chế độ xem và nội dung sẽ được đặt ở vùng tên:

tạo ứng dụng / controllers / my_mountable_engine /application_controller.rb
tạo ứng dụng / helpers / my_mountable_engine /application_helper.rb
tạo ứng dụng / người gửi thư tạo ứng dụng / mô hình
tạo ứng dụng / views / layouts / my_mountable_engine /application.html.erb
tạo ứng dụng / tài sản / hình ảnh / my_mountable_engine
tạo ứng dụng / tài sản / stylesheets / my_mountable_engine /application.css
tạo ứng dụng / tài sản / javascripts / my_mountable_engine /application.js
tạo cấu hình / tuyến đường.rb
tạo lib / my_mountable_engine.rb tạo lib / nhiệm vụ / my_mountable_engine.rake
tạo lib / my_mountable .rb
tạo lib / my_mountable_engine / engine.rb


Giải trình

Trường hợp sử dụng cho --fulltùy chọn dường như rất hạn chế. Cá nhân tôi không thể nghĩ ra bất kỳ lý do chính đáng nào tại sao bạn muốn tách mã của mình thành một công cụ mà không tách riêng không gian tên- Về cơ bản, nó sẽ chỉ cung cấp cho bạn hai ứng dụng kết hợp chặt chẽ chia sẻ cấu trúc tệp giống nhau và tất cả các xung đột và rò rỉ mã điều đó đòi hỏi.

Mỗi phần tài liệu tôi đã xem đều thể hiện --mountabletùy chọn và thực sự là hướng dẫn biên hiện tại rất khuyến khích bạn nên đưa isolate namespacevào - điều này cũng giống như nói sử dụng --mountablehết --full.

Cuối cùng, có sự nhầm lẫn thuật ngữ: Thật không may rails plugin -hcho thấy các mô tả sau:

[--full] # Tạo công cụ đường ray với ứng dụng Rails đi kèm để thử nghiệm
[--mountable] # Tạo ứng dụng riêng biệt có thể gắn kết

Điều này tạo ấn tượng rằng bạn sử dụng --fullđể tạo một "động cơ" và --mountableđể tạo ra một thứ khác được gọi là "ứng dụng có thể gắn được", trong khi thực tế chúng là cả hai động cơ - một có chỗ đặt tên và một thì không. Điều đó chắc chắn dẫn đến sự nhầm lẫn vì người dùng đang tìm cách tạo một công cụ có thể sẽ cho rằng đó --fulllà tùy chọn phù hợp hơn.

Phần kết luận

  • rails plugin new something --full= Động cơ trong không gian tên ứng dụng của bạn. (Tại sao bạn?)
  • rails plugin new something --mountable= Động cơ với không gian tên riêng của nó. (Tuyệt vời)

Người giới thiệu


9
Có một lý do chính đáng để sử dụng --full: nếu bạn có các phần của trang web đường ray mà bạn muốn tích hợp (không phải trong không gian tên riêng biệt) và vẫn chia sẻ giữa các dự án đường ray khác nhau. Ngoài ra, nó có thể đơn giản hơn thế: có thể đá quý của bạn không thêm nhiều như vậy, nhưng bạn muốn có thể móc nó vào một cách chính xác.
nathanvda

2
@nathanvda - Đúng vậy, nhưng tôi nghĩ rằng nếu bạn đang một cái gì đó chia sẻ trên nhiều dự án đó thực sự nên được namespaced, bởi vì bạn đang về cơ bản sử dụng nó như một plugin
Yarin

Tôi nghĩ bạn có thể muốn sử dụng --full nếu bạn muốn cô lập các tệp của mình, không gian tên các trang web cuộc gọi của bạn khi bạn làm vậy Admin::AdminService.some_actionnhưng không phải thay đổi các tuyến đường của bạn nếu các ứng dụng phía máy khách khác như ứng dụng Ember sử dụng các tuyến đường liên quan đến mã bạn muốn cô lập. --full có vẻ như là một bước trung gian có thể dễ thực hiện hơn.
Jwan622,

Tôi hiện đang làm việc trên một ứng dụng quốc tế cần xử lý các quy định của quốc gia cụ thể, nhưng vẫn để lộ giao diện tương tự với thế giới. Tôi có một phiên bản "Core" cho mỗi quốc gia, không cần phải xử lý tất cả cùng một lúc. Các "công cụ quốc gia" không có ý nghĩa riêng, vì vậy việc kết hợp với ứng dụng "cốt lõi" không phải là một vấn đề. Tuy nhiên, tôi không muốn chúng ở trong không gian tên riêng của chúng vì ứng dụng cốt lõi không được biết nó hoạt động ở quốc gia nào. Tôi cảm thấy một công cụ "đầy đủ" giống như sắp xếp các tệp và lớp của bạn theo cách mô-đun, nhưng vẫn giữ nguyên "nguyên khối" của bạn.
Mankalas

17

tôi đã tự hỏi như vậy và do đó, kết thúc ở đây. đối với tôi dường như các câu trả lời trước đó về cơ bản bao hàm câu hỏi, nhưng tôi nghĩ những điều sau đây cũng có thể hữu ích:

# 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

mối quan tâm đặc biệt (đối với tôi) là thực tế là không có sự khác biệt giữa

rails plugin new test-plugin -T --mountable

rails plugin new test-plugin -T --full --mountable

Có lẽ đó là vì --fullđã được ưu tiên hơn --mountable?
Mankalas

8

Sự khác biệt của tôi là công cụ giống như các plugin và thêm chức năng vào các ứng dụng hiện có. Mặc dù các ứng dụng có thể gắn kết về cơ bản là một ứng dụng và có thể đứng độc lập.

Vì vậy, nếu bạn muốn có thể chạy nó một mình hoặc trong một ứng dụng khác, bạn sẽ tạo một ứng dụng có thể gắn được. Nếu bạn có ý định cho nó là một phần bổ sung cho các ứng dụng hiện có, nhưng không chạy bởi chính nó, bạn sẽ biến nó thành một động cơ.


2

Tôi tin rằng sự khác biệt là ứng dụng có thể gắn kết được tách biệt với ứng dụng chủ, vì vậy chúng không thể chia sẻ các lớp - mô hình, trình trợ giúp, v.v. Điều này là do ứng dụng có thể gắn kết là một điểm cuối Rack (tức là ứng dụng Rack theo đúng nghĩa của nó ).

Tuyên bố từ chối trách nhiệm: Tôi, giống như hầu hết, chỉ mới bắt đầu chơi với Rails 3.1.


Đã đồng ý. Tuy nhiên, có một điều có vẻ lạ là theo mặc định, Engine cung cấp cho bạn một thư mục "models", nhưng Mountable App thì không. Tôi tự hỏi nếu "thực hành tốt nhất" sẽ có máy phát điện để tạo ra mô hình cho các ứng dụng bao gồm cả, vì nó có vẻ như bạn sẽ không muốn có bất kỳ di cư trong công cụ / moutable
Jeremy Raines
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.