Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails: Where do I put my API methods?

I'm very new to Rails, and I'm a little overwhelmed where I do simple things like create an API call. I've set up a route at /reports which has this controller:

class ReportsController < ApplicationController

  @client = # Api-accessing gem

  @all_reports = []

  def self.request_report

    begin
      puts "Step 1:"
      step1 = @client.request_report(opts = {"max_count" => 1})
      step1_result = step1.parse
      puts "Done!"
      puts step1_result

    rescue Excon::Errors::ServiceUnavailable => e
      puts "Didn't work"
      logger.warn e.response.message
      retry
    end
  end # End request_report

  request_report

end

This correctly calls the external API when I first load the /reports route, but when I refresh the page the code isn't re-run.

Perhaps I'm misunderstanding what controllers are used for? Am I meant to be putting this code somewhere else? Or is there a caching issue?

like image 898
JVG Avatar asked Dec 18 '22 20:12

JVG


1 Answers

The only public API of controller are the actions which respond to a HTTP request. In your case get "/reports" => "reports#request_report" is a route which corresponds to the action request_report.

However actions are instance methods, not class methods:

class ReportsController
  def request_report # self.request_report would make this a class method!
    # @todo get reports from somewhere and
    # return some sort of response.
  end

  # any method call here happens when the class is evaluated.
end

You are declaring the action as a class method and then calling it when the ReportsController class is evaluated. Sorry to say but just about everything about your controller is wrong.

The Rails convention would be to call the action index.

Controllers in Rails should only be instantiated by the router (or your test framework). So they are definatly the wrong place to put resuable bits and bobs. If you ever see someone doing ReportsController.new.foo or ReportsController.foo - fire them on the spot.

So where do you put external API calls?

If its a pretty trivial one-off you can place it in private method in your controller.

Some place API calls on the model layer - however that is debatable since ActiveRecord models already are supercharged to the gills with powers and responsibilities.

One solution that has worked well for me is Service Objects. They are easy to test and have a clear single responsibility.

class RequestReportService
  def initalize(client)
    @client = client
  end 
  def call(opts = {})
   begin
      return @client.request_report(opts.merge("max_count" => 1))
    rescue Excon::Errors::ServiceUnavailable => e
      nil
    end
  end
end

class ReportsController
  def index
    @reports = RequestReportService.new(@client).call
  end
end
like image 116
max Avatar answered Dec 26 '22 10:12

max