Rails 5: form_for vs form_with - ruby-on-rails-5

Rails 5 has introduced new form helper method form_with.
How does it differs with form_for and when is it more appropriate to use?

This is really in preparation for rails 5.1 where only form_with should be used. It's meant to serve as a replacement for the two methods form_for and form_tag.
form_for and form_tag in Rails were very similar, both allowed you to create a form tag but the first one uses model’s attributes to build create or update form, while the second one simply creates an HTML form tag with the passed URL as action.

Use form_with (more up-to-date)
form_with is the latest.
form_for and form_tag are obsolete.
Why is there a change?
See Kasper Timm Hansen's pull request - I cannot state it better than pull request itself:
form_tag and form_for serve very similar use cases. This
PR unifies that usage such that form_with can output just
the opening form tag akin to form_tag and can just work with
a url, for instance.
This means you don't need to use form_tag if you don't have a model. You can use the form_with helper and it can still handle URLs.
Note Re: Ids and Classes: The Ruby 5.1 version of form_with, by default doesn't attach a class or id to the form. For Rails 5.2 and later, however, form_with WILL automatically generate ids based on the model, if present—basically the same id behavior as form_for, so you will NOT need to manually specify them anymore. (source)

Existing answers are great. What I also found helpful were the first few paragraphs here. Basically:
form_tag and form_for are soft deprecated and they will be replaced by form_with in the future.
DHH's proposal: https://github.com/rails/rails/issues/25197
The pull request: https://github.com/rails/rails/pull/26976/files (I didn't read it, but it could be useful)

Related

The relationship between routes.rb and the "paths" that link to pages?

This question stems from Hartl's Rails Tutorial (progressed in chapter 9) - sorry if it seems particularly noobish...
Currently, I understand that in the routes.rb file, when a page was defined using, for example:
match '/help', to: 'static_pages#help'
a link generated in an embedded ruby:
<li><%= link_to "Help", help_path %></li>
would function. The help_path, and specifically the word "path", would correspond to the "#help" defined in routes.
However, the routes file did not define links for items such as users_path (used to show all users), or edit_user_path(current user) (used to edit settings of the current user) - I was confused as to where they were defined, and how they are able to correctly function and link me the desired places.
Thanks!
The routes file does, in fact, get an entry determining the behavior of users (shown in listing 2.2). It gets created through the scaffolding described here.
resources :users
By convention a "resource" entry in your routes.rb file sets up a number of paths that will be associated with users. Refer to the Rails Guide for a good overview.

Basic nested routing in Rails 3

NOTE: I have read Routing From the Inside Out AND the Engine Yard blog post on routing.
I'm building a fantasy sports league, I have a League model that supports the seven basic restful operations, and they all work fine.
I've added the following my routes.rb
resources :leagues do
member do
get :invite
post :sendem
end
Later in the file I have a resources :leagues for the basic actions
and when I rake routes I can see:
invite_league GET /leagues/:id/invite(.:format) {:action=>"invite", :controller=>"leagues"}
sendem_league POST /leagues/:id/sendem(.:format) {:action=>"sendem", :controller=>"leagues"}
which is what I would expect. I have two functions in the League controller: "invite" which creates the form for collecting email addresses, and "sendem" which invokes a mailer to actually send the invitations.
The get for /league/:id/invite does indeed produce the form.
The form tag I am using looks like:
<%= form_tag(sendem_league_path, :method => "post") do %>
and yet the HTML that is rendered looks like:
<form accept-charset="UTF-8" action="/leagues/1" class="edit_league" id="edit_league_1" method="post">
And hence on submit generates a PUT which is completely wrong. (It should post to the sendem function.) My change to the routes file appears above the generic resources :leagues line, so it should have a higher priority.
I'm sure there is something dead-simple that I missed but I'm out of ideas. (And hair).
You should not use form_tag for manipulating resources.
You should use form_for.
Check out form helper guide - section 2 "Dealing with Model Objects". It takes care of deducing whether to use POST or PUT for a model object. For example, if your model object is new, it will use post on "resources"'s URL. if it is already existing database entity, it will use PUT to that "resource"'s URL.
ARGH some form handling error code at the top (form for #league) created a second form on the page for editing.... (left out of code snippets above for brevity). Original code seems to work as expected with that other code commented out. Thanks to vladdruzh for convincing me I was on the right track and to Salil for making me think to read the rendered HTML top to bottom.

form_tag or form_for for updating an external API? Rails 3.2

When connecting to an external json api and submitting a form to update a resource is it better to use form_for or form_tag ?
Specifically I'm using the Shopify API http://api.shopify.com/
In config/routes.rb I made default resource routes with resources :variants and now I'm trying to make a form that updates a variant resource but can't configure the form to have the proper action.
==== Update ====
Yes there's a shopify API gem: https://github.com/Shopify/shopify_api that does most of the heavy lifting- just can't quite figure out how to make it work.
To update an #variant object I need to PUT here: PUT /admin/variants/#{id}.json
But I can't quite construct the form_tag properly. I have these routes:
rake routes:
variants GET /variants(.:format) variants#index
POST /variants(.:format) variants#create
new_variant GET /variants/new(.:format) variants#new
edit_variant GET /variants/:id/edit(.:format) variants#edit
variant GET /variants/:id(.:format) variants#show
PUT /variants/:id(.:format) variants#update
DELETE /variants/:id(.:format) variants#destroy
Does your app define the Model? If so, you should be able to use form_for. If not, then you have to use form_tag.
form_for takes a Model instance as parameter (hence the name). Output of form fields is more concise, since form_for can infer a lot about from the model. You can also use fields_for to do nested forms.
If you use form_tag, then you need to write more code to properly construct the HTTP parameters so that Rails can reconstruct the params hash on the server.

Rails Routing with Dynamic Segments

So I just started using Dynamic segments as I need them to specify certain elements for grabbing data from AWS S3 via HTTParty.
I have this match statement in my routes.rb file:
match ':installation/:venue/:controller(/:action(/:id))'
This works great and allows me to do exactly what I want to do, which is pull in the installation and venue and use them with HTTParty to get their corresponding information from S3.
Now I need to keep my links through out persistent like these due to the fact that my application controller reads these in. So for example when I write a link_to I have had to do the following in a view/partial:
<%= link_to some_name,
"#{#installation}/#{#venue}/#{controller.controller_name}/show/some_id" %>
If it was just this ugliness I had to deal with that wouldn't be a problem, but I don't understand how I can pass around options in regards to this.
So basically is there a way to have resourceful routes for dynamic segments?
You can use polimorphic_url
http://apidock.com/rails/ActionDispatch/Routing/PolymorphicRoutes/polymorphic_url

rails omniauth, routes, link_to, and base_uri

I've started using omniauth in a project but having some issues in production vs. development. In production my RailsURI is /myApp and in development it's just /
In my templates I use <%= link_to "/auth/google" do %> which works for production but not development since I need to link to "/myApp/auth/google". My route look like this:
match '/auth/:service/callback' => 'services#create'
Ideally, I'd like to have some setup like:
<%= link_to provider_google_path %> generated for me for each provider I support. I guess I could support this by putting in a helper like <%= link_to provider(:google) %> but was wondering if there was a best practices way of solving this.
It appears there is a similar question here that has an answer. You may want to take a look at that.
Create named routes for OmniAuth in Rails 3