How to specify nested parameters in the routes - ruby-on-rails

In routes.rb I have described how the search will look like this
match "results//:transaction/:city(.:format)" => "search#index", :as => :seo_search_index
which generates me this kind of routes
seo_search_index /results/:transaction/:city(.:format) {:action=>"index", :controller=>"search"}
And the params object is filled with
params[:transaction]
params[:city]
params[:zip5]
But I want the param object to be filled like this
params[:search][:transaction]
params[:search][:city]
params[:search][:zip5]
Is there a way to specify this like this
Just an example:
match "results//:search[transaction]/:search[city](.:format)" => "search#index", :as => :seo_search_index

I'm not sure there's a way to tell the Rails routing system that you want your parameters nested. You could work around this issue with a before filter in your controller:
class MyController < ApplicationController
before_filter do
params[:search] = params.slice(:transaction, :city, :zip5)
end
end
Update
To answer your real question, you could do either:
seo_search_index_url(#search)
or
seo_search_index_url(#search.slice(:transaction, :city, :zip5))
depending on whether the #search hash contains only the keys you want or some additional ones.

With the help routing filter you can make what ever you want with the urls https://github.com/svenfuchs/routing-filter

Related

Rails 4 - Hash in route gets rendered wrongly

I have this line of code in routes.rb
get 'products#:id' => 'products#index', as: :products_hash
It works, but the problem is that the hash symbol (#) gets rendered as %23.
http://localhost:3000/products%2368
This is what it should be:
http://localhost:3000/products#68
How can I achieve this? Thanks!
Rails
I feel you're missing the point of Rails' routing system, which is that you are meant to construct it around objects (hence why you call resources :controller etc).
Although I don't understand why you're defining the route you are, I'll give you some ideas. There is a "wildcard" setting for your routes in Rails, which allows you to define & pass parameters to your application out of the scope of the typical "CRUD" based applications:
#config/routes.rb
get 'products#*id' => 'products#index', as: :products_hash
This will give you the ability to send requests to the following route:
<%= link_to "Product", products_hash_path("68") %>
You'd then be able to call the param in the controller:
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def index
id = params[:id]
end
end

Routing in rails - When to route to a crud method and when to route with the given parameter

I am in need of a little help regarding routing in rails. Traditionally if I have a route in routes.rb
resources:categories
it will match www.website.com/categories/id were id is an integer. But what happens if I wanted to route to a specific user or category like so:
www.example.com/categories/apple
instead of the traditional:
www.example.com/categories/4
I currently have these two lines in my routes.rb
match 'categories/:name' => 'categories#show'
resources :categories
so www.example.com/categories/apple will route to the show method in the categories controller correctly.
but
What if I want to create a new category? Here's the problem
www.example.com/categories/new
will route to the show method, and will not route to the new method
I can place an if statement in the show method checking is params[:name] == new, but I feel that there must be a better way to solve this problem.
My end goal is to route based on the string of the category (apple) and not based on it's ID (4) but also be able to create, update, and destroy a category.
Any tips?
Thanks!
you can pass a name as a parameter, for example you can try
link_to category_path(category.name) instead of link_to category_path(category)
and it will go to categories#show in controller, the route will look like example.com/categories/categoryName, in the show action of your cateogries controller, params[:id] will have the name, you'll need something like
Category.find_by_name params[:id] instead of Category.find params[:id]
Hope this helps you.
As #pdoherty926 says in the comment, you could use FriendlyId. If you want to manage it manually, you don't have to overwrite the routes, just put the needed code in the :show action:
class CategoriesControllers < ...
...
def show
if /^\d+$/ =~ params[:id]
# ... normal workflow for integer id
elsif /soe_regexp_that_matches_names>/
# ... do your thing with names
else
raise 404
end
end
...
end
I think the most simple way to do this your way is to define a route before your
match 'categories/:name' => 'categories#show'
like this:
match 'categories/new' => 'categories#new'
match 'categories/:name' => 'categories#show'
the order matters.

Rails Routing: how to separate a token from surrounding static segments

I'm having one of those bizarre "this used to work and then it stopped working" issues.
In my routes file I have
controller :questions do
match 'q/:topic-questions/:tag' => :search
end
So a URL of format q/java-questions/performance would route to the search action with params[:topic] = java and params[:tag] = performance
This used to work, but now I get a route not found error. If I switch to
match 'q/(:topic)-questions/:tag' => :search
it finds the route again, but I don't want topic to be an optional parameter. I think this implies that it's having trouble separating out :topic-questions into a token and then a static string. If there another way to neatly separate out the token, other then putting it in ()?
Note - the reason why topic cannot be an optional parameter, is that optional parameters are not included in the cache keys when doing action caching.
what about:
controller :questions do
match 'q/:topic-:modifier/:tag' => :search
end
then you would have three parameters
params[:topic]
params[:modifier]
params[:tag]
and you could then ignore the params[:modifier] one.
according to your caching issues, just make the "-questions" part optional:
controller :questions do
match 'q/:topic(-questions)/:tag' => :search
end
this will match q/java-questions/performance and q/java/performance the cache key is always distinct to the topic "java"
Edit:
This is a modification of #sorens post (he did 99% of the work):
controller :questions do
match 'q/:topic-:modifier/:tag' => :search, :defaults => {:modifier => 'questions'}, :as => :question_topic_tag
end
now your helper looks like:
question_topic_tag_path('java', 'performance') gives you q/java-questions/performance
I would agree with Dave Newton about trying to re-factor your URL structure, but you could possibly allow the "-questions" through the route and chop it off from params[:topic] in your controller and use constraints to validate the presence of something before "-questions" in the URL
controller :posts do
match 'q/:topic/:tag' => :index, :topic => /.+-questions/
end
Then in your controller you would need something like
topic = params[:topic].gsub!(/-questions/, "")
This smells a bit ;)
You could give it a regex condition that forces it to non-empty.
match 'q/(:topic)-questions/:tag' => :search, :topic => /[A-Za-z]*/ # Or whatever.
See the Segment Constraints section of the routing docs for details.
match 'q/:topic:fix_it/:tag' => :search, :fix_it => /-questions/

A RoR v3 routing question?

I was wondering if it is possible to route to something like this /:user_id user_id is a custom id that doesn't just use integers it uses other characters like so NM-001. Then in my controller I have #user = User.find(params[:user_id]). Then in view <%= #user.name %>
Yes, you can have such a route. However, if your :user_id will contain periods then you'll want to include
:constraints => { :user_id => /.*/ }
in the route options to keep Rails from trying to interpret the .whatever part of the :user_id as a format specifier.
Then, you'll get params[:user_id] in your controller and you can turn that into an object however you want. You'd probably want to do what mischa said in the comments:
#user = User.find_by_user_id(params[:user_id])
Also, if you really want to use /:user_id as your route, you'll want to make sure that none of your userids match any of your present or future top-level routes.

Rails Routing with Query String

I have a problem where I need values passed in from a GET request and I don't know how to set up the routing definition.
My Category object has a type(string),a color(string) and many products. I want to create a simple web service that lets the caller get all of a Category's products by passing in the Category's type and color:
http://www.myapp.com/getProducts?catType=toy&color=red
or ?
http://www.myapp.com/categories/getProducts?catType=toy&color=red
How do I define the correct routing for this situation? Are there better ways to do this in a Restful manner... because I know that Rails is Restful, so if there is a way to do it "correctly" then that would be even better.
Thanks
Your first example:
map.getproduct '/getProduct', :controller => 'your_controller', :action => 'your_action'
In controller you will have catType and color in params hash:
params[:catType]
=> 'toy'
params[:color]
=> 'red'
Is there better way? Probably yes, but it depends on your needs. If you will always have catType and color parameters, than you can add route like this:
map.getproduct '/getProduct/:catType/:color', :controller => 'your_controller', :action => 'your_action'
You will have access to those parameters with params hash like in previous example. And your urls will look like this:
www.myapp.com/getProduct/toy/red
If your parameters may change, you can use route globbing:
map.getproduct '/getProduct/*query', :controller => 'your_controller', :action => 'your_action'
Then it will catch all request that has www.my.app.com/getProduct/... at the begining. But you will have more work in controller. You will have access to query with this:
params[:query]
and for www.myapp.com/getProduct/color/red/catType/toy it will give:
params[:query]
=> ['color', 'red', 'catType', 'toy]
So you have to parse it manualy.
One RESTful way to to do this would involve a product resource nested beneath a category resource, like so:
http://www.myapp.com/categories/toy/products?color=red
Your routes.rb would need to contain:
map.resources :categories do |category|
category.resources :products
end
Since my url above using the Category's type attribute for routing, I'm implying that each type is unique, like an id. It'll mean that whenever you're loading a category in the Categories controller (or anywhere else) you'll need to load the category with Category.find_by_type(params[:id]) instead of Category.find(params[:id]). I like routing categories this way whenever possible.
Your ProductsController controller index action would find products using lines like:
#category = Category.find_by_type(params[:category_id])
#products = #category.products.find(:all, :conditions => { :color => params[:color]} )
Remember, your Category model must contain the line:
has_many :products
It's probable a good idea to enforce that in the model with validations:
validates_presence_of :type
validates_uniqueness_of :type
To make routing work you should also overwrite the to_param method in the Category model to return type instead of id:
def to_param
self.type
end

Resources