Defined routes in engine:
# /myengine/config/routes.rb
Myengine::Engine.routes do
resources :articles
endMount engine to the main app:
# config/routes.rb
Rails.application.routes.draw do
# app routes
#
mount Myengine::Engine => "/myengine", :as => "myengine"
endEngine's pages will have URL prefix 'myengine/'. For example, URL for articles page from Engine:
http://mysite.com/myengine/articles
Access engine's route from the main app view:
# use the name specified in mount in 'as': mount Myengine::Engine => "/myenginepath", :as => "myengine"
= link_to 'Articles from Engine', myengine.articles_path
# this will try to find route defined in the main app, NOT from engine
= link_to 'Articles', articles_pathfrom any place in app (in controller, or in a class in lib/ )
Myengine::Engine.routes.url_helpers.articles_pathroutes defined in app:
# config/routes.rb
Rails.application.routes.draw do
resources :users
# mount engine
mount Myengine::Engine => "/", :as => "myengine"
endAccess app's routes from engine's view:
# myengine/app/views/somedir/someview.html.haml
= link_to 'Users', main_app.users_path
If you want the two sets of routes to be merged, you can use a non-isolated engine.
Removing the isolated_namespace in the engine definition:
# myengine/lib/myengine/engine.rb
module Myengine
class Engine < Rails::Engine
#isolate_namespace Myengine # remove this line
...
end
endDefine routes in Engine with Rails.application.routes.draw, NOT Myengine::Engine.routes.draw:
# myengine/config/routes.rb
Rails.application.routes.draw do
resources :articles
endRemove mount in the main app:
App::Application.routes.draw do
#mount Myengine::Engine => "/myengine" # remove this line
endNow you can access routes from app and engine just using:
= link_to 'Articles', articles_path
Find more in discussion on stackoverflow.
## Improving (Extending or overriding) Engine functionalityA common task after including Engine in your Rails app is extending some classes (models, controllers, other classes) defined in the Engine.
It can be done using Decorator pattern.
There are two options of extending a class defined in Engine:
- use Class@class_eval
- use ActiveSupport::Concern
For simple class modifications, use Class#class_eval.
For complex class modifications, consider using ActiveSupport::Concern.
Read more in Rails guides.
# lib/myengine/engine.rb
module Myengine
class Engine < ::Rails::Engine
isolate_namespace Myengine
config.to_prepare do
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
require_dependency(c)
end
end
end
endin the Engine:
# Blorgh/app/models/article.rb
class Article < ActiveRecord::Base
has_many :comments
def summary
"#{title}"
end
endin the main app:
# MyApp/app/decorators/models/blorgh/article_decorator.rb
Myengine::Article.class_eval do
# add new method
def time_since_created
Time.current - created_at
end
# override the method
def summary
"#{title} - #{truncate(text)}"
end
endWe have a class defined in Engine:
# myengine/lib/myengine/mymodule/myclass.rb
module Myengine
module Mymodule
class Myclass
include Myengine::Concerns::Mymodule::Myclass
end
end
end
Add concern to Engine:
# myengine/lib/concerns/mymodule/myclass.rb
module Myengine::Concerns::Mymodule::Myclass
extend ActiveSupport::Concern
included do
end
def my_object_method
'it is engine'
end
module ClassMethods
# will be overridden in the main app
def my_class_method
[]
end
end
end
Use concern in the main app:
# myapp/lib/myengine/mymodule/myclass.rb
module Myengine
module Mymodule
class Myclass
include Myengine::Concerns::Mymodule::Myclass
# override class method
def self.my_class_method
['1', '2', '3']
end
# override object method
def my_object_method
'this is app'
end
end
end
end