What does ":root" mean in the output of rake routes? - ruby-on-rails

When I run
rake routes
I see the following:
POST /articles/:article_id/comments(.:format) {:action=>"create", :controller=>"articles/comments"}
This makes perfect sense. It means if I make a post request to a url of the form /articles/1234/comments, it runs the create action of the controller in articles/comments_controller.rb with the id paramater set as 1234.
But then I see this line also:
/article/:id/:action {:root=>"article", :controller=>"article/article", :title=>"Article"}
And I'm not sure what the ":root" means. Can someone please explain?
EDIT:
I'm using Rails 2.3.18.
Here is the relevant line in the routes.rb file
#routes.rb
map.connect '/article/:id/:action', :controller => 'article/article', :root => 'article', :title => 'Article'

Like :title, it's just another key,value that gets merged into the params hash.
From http://rubydoc.info/docs/rails/2.3.8/ActionController/Routing ( Defaults routes and default parameters)
More formally, you can include arbitrary parameters in the route,
thus:
map.connect ':controller/:action/:id', :action => 'show', :page =>
'Dashboard'
This will pass the :page parameter to all incoming
requests that match this route.
It doesn't have any additional meaning in Rails. My guess is that your app is using it for breadcrumbs or something similar.

Related

Ruby on Rails routing for variable controller

I have a URL I want to be able to redirect to.
Something similar to:
"http://localhost:3000/username/admin/page".
I have a match in routes.rb as:
match ':account/admin/:page' => "admin#index"
I have redirect code:
redirect_to :controller => account.username, :action=>"admin", :page=>"index"
This, however comes up with a routing error:
No route matches {:action=>"admin", :controller=>"sdunn", :page=>"index"}
I know what I have done is wrong, but how can I fix this?
Many thanks.
Route is expecting 2 parameters, first one is :account, second is :page, i think you are only passing :page. I would add :as => 'some_name' to your route and then use _path :
routes.rb
match ':account/admin/:page' => "admin#index", :as => 'my_route'
controller:
redirect_to my_route_path(#user, #page)
my_route_path could be something different depending on your exact route file, so use
rake routes | grep my_route
to see exact name, then add _path to the end.

How to pass a default query param with Rails 2.3.x routing

I'm trying to do something trivial. I have a bunch of URLs that I need to map like the following:
http://example.com/foo
http://example.com/foo/something
Both need to go to the same controller/action. The problem I'm having is when http://example.com/foo is invoked, I need to specify a default query parameter. I thought that's what the :defaults hash does in routes.rb, but unfortunately the following doesn't work:
map.connect 'foo', :controller => 'something', :action => 'anaction',
:defaults => { :myparam => 'foobar' }
This should route http://example.com/foo to the something controller, anaction action, and make params[:myparam] point to the string "foobar".
I'm assuming for the second example http://example.com/foo/something, I'll need an additional route.
What's the best way to tackle this?
I wouldn't complicate things by adding such logic to my routes file, I'd just do it in my action:
params[:my_param] ||= 'foobar'
Untested, but:
map.connect 'foo', :controller => 'something', :action => 'anaction', :myparam => 'foobar'
It looks like the :controller and :action arguments in there are not in any way special, but just end up feeding into params. The 2.3.8 documentation seems to confirm this.
More formally, you can include
arbitrary parameters in the route,
thus:
map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard'
This will
pass the :page parameter to all
incoming requests that match this
route.

Is it possible to create a route like '~:id' in Rails 2.x?

I have been using the following route successfully in my Rails 2.x application:
map.user ':id', :controller => 'users', :action => 'show'
This, as my lowest route, properly catches things like /tsmango and renders Users#show.
I'm now trying to add a second, similar route like:
map.post '~:id', :controller => 'posts', :action => 'show'
Because neither my users or my posts are allowed to contain ~ and because this route will appear above my map.user route, I assumed this would properly catch any call starting with /~ and render my Posts#show action. Unfortunately, I'm having trouble getting this one to work.
What's interesting is that this similar route works perfectly:
map.post ':id~', :controller => 'posts', :action => 'show'
Although, I'm certainly willing to go with ':id~' since it has the same result, at this point I'm really just frustrated and curious as to how you would build a route that matches '~:id'.
It's worth mentioning that I do not want to modify my to_param method or my actual user and post slugs to include the prepended ~. I just want that in a route to indicate which action should handle it. Unless I'm mistaken, this rules out the use of something like:
:requirements => {:id => /\~[a-zA-Z0-9]/}
Thanks, in advance, for any help you can provide!
Update: I'm aware of route priority and stated above that I am placing the '~:id' route above the ':id' route. I receive the following error while trying to generate the url like post_path(#post):
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.to_sym
Routes are prioritized depending of the order in which they're declared.
When you define first the :id route, the second one is never executed.
In order for this to work, you just have to first define the ~:id route and then the :id one.
map.post '~:id', :controller => 'posts', :action => 'show'
map.post ':id', :controller => 'users', :action => 'show'

Rails routes matching query parameters

Rails routes are great for matching RESTful style '/' separated bits of a URL, but can I match query parameters in a map.connect config. I want different controllers/actions to be invoked depending on the presence of a parameter after the '?'.
I was trying something like this...
map.connect "api/my/path?apple=:applecode", :controller => 'apples_controller', :action => 'my_action'
map.connect "api/my/path?banana=:bananacode", :controller => 'bananas_controller', :action => 'my_action'
For routing purposes I don't care about the value of the parameter, as long as it is available to the controller in the params hash
The following solution is based on the "Advanced Constraints" section of the "Rails Routing from the Outside In" rails guide (http://guides.rubyonrails.org/routing.html).
In your config/routes.rb file, include a recognizer class have a matches? method, e.g.:
class FruitRecognizer
def initialize(fruit_type)
#fruit_type = fruit_type.to_sym
end
def matches?(request)
request.params.has_key?(#fruit_type)
end
end
Then use objects from the class as routing constraints, as in:
map.connect "api/my/path", :contraints => FruitRecognizer.new(:apple), :controller => 'apples_controller', :action => 'my_action'
Unless there is a concrete reason why you can't change this, why not just make it restful?
map.connect "api/my/path/bananas/:id, :controller => "bananas_controller", :action => "my_action"
If you have many parameters, why not use a POST or a PUT so that your parameters don't need to be exposed by the url?

How do I route user profile URLs to skip the controller?

Right now my user profile URLs are like so:
http://example.com/users/joeschmoe
And that points to the show method in the user controller.
What I'd ideally like to do is offer user profile URLs like this:
http://example.com/joeschmoe
So, what sort of route and controller magic needs to happen to pull that off?
I disagree with what jcm says about this. It's not a terrible idea at all and is used in production by the two biggest social networks Facebook and MySpace.
The route to match http://example.com/username would look like this:
map.connect ':username', :controller => 'users', :action => 'show'
If you want to go the subdomain route and map profiles to a URL like http://username.example.com/, I recommend using the SubdomainFu plugin and the resulting route would look like:
map.root :controller => 'users', :action => 'show' , :conditions => {:subdomain => /.+/}
These broad, catch all routes should be defined last in routes.rb, so that they are of lowest priority, and more specific routes will match first.
I also recommend using a validation in your User model to eliminate the possibility of a user choosing a username that will collide with current and future routes:
class User < ActiveRecord::Base
validates_exclusion_of :username, :in => %w( messages posts blog forum admin profile )
…
end
This does not make sense unless you have no controllers. What happens when you want to name a controller the same as an existing user? What if a user creates a username the same as one of your controllers? This looks like a terrible idea. If you think the /user/ is too long try making a new custom route for /u/
So your custom route would be...
map.connect 'u/:id', :controller => 'my/usercontroller', :action => 'someaction'
In routes.rb this should do the trick:
map.connect ":login", :controller => 'users', :action => 'show'
Where login is the name of the variable passed to the show method. Be sure to put it after all other controller mappings.
Well, one thing you need is to ensure that you don't have name collisions with your users and controllers.
Once you do that you, can add a route like this:
map.connect ':username', :controller => 'users', :action => 'show'
Another thing people have done is to use subdomains and rewrite rules in the web server, so you can have http://joeshmoe.example.com
In Rails 4 to skip controller from url you have to do add path: ''.
resources :users, path: '' do
end

Resources