non resourceful routes with model attributes and url helpers - ruby-on-rails

I have a problem with the helpers url and path helpers for a non-resourceful route,
There is a model Item that has, among other attributes, an sku attribute
so the default routing with
resources :items
creates the default index route
item GET /items/:id(.:format) items#show
But I want to override that route to match urls like:
/sku/:sku/id/:id
instead of the default urls:
/items/:id
So I created the following route and put it above resources :items to be matched first:
get "sku/:sku/id/:id" => "items#show", as: "item"
and it works correctly if I go to www.example.com/sku/2342/id/8484 it shows me the correct Item.
However, my problem is if I want to use the item_path or item_url helpers passing the object to them.
Instead of getting the desired path /sku/2342/id/8484 I am getting a path with the id of the item applied twice like /sku/8484/id/8484
I searched in google and here in StackOverflow for several minutes and I cant find the answer, I already read the Rails routing documentation but still no success, hope some one can help me, thanks.

Are you doing something like item_path(item, item). I'm not sure that will work. If you pass in the actual object, it grabs the id field.
Did you try item_path(item.sku, item)?

Related

index_ appended to path erroneously added to rails 4 route

I added a controller named "Triage" to my application, and added a PUT route as follows:
resources :triage do
collection do
put :process_multiple
end
end
Instead of the expected process_multiple_triage_path route, it seems it is processed as process_multiple_triage_index_path:
process_multiple_triage_index_path PUT /triage/process_multiple(.:format) triage#process_multiple
triage_index_path GET /triage(.:format) triage#index
POST /triage(.:format) triage#create
Answer: Turns out it is because triage is seen as a singular resource by Rails, thus by way of convention, you'd request the "index" of the resource.
This was because of the singular form of triage. Rails noticed that triage was used, instead of triages, and thus as a result of convention, the request would be of the index. I've linked below another StackOverflow that explains this well.
You can use resource :triage instead of resources (which also won't create an index route automatically either)
More info here
It's on the collecion, so Rails appends an _index. If you want to overwrite it, just change to:
resources :triage do
collection do
put :process_multiple, as: :proccess_multiple_triage
end
end
And now you'll be able to use process_multiple_triage_path

Rails rename model name in url dynamically

In Rails, I have a model named item. Each item belongs_to category (another model), which has an attribute called name.
When seeking any item, I see the url as /item/:id
instead, I would like the url to show up as /'item.category.name'/:id
e.g. if the item belongs to category whose name is "footwear", then, I would like to see the url as /footwear/:id. And if there is another item that belongs to category whose name is "clothing", then I would like to see the URL as /clothing/:id
Is this possible?
If you setup a route like
# config/routes.rb
RouteTest::Application.routes.draw do
get ':category_name(/:id)', to: 'items#show', constraints: {id: /\d+/}
end
It will map to the show action in your ItemsController, and in there, you will have access to params[:category_name] and params[:id]. With this information, you should be able to get the data you want and render it.
Note that this route however will likely have the undesirable effect of masking any routes that follow. You could use rails advanced route constraints to further narrow down 'which values would be considered valid category_names' but this wouldn't be a very scalable or manageable approach.
For example, you could do something like
RouteTest::Application.routes.draw do
get ':brand_name(/:id)', to: 'items#show', constraints: lambda { |request| BrandList.include?(request.params[:brand_name]) }
# etc. ...
get ':category_name(/:id)', to: 'items#show'
end
but this only really works well when the BrandList is a finite list that you could setup during application initialization.
A better, more scalable approach might be to design your URLs like
/brand/adidas
/brand/teva
/shoes/1
/shoes/2
/jackets/45
IOW, prefix known namespaces like brand with an appropriate human friendly URL prefix and use category based route as a catch-all at the bottom.
Hope this helps.

Custom rails routes not using :id column

I am attempting to create a custom rails route that allows me display information based upon the url. For example, I have products in a database with category_ids and country of origin fields. I would like to be able to type something like /products/(category_id)/canada or something to list items that match that category and country however my attempts have (obviously) been unsuccessful.
So far I've attempted
match 'products/:category_id/:country', to: "products#var_show"
and had no luck.
I've even just tried to make a route that shows the product via the serial code but rails seems to think I'm looking for an id even though I've specified the field in the route and in the controller.
match 'products/:serial', to: "products#show"
Can someone lead me in the right direction and show me what I'm doing incorrect? Thanks.
edit:
Rails seems to make the parameter :id no matter what I call it in the route and controller
Processing by ProductsController#show as HTML
Parameters: {"id"=>"481598745"}
Ideally that would be Parameters : {"serial" => "481598745"} in the second case I asked about.
Try this,
match 'products/:category_id/:country' => 'products#show',:as => :show

checking restful features of the action in controller

when drawing restful routes in ruby on rails you specify
if a controller action applies to a member or collection and
what http verb is used. http://guides.rubyonrails.org/routing.html
How can I check 1 from within the controller instance?
(The HTTP verb can be checked via request.method)
The ugly hack is to check if the named route exists with singular or plural, but the route can have alternative name.
Yet another is to check for the request param used for identifying the member, but collection routes could have the same (optional) param.
If the route is following restful conventions, then running rake routesshould give you the answers you are looking for.
You will see what http verb is expected (answer to 2) and the path should answer your 1) question. For a member action, you need the ID, so typically the path contains the id
This route configuration:
resources :photos do
member do
get 'preview'
end
end
will give you the path photos/:id/preview -> ment to preview a single photo
And the route configuration:
resources :photos do
collection do
get 'preview'
end
end
will give you the path photos/preview -> ment to preview a list of photos
If you need to get this information from within the code of an action:
Inspect the params[:id] to distinguish the intent. If it is for a member, that value will be not nil.
Have you looked at the Journey gem for such a method? Assuming 3.2, that would have it, if anywhere, rather than request param. By then it's too late isn't it?

rails_3_question :as => why is my /posts/new routing to posts/show after setting up a slug

I'm using Rails 3 and after setting up slugs, I found that posts/new no longer works.
posts/:id, posts/:id/edit and all the other CRUD operations work.
However /posts/new gives me a routing error
No route matches {:action=>"show", :controller=>"posts"}
Now for some reason posts/new is routing to posts#show. In my routes, its just
resources :posts
My theory is that since /posts/:slug now matches against things other than numbers ids, the show verb is being routed to first. However it doesn't make sense since posts/grr a nonexistent entry gives a different error than posts/new and posts/first comes out just fine with all its associated paths working fine as well.
Anyone know what might be going on?
I've uploaded the repo to https://github.com/cultofmetatron/cassowary/tree/photogallary
I know my code sucks, I'm still learning the ins and outs of the system and I'd appreciate any insight into whats going on.
In your comment the first part seems fine: add a column to the Post column called slug and so on, and the contents of that will become some or all of the URL used to display a specific post. (I'll assume the other CRUD operations should work as normal)
To find the URL, the router has to know how to know which controller and action will handle this URL (as compared to others). A normal resources :posts route will match all of the RESTful methods, e.g. mapping a GET request onto a path starting with the controller name, and if an id is specified (/posts/1) map to the posts#show controller method, if not, it will map to posts#index method. If the request is a PUT, or DELETE or POST, different actions around a standardized URL format will occur.
Two changes are needed:
URL with the post slug format needs to map to the posts#show method (which is modified accordingly), and
Any links to the show page that are generated on your site need to use the post slug instead of the id
I'll assume you're OK with URLs start with /posts (if not, you'll need to identify some other unique pattern).
The first change requires that you override the specific case of the show method using route globbing, my adding something like match 'posts/*slug before the standard resource route. Here's a link to the guide on route globbing: http://guides.rubyonrails.org/routing.html#route-globbing
The next change, modify the existing posts#show method so that it looks for slug instead of id, e.g.
def show
#post = Post.where("slug = ?", params[:slug])
...
end
Finally, change the way Rails handles the URL helper posts_path. Do this by overriding to_param in your Post model, e.g.
def to_param
"/posts/#{slug}"
end
And then you're done. Maybe.
After that, see how the friendly_id gem does the same thing :-) https://github.com/norman/friendly_id

Resources