difference between scope and namespace of ruby-on-rails 3 routing - ruby-on-rails

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.

Related

Routes on Rails application are failing

I'm taking an MOOC and the goal of this exercise is to add a new functionality to typo, where i can merge two articles together.
When I add the route to my new function merge to the routes.rb I'm losing the functionality to delete articles. I think something clashes here, but I have no idea what.
The original routes.rb:
%w{advanced cache categories comments content profiles feedback general pages
resources sidebar textfilters themes trackbacks users settings tags redirects seo post_types }.each do |i|
match "/admin/#{i}", :to => "admin/#{i}#index", :format => false
match "/admin/#{i}(/:action(/:id))", :to => "admin/#{i}", :action => nil, :id => nil, :format => false
end
This method in articles.rb creates the correct url for deleting
def delete_url
blog.url_for(:controller => "/admin/content", :action =>"destroy",:id => id)
end
correct url:
http://example.com/admin/content/destroy/7
If i follow this link i can successfully delete an article.
However, if I add the following before that to my routes.rb:
namespace "admin" do
resources :content do
post :merge, on: :member, as: :merge
end
end
The new merging functionality and forms are working fine, but the method delete_url now produces something like this:
http://example.com/admin/content/7
and if I follow a link created by this method i get:
Unknown action
The action 'show' could not be found for Admin::ContentController
Maybe I'm overwriting something? I can't figure out what's happening here and why this affects the delete action / route.
Thanks in advance!
EDIT: rake routes | grep content:
with the original routes.rb gives me:
admin_content /admin/content {:controller=>"admin/content", :action=>"index"}
/admin/content(/:action(/:id)) {:action=>nil, :id=>nil, :controller=>"admin/content"}
whereas my modified routes.rb produces
merge_admin_content POST /admin/content/:id/merge(.:format) {:action=>"merge", :controller=>"admin/content"}
admin_content_index GET /admin/content(.:format) {:action=>"index", :controller=>"admin/content"}
POST /admin/content(.:format) {:action=>"create", :controller=>"admin/content"}
new_admin_content GET /admin/content/new(.:format) {:action=>"new", :controller=>"admin/content"}
edit_admin_content GET /admin/content/:id/edit(.:format) {:action=>"edit", :controller=>"admin/content"}
admin_content GET /admin/content/:id(.:format) {:action=>"show", :controller=>"admin/content"}
PUT /admin/content/:id(.:format) {:action=>"update", :controller=>"admin/content"}
DELETE /admin/content/:id(.:format) {:action=>"destroy", :controller=>"admin/content"}
/admin/content {:controller=>"admin/content", :action=>"index"}
/admin/content(/:action(/:id)) {:action=>nil, :id=>nil, :controller=>"admin/content"}
Okay, thanks to #guitarman i worked through my routes code and found out I can add the following except:
namespace "admin" do
resources :content, except: [:index, :show, :update, :destroy, :edit, :new, :create] do
post :merge, on: :member, as: :merge
end
end
after that, the rake routes just shows the additional merge route I wanted and my destroy action works fine again.
Check rake routes command. I think there is a route /admin/content/:id which will be created by resources :content in the namespace "admin".
Your request to http://example.com/admin/content/7 will be catched be the defined route but I gess you have no show action in the controller.
Better:
namespace "admin" do
post "/content/:id/merge", to: "admin/content#merge", as: :merge
end
For more information about recources and the CRUD operations please see the rails routing guide.

Rails routing url name helpers

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

No Route Matches error - with Nested Resource

I have the following Nested Resource in my routes.rb file
resource :issuer do
resources :certificates
end
My Models:
class Issuer < ActiveRecord::Base
has_many :certificates
end
class Certificate < ActiveRecord::Base
belongs_to :issuer
end
I get the following error when i visit: /issuer/2/certificates
No route matches {:action=>"edit", :controller=>"certificates"}
Any help would be appreciated.. Thanks in advance!
EDIT : MY rake routes
issuer_certificates GET /issuer/:issuer_id/certificates(.:format) {:action=>"index", :controller=>"certificates"}
POST /issuer/:issuer_id/certificates(.:format) {:action=>"create", :controller=>"certificates"}
new_issuer_certificate GET /issuer/:issuer_id/certificates/new(.:format) {:action=>"new", :controller=>"certificates"}
edit_issuer_certificate GET /issuer/:issuer_id/certificates/:id/edit(.:format) {:action=>"edit", :controller=>"certificates"}
issuer_certificate GET /issuer/:issuer_id/certificates/:id(.:format) {:action=>"show", :controller=>"certificates"}
PUT /issuer/:issuer_id/certificates/:id(.:format) {:action=>"update", :controller=>"certificates"}
DELETE /issuer/:issuer_id/certificates/:id(.:format) {:action=>"destroy", :controller=>"certificates"}
You should use resources (plural) instead of resource in refer to :issuer. So please try this:
resources :issuers do
resources :certificates
end
and issuers/2/certificates will work.
In your routes.rb file you are defining issuer as a singular resource
resource :issuer do # <----- using resource instead of resources
resources :certificates
end
With singular resources, the routes you get won't allow an :id to be matched, so in your example,
issuer/2/certificates
it's the "2" that's giving you grief. You should either get rid of it or define :issuer as a standard (non-singular) resource in your routes.rb file.
You don't have PUT defined for /issuer/2/certificates. instead you have it defined for /issuer/2/certificates/123. That's why you get that error.
If you are trying to edit the certificate then make sure you generate a correct link and include certificate_id.

scope equivalent in rails 2.3.x?

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.

Rails 2 Namespace and Shallow Routes Issue

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.

Resources