lib/rhack/frame.rb in rhack-1.3.3 vs lib/rhack/frame.rb in rhack-1.3.4
- old
+ new
@@ -88,11 +88,11 @@
@use_cache = use if use.in [true, false]
end
def inspect
sssize = @ss.size
- "<#Frame @ #{@ss.untargeted ? 'no target' : @loc.root}: #{sssize} #{sssize == 1 ? 'scout' : 'scouts'}#{', static'+(' => '+@static.protocol if @static.is(Hash)) if @static}, cookies #{@ss[0].cookie_enabled ? 'on' : 'off'}>"
+ "<#Frame @ #{@ss.untargeted ? 'no target' : @loc.root}: #{sssize} #{sssize == 1 ? 'scout' : 'scouts'}#{', static'+(' => '+@static.protocol if @static.is(Hash)) if @static}, cookies #{@ss[0].cookies_enabled ? 'on' : 'off'}>"
end
# All opts going in one hash.
# Opts for Frame:
# :wait, :sync, :thread_safe, :raw, :proc_result, :save_result, :zip, :result, :stream
@@ -101,27 +101,49 @@
# Opts passed to result:
# :xml, :html, :json, :hash, :eval, :load_scripts
# Opts passed to Scout:
# :headers, :redir, :relvl
#
- # @ :result : враппер результата исполнения; по умолчанию Page, для Client — если определён — Result; при асинхронном вызове будет возвращён незамедлительно
- # @ &callback : в него будет передан инстанс result, а его результат будет записан в result#res (по умолчанию это ссылка на себя)
- # @ :thread_safe : не использовать луп исполнения Curl::Multi#perform, а вызывать #perform прямо в этом треде; если установлен, то невозможно прерывание исполнения клавиатурой (продолжит работать, выполняя колбеки, в фоне), и невозможно задавать больше параллельных реквестов, чем разрешено параллельных соединений (просто застрянет)
- # @ :sync : остановить (Thread#kill) perform-loop после исполнения всех запросов; подразумевает wait=true; при вызове одиночного реквеста подразумевает thread_safe=true
- # @ :wait : ждать исполнения всех реквестов
- # @ :save_result: возвращает #res для каждого инстанса result вместо самого инстанса; если не задан :proc_result, то подразумевает wait=true
- # @ :proc_result: Proc, в который будет передан result#res, если задан также &callback; служит для создания вложенных блоков для клиентов; если =nil, то подразумевает wait=true
- # @ :raw : сохраняем *только* тело ответа, без хедеров, без отладочной инфы в #res
- # @ :raw + :sync : подразумевает save_result=true
- # @ :xhr, :content_type, :auth : формируют хедеры X-Requested-With, Content-Type, Authorization для передачи в Scout
+ # Формирование хедеров запроса X-Requested-With, Content-Type, Authorization для передачи в Scout:
# @ :xhr : boolean
# @ :content_type : symbol<extension> | raw string
# @ :auth : "<username>:<password>"
+ #
+ ### Обработка результата:
+ # преобразование к понятному для клиента формату производится в result#process
+ # использование данных из result производится
+ # либо в &callback (functional),
+ # либо использованием результата #run (imperative)
+ # в первом случае в целях сборки мусора будет возвращён
+ # @ :result : враппер результата исполнения; по умолчанию Page, для Client — если определён — <Class>::Result; при асинхронном вызове будет возвращён незамедлительно
+ # @ &callback : в него будет передан инстанс result, а его результат будет записан в <result>.res (по умолчанию это ссылка на <result>); в целях сборки мусора, если &callback задан, #run возвращает #res для каждого инстанса result вместо самого инстанса
+ # @ :complete : при вызове нескольких реквестов, в него будет передан [ <result>.res, ... ] от каждого из них, при вызове единичного — <result>.res от него
+ # @ :complete : в него будет передан [ <result>.res, ... ] относительно всех реквестов, когда все
+ # @ :raw : сохраняем в #res *только* тело ответа — без хедеров, без отладочной инфы
#
- # @ :zip, :stream и все опции для result : deprecated
+ # @ [deprecated] :save_result : подразумевает callback=Proc::SELF; если не задан :proc_result, то подразумевает wait=true
+ # @ [deprecated] :proc_result : Proc, в который будет передан result#res, если задан также &callback; если =nil, то подразумевает wait=true
#
+ ### Управление потоками:
+ # @ :thread_safe : не использовать луп исполнения Curl::Multi#perform, а вызывать #perform прямо в этом треде;
+ # если установлен, то невозможно прерывание исполнения клавиатурой (продолжит работать, выполняя колбеки, в фоне), и невозможно задавать больше параллельных реквестов, чем разрешено параллельных соединений (просто застрянет)
+ # @ :sync : остановить (Thread#kill) perform-loop после исполнения всех запросов; подразумевает wait=true; при вызове одиночного реквеста подразумевает thread_safe=true
+ # @ :wait : ждать исполнения всех реквестов
+ # @ :raw + :sync : подразумевает save_result=true
+ #
+ # @ [deprecated] :zip, :stream и все опции для result
+ #
# TODO: Семантически разделить синхронное и асинхронное выполнение запросов (не важно, серии или отдельных), с учётом, что асинхронность по сути своей перегружена и требуется, например, в очередях сообщений, но не в синхронных контроллерах Rails
+ #
+ # Пример использования коллбеков в общих/common методах клиента:
+ # def api(requests, **params, &callback)
+ # @f.run(requests, **params.slice(:complete, :sync)) {|data|
+ # params[:before].(data)
+ # process_result = common_process(data)
+ # custom_result = (callback || params[:after]).(data, process_result)
+ # } # => [ custom_result, ... ]
+ # Весь процессинг
def exec *args, &callback
many, order, orders, with_opts = interpret_request *args
L.log({:many => many, :order => order, :orders => orders, :with_opts => with_opts})
if !Johnson::Enabled and with_opts[:eval]
@@ -130,10 +152,13 @@
end
# JS Runtime is not thread-safe and must be created in curl thread
# if we aren't said explicitly about the opposite
Johnson::Runtime.set_browser_for_curl with_opts
+ if with_opts[:save_result]
+ callback ||= Proc::SELF
+ end
if many
result = exec_many orders, with_opts, &callback
else
result = exec_one order, with_opts, &callback
end
@@ -338,29 +363,36 @@
page.res = page
if callback
yres = callback.call page
# if we don't want callback to affect page.res
# then we should not set :save_result
- if yres != :skip
+ if yres == :skip
+ return # DEPRECATED
+ else
+ page.res = yres
if opts[:proc_result].is Proc
# yres is intermediate result that we should proc
page.res = opts[:proc_result].call yres
- elsif opts[:save_result] or :proc_result.in opts
+ #elsif opts[:save_result] or :proc_result.in opts
+ else
# yres is total result that we should save
page.res = yres
end
# in both cases page.res is set to total result
# so we can return result from any depth as @res attribute of what we have on top
end
end
+ if opts[:complete]
+ page.res = opts[:complete].call page.res
+ end
end
# TODO: found why/how IO on callbacks breaks +curl.res.body+ content and how to fix or how to avoid it
def exec_one(order, opts, &callback)
if @use_cache and order[0] == :loadGet and page = @@cache[order[1]]
run_callbacks! page, opts, &callback
- res = opts[:wait] && (opts[:save_result] or :proc_result.in opts) ? page.res : page
+ res = opts[:wait] && (callback or :proc_result.in opts) ? page.res : page
return res
end
# must result in Page (default) or it's subclass
page = opts[:result].new
# if no spare scouts can be found, squad simply waits for first callbacks to complete
@@ -397,11 +429,11 @@
unless opts[:wait] and opts[:thread_safe] or opts[:exec] == false
Curl.execute :unless_already
end
if opts[:wait]
opts[:thread_safe] ? Curl.carier.perform : Curl.wait
- (opts[:save_result] or :proc_result.in opts) ? page.res : page
+ (callback or :proc_result.in opts) ? page.res : page
else page
end
end
def exec_many(orders, with_opts, &callback)
@@ -414,12 +446,25 @@
pages = orders.zip(with_opts[:ranges]).send(iterator) {|order, range|
(with_opts[:headers] ||= {}).Range = "bytes=#{range.begin}-#{range.end}"
exec_one order, with_opts.merge(:exec => false), &callback
}
else
+ if oncomplete = with_opts.delete(:complete)
+ pages = nil
+ completed_count = 0
+ original_callback = callback
+ callback = lambda {|page|
+ page.res = original_callback ? original_callback.call(page) : page
+ completed_count += 1
+ if orders.size == completed_count
+ oncomplete.call pages.ress
+ end
+ page.res
+ }
+ end
# если ss.next будет не хватать скаутов, то он сам запустит курл
# правда, это с :thread_safe никак не вяжется
- pages = orders.send(iterator) {|order| exec_one order, with_opts, &callback }
+ pages = orders.send(iterator) {|order| exec_one order, with_opts, &callback}
end
unless w and with_opts[:thread_safe] or opts[:exec] == false
Curl.execute :unless_already
end
with_opts[:thread_safe] ? Curl.carier.perform : Curl.wait if w
\ No newline at end of file