Making a single create form using Single Table Inheritance in Rails - ruby-on-rails

I'm using STI in Rails, and I've got a Vehicle object, that has many different types of subclasses, like Car, Truck, etc. It's for a simple app, so STI works fine in this case, but I'm having trouble creating a single form where any type of Vehicle record can be created.
Using the following routing:
resources :vehicles
resources :cars, :controller => 'vehicles'
resources :trucks, :controller => 'vehicles'
I can have /cars and /trucks routing set up, and both pointing to the same form. However, since the form is pointing to the vehicles controller, and generating a Vehicle object for the form, it has no way to know that the /cars url should create a Car object.
I'm trying to get a routing system set up where /cars would point to a form that would intrinsically know to make a object for the form using either Car.new or even Report.new(:type => "Car"). I thought about working a routing system like /vehicles/:subclass, and somehow using params[:subclass] in the controller, but I also can't figure out how to do that sort of routing and still avoid other routing errors caused by Rails' STI magic.
I could always parse the URL to get the value, but that seems like an unsafe and hacky way to go about it.
I'm curious if anyone has any advice or experience on the Rails way to do this. Thanks!

Since you want to use the same form for all vehicles, then I'm assuming all the fields are the same except for the object type. Then why not have a combo box in the form to allow the user select what type of object the user want to create?
You can then handle the proper object persistent in the create action in the controller.

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.

Rails index on show page

What is the standard practice for a rails app when a standard 'show.html.erb' for one model essentially just lists its has_many of another model? There is no index for the 'child' model as without the context of its 'parent' model, a list of them would be useless.
Creating /parents/1/children/1 feels like the right thing to do (especially in terms of the API) and yet on the /parents/1 page there would be nothing but a bit button saying "carry on" ... again, useless.
Do I creat separate routes, like the one above, purely for the API or am I missing a cleaner more 'Railsy' way.
EDIT
I'm not sure the above is totally clear. If I have an Exam that was completely made up of Questions, would the Rails way to be to link to /exam/1/questions or purely to /exam/1 with a list of questions. Obviously the API would go to /exam/1/questions in most cases but what about the standard HTML page? It would seem like it wants to be /exam/1 but with nested resources the create route (by default) goes to the /exam/1/questions route. If there is a validation error on the /exam/1/questions/new route the form is rendered again on the create (/exam/1/questions) route; if the page is then refreshed you then get a 404 error as there is no index for the children.
All this leads me to believe that Rails expects be an index action for the majority of models; but in the above example this feels odd.
If I understand you right you dont whant the route to /parents/1 to be on your path. For this reason you should specify it in your routes.rb
resource :parents, except: :show do
resource :children
end

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.

Forms for polymorphically associated resources where a basic resource path "/resource" does not exist

I have a polymorphic resource Location that can latch on to, let's say, instances of Store. The extent to which this "should" be a polymorphic resource is questionable, but that's besides the point at the moment.
A location accesses its polymorphic parent via location.locatable.
Since the location will always be the child of a resource like a store, I'd like to remove the route to POST /locations and instead have the application direct POST requests to /stores/1/locations.
Now, the default Rails method when it comes to dealing with polymorphic associations is to do it at the controller level with
def new
#location = Store.find(params[:store_id]).vendors.new
end
This, as expected, prepopulates location.locatable_id and location.locatable_type. Wonderful. But it still routes to locations_path by default, which I would like to get rid of.
And I try, it raises a route not found error.
So how can I prepare a general location form that is extensible to new parents?
I have come to the following:
form_for(#location, :url => [#location.locatable, #location]) do |l|
Which seems to work fine, but the passing of a path as an array irks me, and for some reason I can't find any documentation on the subject. Is there a better way to accomplish this? I'm new-ish to polymorphic resources, and it would be nice to do this right from the start.
Also, a (quite) minor peripheral issue is that this doesn't cooperate out of the box with the default RSpec-generated view spec since Rails actually tries to find #location.locatable. This is fairly easy to get around though. I was planning on rewriting the view spec from top to bottom anyway.

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