← Back to Upcase

Reusing Private method from different class


(Anthony Lee) #1

Dear fellow upcase community developers,

Some rails newbie question.

I have a private method on accounts_controller.rb that authenticates 3rd party service (Salesforce) allowing me to pull accounts record from 3rd party (Salesforce):

private

def client
	client = Restforce.new :username => ENV['SALESFORCE_USERNAME'],
		:password			=>	ENV['SALESFORCE_PASSWORD'],
		:client_id			=>	ENV['SALESFORCE_PASSWORD'],
		:client_secret		=>	ENV['SALESFORCE_PASSWORD'],
		:password			=>	ENV['SALESFORCE_PASSWORD'],
	client.authenticate!
	client
end

Now, I created another controller called cases_controller.rb in which I want to do CRUD operation with different object/table. Do I just copy and paste this authenticating method to every different controllers that needs authentication with 3rd party, or is there a better way not to repeat this code? How can I use same private method to different classes?

Any tips will be much appreciated! Thanks!


(Anthony Lee) #2

Do I use put this in ApplicationController and do before_action?


(Frank West) #3

Throwing this method into the ApplicationController is a good start. This tends to lead to a bloated ApplicationController over time, so it is something to be aware of.

With the code that you posted, there is no advantage of performing a before filter. You are returning an instance of a Restforce class, “client”, and in a before filter you would no longer have a handle on the instance of the Restforce class “client”. Something like this is a short implementation.

class ApplicationController

  private
  
  def restforce_client
    client = Restforce.new :username => ENV['SALESFORCE_USERNAME'],
		:password			=>	ENV['SALESFORCE_PASSWORD'],
		:client_id			=>	ENV['SALESFORCE_PASSWORD'],
		:client_secret		=>	ENV['SALESFORCE_PASSWORD'],
		:password			=>	ENV['SALESFORCE_PASSWORD']
	client.authenticate!
	client
  end
end

class AccountsController < ApplicationController
  def index
    accounts = restforce_client.query("accounts query code")
  end
end

class CasesController < ApplicationController
  def index
    cases = restforce_client.query("cases query code")
  end
end

However the details of Restforce is leaking into your controllers using it this way and is probably not a final solution either. One solution could be to wrap the Restforce client in a wrapper class with something along the lines of this pseudocode (no error handling and such):

class CrmClient
  def initialize
    @client = Restforce.new :username => ENV['SALESFORCE_USERNAME'],
		:password			=>	ENV['SALESFORCE_PASSWORD'],
		:client_id			=>	ENV['SALESFORCE_PASSWORD'],
		:client_secret		=>	ENV['SALESFORCE_PASSWORD'],
		:password			=>	ENV['SALESFORCE_PASSWORD']
    @client.authenticate!
  end

  def accounts
    @client.query("accounts query code")
  end

  def cases
    @client.query("cases query code")
  end

end

class ApplicationController

  private
  
  def crm_client
    CrmClient.new
  end

end

class AccountsController < ApplicationController
  def index
    @accounts = crm_client.accounts
  end
end

class CasesController < ApplicationController
  def index
    @cases = crm_client.cases
  end
end
  

This just extracts the details out of your controllers and the implementation could be changed in one place or swapped out completely.


(Anthony Lee) #4

Thank you! I will try to follow through your suggestion. I really appreciate you taking time to explain it to me.


(Luke Mayhew) #5

As a small addition to @frank_west_iii’s great post, you may want to memoize #crm_client in ApplicationController so that you can reuse the same CrmClient object each time. This helps with performance, since you don’t get slowed down by an extra authentication request to an external service each time you access #crm_client.

class ApplicationController

  private
  
  def crm_client
    @crm_client ||= CrmClient.new
  end

end

On the other hand this may have drawbacks depending on your specific use case, so your mileage may vary - take this with a grain of salt.


(Anthony Lee) #6

Hey @frank_west_iii, are you suggesting to create a different class called “CrmClient” in the app/controller directory? I am still learning what needs to be implemented as service objects, helpers, concerns, etc as mentioned in following links:

https://github.com/justin808/fat-code-refactoring-techniques/pull/3


(Frank West) #7

My opinion is that this is just a plain old ruby object that sits inside your app/model directory named crm_client.rb (class CrmClient). It could go wherever you want it to go. If you have not listened to the latest Bike Shed episode, they have a good discussion on this. http://bikeshed.fm/5

It might be considered a service object in some circles, not sure, but I wouldn’t bother putting a label on it myself.