Rails pattern to build new object related to current view/model - ruby-on-rails

Rails newb question: Say I'm currently viewing the show.html for one of my classes(Dog). This class has_many Flea(s). If I put a "add new flea" link that links to new_flea_path (/fleas/new) when clicked it will build a new Flea (`#flea = Flea.new) and render flea's new.html.haml with its form fields...
But, how/where do I automatically pass in the dog_id that the flea needs to reference?
is this a routes issue? maybe /dog/3/flea/new is what I need to handle in routes? or is this a controller thing?

What you are looking for is referred to as "Nested Resources". you can read up on them here

Related

Rails 4 new form defaults from params

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.

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

adding a method to an existing controller

I'm pretty new to rails, and I'm hoping you can help me understand how the following works.
At present I have a controller named projects (projects_controller.rb). From what I understand of ROR each controller has some basic (inherent) methods such as index, new, create, edit, show, etc. I would like to add a method called "help" and a view to display the help information.
At present a user can create many projects. The projects contain a set of fields that are populated by the user. I would like to add a help page that the user can access (via a link on the project screen) which explains each project field and how to best fill it out. I would like this to be an independent page (not just info displayed on the project's page).
As of now, the URL to the projects is (when editing a project): localhost:3001/projects/id/edit
I would like for the path to the help file to be localhost:3001/projects/id/help
If I want the help file to be located in the path listed above, am I correct in assuming that I need to create a new method, called "help", in the projects controller? And if so, is there something that I need to add to routes.rb to make it function? And would I use a link_to function in the Haml to create a link to it?
I'm sorry if this seems confusing or a lot of question. I appreciate your time. If you have any suggestions on whether on the right path please let me know. Thank you so much!
I think currently you have this in routes
resources :products do
end
just replace this with
resources :products do
get :help, :on => :member, :as => :help
end
And add method in controller and add view named help.erb.html(if you r using erb) in views/product folder.
you can use help_path and help_url

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

In Rails, I'm trying to pass an attribute to a Model's "new" method, then to the "create method". What's the most efficient way to do this?

In my Rails application I have a Forum with many Topics. From the Forum's, "show" page I have a list of Topics with a link to create a new Topic. My goal is to make sure that when creating the new Topic I have the ID of the Forum when I get to the Topic's "create" method. Right now I'm thinking I'd have to create a hidden field on the Topic's "new" page and set the hidden field to the Forum's ID. I was wondering if there is a better, cleaner way.
Thank you for looking!
Nested resources are the way to go for this, so you nest the Topic resource within the Forum resource. Take a look at these tutorials:
Nested Resources Rails Guide
Nested Resources Railscast

Resources