Rails 4 routing a value besides :id - ruby-on-rails

I either have an issue with my link_to code in my view or my route. I am trying to pass in a non primary key value from a view and do a query using that value. No matter what I do rails always just takes the :id of that table as its only param.
Here is my view
<td> <%= link_to c.app_version_id, compliance_suite_path(:app_version_id => c.app_version_id) %></td>
My Route
resources :suites, :only => [:index] do
member do
get 'report'
end
member do
get 'compliance', param: :app_version_id
end
end
If I do a rake routes I get
compliance_suite GET /suites/:id/compliance(.:format) suites#compliance {:param=>:app_version_id}
Here is my controller which right now is just trying to view the prams list
def compliance
Rails.logger.info "***********PARAMS PARAMS *******************"
Rails.logger.info {params.inspect}
end
I do have other methods in that controller that use the :ID I just have this one method I want to use another value an app version id to do a query. So I don't want the entire controller to use this other id just 1 method. The value I want is in my view I can see it in my table. In my controller its always just the :id and never the app_version_id.
Its either my route or my link_to?

Because your route is a nested member route, rails expects the id of the parent. You don't really need to include the params clause in your route. It would be better to change your route to reflect your intentions by including the app_version_id in the route.
routes.rb
get 'suite/:app_version_id/compliance', to: 'suites#compliance', as: 'compliance_suite'

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 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.

How do I create a rails controller action?

My rails app has a single CustomerSelectionController, with two actions:
index: which shows a form where the user can enter customer information and
select: which just displays a static page.
class CustomerSelectionController < ApplicationController
def index
end
def select
end
end
I've created an entry in my routes.rb file:
resources :customer_selection
and the form in the index view looks like:
<h1>Customer Selection</h1>
<%= form_tag("customer_selection/select", :method => "get") do %>
<%= submit_tag("Select") %>
<% end %>
however when I click on the Select button in the browser, all I get is:
Unknown action
The action 'show' could not be found for CustomerSelectionController
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
I'm not sure why it is trying to perform an action called show? I haven't defined or referenced one anywhere.
Yes you have. That's what resources does. It defines the seven default RESTful routes: index, show, new, create, edit, update and destroy. When you route to /customer_selection/select, the route that matches is "/customer_action/:id", or the "show" route. Rails instantiates your controller and attempts to invoke the "show" action on it, passing in an ID of "select".
If you want to add a route in addition to those, you need to explicitly define it, and you should also explicitly state which routes you want if you don't want all seven:
resources :customer_selection, only: %w(index) do
collection { get :select }
# or
# get :select, on: :collection
end
Since you have so few routes, you can also just define them without using resources:
get "/customer_selection" => "customer_selection#index"
get "/customer_select/select"
Note that, in the second route, the "customer_select#select" is implied. In a route with only two segments, Rails will default to "/:controller/:action" if you don't specify a controller/action.

Do you have to define controller helpers to get paths for new routes in rails?

I have a Customer model and I want his controller to repond to a find method
I added this in my routes.rb file:
match 'customers/find/:name' => 'mymodel#find' resources :customers
In my controller I have something like this:
def find
#customers = Customer.fin_all_by_name(params[:name])
end
in my views, when I need to create a link for that resource I'm using this:
= link_to 'Find By Name', :controller => "customers", :action => "find", :name => #customer.name
now, I'm trying integration tests with cucumber and I have a problem: I have to create a step definition in my customer_step.rb file for customers having same name:
when /^customers having same name as "(.*)"/
url_encode('/customers/find/' + $1)
now that line doesn't work, it says undefined method `url_encode'
I need to encode that string because if the name contains a space I get obvious errors.
I'm new to ruby and rails and I think I'm missing something here.
Am I following the right pattern to accomplish this search?
Should I define an helper method in my controller to generate search urls?
Is it correct that line I have in my _step.rb file?
I don't want urls to be like this: customers/find?name=test
but: customers/find/test
I just sorted it out, I slightly modified my match expression and added the :as parameter
and this gave me the possibility to call find_path() helper method
match 'customers/find/:name' => 'customers#find', :as => :find
Is this correct?
Using :as should indeed create a route helper for you. If you want to get a list of your matched routes, to which controller/action they route, and the name of the route helper, use rake routes in console.

Resources