It doesn't.
form_with
just uses the polymorphic routing helpers to find a routing helper method based on convention over configuration and calls it dynamically to get the path for the forms action
attribute.
The routing helper methods are generated by calling the routing macros in your config/routes.rb
file and available in the view and controllers. When you call resources :products
to declare the conventional routes it will define the helper methods products_path
and product_path
(and many more).
This has nothing to do with the controller and you can actually create the form just fine with just a model and the expected helper methods.
The destination controller only actually matters after the user submits the form and the Rails router attempts to match the incoming HTTP request to a controller and action combo.
ActiveModel::Naming
The polymorphic routing helpers can guess what the helpers are named through the ActiveModel::Naming API. This just assumes that routes, tables etc can be derived from the name of the model class. This is a huge part of the "magic" in Rails which lets your models interact with the rest of the framework.
irb(main):006:0> class Product; include ActiveModel::Naming; end
=> Product
irb(main):007:0> Product.model_name
=>
#<ActiveModel::Name:0x00005570c2876c60
@collection="products",
@element="product",
@human="Product",
@i18n_key=:product,
@klass=Product,
@name="Product",
@param_key="product",
@plural="products",
@route_key="products",
@singular="product",
@singular_route_key="product",
@uncountable=false>
New vs old records
polymorphic_url
first figures out if its generating the collection path or member path by calling new_record?
on the model.
If the record is a new record or the class itself it will call model_name.route_key
and ends up calling the collection path helper products_path
.
If the model is persisted it calls model_name.singular_route_key
and passes model.to_param
as the argument resulting in a call to the the member path helper product_path(product.to_param)
. to_param
defaults to using product.id
as the identifier for the record.
form_with
also uses persisted?
to determine if it should use the POST or PATCH HTTP method for creating or updating.