Rails 4 - Hash in route gets rendered wrongly - ruby-on-rails

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

Related

route is redirecting to wrong path

This is what my routes currently look like:
which gives
On my homepage I have a create vacancy button
<%= link_to "plaats", new_employer_vacancy_path(:employer_id)%>
Which should be linked to the line from the first image
get '/employers/:employer_id/vacancies/new', to: 'vacancies#new', as: 'new_employer_vacancy'
In the vacancies_controller#new - create I have:
def new
#vacancy = Vacancy.new
#employervacancy = Employervacancy.new
end
def create
#vacancy = Vacancy.create(vacancy_params)
createEmployervacancy
redirect_to employer_vacancy_path(current_employer, #vacancy)
end
def createEmployervacancy
#employer = current_employer
Employervacancy.create(vacancy_id: #vacancy.id, employer_id: #employer.id)
end
But whenever I click the button I get redirected to some other method in my vacancies_controller that is totally irrelevant.
How is this even possible? Don't I clearly define that when that path is clicked he should go to vacancies#new? and not to vacancies#show_specific_employer_vacancies?
EDIT
After following the answers I am indeed being linked to the correct route.
First, it gave me this error.
After trying to pass the current_employer.id instead of #employer like suggested I got following error:
For your routes, you'd better to change into nested route for easily maintaining routes.
Remove these codes:
get '/employers/:employer_id/vacancies/:id', to:"vacancies#show_specific_employer_vacancies", as: "employer_vacancy"
get '/employers/:employer_id/vacancies/edit/:id' ...
get '/employers/:employer_id/vacancies/index' ...
get '/employers/:employer_id/vacancies/new' ...
path '/employers/:employer_id/vacancies/:id' ...
change into:
resources :employers do
resources :vacancies
end
Try to use basic routes here because you use standard simple form url. For example:
<%= simple_form_for(#employee, #vacancy) %>
The simple_form_for will generate url well if you use nested routes above.
Finally, in your link you have to add #employer_id
<%= link_to "plaats", new_employer_vacancy_path(:employer_id => #employer_id)%>
I hope this help you
Your router cannot tell the difference between your employer_vacancy and new_emplyer_vacancy routes because the :id parameter accepts anything. Because of this, when you point your browser to "/employers/5/vacancies/new", the route is taking your employer_vacancy route and assigning {:employer_id => 5, :id => "new"} instead of going to your new_employer_vacancy route (because routes are first-come-first-serve).
To correct this, add a constraint to your first route to ensure that only numbers (and not the string "new") is accepted into the employer_vacancy route:
get '/employers/:employer_id/vacancies/:id',
to: 'vacancies#show_specific_emplyer_vacancies',
as: 'employer_vacancy',
constraints: { id: /\d+/ } # <- This line
As Wes Foster said rails router is trying to find a first match.
It means that given a path /employers/999/vacancies/new your router looks through the routes and when it sees get '/employers/:employer_id/vacancies/:id he thinks that this route matches. So :employer_id is 999 and :id is new.
I'd suggest to put the route with :id at the end of employers routes:
...
get '/employers/:employer_id/vacancies/new'
...
get '/employers/:employer_id/vacancies/:id'
Btw this is better than adding a constraint because:
It is easier
It doesn't pollute routes file
Later you may want to change ids to be hashed or alphabetic and then you'd have to change the constraint

Rails same named route but with parameter raises an error

As of Rails 4.2, I cannot do the following:
get 'profile', to: 'profile#index', as: 'profile'
get 'profile/:slug', to: 'profile#show', as: 'profile'
because it will raise error saying route is already defined. Why is that? Obviously profile_path and profile_path(User.last.slug) are not the same, and there should be no difficulty differentiating the two even if they happen to share the same base name (You check if a param is passed).
Thoughts?
In rails, the helper names for different routes should be different. And hence, as you rightly understood, you will receive an error if you use the same helper name (ie as: 'profile' in your case) for two different routes.
This restriction in Rails helps maintain sanity in your routes.rb file as well as in your application. For instance consider two methods for a controller:
class XyzController < ApplicationController
def method_a(param1)
end
def method_b(param1)
end
end
In your routes file if there was no restriction of keeping helper names different, you could have used :
get 'xyz/method_a', to: 'profile#method_a', as: 'profile_method'
get 'xyz/method_b', to: 'profile#method_b', as: 'profile_method'
Correspondingly in your view file:
link_to 'link_1', profile_method_path('param1') #intended to route for method_a
link_to 'link_2', profile_method_path('param2') #intended to route for method_b
As obvious, in the view file, not only is it difficult to make out which route is intended for which method, its also not possible to route to any other controller method using the helper 'profile_method' except the method that is first to use this helper in your routes.rb file (as routes are read sequentially).
Hope this helps :)
For both routes you specified as: 'profile' and that's your problem here. Besides that, use pluralized route names for #index action, e.g:
get 'profiles', to: 'profile#index'

understanding passing rails parameters between controllers

I am just starting to wrap my head around parameters in rails. I am currently working on a project that isn't accessible to the public, so keeping params secure isn't exactly a priority in this case.
I have a link_to to a different controller action that requires an object id to fulfil the controller action.
=link_to "Barcode", print_barcode_label_admin_items_path(:item_to_print => { :article_id => article.id })
Then in the relevant controller
def print_barcode_label
if params[:item_to_print][:article_id].present?
return if force_format :pdf
..........
private
def params_document
params.require(:document).permit!
end
As I was writing the code for this controller I am certain the parameters were being passed (I am using the better-errors gem to debug along the way so I could see them being passed in the request parameters hash). But now, not sure what I have done, but i get the error
undefined method `[]' for nil:NilClass
failing at line two in my above controller action. I am sure there is something really basic I am missing. What is it? Is there a more favourable way of doing this?
Update
So I started playing with other possible solutions, and one is naming a route that specifically carries the parameter
get 'print_barcode_label/:article_id', to: 'documents#print_barcode_label', as: 'print_barcode_label'
This seems a more robust and sensible approach. Howeever, despite passing the variable in the link, like this
=link_to "Barcode", print_barcode_label_admin_items_path(article.id)
Gives a no route matches error
No route matches {:action=>"print_barcode_label", :controller=>"admin/documents"} missing required keys: [:article_id]
It is hard to answer this question without seeing more code with some context. But if you want to do rails way you should propably create custom action on document resource.
In your routes.rb:
namespace :admin do
resources :documents
get :print_barcode_label, :on => :member
end
end
And then you can create link to this action:
= link_to 'Barcode', print_barcode_label_admin_document_path(article)

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.

How to specify nested parameters in the routes

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

Resources