Links and routes in Rails - ruby-on-rails

Rails 4.1
Ruby 2.1.1
I have an application where I need to display a link to the controller show method. As far as I know, something like this will do it:
<%= link_to "Agent", agents_path(:id => agent.id), :class => "btn btn-warning" %>
But when Rails generates the link, it's this:
http://mydomain/agents?id=9
What I need is
http://mydomain/agents/9.
When I enter this manually, I get the agents/show view. How do I get there?
In my routes, I do not have anything special for agents. Here's the relevant code from routes.rb:
resources :agents
Which means it will generate all the routes.
rake routes output:
agents GET /agents(.:format) agents#index
POST /agents(.:format) agents#create
new_agent GET /agents/new(.:format) agents#new
edit_agent GET /agents/:id/edit(.:format) agents#edit
agent GET /agents/:id(.:format) agents#show
PATCH /agents/:id(.:format) agents#update
PUT /agents/:id(.:format) agents#update
DELETE /agents/:id(.:format) agents#destroy
and I did not get any errors generating the routes
Solution:
Apologies, but I was using agent_path, not agents_path. I did try agents_path at one point, as one of the things I tried. The problem is that I had two different views. In one, I was using the correct syntax:
<%= link_to "Agent", agent_path(agent.id), :class => "btn btn-warning" %>
and in the other I was using:
<%= link_to "Agent", agents_path(:id => agent.id), :class => "btn btn-warning" %>
I kept making changes to one of them, when the other was actually the one being rendered. I put the correct syntax in a partial and now it's working fine. The moral of the story is to always use partials, even when you think you don't need them.

You want agent_path instead of agents_path, and you don't need to specify the id. This will work just fine:
<%= link_to "Agent", agent_path(agent), :class => "btn btn-warning" %>
Whenever in doubt about what the routes are, you can always run:
bundle exec rake routes
And you'll be able to see all existing routes, and their naming.

Routing
Something you need to consider is the resourceful nature of Rails' routing:
Every time you create a resources part of the Rails routing structure, you're actually telling rails to build the above routes. This is standard practice, and means you'll be able to call article_path(article.id) as default functionality.
I believe your problem stems from this idea:
<%= link_to "Agent", agents_path(:id => agent.id), :class => "btn btn-warning" %>
--
Reference
As mentioned, you're referencing agents_path (plural), when it should be agent_path (singular).
However, you're also defining the :id parameter in the route helper itself. This can just be handled by passing the object itself to the helper:
<%= link_to article.title, article_path(article.id) %>
Or, more succinctly, Rails is able to determine the path based on the resource you pass it (hence why I mentioned resourceful routes structure):
<%= link_to article.title, article %>
This will you autimagically to the show action of the articles controller.

Simply pass your record to link_to, this will automatically generate a link to its view page
= link_to 'Agent', agent
Agent
You can do much more by using the record, example if you need a link to the edit page
= link_to 'Agent', [:edit, agent]
Agent
And so on... This technique is very useful when writing helpers that must be compatible with different models

Related

Rails - redirect to different page in same path

In my Rails project, I have two different pages that exist within the same path - one is an index of stores, the other is a store update page (though I don't use those exact names for them). The problem that I am having is that I am trying to add dynamic links to the index page for each store to take a user to the associated update page, but I created the routes manually (rather than use :resources) and both pages are listed under the same path in my Rails routes summary. How can I use the <%= link_to %> helper in this situation?
First off, here is the relevant routing information...
stores_study_sites_path GET /stores/study_sites(.:format) stores#study_sites
GET /stores/store_details/:id(.:format) stores#store_details
And from my routes file...
get 'stores/study_sites' => 'stores#study_sites'
get 'stores/store_details/:id' => 'stores#store_details'
The first route, 'study_sites' is an index page, 'store_details' is the update page.
The redirect is rendered as a partial on the study_sites page...
<% #store.each do |store| %>
<ul>
<li>
<%= store.name %></br>
<%= store.street %></br>
<%= store.city %> <%= store.state %></br>
<%= render "shared/store_details", :store => store %>
</li>
</ul>
<% end %>
And finally, the partial that I would like to use, but does not currently work...
<%= link_to "Build store profile", stores_study_sites_path(store.id) %>
The urls generated from this look like http://localhost:3000/stores/study_sites.26 whereas I need them to be http://localhost:3000/stores/store_details/26
I've gone through this basic procedure for a number of other redirects with no problem, but I've created these custom urls/routes, and now I'm in a bit of a pickle. In a situation like this, how do I specify which url in the path I want the link to route to?
As a follow-up question (keep in mind I'm really new to Rails), why is the store_details page falling under the stores_study_sites_path?
Thank you very much.
Be RESTful
When starting out with Rails try to avoid falling off the band wagon - most real world problems can be solved with the standard CRUD routes and RESTful representations. When you start creating a bunch of custom routes just for different representations then the quality tends to dip sharply.
A better URL scheme would be:
/stores # index of stores
/stores/1 # show a single store
/stores/1/details # index of details of a store.
/stores/1/details is what you would call a nested resource. Its very clear from the URL that we are looking something which belongs to a store.
You can declare the routes with:
resources :stores, shallow: true do
resources :details
end
You can then create a link to the details of a store with:
<%= link_to "Build store profile", store_details_path(#store) %>
http://guides.rubyonrails.org/routing.html#nested-resources
You can name your routes manually:
get 'stores/study_sites' => 'stores#study_sites', as: "first_route"
get 'stores/store_details/:id' => 'stores#store_details', as: "second_route"
Then use them:
<%= link_to "one", first_route_path %>
## this will generate http://localhost:3000/stores/study_sites
<%= link_to "two", second_route_path(5) %>
## this will generate http://localhost:3000/stores/store_details/5
You really should put this setup within the context of your controller in the routes:
#config/routes.rb
resources :stores do
get :study_sites, on: :collection #-> url.com/stores/study_sites
get :store_details, on: :member #-> url.com/stores/:id/store_details
end
... and then in the view:
<%= link_to "x", stores_store_sites_path %>
<%= link_to "y", stores_store_details_path(store.id) %>

Non resourceful routing and dynamic segmentation

I have this in my route file
get '/registrations/:student_id/:subject_id' => "registrations#show", :as => 'custom'
Now I want to use this in link_to helper so that I can send student_id and subject_id to show action of the controller
<%= link_to "Custom" .... %>
Assuming you have instance variables #student_id and #subject_id availeble to your view, then this should work:
<%= link_to "Custom", custom_path(#student_id, #subject_id) %>
looks like similar to this question, the same solution can be applied. Custom dynamic routing and get ids from url

Method Accesibility/Scope - NoMethodError

I'm working on my first rails project and I have a problem that I just cannot figure out.
I generated a Scaffold for an object named Archive
to this object I added the method processfile
when I try to link_to said method from Archives#Index I'm getting this:
undefined method `processfile' for #<Archive:0x702de78>
This is the model archive.rb
class Archive < ActiveRecord::Base
belongs_to :users
attr_accessible :file, :user_id
mount_uploader :file, FileUploader
end
This is the code on the index.html.erb (belonging to archives)
<% #archives.each do |archive| %>
<tr>
<td><%= archive.file%></td>
<td><%= User.find(archive.user_id).name %></td>
<td>
<%= link_to 'Download', archive.file_url %>
::
<%= link_to 'Show', archive %>
::
<%= link_to 'Edit', edit_archive_path(archive) %>
::
<%= link_to 'Delete', archive, confirm: 'Esta Seguro?', method: :delete %>
::
<%= link_to "Process", archive.processfile %>
</td>
</tr>
<% end %>
this is the routes.rb line:
match "archives/processfile/:id" => "archives#processfile", :as => :processfile
the processfile method defined whitin archives_controller.rb doesn't have anything on it, i just wanted to test the functionality since I'm having a hard time getting the grip of the "rails way"
archives_controler.rb
def processfile
# #archive = Archive.find(params[:id])
#do something with the archive
end
All in all, what I ultimately want to achieve is to call the processfile method on a given archive(taken from the index table) to do something with it. On the example, I watered down the method call (not passing an archive or archive.file to it) to make it run, to no avail.
I've searched a lot (on google and in here) and haven't found a clear guide that would address my problem, probably because i'm new and can't fully grasp the concepts behind rails MVC.
I've read something about methods only being accessed by same controlers but I've seen sample code when people call methods on controllers from index views without declaring them as helpers. o.0
I know it's probably a silly confusion, but I can't figure it out :(
The way you've structured your route (i.e., match "archives/processfile/:id" => "archives#processfile") means that it's expecting an archive id to be passed. You need to adjust your link_to to pass one:
# app/archives/index.html.erb
<%= link_to "Process", processfile_path(archive.id) %>
The error you're receiving is because you're trying to call an instance method called processfile on archive, but there's presumably no method by that name. The second parameter of the link_to helper is a path, not an instance method.
EDIT:
If you're looking to make your routes more RESTful (which you should do if you've created an Archive resource), you can generate all your CRUD routes by declaring resource :archives in your routes. Then, within a block, you can declare a block of member routes, all of which will route to the specified action in your archive_controller.rb and enable you to pass an archive id to the action.
# config/routes.rb
resources :archives do
member do
get 'processfile'
end
end
You added the processfile method to your ArchiveController. That does not make the method available to the Archive model. If you want the method to be available to instances of Archive models then you need to put it inside the model as an instance method.
If you what you want to do is place a route to the action processfile in your ArchiveController then you can do so by adding link_to "Process", processfile_path(id: archive.id)

Ruby on Rails Passing Parameter In URL

This is probably a very simple fix but I've been unable to find an answer just yet.
My application has orders and tasks. Orders have many tasks. When a user clicks new task in the show order view, it passes the order.id:
<%= link_to "New Task", new_task_path(:order_id=> #order.id) %>
The url shows:
/tasks/new?order_id=1
I just don't know how to extract this and use it in my form? I have tried:
<%= f.text_field :order_id, :value => #order_id %>
But it's not working.
You can do:
<%= f.text_field :order_id, :value => params[:order_id] %>
Alternately, capture the value (with params) in the controller and assign it to #order_id there.
You are doing this wrong, which is a big deal in Rails where convention-over-configuration is such an important ideal.
If an order has many tasks, your route should look like this:
/orders/:order_id/tasks/new
And your routes should be configured thusly:
resources :orders do
resources :tasks
end
You should [almost] never find yourself passing around record ids in the query string. In fact, you should almost never find yourself using query strings at all in Rails.

How Rails 3 decides which HTTP verb will be used when clicking on a link generated by "link_to"?

I have two links:
<%= link_to("Edit", edit_product_path(product.id)) %>
<%= link_to("Delete", product, :method => :delete) %>
The generated links are:
Edit
Delete
When clicking both on Edit and on Delete, the GET method is used.
How Rails decided which method to use ?
What does data-method="delete" and rel="nofollow" mean in the Delete link ?
Browsers usually support GET and POST HTTP methods. To emulate PUT and DELETE verbs, Rails injects a special _method parameter when a form is submitted.
You specify the method you want to use by passing the :method option, as you did.
<%= link_to("Action with DELETE", path_to_something, :method => :delete) %>
<%= link_to("Action with PUT", path_to_something, :method => :put) %>
Unless specified, the default value is GET.
Starting from Rails 3, Rails uses unobtrusive JavaScript to handle the DELETE method. It passes the HTTP verb in the data-method attribute, which is an HTML 5 feature.
In your case, it doesn't work because you probably forgot to include a JavaScript library (e.g. Prototype or jQuery) and the Rails adapter.
Make sure you are using either jQuery or Prototype, and you are including the rails.js javascript file.
Also, don't forget to add the csrf_meta_tag.
<%= csrf_meta_tag %>
If you want to learn move, I wrote an article about Unobtrusive JavaScript in Rails 3 a few months ago.
Interesting. I can confirm that link_to with the ":method => :delete" option does work in the default scaffold. However, in trying to build a project without scaffolding, I cannot get link_to to work. button_to works with no problem, using the same parameters that failed with link_to.
In my HEAD tag I have the required javascript includes:
javascript_include_tag :defaults
csrf_meta_tag
Very confusing. I couldn't figure out what "secret sauce" scaffolding puts into play to make link_to work. While Rob's assertion that link_to delete does not work in Rails 3, may not be technically accurate, I found it practically accurate. Thanks Rob, I had been stuck for hours until I saw your post.
You can't use a link for a delete in rails 3
You need to use a button_to eg.
<%= button_to("Delete", product, :method => :delete) %>

Resources