在使用大量异步代码时,我对如何编写像样的代码感到困惑。
在下面的代码片段中,我登录以获取身份验证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方法。
如果没有第一个示例中所示的嵌套,如何编写这样的应用程序?
发布于 2012-11-01 05:46:44
你不会完全脱离嵌套的。接受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-11-01 09:25:03
我也遇到过类似的问题,试图包装本身需要块的方法。我希望新的包装器方法仍然能够接受块。下面是我在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-12-01 06:51:35
Jamon的答案是+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
endhttps://stackoverflow.com/questions/13167304
复制相似问题