Rails: Mapping Conflicting Routes by Detecting Param - ruby-on-rails

Hey! I am trying to set up routes in a Rails application so that, depending on the type of parameter passed, Rails sends the request to a different action.
I have courses which have an attribute state which is a string with a two letter state abbreviation. When a user visits /courses/1, I want Rails to display the show action in the courses controller (and pass the parameter as :id). When a user visits /courses/CO though, I want Rails to display the index action and pass the parameter as :state.
So /courses/1 would be equivalent to
:controller => 'courses', :action => 'show', :id => '1'
And /courses/CO would be equivalent to
:controller => 'courses', :action => 'index', :state => 'CO'
I have tried this:
map.resources :courses, :except => { :index, :show }
map.connect 'courses/:state', :controller => 'courses', :action => 'index', :state => /[A-Z]{2}/
map.connect 'courses/:id', :controller => 'courses', :action => 'show', :id => /[0-9]+/
But it breaks (the rails server wont even start). I don't usually do things like this with routes, so I am outside of my know-how. Thanks!
Edit: Fixed a typo, thanks JC.
Current solution looks like this:
map.resources :courses, :except => [ :index, :show ]
map.courses '/courses', :controller => 'courses', :action => 'index', :state => 'AL', :method => :get
map.courses '/courses/:state', :controller => 'courses', :action => 'index', :requirements => { :state => /[A-Z]{2}/ }, :method => :get
map.course '/courses/:id', :controller => 'courses', :action => 'show', :requirements => { :id => /[0-9]+/ }, :method => :get

This works, but you will need to go edit all your links to the index to say things like courses_path('AA') and you won't be able to use some of the nice helpers, like form_for, which assume you are following the convention that #create is simply #index with a POST request. (Get comfortable with form_tag)
ActionController::Routing::Routes.draw do |map|
map.resources :courses, :except => [ :index, :show ]
map.courses '/courses/:state', :controller => 'courses', :action => 'index', :requirements => { :state => /[A-Z]{2}/ } , :method => :get
map.course '/courses/:id', :controller => 'courses', :action => 'show', :requirements => { :id => /[0-9]+/ } , :method => :get
end
It will keep your routes named the same, though.
(by the way, your /co does not match your regex, which requires upper case chars)
Fun aside: Do we really need the abstraction of a router? http://blog.peepcode.com/tutorials/2010/rethinking-rails-3-routes

I'm afraid this won't work since the structure that maps paths to controllers and actions is setup on start of the rails application, parameter handling happens at request time.
What you could do is to match the :id-parameter in the show-action of the CoursesController against a list of valid states and then either redirect or render a different action.
Hope this helps.

Related

How to routes to a specific controller with a variable value ahead of it in rails 5?

Normally its like:
resources :users
To keep a value ahead we can do like
scope :url do
resources :users
end
but, issue is that I have the just variable in front of the routes not a fixed value with few conditions for it.
Example in rails 2. and want to convert the routing accoding to rails5
map.with_options :controller => 'users' do |user|
user.forgot_user ':url/users/forgot', :action => 'forgot', :url => /([a-zA-Z0-9\-]*)/
user.user ':url/users/retrieve', :action => 'retrieve', :url => /([a-zA-Z0-9\-]*)/
user.login ':url/users/login', :action => 'login', :url => /([a-zA-Z0-9\-]*)/
user.logout ':url/users/logout', :action => 'logout', :url => /([a-zA-Z0-9\-]*)/
user.new_user ':url/users/new', :action => 'new', :conditions => { :method => :get }, :url => /([a-zA-Z0-9\-]*)/
user.users ':url/users/:id', :action => 'show', :conditions => { :method => :get }, :url => /([a-zA-Z0-9\-]*)/
end
Thanks in advance.
scope "/(:subdomain)", :defaults => {:subdomain => "default"} do
...
end
use subdomain as a variable.

How to implement different http method match different action with the url defined by myself based on Rails` REST

Now,I have:
map.resources :questions, :collection => {
:query_by_student => :get
}
that matches:
query_by_student_questions GET /questions/query_by_student(.:format)
{:controller=>"questions", :action=>"query_by_student"}
I want add a "POST" request,to the same url:/questions/query_by_student,but the action should be "post_by_student"
waht shall I do?
怎么在Rails的REST基础上对自己定义的url,实现不同http方法对应不同action?
我现在有这:
map.resources :questions, :collection => {
:query_by_student => :get
}
对应路由信息:
query_by_student_questions GET /questions/query_by_student(.:format)
{:controller=>"questions", :action=>"query_by_student"}
我想加一个post请求,同样到/questions/query_by_student,但是action为post_by_student
怎么写?
I don't have a testing app but with rails 2.2, you can try
map.resources :questions, :collection => {
:query_by_student => [:get, :post]
}
and see what it looks like.
Otherwise, you can probably do it manually with something like
map.connect '/questions/query_by_student', :conditions => { :method => :get }, :controller => 'questions', :action => 'query_by_student'
map.connect '/questions/query_by_student', :conditions => { :method => :post }, :controller => 'questions', :action => 'post_by_student'

Rails: Routing subdomain to a resource

Is it possible to map a subdomain to a resource? I have a company model. Currently, using subdomain_fu, my routing file contains:
map.company_root '', :controller => 'companies', :action => 'show',
:conditions => { :subdomain => /.+/ }
My Company model contains a "subdomain" column.
Whilst this works as intended, it's a named route and isn't restful. Essentially, I need to map "name.domain.com" to the show action for the companies controller. Is a named route the way to go, or can I use a resource route?
One can pass conditions to a resource route as well as a named route. In an application I am involved with everything is scoped to an account. A :before_filter loads the account using the subdomain. Thus for resources scoped to an account, we want to scope the routes to urls with subdomains. The DRY way to do this is to use map with options:
map.with_options :conditions => {:subdomain => /.+/} do |site|
site.resources :user_sessions, :only => [:new, :create, :destroy]
site.resources :users
site.login 'login', :controller => "user_sessions", :action => "new"
site.logout 'logout', :controller => "user_sessions", :action => "destroy"
…
end
map.connect 'accounts/new/:plan', :controller => "accounts", :action => "new"
map.resources :accounts, :only => [:new, :create]
As you can see a named route will accept a conditions hash with a subdomain too. You can also adopt the approach Ryan illustrated above or you can specify conditions on a per resource basis:
map.resources :users, :conditions => {:subdomain => /.+/}
I don't know of a way to do this with map.resources. It does accept a :conditions option but I'm not sure how to remove the /companies/ portion of the URL. However, map.resources is primarily a convenient way to generate a bunch of named routes, which you can do manually. Something like this.
map.company '', :controller => 'companies', :action => 'show', :conditions => { :subdomain => /.+/, :method => :get }
map.new_company 'new', :controller => 'companies', :action => 'new', :conditions => { :subdomain => /.+/, :method => :get }
map.edit_company 'edit', :controller => 'companies', :action => 'edit', :conditions => { :subdomain => /.+/, :method => :get }
map.connect '', :controller => 'companies', :action => 'create', :conditions => { :subdomain => /.+/, :method => :post }
map.connect '', :controller => 'companies', :action => 'update', :conditions => { :subdomain => /.+/, :method => :put }
map.connect '', :controller => 'companies', :action => 'destroy', :conditions => { :subdomain => /.+/, :method => :delete }
Untested, but it should get you close.
Here's a complete example implementation of Rails 3 subdomains with authentication (along with a detailed tutorial). It's much easier to do this in Rails 3 than in Rails 2 (no plugin required).
Using the resource linked from Daniel's answer, in Rails 3 the way to route '/' to a different controller depending on the subdomain is as follows:
match '/' => 'somecontroller#action', :constraints => { :subdomain => 'yoursubdomain' }

Rails routing for namespaced resource deletion

I have a page model and a pages_controller within an admin namespace. My routes file looks like this:
map.resources :pages, :only => [:index,:show]
map.resources :admin, :only => [:index]
map.namespace :admin do |admin|
admin.resources :pages
end
I am not able to figure out the correct method to create a link for deleting a page (In the same way the scaffold generator generates a delete link on the index page).
Any ideas on the correct parameters for the link_to function?
TIA,
Adam
rake routes is your friend here. It'll spit out the list of your generated routes - particularly useful if you have a bunch of nested or custom routes.
the paths will be
admin_pages_path #(with GET) routes to :controller => 'admin/pages', :action => 'index'
admin_pages_path #(with POST) routes to :controller => 'admin/pages', :action => 'create'
new_admin_page_path #(with GET) routes to :controller => 'admin/pages', :action => 'new'
edit_admin_page_path(:id) #(with GET) routes to :controller => 'admin/pages', :action => 'edit'
admin_page_path(:id) #(with GET) routes to :controller => 'admin/pages', :action => 'show'
admin_page_path(:id) #(with PUT) routes to :controller => 'admin/pages', :action => 'update'
admin_page_path(:id) #(with DELETE) routes to :controller => 'admin/pages', :action => 'delete'
Your link_to for delete should therefore be:
<%= link_to("delete page", admin_page_path(#page), :confirm => "sure you want to delete this page?", :method => :delete) %>
Note that Rails will work its magic calling to_param on #page, so that you don't have to specify #page.id - useful for an example like this as you often want to use permalinks for 'pages'.

Problem with link_to_remote in rails

I'm having a consistency problem using link_to_remote in rails.
I have 2 use cases of link_to_remote, and they generate different ajax. I can't figure out why, and it is driving me crazy.
Here is use case one...
<%= link_to_remote "section-", :update => "sections", :url => {:action => :destroy, :controller => "sections", :id => #section.id } %>
This generates the appropriate ajax (as below) and works as I expect. Note that it picks up the :action param from the call and inserts it correctly in the ajax.
section-
I also have another instance where I use link_to_remote, but it generates incorrect ajax. The use case is nearly identical, except the controller is different. Either way, I wouldn't expect that to result in different ajax.
The call...
<%= link_to_remote "question-", :update =>"questions-1", :url => {:action => :destroy, :controller => "questions", :id => #question.id} %>
The resulting ajax...
question-
The obvious difference here is in the second arg to Ajax.Updater. The :action param is missing from that path. Why? This results in broken code for me, but I can't understand why this is happening. The link_to_remote calls are nearly identical.
Please point me in the right direction. Thanks.
Below is my routes.rb file...
ActionController::Routing::Routes.draw do |map|
map.resources :questions, :has_one => :section, :collection => { :sort => :post }
map.resources :sections, :has_many => :questions, :has_one => :form, :collection => { :sort => :post }
map.resources :forms, :has_many => :sections
# You can have the root of your site routed with map.root -- just remember to delete public/index.html.
map.root :controller => "forms"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
What do you get if you copy this into your view?
<%= url_for :action => :destroy, :controller => :questions, :id => #question.id %>
I suspect the problem's not really with link_to_remote, but with routing. Once url_for is returning the URL you expect, then try link_to_remote again.
Simply adding a :method => :delete to your link_to_remote call may be the simplest fix for you:
<%= link_to_remote "question-", :update =>"questions-1", :url => {:action => :destroy, :controller => "questions", :id => #question.id}, :method => :delete %>
This should force the call to /questions/:id to use the HTTP DELETE method. If you want the above call to generate the url /questions/destroy/:id instead I believe you would need a manual change to your routes, as the default map.resources doesn't seem to be achieving that result.

Resources