Rails routes based on a value in a column - ruby-on-rails

Rails 3.1. I have a table places. It has a column called type which has value such as cafe, restaurant, etc. Just one value in each row.
In my routes, I define the resources as:
resources :places
The URL is:
http://domain.com/places/123?type=cafe
I always have the type appended in my URLs because I use that to determine which navigation menu to be highlighted.
Now, I want to create a friendlier URL. How can I create a URL that reads either of these:
1. http://domain.com/places/cafe/123
2. http://domain.com/cafe/123
Many thanks!

There are two types of routes - resourceful and non-resourceful. It sounds like you're trying to do a mix between the two. Unfortunately, resourceful route URLs can't be customized like you're trying to do via resources, but you can add additional routes to match the URLs you're trying to make.
To accomplish this, you'll have to create non-resourceful routes such as these:
resources :places, :only => [:create, :update, :destroy]
match 'places/:type/:id' => 'places#show'
match 'places/:type' => 'places#index'
match 'places/:type/new' => 'places#new'
match 'places/:type/edit' => 'places#edit'
You don't need to create special routes for create, update, and destroy (though you could). The user never really sees those anyway. Just include the place type as a parameter in your forms and delete links, and it will be available in the controller the same way that it would as if it were coming from the URL.
In the controller, access the type via params[:type].

Why don't you use the value stored in #place.type, where #place = Place.find params[:id], to determine which navigation to use?

Related

Rails: in controller, a new view is always rendered by `show`?

I want to implement a search function in the controller, which contains "show, new, create, etc..."
I added in route.rb:
get 'apps/search' => 'apps#search'
and in apps_controller.rb:
def show
#app_info = App.find(params[:id])
end
def search
# get parameter and do search function
end
but each time when i request the /apps/search?xxx=xxx then it will be rendered by show... and then search?xxx=xxx is the parameter for method show...
should I rather create a new controller for search? Or is it possible to implement search as my requirements?
Your routes are incorrectly prioritized - somewhere else in your routes file (before the get 'apps/search' line) you have resources :apps, which defines a route that matches the same regex as apps/search.
Routes match from top to bottom, so if you check the output of rake routes, you'll see that your request to apps/search is actually matching apps/:id - which is the show route of your apps resource.
Either move the apps/search route above the resources :apps declaration, or alternatively declare your search route as part of the apps resource, eg.
resources :apps do
get :search, on: :collection
end
(this will define apps/search in the way you want).
For more information on routing: http://guides.rubyonrails.org/routing.html
I think you should edit route.rb as the following:
get 'apps/search' => 'apps#show'
The Rails' way to "say" search is a new route to the apps controller is using collection. So, for example, supposing you already have a resources :apps, you can do:
resources :apps do
get 'search', on: :collection #or you can use another HTTP verb instead of get
end
And that would give you:
search_apps GET /apps/search(.:format) apps#search

Rails routing get and match

When I'm generating controller with methods in routes I have something like this:
get "vehicle_manufacturers/show"
Is it a good practice that after that I will write such code:
match 'vehicle_manufacturers/:id/' => 'vehicle_manufacturers#show', :as => :vehiclemanufacturers
Or, is there another way of writing this code so that will work properly?
It's best practice to follow Rails conventions for naming and routing to your actions. This lets you say:
resources :vehicle_manufacturers
This will automatically create routes for index, new, create, show, edit, update, and delete with the appropriate HTTP methods and helper names. Assuming it corresponds to a VehicleManufacturer object that conforms to ActiveModel conventions (like ActiveRecord, Mongoid, etc.), url_for will automatically Do The Right Thing™, letting you use forms and redirects and such with no extra routing-related work.
It's pretty common that you might only want a subset of these methods. That's fine too:
resources :vehicle_manufacturers, :only => [:index, :show]
You want to support additional methods outside the normal CRUD methods. Go for it:
resources :vehicle_manufacturers, :only => [:index, :show] do
get :stock_price, :on => :member
end
This would add a route named stock_price_vehicle_manufacturer mapping to /vehicle_manufacturers/:id/stock_price. Although, strictly speaking, I might consider a stock price to be a sub-resource...
The point is, try to use resources as the basis of your routing. It makes everything easier.

What does a member and collection route mean? [duplicate]

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.

Rails3: Appropriate use of routing and resources

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.

Rails: Override RESTful paths?

Is it possible to override a RESTful path?
For instance, I have photo_path(photo) which would generate /photos/12345
But I'd like for all uses of photo_path to actually generate URL's like /photos/joeschmoe/12345 (which is the photo's user and the photo id).
Obviously I could just create a new route, but wanted to make sure there wasn't a more RESTful way of doing it.
You could make photos a sub-resource to user's, so you'd have users/joeschmoe/photos/12345 (of course here, your users controller would require the ability to accept a username instead of an id, which is another routing problem to solve but not difficult)
resources :users do
resources :photos
end
Then your controller could maybe call
#photos = Photo.find_by_username(params[:id])
Although I think there are less hacky ways of doing that.
You could also add joeschmoe as a query string parameter.
Or you could make username an optional parameter in the route, so it would be something like
match "/photos(/:username)/:id" => "photos#show"
Or if you want a new named route:
match "/photos/:username/:id" => "photos#show_by_user", :as => :user_photo

Resources