Search code examples
ruby-on-railsrubyruby-on-rails-4devisesuper

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


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

Solution

  • 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