Archive for April 2010

redirecting devise

Been playing around with Devise for a project that I’m helping a friend with. I ran into something that isn’t too intuitive, so I figured this would a good candidate for a post.

I have the following desired workflow:

  • Anonymous user submits form
  • Controller redirects to login/signup form
  • User logs in
  • Controller posts original form

With Devise, I found that I was being redirected back to the root_url no matter what I tried to throw in the way in the controllers. After I poked around a bit on the documentation, the google group and various blogs I came to the Warden documentation.

Note: Warden is awesome.

I realized that one way of solving the problem would be to add a Warden before_failure callback to my Devise configuration. This would intercept the request and save it to the session, so that I could come back to it in my application_controller. Devise provides an easy way to redirect out of a login, just no method to capture the original request (unless I’m completely missing something).

My attempts to put this into code suffered abject failure.

Fortunately I found this: http://rubyglasses.blogspot.com/2008/04/redirectto-post.html

Turns out you can’t redirect to a POST.

Philosophy of HTTP aside, this makes my desired workflow impossible. The only way to mimic it would be to add model creation logic to my ApplicationController#after_sign_in_path_for… and that would make me cry. Instead I’ve changed my workflow so it works with a GET:

  • Anonymous user submits form
  • Controller redirects to login/signup form
  • User logs in
  • User redirected back to form, which is filled out

Here’s my devise initializer:

Devise.setup do |config|
  # ... normal setup options
  Warden::Manager.before_failure do |env,opts|
    request = Rack::Request.new(env)
    request.session[:return_to] = request.env['REQUEST_METHOD']=="GET" ? request.env['action_dispatch.request.path_parameters'] : request.env['HTTP_REFERER']
    request.session[:return_params] = request.env['action_dispatch.request.request_parameters']
  end

And here’s what I added to my application_controller.rb:

def after_sign_in_path_for(resource)
  if request.session[:return_to].is_a? String
    [request.session[:return_to], request.session[:return_params].to_query].join("?")
  elsif request.session[:return_to].is_a? Hash
    request.session[:return_to].merge!(request.session[:return_params])
  else
    super
  end
end

Now in my form action, I need to make a small update:

## before
  @thing = Thing.new
## after
  @thing = Thing.new(params[:thing])

So after logging in, the user is redirected back to the form. All the params are there in the GET, though, so it fills itself out.

I could see cases where you would NOT want to do this… confidential information in a GET request may show up in a browser’s history, even if the page content passes over SSL. But for anonymous information, this works out pretty nicely.

ipad webkit

I can’t tell yet for sure, but I think the iPad Safari does finger interactions slightly differently from the iPhone version. On clicking something with a hover state, it activates the hover state. Then when you click again, it activates the link.

This is confusing. It also makes the coding of touch interactions trickier when taking both phones and tablets into consideration.

Just saying.