I'm using Rails 4.2 with Rails Engine to handle my admin panel. I want to move the engine code to the main rails app and part of doing that is moving the routes.
For now, my routes in the Engine my-web-app/lib/admin/config/routes.rb is something like:
Admin::Engine.routes.draw do
resources :countries, only: [:index]
end
And at my app's routes config/routes.rb:
Rails.application.routes.draw do
.. some routes ...
mount GlobalAdmin::Engine => "/admin"
end
Now the engine's route for countries is countries_url NOT admin_countries_url but for URL you'll need to access it with something like admin/countries (this is the default behavior for the engine and I want to keep it this way).
What I did is that I moved my-web-app/lib/admin/config/routes.rb to my-web-app//config/routes.admin.rb and made it look something like:
Rails.application.routes.draw do
resources :countries, only: [:index]
end
And then in my config/application.rb I added something like this:
config.paths["config/routes.rb"] = [
Rails.root.join("config/routes.admin.rb"),
Rails.root.join("config/routes.rb")
]
The problem with this approach is that I have countries_url, however, I can't access it with admin namespace URL like admin/countries. If I add namespace admin something like:
Rails.application.routes.draw do
namespace :admin do
resources :countries, only: [:index]
end
end
Then I'll need to refer to countries url with admin_countries_url which is not the behavior I need.
Any help of how to move the routes from the engine without affecting the previous engine routes?
I'm aware that routes inside an engine are isolated from the application by default. The application and its engines can have routes with the same names, so integrating both can cause some issues, however I want to preserve the exact routes if possible and not to add a namespace and stick with the exact routes names.
Use scope instead of namespace:
Rails.application.routes.draw do
scope path: "/admin" do
resources :countries
end
end
max#MaxBook ~/p/sandbox> rails routes
Prefix Verb URI Pattern Controller#Action
countries GET /admin/countries(.:format) countries#index
POST /admin/countries(.:format) countries#create
new_country GET /admin/countries/new(.:format) countries#new
edit_country GET /admin/countries/:id/edit(.:format) countries#edit
country GET /admin/countries/:id(.:format) countries#show
PATCH /admin/countries/:id(.:format) countries#update
PUT /admin/countries/:id(.:format) countries#update
DELETE /admin/countries/:id(.:format) countries#destroy
Related
I have a basic Page model in Rails that I'm using with FriendlyId to allow admins to create pages like "/about" or "/contact".
I have the following in my routes file (config/routes.rb) to ensure that the slugs for each page appear at the root of the site, such as https://example.com/about, etc:
resources :pages, except: [:show]
resources :pages, only: [:show], path: "/"
The problem is, with this approach, I can't use the normal named route like page_path(#page) in my views(or tests or controllers for that matter) because that routes to "/pages/about" and I get a "No route matches [GET] pages/about" error.
I could do both routes in my routes file so that "/pages/about" and "/about" work like this:
resources :pages
resources :pages, only: [:show], path: "/"
But, that creates an SEO duplicate content problem. I suppose I could create a helper that sets the rel="canonical" url for each page in the html header, but that also feels like a hack. I'd prefer for there to just be 1 version of each page at the root and also have a named route such as "page_path" or "root_page_path" that I can use throughout my app.
The hack I've come up with for the time being is <%= link_to "#{page.slug}" %>, but not having a named route seems very brittle.
What is a more "correct" way to do this in Rails?
I expected something like this to work:
resources :pages, only: [:show], path: "/", as: "page"
But that doesn't work either. Nothing in the Rails guide on routing is really helping either.
You need top switch the order of their definitions:
resources :pages, only: [:show], path: "/"
resources :pages, except: [:show]
resources only give name to the first path with given url. However - you will have the problem now as the pages/:id path (for delete and update) has now no route helper (as it is normally the same as show).
EDIT: As mentioned in the comment - it will also automatically match /pages path to a show action with id equal to pages - not a great idea! Which leads to better option:
resources :pages, except: [:show]
get :id, to: "pages#show", as: :root_page
Which gives you root_page_path(#page) helper for :show action and page_path(#page) for :update and :delete
I have a blog with root
root 'posts#index'
And works best with example.com/ to example.com/posts
But what I want is something like this:
example.com/blog/posts/1.
I've tried creating blog Controller and add
resources :blog do
resources :posts
end
But this is making my routes to blog/:id/posts/:id
If you don't have the relationship between the post and the blog as you mentioned, rails gives you the freedom to declare routes as our own.
so, to make the route example.com/posts/1 to, example.com/blog/posts/1, just add a custom route at the last.
get '/blog/posts/:id', to: :show, controller: 'posts'
what this does is over rides the previous route and make this route final.
Now type rake routes and it will give the last route for you as,
GET /blog/posts/:id(.:format) posts#show
Now you can access using,
example.com/blog/posts/1
Reference for rails routing
Just to expand upon #Sravan answer. If you have multiple routes that will start with /blog/ you might want to check Rails guide on routing.
You can add something along the lines of
scope '/blog' do
resources :posts
resources :users
resources :images
end
Which will create corresponding routes under /blog/.
namespace :blog do
resources :posts
resources :users
resources :images
end
And your controller with namespace will look like this: Blog::PostsController
I am trying to place my tasks into my dashboard/admin routes, for example rather than todos/, todos/new, etc. I would like dashboard/todos/, dashboard/todos/new etc. etc.
I have tried to do that here like so;
namespace :dashboard do
resources :todos
end
Though this also changes the controller, which I don't want - I only want to nest the todos inside of the dashboard controller
Can anyone point me in the right direction please?
You can use scope or path as described in the Rails Guides
scope '/dashboard' do
resources :todos
end
or
resources :todos, path: '/dashboard/todos'
This will generate the routes with the path /dashboard/todos which maps to todos_controller
If you want to route /admin/todos to TodosController you can use scopeinstead of namespace:
scope '/admin' do
resources :todos
end
You could check the rails documentations about this point : http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing
I have the following routes.rb:
resources :users
namespace :vmt do
resources :dashboards do
resources :users
resources :evaluation_units
resources :orga_units
end
end
I want to set the user in an overall context and nested in a single dashboard context within a namespace. The users-Controller is not in the namespace. So when I open the path /vmt/dashboards/1/users in browser, I get the following Routing Error
uninitialized constant Vmt::UsersController
So how can I specify, that in this resource
namespace :vmt do
resources :dashboards do
resources :users
that the controller is not in a namespace? I tried to set the controller explecitly with
resources :users, controller: 'user'
but it's still in the vmt namespace.
Using scopes will point rails to the proper url, but does not seem to provide the same useful route url helpers. We can, however, use / to point to the 'top level' controller.
Say you have two routes we want to display the users on:
/users and /admin/users
resources: users
namespace :admin do
resources :users, controller: '/users' # 'users' alone would look for a '/admin/users_controller'
end
With this, we can continue to use the url helper admin_users_path
(Note: Not a rails expert, there may be a way to create url helpers for scopes, or some other solution. Above tested on rails 5.2)
My original answer didn't work in the end, once you're inside a namespaced scope within a route you can't get out anymore.
The easiest way to re-use your logic is to create a Vmt::UsersController like so:
class Vmt::UsersController < ::UsersController
end
You can specify a different module with the module key.
For example:
resources :users, module: nil
Edit: I'm not 100% sure if this will work inside a namespace. If not, you can change it to a scope, and add the module explicitly to the other resources.
I'm trying to set up routes for a mobile API, which should have a versioned api-path. I already could make the mobile Auth work, which is implemented in a separate Controller AuthController located in /controllers/api/v1/mobile/.
Usage example:
myapp.com/api/v1/mobile/auth
But now I want to register my existing ressources-Controllers to this path-pattern as additional api-routes. Concrete: this would be the TasksController located at /controllers/tracker/tasks_controller.rb. So I added a mobile route to the routes-definition:
# routes.rb
namespace :tracker, path: 'timetracking' do
resources :tasks, 'jobs'
end
namespace :api do
namespace :v1 do
namespace :mobile do
resources :auth, :only => [:create, :destroy]
namespace :tracker do #added mobile route
resource :tasks, controller: 'tracker/tasks', as: :mobile_tasks
end
end
end
end
But when I call myapp.com/api/v1/mobile/tracker/tasks it results in an error-message:
Routing Error
uninitialized constant Api::V1::Mobile::Tracker
I especially added the alias :mobile_tasks to this route, to avoid any conflicts with the original tasks-route above. Any ideas, how to set the controller properly for this route?
Update#1
Defining this route as a scope instead of a namespace, didn't work aswell.
scope "/api/v1/mobile/tracker" do
resources :tasks, controller: 'tracker/tasks', as: :mobile_tasks
end
But this time, it didn't even resolve the route-path itself.
Routing Error
No route matches [GET] "/api/v1/mobile/tracker/tasks"
I assume it might be a problem, that my additional mobile-api route tries to point to a completely different namespace tracker.
According to http://guides.rubyonrails.org/routing.html#controller-namespaces-and-routing you should use scope instead of namespace.
If you want to route /admin/posts to PostsController (without the Admin:: module prefix), you could use:
scope "/admin" do
resources :posts, :comments
end
Adding this answer to get clarity on namespace & scope.
When you use namespace, it will prefix the URL path for the specified resources, and try to locate the controller under a module named in the same manner as the namespace.
# config/routes.rb
namespace :admin do
resources :posts, only: [:index]
end
# rake routes
Prefix Verb URI Pattern Controller#Action
admin_posts GET /admin/posts(.:format) admin/posts#index
When we add scope, it will just map the controller action for the given scope patterns. No need to define controller under any module.
# config/routes.rb
scope :admin do
resources :posts, only: [:index]
end
# rake routes
Prefix Verb URI Pattern Controller#Action
admin_posts GET /admin/posts(.:format) posts#index
Note that, controller is just posts controller without any module namespace.
If we add a path option to scope it will map to the controller with the path option specified as follows
# config/routes.rb
scope module: 'admin', path: 'admin' do
resources :posts, only: [:index]
end
# rake routes
Prefix Verb URI Pattern Controller#Action
admin_posts GET /admin/posts(.:format) admin/posts#index
Note that the controller now is under admin module.
Now, if we want to change the name of path method to identify resource, we can add as option to scope.
# config/routes.rb
namespace module: 'admin', path: 'admin', as: 'root' do
resources :posts, only: [:index]
end
# rake routes
Prefix Verb URI Pattern Controller#Action
root_posts GET /admin/posts(.:format) admin/posts#index
You can see the change in the Prefix Verb.
Hope it helps others.
Late answer, but still might be helpful:
scope '/v1' do
resources :articles, module: 'v1'
end
controller
# app/controller/v1/articles_controller.rb
class V1::ArticlesController < ApplicationController
end
Now you should be able to access this url:
http://localhost:3000/v1/articles