【问题标题】:RubyMotion async programming with BubbleWrap使用 BubbleWrap 进行 RubyMotion 异步编程
【发布时间】:2012-10-31 20:57:28
【问题描述】:

在使用大量异步代码时,我对如何编写体面的代码感到困惑。

在以下代码 sn-p 中,我登录以获取身份验证 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 方法。

如果没有第一个示例中所示的嵌套,如何编写此类应用程序?

【问题讨论】:

    标签: ruby asynchronous rubymotion single-responsibility-principle


    【解决方案1】:

    你不会完全摆脱嵌套。接受艾伦的回答并稍微按摩一下,这就是我想出的。它涉及通过几个方法传递一个块。

    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
    

    【讨论】:

    • 谢谢 Jamon,但我还不相信。如果我是对的(将其定义为实例变量?),projects_controller 将在 getProjects 方法中不可用,并且 doLogin 方法正在执行项目请求,再次违反 SRP。我觉得应该有一种更简洁的方式来写这个,还是我不像一个 Obj-C 程序员那样思考?
    • 抱歉,我发的有点太仓促了。我的错。我重构了我的例子。 Alan 正在做某事,所以我结合了传递街区的想法。
    • 这似乎是在公开发送密码。是否有更安全的方式来处理身份验证?将不胜感激任何指针!
    • 或者您在发送之前先对密码进行哈希处理?
    【解决方案2】:

    我在尝试包装本身占用块的方法时遇到了类似的问题。我希望新的包装方法仍然能够占用块。这是我在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 方面并没有完全脱离困境,但这应该是一个好的开始。

    【讨论】:

      【解决方案3】:

      +1 表示 Jamon 的回答。

      如果您喜欢 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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-01-19
        • 2016-12-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-29
        • 2013-04-11
        相关资源
        最近更新 更多