2

多くの非同期コードを使用する場合、適切なコードを記述する方法について混乱しています。

次のコード スニペットでは、ログインして認証 Cookie を取得し、次のリクエストでその Cookie を使用してプロジェクト名のリストを取得します (例として)。

def self.populateProjectsTable(projects_controller)
  payload = {email: "email", password: "pass"}
  HTTP.post("http://example.com/login", {payload: payload}) do |response|
    authCookie = response.headers['Set-Cookie']
    HTTP.get("http://example.com/projects.json", {cookie: authCookie}) do |response|
      projects = JSON.parse(response.body.to_str)
      projects_controller.projects = projects
      projects_controller.reloadData
    end
  end
end

これは機能しますが、コードは汚れているように感じます。単一責任の原則に従っていない。これをいくつかの方法で抽出したいと思います。

def self.populateProjectsTable(projects_controller)
  @taskList = TaskList.new
  @taskList.doLogin
  projects = @taskList.getProjects
  projects_controller.projects = projects
  projects_controller.reloadData
end

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

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

これは明らかに機能しません。getProjectsメソッドは終了する前に呼び出され、プロジェクトはブロックのスコープ内でのみ認識され、データはメソッドdoLoginに返されません。populateProjectsTable

最初の例で示した入れ子を使用せずに、このようなアプリケーションをどのようにプログラムするのでしょうか?

4

3 に答える 3

4

巣作りから完全に逃れることはできません。Alan の回答を参考にして、少し手を加えると、これが私が思いついたものです。いくつかのメソッドを介してブロックを渡す必要があります。

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
于 2012-10-31T21:46:44.100 に答える
4

それ自体がブロックを取るメソッドをラップしようとして、同様の問題が発生しました。新しいラッパー メソッドが引き続きブロックを取得できるようにしたかったのです。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

この概念を問題に適用すると、メソッドを書き直してブロック自体を取ることができます。これは、役立つと思われるリファクタリングの一部です。

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

あなたが SRP に関して完全に森から外れているとは思いませんが、これは良いスタートになるはずです。

于 2012-11-01T01:25:03.237 に答える
1

ハモンの答えに+1。

SRP が好きなら、クラスを使用してセッションを管理し、API をモジュールに分割することをお勧めします。これは、API 呼び出しを追加する場合に特に役立ちます。ここでは、ログインが完了すると満たされるリクエストをキューに入れます。後でタイムアウトなどの処理を追加できます。

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
于 2012-11-30T22:51:35.003 に答える