Difference between "super" and "super do |u|" using context of Devise

james picture james · May 24, 2015 · Viewed 7.7k times · Source

Ok so I think I get what super does standalone. Basically in Devise, if Users::RegistrationsController < Devise::RegistrationsController, then on any action, having a super will first call the logic for that same named action in the parent Devise::RegistrationsController, before then calling what you've written.

In other words...

class Devise::RegistrationsController
  def new
    puts "this is in the parent controller"
  end
end

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    super
    puts "this is in the child controller"
  end
end

# Output if users#new is run would be:
# => "this is in the parent controller"
# => "this is in the child controller"

# If super were reversed, and the code looked like this
# class Users::RegistrationsController < Devise::RegistrationsController
  #  def new
    #  puts "this is in the child controller"
    #  super
  #  end
#  end
# Then output if users#new is run would be:
# => "this is in the child controller"
# => "this is in the parent controller"

What I'm curious about is that I've seen some people do this:

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    super do |user|
      puts "something"
    end
  end
end

I'm having a hard time wrapping my head around what the do block is accomplishing. In my case, after the resource (the user) is created, I want to call an additional method on that resource (the user).

Current code:

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    super do |user|
      user.charge_and_save_customer
      puts user.inspect
    end
  end
end

I'm just wondering if this would be any different than doing:

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    super
    resource.charge_and_save_customer
    puts resource.inspect
  end
end

In case it's helpful, I've included the parent Devise::RegistrationsController code below:

def new
  build_resource({})
  set_minimum_password_length
  yield resource if block_given?
  respond_with self.resource
end

Answer

Sharvy Ahmed picture Sharvy Ahmed · May 24, 2015

Let me try to explain what is going on here:

class Users::RegistrationsController < Devise::RegistrationsController
  def new
    super do |user|
      user.charge_and_save_customer
      puts user.inspect
    end
  end
end

When you call super, you are going back to the parent new action, so the following code will be executing now:

def new
  build_resource({})
  set_minimum_password_length
  yield resource if block_given?
  respond_with self.resource
end

But wait... here is an yield, so it yields the current resource to the block, you can think of the block like a method, it needs a parameter (user), and here resource (from parent) will be the parameter:

# Here resource is assigned to user
user.charge_and_save_customer
puts user.inspect

Now, since the block is executed completely, it will start executing the super again:

respond_with self.resource