I've recently joined the world of Rails app development (Rails3) and I may be abusing resourceful routing.
The default resourceful routing makes some really convenient helper methods for the URLs which I use constantly. My problem is that I have controllers that I specified the routing as resourceful simply to take advantage of those helper methods. I have some basic site navigation that has no business with resources.
resource :home do
member do
get 'main'
get 'about'
get 'login'
get 'help'
end
end
Is there a better way to do what I've been doing? Anything that doesn't require that I manually add routing entries each time I have a new controller action?
Just to clarify, I want to specify routing for a controller without having to explicitly add any new actions but I also want it to auto-generate helper methods. So far, I have to explicitly add routes for each action I want that for. I can get something similar by doing this (in a non-resourceful way),
match 'home/about' => 'home#about'
But I don't want to have to write that very every route that doesn't fall into the convention.
Here's another simpler one. Just add a generic route to the bottom of your routes.rb
match ":controller/:action"
and it will map directly to the specified action of the specified controller. You can be a bit more specific if you like. For example, using get instead of match to restrict to HTTP GET requests, specifying the applicaple controllers etc.
get ":controller/:action", :constraints => { :controller => /home|help/ }
You can look into your controller for public instance methods and generate routes automatically.
# routes.rb
HomeController.public_instance_methods(false).select{|m| !(m.to_s =~ /^_/)}.each do |m|
match "home/#{m}", :action => m, :controller => HomeController, :as => "home_#{m}"
end
This will take the explicit(non-inherited) public instance methods from your controller, and select the ones that don't begin with an underscore(because underscored ones are generated methods for filters, the rest are actual actions). Then it will generate a named route for each.
Keep in mind that routes.rb is processed only at server startup so you will have to restart the server after you add new actions.
Related
I have a situation where there are lots of models and I am using STI in rails to help minimize the creation of so many models that share similar attributes.
However, I am trying to dynamically generate routes without having to duplicate controller logic.
I am using url_for(controller: controller_name, action: :show) or whatever the url should be. However, I would simply like these routes to be listed in the routes file and not have to create controller files for each. But looks like url_for expects the actual controller file to be created. Otherwise, it would generate the wrong url (its generating some auth/failure url, which, I have no idea why it is doing). How can I make it pick up the route in the routes file and not have a separate controller file?
I would take a look at Rails Routing and become familiar with resources.
I believe you could use the one controller for the different models. So in your routes.rb:
resources :sti_model1, :controller => 'sti_controller'
resources :sti_model2, :controller => 'sti_controller'
resources :sti_model3, :controller => 'sti_controller'
I have a very simple question.
Trying to figure out what is the simplest way to route the custom action in rails 3.
Let's say i have controller UsersController and action promote_to_premium
Nor
http://localhost:3000/users/#{user_id}/promote_to_premium
neither
http://localhost:3000/users/promote_to_premium/#{user_id}
works.
Should I specify in routes.rb every custom action that differs from new/delete/update/create/ect/....?????
Thank You.
Yes you need to specify in your routes.rb.
Example:
resources :users do
member do
post :promote_to_premium
end
end
This way you can access the route like this:
http://localhost:3000/users/#{user_id}/promote_to_premium
You should use this in routes.rb:
match "/users/:id/promote_to_premium" => "users#promote_to_premium"
You should mention the route in routes.rb file for custom methods in the controller.
You can specify the routes using either get"" or a match""=>"" or a "post"
when you write get "controller/something" something should be an action(method) called by the name "something" in your controller. But in your case you cannot use get"controller/:id" as there is no ":id" method in your controller. So, you should match your controller/:id to some 'action' in your controller.
Hence you need to write
"match users/:id/promote_to_premium"=> "users#promote_to_premium"
But if you are writing something into the database then you should use 'post'. From whatever i know, i think you can try
match 'users/:id/promote_to_premium' => 'users#promote_to_premium', :via => :post
You can study more about routes in the below link:
http://guides.rubyonrails.org/routing.html
Yes you need to specify every route. Actually you define the normal routes too with the resource command.
There is a specific wildcard command to allow access of any action, but it is only for debug purposes, because it allows access to actions you may not want to be accessible:
match ':controller(/:action(/:id(.:format)))'
I'm looking to "automatically" generate routes based on actions defined in a controller. resources routing (as far as I know) only automatically generates routes & helpers for http verbs. In this instance I'm serving mostly static pages using rails and have no need for http verbs in my controller.
Specifically:
In a controller I have defined actions which refer to those mostly static pages.
def action
end
In the routes file I have a bunch of
match "/url" => 'controller#action'
I'd like all those matched routes to be generated automatically based on the actions in the controller. Something CONCEPTUALLY along the lines of:
for actions in controller.erb do |action|
'match "/action" => "controller#action"
end
Is this possible? Would I write the code in the routes file directly?
I also have some nested actions to consider... a controller action may be:
def action
def nested_action
end
end
I'd appreciate any thoughts on this matter. Thanks.
What's wrong with the normal /:controller/:action idea?
That won't deal with nested actions, but... I'm having a difficult time understanding why you'd ever want that.
You can do something like this:
controller :controller_name do
get "path/action" => :method_name, :as => :path_action
post "path/save" => :method_name, :as => :path_save
end
That is, you can group different routes within a controller using the method above.
This question already has answers here:
difference between collection route and member route in ruby on rails?
(5 answers)
Closed 9 years ago.
Reading this: http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
What does it mean to add a 'member route'?
or do add a route to the collection?
What is a member and a collection when talking about routes?
They're both ways to add additional actions to a resource-based route in Rails.
A member route requires an ID, because it acts on a member.
A collection route doesn't require an ID because it acts on a collection of objects.
I like to think of them in terms of RESTful URLs. Consider the basics for a resource/model Foo
GET /foo # FooController#index
GET /foo/:id # FooController#show
GET /foo/new # FooController#new
POST /foo # FooController#create
GET /foo/:id/edit # FooController#edit
PUT /foo/:id # FooController#update
DELETE /foo/:id # FooController#destroy
Notice how:
Some routes have :id placeholders for Foo.id, and so refer to a specific Foo
Some routes have no :id, and thus refer to all Foos (and/or no specific foo, as in #new and #create)
Some routes (index/create, show/update/destroy) have the same URL, and use HTTP methods to differentiate between them
Some routes (edit/show) are basically the same (method & URL prefix) except for a different suffix (including "no suffix") at the end.
Member routes and collection routes let you add additional routes/actions using the same techniques as I listed above.
A member route adds a custom action to a specific instance using the URL suffix and HTTP method you provide. So, if you had a member route declaration of :member => { :bar => :get }. you'd get an additional route of:
GET /foo/:id/bar # FooController#bar
Note how it overloads GET /foo/:id in the same way that `edit' does. This is how you'd implement a "delete" action that provides a UI for the "destroy" action.
Similarly, a collection route adds an overload to the collection and/or a non-specific instance (it's up to you to decide exactly what it implies). So, if you declared :collection => { :baz => :get }, you'd get an additional route:
GET /foo/baz # FooController#baz
...in very much the same way as new.
You can also customize the HTTP method.
For example, I just recently had a project where I needed a "reply" action on a Comment. It's basically the same idea as Comment#create (which uses POST), except that it's in reference to a specific parent Comment. So, I created a member route: :member => { :reply => :post }. This gave me:
POST /comment/:id/reply # CommentController#reply
This keeps the routes restful while still expanding upon the basic 7 actions.
The built in member routes are show, edit, update and destroy, since they handle an individual record. index would be a collection route as it returns a collection of records.
So it really depends if you want to do something with a single record (member) or multiple records (collection).
The url helpers reflect singular (member) and plural (collection). For example:
This is a member:
person_path(#person)
This is a collection:
people_path()
If you define a custom collection path, it could look like this in your routes.rb:
resources :people do
member do
put :make_manager
end
collection do
get :show_managers
end
end
To make somebody a manger:
make_manager_person_path(#person)
To list all managers:
show_managers_people_path()
I don't think that the route "cares" if you use it differently, but this is the Rails way. It will make your code easier to read and other coders will have it easier to understand and maintain your code.
I want the following urls for my UserController:
localhost/user/join
localhost/user/login
localhost/user/user_name (this can be any name in here, it should fire the 'profile' action)
Then within the /user/user_name_here/ folder I want:
/user/user_name/blah1/
/user/user_name/blah2/
/user/user_name/blah3/
It seems doing resources :user only creates things for index/show/get, so I'm confused as to how to do this other than creating so many match '/user/join' etc. lines in routes.
match "user/:user_name" => "users#show"
then /user/username will redirect to the User controller, call the show method, and pass the :user_name param
you could do the same to other actions that doesn't neet parameters,
match '/user/login' => "sessions#new"
match '/user/join' => "user#new"
Yup - 'resources :user' is just scaffolding for the usual CRUD methods. If you want paths additional to those, you're going to have to create routes (it's the only way your app knows how to route a given URL to a given controller and method).
The friendly_id gem does something similar to what you're suggesting (though I believe it's monkey-patching the .find method on ActiveRecords classes rather than handling routing specifically).