RubyMotion async programming with BubbleWrap

天大地大妈咪最大 提交于 2019-12-06 06:23:37

You're not going to totally get away from the nesting. Taking Alan's answer and massaging it a bit, this is what I've come up with. It involves passing a block through a couple of methods.

def self.populateProjectsTable(projects_controller)
  @taskList = TaskList.new
  @taskList.loginAndGetProjects do |projects|
    projects_controller.projects = projects
    projects_controller.reloadData
  end
end

def loginAndGetProjects(&block)
  payload = {email: "email", password: "pass"}
  HTTP.post("http://example.com/login", {payload: payload}) do |response|
    @authCookie = response.headers['Set-Cookie']
    getProjects(&block)
  end
end

def getProjects(&block)
  HTTP.get("http://example.com/projects.json", {cookie: @authCookie}) do |response|
    projects = JSON.parse(response.body.to_str)
    block.call(projects)
  end
end

I've had a similar problem trying to wrap methods that themselves took blocks. I wanted the new wrapper methods to still be able to take blocks. Here's what I did in ParseModel:

# with block:
# ParseModel::Cloud.callFunction("myFunction", {"myParam" => "myValue"}) do |result, error|
#  # do something...
# end

# without block:
# ParseModel::Cloud.callFunction("myFunction", {"myParam" => "myValue"})
module ParseModel
  class Cloud
    def self.callFunction(function, params={}, &block)
      return PFCloud.callFunction(function, withParameters:params) unless block_given?

      PFCloud.callFunctionInBackground(function, withParameters:params, block:lambda do |result, error|
        block.call(result, error)
      end)
    end
  end
end

Applying this concept to your problem, you could rewrite your methods to take blocks themselves. Here's a bit of a refactor that I think might be helpful:

def self.populateProjectsTable(projects_controller)
  @taskList = TaskList.new
  @taskList.doLogin do |login_response|
    authCookie = login_response.headers['Set-Cookie']
    @taskList.getProjects(authCookie) do |projects_response|
      projects = JSON.parse(projects_response.body.to_str)
      projects_controller.projects = projects
      projects_controller.reloadData
    end
  end
end

def doLogin(&block)
  payload = {email: "email", password: "pass"}
  HTTP.post("http://example.com/login", {payload: payload}) do |response|
    block.call(response)
  end
end

def getProjects(cookie, &block)
  HTTP.get("http://example.com/projects.json", {cookie: cookie}) do |response|
    block.call(response)
  end
end

I don't think you're totally out of the woods regarding SRP, but this should be a good start.

+1 for Jamon's answer.

I might suggest using a class to manage your session and splitting out the API into a module if you like SRP. This is especially helpful as you add additional API calls. Here I queue up requests that will be satisfied once login is completed. Later you can add handling for timeouts, etc.

module ProjectApi
  def get_projects(&block)
    with_session do
      HTTP.get("http://example.com/projects.json", {cookie: @auth_cookie}) do |response|
        projects = JSON.parse(response.body.to_str)
        block.call(projects)
      end
    end
  end
end

class MySession
  include ProjectApi

  def initialize(login, password)
    @login = login
    @password = password
    @state = nil
    @requests = []
  end

  def active?
    @state == :active
  end

  def with_session(&block)
    @requests << &block
    active? ? handle_requests : login(true)
  end

  private

  def login(do_handle_requests = false)
    payload = {login: @login, password: @password}
    @state = nil
    HTTP.post("http://example.com/login", {payload: payload}) do |response|
      @state = :active
      @auth_cookie = response.headers['Set-Cookie']}
      handle_requests if do_handle_requests
    end
  end  

  def handle_requests
    while request = @requests.shift do
      request.call
    end if active?
  end    

end

def self.populateProjectsTable(projects_controller)
  @session ||= MySession.new('mylogin', 'mypassword')
  @session.get_projects do |projects|
    projects_controller.projects = projects
    projects_controller.reloadData
  end
end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!