Engine routes in Application Controller - ruby-on-rails-3

I have a before_filter hook in my main app's application controller that does something like: (It doesn't just put a link in the flash, there is a message, but it isn't relevant to the question, it just accesses the route in the method)
class ApplicationController < ActionController::Base
before_filter :set_link
def set_link
flash[:notice] = items_path
end
end
This works fine for the app, however when I go into the controllers for an engine I made I get the exception
No route matches {:controller=>"items", :action=>"index"}
I understand that when in the engine, the routes helpers are for the engine unless prefixed with main_app
So changing the method in the application controller to
def set_link
flash[:notice] = main_app.items_path
end
Gets rid of the exception but I really don't want to have to do that. Is there another solution to getting the engine to recognize the main_app routes?
EDIT:
This also happens if the application layout calls path helpers. So if the engine is designed to integrated into the main_app's layout then this issue will crop there up too.

Mountable engines are designed to work like this, that is isolate the main app routes and the engine routes.
If you want the two sets of routes to be merged, you can use a non-isolated engine. The first step is removing the isolated_namespace method call in your engine definition:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine # remove this line
end
end
The second step is to convert your routes in my_engine/config/routes.rb, you should go from this:
MyEngine::Engine.routes.draw do
# stuff that routes things
end
to this:
Rails.application.routes.draw do
# stuff that routes things
end
and remove the mount method call in your application's routes:
App::Application.routes.draw do
mount MyEngine::Engine => "/engine" # remove this line
end
The main advantages of doing it this way would be:
No need to monkey-patch rails. I know devise does this, but this could be a leftover from the days when engines didn't exist in rails.
No need to mount the engine in the application routes. On the other hand, this could backfire if you'd like to control more precisely the insertion point as all you engine routes would be called after (or before, I don't have the answer to this question) your main routes.
If you're looking for documentation on engines, the rails docs for the Engine class are a pretty good starting point. I'd strongly recommend that you read them (in case you haven't yet) if you're interested in the subject.

I figured out how to do this. The problems lies within the isolated namespace. In order to integrate the engine with the app and share the same layout (which may have path helpers from the main app) I did this:
Firstly I removed config/routes.rb from the engine
Then I removed the isolate_namespace from the engine class
module MyEngine
class Engine < Rails::Engine
- isolate_namespace MyEngine
end
end
end
I added a file that was loaded in the engine:
module ActionDispatch::Routing
class Mapper
def mount_my_engine_at(mount_location)
scope mount_location do
#Declare all your routes here
end
end
end
end
Finally, in the main app's config/routes.rb instead of 'mount'ing the engine, you can call your method
mount_my_engine_at "mount_location"
This will basically 'mount' your engine as part of the main app instead of being isolated from it. It is similar to how Devise does it too.

You can keep the isolate_namespace.
In your engine routes.rb
MyEngine::Engine.routes.draw do
...
root to: "something#index"
end
Rails.application.routes.draw do
get "something", to: "my_engine/something#index"
end
And then in the main app routes.rb
Rails.application.routes.draw do
mount MyEngine::Engine => "/anything_you_want"
root to: "main#index"
end
This way you can choose what routes you want to expose (and which you do not)

You can keep the isolate_namespace, as strongly recommended by Rails Engine guide, and do this:
# inside your main_app's config/routes.rb
Rails.application.routes.draw do
root to: 'home#index'
mount MyEngine::Engine, at: "/"
end
# inside your engine's controller
module MyEngine
class SomeController << ::ApplicationController
# include main_app's route helpers
helper Rails.application.routes.url_helpers
end
end
And inside your gem, make sure all the url helpers are prefixed with the correct routing proxy method (e.g. my_engine.pages_path).
Your main_app's layout and engine's controller will route and link to those url helpers correctly to the main app. You don't have to add "main_app" prefix anywhere to the main app. The only downside is you're mounting your engine's routes at main_app's root path, which could collide with any routes by the same name. This is expected anyway if you were to do non-isolate_namespace.

The easiest way is to draw the routes in both the main app, and the engine, so that they are accessible to both:
[MyEngine::Engine, App::Application].each do |app|
app.routes.draw do
# Declare all your routes here
end
end

Related

Configure routes with post method in rails

I am creating a web service in rails application and I need to configure routes with respect to "books/CreateBookItem" with post method.
But since I will not have a view page for this to create a book item, for testing purpose I am typing the url in the browser as
localhost:3000/books/CreateBookItem
But now when I type the above url in browser it is taking as GET request.
So how do I configure the routes for the above.
Add this createbookitem action to books resources in routes.rb:
resources books
collection do
post :createbookitem
end
end
Don't want to have any view for this action then create createbookitem action with disabled render.
class BooksController < ApplicationController
def createbookitem
#do your stuffs
render nothing: true
end
end

Removing url scope segment with url helpers

I'm writing an app that makes use of AngularJS, so the app is setup to route all requests to the main home page where angular takes over. All routes are then defined within an api scope which angular uses to retrieve the data. It's setup though, that if the user navigates to a page with a normal URL, then when it redirects to the home page, it maintains that URL which angular then uses to load the correct state.
What I now want to do, is be able to use URL helpers within rails to generate my URL's, but not have the generated URL's include the /api of the scope. Is there any way I can get around this?
routes.rb looks a bit like
scope "/api", shallow_path: "/api" do
... normal stuff here ...
end
And if I try using one of the helpers,
meeting_url(#meeting, subdomain: "test")
the url it generates is
http://test.domain.com/api/meetings/1
Is there a way I can have it strip the /api?
I don't believe there's a built-in way to do it.
But, you're in ruby, so there are plenty of ways to do what you want.
One way to go, since you're in your own app, is do monkey-patch String:
class String
def no_api
self.gsub(/\/api/, '')
end
end
meeting_url(#meeting, subdomain: 'test').no_api #=> http://test.domain.com/meetings/1
If you find that distasteful, you can define a method on ApplicationController or in a helper module:
def no_api(url)
url.gsub(/\/api/, '')
end
etc. etc. etc.

Isolated engine (doorkeeper) - use helper methods from the main_app

I want my doorkeeper views to use the application layout:
https://github.com/applicake/doorkeeper/wiki/Customizing-views
This contains routes and helper methods from the main application.
For the routes, I can prefix main_app to the path but for the helper method I get the following error:
undefined method `is_active?' for #<ActionDispatch::Routing::RoutesProxy:0xade808c>
<li class="<%= main_app.is_active?("high_voltage/pages", "api") %>"><%= link_to t('developers'), page_path('api') %></li>
Why is this? The helper is in app/helpers/application_helper.rb
If you generated the views and they are placed in app/views/doorkeeper/** then the engine still uses doorkeeper controllers.
To fix this, you have to include your helper(s) into the engine's ApplicationController. Let's say you have something like this:
app/helpers/application_helper.rb
module ApplicationHelper
def my_helper
"hello"
end
end
app/views/doorkeeper/applications/index.html.erb
<p>
<%= my_helper %>
</p>
This won't work until you include your application helpers into doorkeeper controllers. So in config/application.rb:
class YourApp::Application < Rails::Application
config.to_prepare do
# include only the ApplicationHelper module
Doorkeeper::ApplicationController.helper ApplicationHelper
# include all helpers from your application
Doorkeeper::ApplicationController.helper YourApp::Application.helpers
end
end
this is similar configuration when you want to customize the layout.
A helper method in application_helper.rb would not be a method for main_app.
The main_app variable is an object with a class/module of ActionDispatch::Routing::RoutesProxy.
main_app is a helper that gives you access to your application routes. main_app.page_path('api'), for example.
I'm assuming, with doorkeeper, you need to access the path you want; main_app.highvoltage_page_path('api').some_doorkeeper_active_method
This should hopefully, at least, send you in the right direction, see also:
http://edgeapi.rubyonrails.org/classes/Rails/Engine.html#label-Using+Engine%27s+routes+outside+Engine
Good luck.
Well, this question is very old, but I ran into exactly the same problem, and I have a solution. The one requested on Felipe Elias Philipp's answer.
You need to do a little "overwrite". Copy the doorkeepers' engine application_controller.rb file to your app into app/doorkeeper/. Then just change
module Doorkeeper
class ApplicationController < ActionController::Base
to
module Doorkeeper
class ApplicationController < ::ApplicationController
Which now makes doorkeeper use your ApplicationController which probably will have all the methods you need. Together with https://github.com/doorkeeper-gem/doorkeeper/wiki/Customizing-views everything works splendidly.

Render engine within application layout

Background
I am creating a application that is made up of a core and several modules. The modules are rails engines, and provide the actual functionality as the core itself only acts as a host.
The engines are hosted from /lib and mounted at their respective paths.
coreApp
└──lib
├── module1
├── module2
└── etc
The modules are then mounted like this
mount Module1::Engine => "/module1", :as => "module1"
mount Module2::Engine => "/module2", :as => "module2"
The core is also responsible for handeling the session, although the login itself is done by a module.
Problem
I have yet to find a great way of sharing the core application layout with the engines. As of now, this is how I make the layout available to the engines:
coreApp
└── app
└── views
└── layouts
├── application.html.erb
└── core.html.erb
The file core.html.erb only contains
<%= render :template => 'layouts/application' %>
Is is then included in each module like this
module Module1
class ApplicationController < ActionController::Base
layout "core"
end
end
Although it isn't particularly elegant, it works fine, and the content of the module is rendered where the yield statement in the application layout.
The problems are as follows:
1. Module specific stylesheets are not included in the header
I need a way to include the stylesheets of the active module.
2. The header needs access to information about the logged in user
The header contains information about the logged in user, like
Logged in as <%= #user[:realname] %>
This comes from the cores home_controller
def index
#user = User.find_by_id(session[:user])
end
But when I try to access the module, I get the following error
NoMethodError in Module1/home#index
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
Obviously referring to #user.
Question
How can this be solved in as elegantly and DRY as possible without too much tampering on the engine side?
I have Googled this a lot but can't really get my head around how to solve it. It might be total lack of insight in how rails works, so there is a good chance this question doesn't even make sense for someone that knows rails well.
Please comment if anything is unclear or ambiguous, and I'll try to elaborate.
I have successfully used layouts of my parent application in my engines. Firstly, based on Section 4.3.2 of the Rails Guides (Engines), in order to access the parent applications ApplicationController's variables (like session, as you're using above), you need to replace the engine's application_controller.rb from this that you currently have:
module Module1
class ApplicationController < ActionController::Base
layout "core"
end
end
to this:
class Module1::ApplicationController < ::ApplicationController
end
This will inherit the parent application's ApplicationController, along with all it's variables.
Secondly, you'll need to delete the file app/views/layouts/application.html.erb from your engine views, as it will not be needed since you're using the parent application's one.
Now when you render a view of Module1 from the parent application, the layout of the parent application will be used, and all the session[] variables will be evaluated correctly.
Do not forget to add the words "main_app." before each link in your layouts, otherwise it will try and look for the paths in the engine instead of the parent application. For example, if the layout in the parent application includes a link to some_path (that is a view in the parent application), when showing a view in the engine that uses this layout will try and look for some_path in the Engine instead of the parent application. You will need to change the link to main_app.some_path for it to work.
Hope this helps.
Use layout 'layouts/application'
And if you don't want to use main_app.your_path you can also add:
module YourEngine
module ApplicationHelper
def method_missing(method, *args, &block)
if (method.to_s.end_with?('_path') || method.to_s.end_with?('_url')) && main_app.respond_to?(method)
main_app.send(method, *args)
else
super
end
end
end
end
I also did the same thing in my application. All I did was:
Delete the engine layout in /app/view/layouts/
Change your application_controller to
module EngineModule
class ApplicationController < ::ApplicationController
layout 'layouts/application'
end
end
In your views, if you want to refer to any path such as login_path, it can be referred via main_app.login_path

Rails 3.2 Mountable App locale getting duplicated

I am seeing a scenario where my locale parameter is getting duplicated once I step into a mountable app. My app looks like so:
#routes.rb
resources :blogs
scope "(:locale)" do
mount Auth::Engine => '/auth'
end
If I am at a blogs page and look at an auth link it looks like this /en-us/auth/signout which works well, but as soon as I load into any page rendered by the mountable app, for example the profile page /en-us/auth/myprofile', the sign out link now looks like /en-us/auth/signout?locale=en-us
I don't want the querystring getting cluttered, what is causing the app to correctly set the locale parameter and then duplicate it in the querystring?
my bet is that your application controller has something like:
def set_locale
# code here
end
def default_url_options
# code here
end
Since your engine controller inherits from application controller, set_locale may be triggered twice.
Potential Solution: Check if the code is in an engine, then don't trigger locale setting.
This can be done with: self.controller_path().split("/").first == engine_name
I had the same problem. I was setting the default url options on the controller (by redefining default_url_options or by using self.default_url_options=).
The solution was to use Rails.application.routes.default_url_options = instead.