Rails 4 new form defaults from params - ruby-on-rails

I am using form_for in the _form.html.erb view in order to create my form for both the edit and new actions, as per a standard scaffold.
I have a model Owner which has_many pets.
I would like to put an html link on my views/owners/show.html.erb to create a new pet for said owner. This link will point to the new action of pets_controller.rb which when accessed will render the view in pets/new.html.erb
What I want to happen is for the the owner_id to be passed with the link in the url to the new action of pets_controller.rb and then be used as the default for a collection_select in pets/new.html.erb
So I have a link to create a new pet but because that link was on a specific owner page, I want the form to create a new pet to have that owner already set, so the user does not have to select from the list.
This has to be done without changing the behaviour of the edit action/view in pets.
I know that I can pass GET arguments then access them in the controller via params, then create variables in the action which are passed to the view. I can then manually check for a default and set it in the view. I do not need assistance in coding if this is the only solution.
Is there is a better way to do this? A format with which I can pass the params such that the view will just pick them up? Without manually editing my controllers and views?

While my personal inclination would be to do as you said and pass a parameter in the link helper and then access the params array in the pets view, you may find that this is the perfect opportunity to explore Nested Resources. Essentially, you could declare owners/:owner_id/pets/:pet_id route with:
resources :owners do
resources :pets
end
You could then link to this route, and reference :owner_id without having to append the query string to the URI (making somewhat cleaner for reuse).
This is likely more work for you, but also potentially more extensible (and certainly more inline with the Rails way of doing things).
REVISION
Added the following regarding link helpers to the comments, but wanted to reflect it in the answer as well.
To show a pet should be:
<%= link_to owner_pet_path( owner_variable, pet_variable) %>
To view pets' index index should be:
<%= link_to owner_pet_path( owner_variable ) %>
The answer given to this question is fantastic.

As #ConnorCMcKee suggests it would be wise to consider nesting your routes. However, if you are a beginner as myself I found that it helped my learning to simply nest my second controller into the first (i.e. nest PetsController into OwnersController) as a first step. Then afterwards I would continue with the routes.
The method would be something like:
1./ In owners/index.html.erb:
Links to PetsController index action
The key to make this work is to send the :owner_id in your link parameters. Then that Pets index action will have access to that :owner_id and know which :owner_id called it.
2./ In PetsController you would then be able to find that Owner using that id, like so:
params[:owner_id]
Then your actions can start to take advantage of knowing what Owner called them. Remember though that all your redirects inside your PetsController need to preserve params[:owner_id]. That is because once you are inside that nested structure you have to maintain it and stay inside it and always know which :owner_id you are working with.

Related

link_to 'new' action in a different controller?

I want to have a link at the bottom of my show.html.erb that links to the new action in a different controller.
class Sample < ActiveRecord::Base
belongs_to :song
end
class Song < ActiveRecord::Base
has_many :samples
end
So, at the bottom of the show action for songs, I want to link to new for samples. This seems pretty easy, but I'm struggling to figure this out. I would also like to pass the id from the song to the form as :song_id
Fiddy, because you're new, let me explain how this works...
Routes
Your problem is that you don't understand the Rails routing structure - I'll hopefully explain it for you.
Rails, since it's an MVC framework, builds a series of "routes" for you. These "routes" are stored in the file available at config/routes.rb.
Routes, as described by the Rails documentation are as follows:
The Rails router recognizes URLs and dispatches them to a controller's
action. It can also generate paths and URLs, avoiding the need to
hardcode strings in your views.
The most important thing you should consider here is the way the routes generate paths for you. These paths are simply Rails "helper" methods, which you can call from your views. The reason these exist is two-fold -
They provide you with a DRY (don't repeat yourself) way of accessing / manipulating data
They are constructed around objects, helping maintain the object-orientated nature of Rails
These will likely mean nothing to you. However, what you need to realize that if set up your routes correctly, it seriously helps your app's infrastructure immensely.
--
Rails
This leads us quite nicely onto appreciating the way in which Rails works
Rails is an MVC (model view controller) framework. This might seem somewhat trivial, but in reality, it's one of the most important aspects to learn about Rails development, and here's why:
The Rails software system works by taking "requests" (user input) and then routing them to specific controller#actions. Those controllers then build model data from the database, and will translate that into either variables or objects, which you can use in your view.
The reason I mention this is that this type of development takes a lot of getting used-to, in that your program's flow is not about logic / functionality, but the accessibility of data. Therefore, when you ask about the routes or other parts of your app, you need to firstly remember what data you wish to show, and also how you want that data to be shown - this will give you the ability to construct & use the routes / controller actions which will get it to work properly
--
Fix
In terms of what you're saying, the way you'd go about achieving the result you want will be to use a nested route:
#config/routes.rb
resources :songs do
resources :samples #-> domain.com/songs/:song_id/samples/new
end
This will create a new route for you (which you can check by firing rake routes in your rails c (console). This will give you a path to use for your samples#new action:
#app/views/songs/show.html.erb
<%= link_to #song.name, new_song_sample_path(#song) %>
The above link will take you to the samples#show action, which you'll be able to populate with as much data as you require from the samples controller. The important thing to note is this action will have params[:song_id] available for you to either build an object from, or otherwise
<%= link_to "New Sample", new_sample_path(:song_id => #song_id) %>
Where #song_id is the variable that has that id in it.
Set paths in link_to tag which you can get by running rake_routes in terminal.
Ex
link_to "New song", new_sample_path(#song)
In the example given above #song is the instance variable of your current page.
You can also get some idea from here:
link_to Base URL Randomly Changed
Song Model:
accepts_nested_attributes_for :sample, allow_destroy: true
Route:
resources :songs do
resources :samples
end
Song's Show file:
<%= link_to "New Sample", new_song_sample_path(#song) %>
in url it will be:
/songs/:song_id/sample/new
Try this and let me know it works or not... I hope this helps you

Rails MVC: Should a form collection_select know which collection records it should render?

I'm a little stuck here with a conceptual issue. Assume following [abstracted] setup for Post, Tag, and User:
Post belongs_to Tag
Tag has_many Posts
User has_many Tags,
has_many Posts
A user can only tag a post with one of his associated tags.
In the new post form view, I now have following options for selecting a tag:
f.collection_select :tag_id, current_user.tags, ...
f.collection_select :tag_id, #tags, and in the controller's new action:
#tags = current_user.tags
Question: What is the conceptually correct option?
From an MVC perspective, I definitely tend towards using the second option. It does not seem right that the view knows that the tags it should render in the collection_select are associated to a user (even more specific, the current user!).
However, in the official api documentation for collection_select and some other tutorials around the web I see something like this:
collection_select(:post, :author_id, Author.all, ...)
which clearly favors the first option. On the pro-site of this approach, I do not need to redefine the #tags in the create action of the controller in case the post's save action fails and I want to render the new action again.
Thank you for your suggestions in advance.
There's nothing wrong with your first option. For starters, it's simpler (one less line of code). Setting an extra instance variable within your controller doesn't really gain you anything.
A good way of thinking about the controller is that it should only be doing things like setting variables when they are based directly upon input that only the controller receives (such as parameters in the URL, query string, or data from a POSTed form).
The view in this case isn't deciding how to determine the current user - that's still coming from your controller (although probably inherited from the top-level ApplicationController or something like Devise?), all it's doing is deciding that it's the tags of the current user which should be be selectable within the drop-down view.
Does that help?

ruby on rails link_to newly created page

I've created in rails(3) a new html page in my project model named contact.html.erb and I am interested in linking to it from a different page, i've added the code:
<%= link_to 'contact', contact_project_path(#project) %>
Project is a model that belongs_to a User (which is the contact in the contact page).
This link gives me an error message saying that contact_project_path doesnt exist. I know I need to define it somewhere but I don't know where or how. I want the page to be specific per project. I've created an action in the project_controler named contact and left it empty.
What am i missing?
I've searched for a soloution for quite some time and haven't found an answer to this question anywhere else. I'm aware its a little bit basic but I have no other options.
-REVISED ANSWER-
Based on comments I see you're not trying to associate a contact model, just a view. In that case, you still need to change the routes file, but you need to decide if you want to get a single contact page for all projects, or one contact view per project. IE:
# Collection Contact
example.com/projects/contact # IE One contact view for all projects
# Member Contact
example.com/projects/1/contact # IE One contact view per project
Either way you need to use a block for your project resource in your routes file. So, if you want there to be one contact page for the collection (all projects), do:
resources :projects do
collection do
get 'contact'
end
end
Or, if you want one contact view per member (one per project) do:
resources :projects do
member do
get 'contact'
end
end
Either way this will give you helper methods which you can put into link_to
For the collection it should be contact_projects_path (no arguments), and for members it should be contact_project_path(#project) (pass in the project as an argument.
You can use get post put delete or match as a parameter in a collection or member block, that just tells Rails what kind of request to handle at that URL, and what helpers to generate. For normal views you want a GET request.
I hope this finally answers your question :)
-ORIGINAL ANSWER-
This is for routing to a MODEL, not just a view
Add to your routes.rb:
resources :projects do
resource :contact
end
If I understand your question correctly, that should create a helper called project_contact_path().
This record will be projects/123/contact.
If you want more than one contact per project, you'll need to make it plural (resource :contacts). Then your records would look like projects/123/contacts/123.
See http://guides.rubyonrails.org/routing.html
You would link to this using link_to( project_contact_path(#project) ) if there's only one contact per project, or link_to( project_contact_path(#project,#contact) ) if there are multiple contacts per project.
Have a look here:
http://edgeguides.rubyonrails.org/routing.html
You should be aware that contact_project_path(#project) isn't a restful routes (unless contact_project is a model but there is little chance). You'll have to declare this name, use :as

Why does Rails use plurals for new and create?

I understand why a Rails index method would use the plural form of a resource - we're showing all projects, for example.
And I understand why the show method would use the singular form - we only want to see one project, with a particular ID.
But I don't understand why new and create would use the plural. Is there a way to create more than one project at a time? Is there some other reasoning for using the plural here that someone could explain?
New and Create aren't plural, in the way I think about REST. Instead, I think about it like:
whatever.com is your base domain, and whatever.com/books means that you have a collection of resources each named book. The collection itself is named books.
So, when you want to create a new book, you are asking the collection for the information needed to create a new book. This becomes /books/new
When you actually create the book, you are posting information to /books. The HTTP verb is POST, so when you POST to your collection, you execute the create action.
This looks like a good starting point on REST.
I thought they were always plural. Scroll down a bit on this page for an example of the routes generated by resources :photos
Whether you're GETting a single resource or POSTing to the collection, you're still in the domain of photos. So, search the domain of photos given an id, POST a new photo to the domain of photos, etc.

custom action for a nested resource without adding to routes

This is probably really simple but I have a nested resource lets say:
map. resources :book, :has_many => :pages
I write an action called "turn" that increases page.count by 1. How do I call this action using a link_to? Thanks a lot in advance.
It's hard to tell where your page.count comes in. In Railish, you would find pages.count (note the 's'). Further, count (and also size) is a read-only attribute on arrays and hashes et.al. provided by ruby that returns the number of elements. You don't set count.
Next, I'm not sure where your turn action is supposed to live, on the Book or the Page? And what is supposed to happen after it does what it does? Finally, a route is what makes an action an action -- without it, it's just a function.
For a moment, we'll assume you are trying to store the number of times a Page in a Book has been visited. It would be a better idea to have an instance variable called reads or times_viewed etc. in your Page model. Assuming your Book model is using restful routing, in Book's show action, you create an instance variable of the Page model being viewed and increment its reads attribute before rendering the view.
If you are trying to make a link sort of how 'Like' works in Facebook, meaning you want to update a record in a table without sending the user to a different page, you'll need to use link_to_remote* and some javascript. In that case, I'd just refer you to the Railscasts on that subject.
*I think as of Rails 3, link_to_remote became just link_to with :remote => true.

Resources