Matching routes from an array - Rails Routing - ruby-on-rails

I`m looking to implement links that fit a certain format (seo purposes).
Here`s an example:
match '/activities-Palmdale-California', :to => 'explores#activity_by_city', :location=>'Palmdale-California'
Where the location changes for each city+state.
Is there I way to dynamically loop through an array of cities & states (predefined) in the routes file, without creating additional models etc. ?

You can have parameters in your routes, so something like the following should work:
match "/activities-:location", :to => 'explores#activity_by_city'
and the location should be sent to your controller action in params[:location]. If you want to limit the urls your application will accept to just the locations in a predefined array (we'll call it ValidLocations), you can do it either in the route with the :constraints option:
match "/activities-:location", :to => 'explores#activity_by_city', :constraints => proc { |req| ValidLocations.include?(req.params[:location]) }
or in the controller:
def activity_by_city
...
unless ValidLocations.include?(params[:location])
flash[:error] = "Invalid location."
redirect_to ...
return
end
...
end

Related

Rails routing URL folders for product categories on show actions

I'm trying to formulate some better urls for a "product" model I have, only on the show action. I'm currently using friend_id to generate pretty slugs, which is fine, but I'm trying to improve the URL flow if I can.
Current my paths work like this
example.com/products/pretty-url-slug
When saving a parictular product (to the Product Model), I also save a type_of attribute. Which could be android, iphone, windows
So I am trying to ultimately have robust URLS like this
example.com/products/iphone/pretty-url-slug
The problem is, that I don't have or believe I want an actual "iphone", "android", etc controller. But I'd rather just update a combination of the routes and show action to handle this properly.
So far I've attempted to solve this by using a catch all on the routes, but is not working correctly. Any suggestions or different ways to handle this elegantly?
routes
resources :products
# at the bottom of my routes a catch all
match '*products' => 'products#show'
# match routes for later time to do something with to act like a
# normal category page.
match 'products/iphone' => 'products#iphone_index'
match 'products/android' => 'products#android_index'
match 'products/windows' => 'products#windows_index'
show action in the products controller
def show
# try to locate the product
if params[:product].present?
slug_to_lookup = params[:product].split("/").last
type_of = params[:product].split("/").second
#product = Product.find_by_slug(slug_to_lookup)
else
#product = Product.find_by_slug(params[:id])
end
# redirect if url is not the slug value
if #product.blank?
redirect_to dashboard_path
elsif request.path != product_path(#product)
redirect_to product_path(#product)
end
end
This way to handle the problem sort of works, but I can't fiqure out how to append the type_of attribute and generate a valid URL.
What about defining your routes like this:
get ':controller/:action/:id/:user_id'
Here, Anything other than :controller or :action will be available to the action as part of params.
Thanks for the suggestion. I was actually able to solve this and pretty simple when I thought it over. This might be helpful for others.
In my routes I just created a route for every type of category I have. so every time a new category, I would need to add an additional route, example:
# match for each product category
match 'shop/iphone/:slug' => 'products#show', :as => :product_iphone
match 'shop/android/:slug' => 'products#show', :as => :product_android
match 'shop/windows/:slug' => 'products#show', :as => :product_windows
Then in the show action for products instead of directing, you would just render the products/show if the slug matches an existing product
#product = Product.find_by_slug(params[:slug])
Then in your views, you could link to a particular category like this
link_to "product", product_android_path(#product)

Managing URL Parameters in Rails

Right now I am finding routing and URL constructing within rails to be semi-confusing. I have currently matched the following for tags that are passed in when displaying/filtering data.
match '/posts/standard/' => 'posts#standard'
match '/posts/standard/:tags' => 'posts#standard', :as => :post_tag
match '/posts/standard/:tags' => redirect { |params| "/posts/standard/#{params[:tags].gsub(' ', '+')}" }, :tags => /.+/
However, now I want to add a 'skill' parameter that can only take one state; however, I am very confused by how I want to construct this within my URL. I cannot simply have...
match '/posts/standard/:tags/:skill' => 'posts#standard', as => post_tag, as: => post_skill
So, I am very confused by this at this point, does Rails offer any type of help for constructing URL's?
One way is to just keep your main route
match '/posts/standard/:tags' => 'posts#standard', :as => :post_tag
and handle the additional URL params as params. The url would look like:
/posts/standard/1?skill=something
and it is easy enough to inject the additional params, such as by
link_to post_tag_path(:skill=> 'something')
and your controller would then do
def standard
if params[:skill] == 'something'
...
else
...
end
end
Also, not sure about this, but your first line in your routes 'match '/posts/standard/' => 'posts#standard' may catch all of your routes since there is a match. If this is the case, simply move it to after the first line.

Rails: a singular resource nested on the index of another resource

I have an User(plural) and a Subscription(singular) controllers. I am wondering if I can set a route like the following:
/users/subscription/edit
which means editing the subscription of the current user. All examples I see are like /users/1/subscription/edit
This way any user can point to this same url and it will direct to their settings page.
Make the routes like :
match '/users/subscription/edit' => 'subscription#edit' : as => "subscription_edit"
OR
match '/users/subscription/edit', :controller => 'subscription', :action => 'edit' : as => "subscription_edit"

Rails routes constraints based on model

Using Rails 3.1. I have the following in my route:
In my model Shop, I have a column called shop_type that has either boutique or saloon. Instead of having the url:
http://localhost/spots/1
I would like to separate it by the shop_type to:
http://localhost/boutique/1
http://localhost/saloon/2
So I added the following to my route:
resources :boutique, :controller => 'shops', :constraints => { :shop_type => 'boutique' }
resources :saloon, :controller => 'shops', :constraints => { :shop_type => 'saloon' }
The problem with this is I can access record ID 1 with shop_type = boutique with either of the URL. Ideally, it should return error when a user tries to access
http://localhost/saloon/1
But the above URL just works fine, which is not what I want.
Also, is there anyway to redirect all shops/1 to the new URL which is by shop_type?
Many thanks.
If you want to do this, then your application is probably telling you that it really wants two separate classes, Boutique and Saloon. Can you do that? If not, why not?
Maybe its better to tell Rails directo allow urls as:
get "/:shop_type/:id", :to => 'shop_controller#show'
And in controller check if the record exist, and return :status => 404 if not:
#shop = Shop.where(:id => params[:id], :shop_type => params[:shop_type]).first
render :status => 404 and return if #shop.nil?
Note that route provided is too greedy and put it after all other routes so it will not 'eat' other request.

Rails hide controller name

i have match ":id" => "people#show" in my routes.rb
now i can access http://localhost:3000/1
but,in views <%= link_to 'Show', people %> it will generate http://localhost:3000/people/1 ,
i want to it to be http://localhost:3000/1
You could do something like this to ensure that only numeric ids are matched:
match '/:id' => 'people#show', :constraints => {:id => /\d+/}
A good alternative might be to use some kind of identifier, even if it's not the controller name: http://localhost:3000/p/1. This will at least ensure that if you add other controllers and actions you don't end up having to change your routing structure.
You could write a custom route to match that in config/routes.rb. At the bottom of your routes.rb file you will have a route like match ':controller(/:action(/:id(.:format)))'
or something like resources :people. You might have to write a route that matches the route type you want.
You have to create a named route.
match ':id' => 'people#show', :as => :person
And fix your views to use your new method person_path(user_id).

Resources