I have a situation where an action is called on a CallbackController. I have no control over this as it is prescribed by a framework. In response to this action I need to create a new Authentication. My AuthenticationController has create and a destroy actions.
How should I proceed? It seems to me my options are:
- Duplicate the code from AuthenticationCOntroller’s create action into my CallbackController (Obviously far from DRY)
- Call the create method directly from the CallbackController (This kind of inter-controller-communication seems to be frowned on)
- Break the code from the AuthenticationController’s create action out into a helper class which is shared between the two controllers
None of these seem like the correct answer. So can anyone suggest a better approach?
My callbacks controller:
class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController
def facebook
authorize
end
def twitter
authorize
end
private
def authorize
omniauth_data = request.env["omniauth.auth"]
#Check to see if we have an authentication for this provider already
authentication = Authentication.find_by_provider_and_uid(omniauth_data['provider'], omniauth_data['uid'])
#If an authentication already exists, sign its User in
#Otherwise create a new authentication for the current user
if authentication
flash[:notice] = "Signed in successfully with " + omniauth_data['provider']
sign_in_and_redirect(:user, authentication.user)
elsif current_user
current_user.authentications.create(:provider => omniauth_data['provider'], :uid => omniauth_data['uid'])
flash[:notice] = "Authentication successful"
redirect_to user_profile_url
else
user = User.new
user.apply_omniauth_data_as_authentication(omniauth_data)
if user.save
flash[:notice] = "Signed in successfully with " + omniauth_data['provider']
sign_in_and_redirect(:user, authentication.user)
else
#save the omniauth data in a session so we can add the authentication once registration is complete
session[:omniauth] = omniauth_data.except('extra')
redirect_to new_user_registration_url
end
end
end
end
and my authentications controller:
class AuthenticationsController < ApplicationController
#Controller for representing Authentications provided by
def index
current_user.authentications if current_user
end
def create
end
def destroy
@authentication = Authentication.find(params[:id])
provider = @authentication.provider
@authentication.destroy
flash[:notice] = "Destroyed authentication from "+provider
redirect_to authentications_url
end
end
If you control the code for both CallbackController and AuthenticationController (they’re not coming from the framework), you could pull the common code out into a common superclass. Or just put it in a
moduleandincludeit.Rather than cut-and-pasting the entire
createanddestroymethods into a new Module, I would be inclined to find smaller, coherent parts and put them in methods with meaningful names. It may be that thecreateanddestroymethods on both CallbackController and AuthenticationController can be implemented in just 2 or 3 lines using those smaller methods.If you pull coherent groups of lines out into smaller methods, you could consider adding those methods as an extension to
ActionController::Baseif it seems appropriate. (i.e. if they are general enough.) If the small methods are useful in other parts of the application, that’s a bonus.