In my app I have automated e-mails reminding applications to complete the next step in the interview process. The e-mail has an opt out link which, when clicked, should hit a controller action that fires a state machine event to change their state to opted_out
. The link is not working, and from the localhost console it seems to be because the link is still producing a GET request, for which there is no route (the error is ActionController::RoutingError (Not Found):
).
Here is the console displaying the undesired GET request:
Started GET "/worker/application/opt_out.1" for 10.0.2.2 at 2014-08-29 17:08:06 +0000
Processing by LandingController#show as
Parameters: {"category"=>"worker/application", "location"=>"opt_out"}
Here is the link:
link_to 'stop getting these reminders', opt_out_worker_application_url(@worker), method: :put, style: 'background-color: #fff; color: #56A0D3; display: inline-block; margin-bottom: 5px; margin: 0px; padding: 0px; text-decoration: none'
Here are all the routes for the worker
namespace:
# routes.rb
namespace :worker do
resource :application, only: [:create, :new, :show] do
member do
put 'opt_out' => 'application#opt_out'
end
end
resources :jobs do
member do
post 'compete' => 'jobs#compete'
delete 'compete' => 'jobs#withdraw'
end
resources :comments, only: [:create]
resources :timesheets, only: [:create, :new, :show]
end
resources :banks, only: [:create, :new, :destroy]
scope 'money' do
root to: 'money#index', as: 'money'
post 'withdraw' => 'money#withdraw', as: 'money_withdraw'
end
get 'profile' => 'profiles#show'
root to: 'jobs#index'
end
Here is the controller action:
# applications_controller.rb
def opt_out
@worker = Worker.find(params[:id])
if @worker.interview_requested? or @worker.onboarding_requested?
if @worker.fire_state_event(:opt_out)
AdminsMailer.sweeper_opted_out(@worker.id)
redirect_to root_url, notice: "You have successfully opted out of the application process. We're sorry to see you go, and hope you'll consider returning in the future!"
else
redirect_to root_url, alert: "There was a problem. Please contact Support if you need further assistance."
end
else
redirect_to root_url, alert: "There was a problem. Please contact Support if you need further assistance."
end
end
Here is rake routes
:
opt_out_worker_application PUT /worker/application/opt_out(.:format) worker/application#opt_out
worker_application POST /worker/application(.:format) worker/applications#create
new_worker_application GET /worker/application/new(.:format) worker/applications#new
GET /worker/application(.:format) worker/applications#show
It's because this is a link in context of an email. It's documented behavior:
method: symbol of HTTP verb - This modifier will dynamically create an HTML form and immediately submit the form for processing using the HTTP verb specified. Useful for having links perform a POST operation in dangerous actions like deleting a record (which search bots can follow while spidering your site). Supported verbs are :post, :delete, :patch, and :put. Note that if the user has JavaScript disabled, the request will fall back to using GET. If href: '#' is used and the user has JavaScript disabled clicking the link will have no effect. If you are relying on the POST behavior, you should check for it in your controller's action by using the request object's methods for post?, delete?, patch?, or put?.
There are security reasons to disallow executable JavaScript in emails, as well as forms. Therefore, it's impossible to render a form with JS and execute a PUT
request, so this falls back to GET
as stated in citation above.
The method option on link_to
works because your app includes some javascript that intercepts clicks on the link. That javascript creates a form with the correct method (adding an input element for request methods browsers don't support) and supports that.
This won't work in an email: your site's javascript isn't present and email clients don't usually run javascript.
Including an actual form works in some cases, but very frequently doesn't (or displays scary warnings) - see this question for example.
There's not much alternative to just having a plain "get" link unfortunately (you could of course have that go to a confirmation page where you have a button/link that will fire a post request).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With