Is there a way to generate a group of routes under an admin scope without having to create a new physical directory (like namespace requires you to).
I know that in Rails 3 there is a scope method on the route mapper, and this appears to do what I want, but apparently it doesn't exist in Rails 2.3.x
My goal is to have a route like this: "/admin/products" map to "app/controllers/products_controller, not "app/controllers/admin/products_controller".
Is there any way to accomplish this in Rails 2.3.x?
Sure, you need to use :name_prefix and :path_prefix to get to what you want:
ActionController::Routing::Routes.draw do |map|
map.with_options :name_prefix => 'admin_', :path_prefix => 'admin' do |admin|
admin.resources :products
end
end
Will yield routes:
admin_products GET /admin/products(.:format) {:controller=>"products", :action=>"index"}
POST /admin/products(.:format) {:controller=>"products", :action=>"create"}
new_admin_product GET /admin/products/new(.:format) {:controller=>"products", :action=>"new"}
edit_admin_product GET /admin/products/:id/edit(.:format) {:controller=>"products", :action=>"edit"}
admin_product GET /admin/products/:id(.:format) {:controller=>"products", :action=>"show"}
PUT /admin/products/:id(.:format) {:controller=>"products", :action=>"update"}
DELETE /admin/products/:id(.:format) {:controller=>"products", :action=>"destroy"}
It appears to be not well documented, but namespace is actually a very simple wrapper for with_options. It sets the :path_prefix, :name_prefix, and :namespace options, of which I believe you only want the first, so:
map.with_options :path_prefix => 'admin/' do |admin|
admin.connect ':controller/:action'
end
I'm going through this from reading the code. It looks like :name_prefix is used to give named routes a prefix, and :namespace is used to actually look in subdirectories.
Related
What basic settings are required to make sure routing url name helpers work?
For instance in my route I have the following:
Blog::Application.routes.draw do
resources :news, :as => :news_items, :controller => :news_items, :only => [:show, :index]
scope :module => "refinery" do
scope(:path => 'refinery', :as => 'admin', :module => 'Admin') do
resources :news, :except => :show, :as => :news_items, :controller => :news_items
end
end
end
but the following doesn't seem to work:
new_refinery_news_url
I keep on getting the error
undefined local variable or method `new_refinery_news_url'
So I'm pretty sure something is missing in the way I have configured my application, who's main routing is in the RefineryCMS gem which was added in the Gemfile.
Any thoughts?
Had to use main_app.new_refinery_news_url instead.
The helper name will be new_admin_news_item_url.
It's simple to find all routes and their helper methods. Just run rake routes and you will see:
news_items GET /news(.:format) {:action=>"index", :controller=>"news_items"}
news_item GET /news/:id(.:format) {:action=>"show", :controller=>"news_items"}
admin_news_items GET /refinery/news(.:format) {:action=>"index", :controller=>"refinery/Admin/news_items"}
POST /refinery/news(.:format) {:action=>"create", :controller=>"refinery/Admin/news_items"}
new_admin_news_item GET /refinery/news/new(.:format) {:action=>"new", :controller=>"refinery/Admin/news_items"}
edit_admin_news_item GET /refinery/news/:id/edit(.:format) {:action=>"edit", :controller=>"refinery/Admin/news_items"}
admin_news_item PUT /refinery/news/:id(.:format) {:action=>"update", :controller=>"refinery/Admin/news_items"}
DELETE /refinery/news/:id(.:format) {:action=>"destroy", :controller=>"refinery/Admin/news_items"}
With mountable engines you always need to specify "main_app." (or for Refinery routes "refinery.") prefix because engines are isolated from the application.
A solution, if you're using routes outside of refinery, is to prefix the named_path with the Rails object that contains the methods for named routes
Rails.application.routes.url_helpers.new_admin_news_item_path
I've written the admin area of a particular rails app and now I'm ready to set it as own section within the website.
Therefore, it will be /admin
However, I didn't want to have it as /admin within the route itself I wanted to have something less common, so I added a couple of hyphens before and after it.
So the route is /-admin-/ and the namespace is Admin.
After setting this up using :path_prefix => "/-admin-", I have the following code block:
map.namespace "/-admin-/", :name_prefix => "", :path_prefix => "/-admin-" do |admin|
This works for all but shallow routes, instead, in the rake routes output the output is:
new_page GET /-admin-/areas/:area_id/pages/new(.:format) {:action=>"new", :controller=>"admin/pages"}
edit_admin_page GET /admin/pages/:id/edit(.:format) {:action=>"edit", :controller=>"admin/pages"}
admin_page GET /admin/pages/:id(.:format) {:action=>"show", :controller=>"admin/pages"}
PUT /admin/pages/:id(.:format) {:action=>"update", :controller=>"admin/pages"}
DELETE /admin/pages/:id(.:format) {:action=>"destroy", :controller=>"admin/pages"}
areas GET /-admin-/areas(.:format) {:action=>"index", :controller=>"admin/areas"}
POST /-admin-/areas(.:format) {:action=>"create", :controller=>"admin/areas"}
new_area GET /-admin-/areas/new(.:format) {:action=>"new", :controller=>"admin/areas"}
Notice how the shallow-routed routes are prefixed as /admin/ and not as /-admin-/ (as are their parent routes).
Any ideas on how to get around this? Is this a bug in rails or do I need to work around it? I tried adding the :path_prefix to each nested route but it doesn't do anything?
Any ideas?
I'm not sure about your rationale on not using /admin - security through obscurity isn't really security - you should be using something like authlogic to keep out unauthorised users.
Try the following to namespace your admin controllers:
map.namespace :admin, :path_prefix => "-admin-" do |admin|
admin.resources :users
admin.resources :pages
end
A sample generated route:
admin_users GET /-admin-/users(.:format) {:controller=>"admin/users", :action=>"index"}
There is no way to get around this. Turns out that all versions of Rails will break down the URL and its resource names to their lowest points when they're set to shallow. The only solution to this is to set all of your resource routes manually without using map.resources.
I have the following routes in my app:
GET /admin/comments(.:format) {:controller=>"admin/comments", :action=>"index"}
admin_comments POST /admin/comments(.:format) {:controller=>"admin/comments", :action=>"create"}
new_admin_comment GET /admin/comments/new(.:format) {:controller=>"admin/comments", :action=>"new"}
GET /admin/comments/:id(.:format) {:controller=>"admin/comments", :action=>"show"}
PUT /admin/comments/:id(.:format) {:controller=>"admin/comments", :action=>"update"}
admin_comment DELETE /admin/comments/:id(.:format) {:controller=>"admin/comments", :action=>"destroy"}
edit_admin_comment GET /admin/comments/:id/edit(.:format) {:controller=>"admin/comments", :action=>"edit"}
admin_approve_comment /admin/comments/approve/:id {:module=>"admin", :controller=>"admin/comments", :action=>"approve"}
admin_reject_comment /admin/comments/reject/:id {:module=>"admin", :controller=>"admin/comments", :action=>"reject"}
which is declared as:
namespace "admin" do
resources :comments
match '/comments/approve/:id' => 'comments#approve', :as => "approve_comment", :module => "admin"
match '/comments/reject/:id' => 'comments#reject', :as => "reject_comment", :module => "admin"
end
and a functional test like this:
context "a POST to :approve" do
setup do
comment = Factory(:comment)
sign_in Factory(:admin)
post :approve, :id => comment.id
end
should respond_with :success
end
However, when I run this I get:
test: a POST to :approve should respond with 200. (Admin::CommentsControllerTest):
ActionController::RoutingError: No route matches {:action=>"approve", :id=>339, :controller=>"admin/comments"}
What's wrong here? What stupid mistake am I making?
These routes look like member routes to me. So routing this way
namespace "admin" do
resources :comments do
member do
get :approve
get :reject
end
end
end
This will generate routes like /admin/comments/:id/approve . This is the rails way as far i know.
I think it's better to put match before resources. Because it's not check if it's good or not.
I can't understand what the difference is between a namespace and a scope in the routing of ruby-on-rails 3.
Could someone please explain?
namespace "admin" do
resources :posts, :comments
end
scope :module => "admin" do
resources :posts, :comments
end
The difference lies in the paths generated.
The paths are admin_posts_path and admin_comments_path for the namespace, while they are just posts_path and comments_path for the scope.
You can get the same result as a namespace by passing the :name_prefix option to scope.
examples always help me, so here is an example:
namespace :blog do
resources :contexts
end
will give us the following routes:
blog_contexts GET /blog/contexts(.:format) {:action=>"index", :controller=>"blog/contexts"}
POST /blog/contexts(.:format) {:action=>"create", :controller=>"blog/contexts"}
new_blog_context GET /blog/contexts/new(.:format) {:action=>"new", :controller=>"blog/contexts"}
edit_blog_context GET /blog/contexts/:id/edit(.:format) {:action=>"edit", :controller=>"blog/contexts"}
blog_context GET /blog/contexts/:id(.:format) {:action=>"show", :controller=>"blog/contexts"}
PUT /blog/contexts/:id(.:format) {:action=>"update", :controller=>"blog/contexts"}
DELETE /blog/contexts/:id(.:format) {:action=>"destroy", :controller=>"blog/contexts"}
Using scope...
scope :module => 'blog' do
resources :contexts
end
Will give us:
contexts GET /contexts(.:format) {:action=>"index", :controller=>"blog/contexts"}
POST /contexts(.:format) {:action=>"create", :controller=>"blog/contexts"}
new_context GET /contexts/new(.:format) {:action=>"new", :controller=>"blog/contexts"}
edit_context GET /contexts/:id/edit(.:format) {:action=>"edit", :controller=>"blog/contexts"}
context GET /contexts/:id(.:format) {:action=>"show", :controller=>"blog/contexts"}
PUT /contexts/:id(.:format) {:action=>"update", :controller=>"blog/contexts"}
DELETE /contexts/:id(.:format) {:action=>"destroy", :controller=>"blog/contexts"}
Here is some good reading on the subject: http://edgeguides.rubyonrails.org/routing.html#controller-namespaces-and-routing
from the rails guide
"The namespace scope will automatically add :as as well as :module and :path prefixes."
so
namespace "admin" do
resources :contexts
end
is the same as
scope "/admin", as: "admin", module: "admin" do
resources :contexts
end
Both scope and namespace are scoping a set of routes to the given default options.
Except that there are no default options for scope, and for namespace
:path, :as, :module, :shallow_path and :shallow_prefix options all default to the name of the namespace.
Available options for both scope and namespace correspond to those of match.
scope is bit complex, but provides more options to fine-tune exactly what you want to do.
scope supports three options: module, path and as. If you see scope with all it options, it will be exactly same as namespace.
In other words, routes generated by
namespace :admin do
resources :posts
end
is same as
scope module: 'admin', path: 'admin', as: 'admin' do
resources :posts
end
In other words, we can say that there are no default options for scope as compared to namespace. namespace add all these options by default. Thus using scope, we can more fine tune the routes as required.
If you take a deep look into scope and namespace default behaviour, you will find that scope by default supports only :path option, where as namespace supports three options module, path and as by default.
For more info, please refer a doc namespace-and-routing.
OK. This is insane.
I'm new to RoR and I really want to get into it as everything about it that I have seen so far makes it more appealing to the type of work that I do.
However, I can't seem to accomplish a very simple thing with RoR.
I want these controlers:
/admin/blog/entries (index/show/edit/delete)
/admin/blog/categories (index/show/edit/delete)
/admin/blog/comments (index/show/edit/delete)
... and so on
And these models:
Blog::Entry (table: blog_entries)
Blog::Category (table: blog_categories)
Blog::Comments (table: blog_comments)
... and so on
Now, I have already gone though quite a bit of misery to make this work. My first attempt was with generating scaffolding (I'm using 2.2.2). I generated my scaffolding, but had to move my model, then fix the references to the model in my controller (see Ruby on Rails model inside namespace can't be found in controller).
That is already a big of a pain, but hey, I got it to work. Now though form_for won't work and I cannot figure out how to use the url helpers (I have no idea what these are called... they are the automatically generated methods that return URLs to controllers associated with a model). I cannot figure out what their name is. My model is Blog::Entries. I have tried to mess with the route.rb's map's resource method, but no luck. When I attempt to use form_for with my model, I get this error
undefined method `blog_entries_path' for #<ActionView::Base:0xb6848080>
Now. This is really quite frustrating. I am not going to completely destroy my code's organization in order to use this framework, and if I cannot figure out how to accomplish this simple task (I have been researching this for at least 5 hours) then I simply cannot continue.
Are there any ideas on how to accomplish this?
Thanks
EDIT
Here are my routes:
admin_blog_entries GET /admin_blog_entries {:controller=>"admin_blog_entries", :action=>"index"}
formatted_admin_blog_entries GET /admin_blog_entries.:format {:controller=>"admin_blog_entries", :action=>"index"}
POST /admin_blog_entries {:controller=>"admin_blog_entries", :action=>"create"}
POST /admin_blog_entries.:format {:controller=>"admin_blog_entries", :action=>"create"}
new_admin_blog_entry GET /admin_blog_entries/new {:controller=>"admin_blog_entries", :action=>"new"}
formatted_new_admin_blog_entry GET /admin_blog_entries/new.:format {:controller=>"admin_blog_entries", :action=>"new"}
edit_admin_blog_entry GET /admin_blog_entries/:id/edit {:controller=>"admin_blog_entries", :action=>"edit"}
formatted_edit_admin_blog_entry GET /admin_blog_entries/:id/edit.:format {:controller=>"admin_blog_entries", :action=>"edit"}
admin_blog_entry GET /admin_blog_entries/:id {:controller=>"admin_blog_entries", :action=>"show"}
formatted_admin_blog_entry GET /admin_blog_entries/:id.:format {:controller=>"admin_blog_entries", :action=>"show"}
PUT /admin_blog_entries/:id {:controller=>"admin_blog_entries", :action=>"update"}
PUT /admin_blog_entries/:id.:format {:controller=>"admin_blog_entries", :action=>"update"}
DELETE /admin_blog_entries/:id {:controller=>"admin_blog_entries", :action=>"destroy"}
DELETE /admin_blog_entries/:id.:format {:controller=>"admin_blog_entries", :action=>"destroy"}
home / {:action=>"index", :controller=>"index"}
/:controller/:action/:id
/:controller/:action/:id.:format
That dosn't look right. Here is my routes.rb (comments removed):
ActionController::Routing::Routes.draw do |map|
map.resources :admin_blog_entries
map.home '', :controller => 'index'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
have you tried looking at the routes list that "rake routes" gives you? if your routes.rb is correct, it should show you the correct name for the blog entries route.
also, maybe this can help: http://www.coreywoodcox.com/2008/08/18/rails-namespaces-subdomains/.
edit:
well, then the correct way to call the route is admin_blog_entries_path instead of blog_entries_path.
Your routes.rb should look like this:
map.namespace :admin do |admin|
admin.namespace :blog do |blog|
blog.resources :entries
blog.resources :categories
...
end
end
but I'm not sure how to handle this '/blog/' part in your url (I didn't use any namespace in my models yet). But with these routes you will be able to use:
admin_blog_categories_path => '/admin/blog/categiries'
admin_blog_category_path(#some_category) => '/admin/blog/categories/1'
and so on.
OK, here my rather hacky way of doing it, which I don't like but does work.
In my case I have the models Blog::Article, Blog::Comment, they are nested in the routes. One caveat if using this approach is in the Blog::CommentsController when loading the article you can expect params[:article_id] or params[:blog_article_id]. By no means nice, but like I said. It does work :/
blog.resources :articles do |article|
article.resources :comments
end
blog.resources :blog_articles, :controller => 'articles' do |blog_article|
blog_article.resources :blog_comments, :controller => 'comments'
end