Puma's "rack.after_reply"
integration for your Rack-applications. Provides #call-able reply
abstraction and configurable non-threaded/threaded invocation flow with before/on_error/after hooks for each added reply.
concurrent-ruby
(~> 1.3
)
gem 'puma_after_reply'
bundle install
# --- or ---
gem install puma_after_reply
require 'puma_after_reply'
- every Puma's worker gets own reply collector;
- during the Puma's request your logic adds replies to the worker's reply collector:
- after processing the request, Puma's worker returns a response to the browser;
- then Puma's worker launches accumulated replies:
- threaded replies are launched in separated threads;
- non-threaded replies are launched sequentially;
- after processing all replies, the worker's reply collector is cleared;
Each separated reply is launched according to the following invocation flow:
- before_reply hook (
config.before_reply
); - reply invocation (
reply.call
); - if
reply.call
failed with an error:- log_error hook (
config.log_error
); - on_error hook (
config.on_error
); - raise_on_error fail check (
config.fail_on_error
)
- log_error hook (
- (ensure): after_reply hook (
config.after_reply
);
Remember: if you add a reply outside of the Puma's/Rack's request context your reply will never be processed and flushed (that can lead to the memory leak/memory bloat).
PumaAfterReply.configure do |config|
# default values:
config.fail_on_error = false # expects: <Boolean>
config.log_error = nil # expects: <nil,#call,Proc> (receives: error object)
config.on_error = nil # expects: <nil,#call,Proc> (receives: error object)
config.before_reply = nil # expects: <nil,#call,Proc> (receives: nothing)
config.after_reply = nil # expects: <nil,#call,Proc> (receives: nothing)
config.run_anyway = false # expects: <Boolean>
config.thread_pool_size = 10 # expects: <Integer>
end
# get configs as a hash:
PumaAfterReply.cofnig.to_h
# get configs directly:
PumaAfterRepy.config.fail_on_error # and etc;
# (IMPORTANT): register the middleware (Rails example)
Rails.configuration.middleware.use(PumaAfterReply::Middleware)
- non-threaded way (this reply will be processed sequentially):
# non-threaded way:
PumaAfterReply.add_reply { your_code }
- threaded-way (this reply will be processed in a separated thread):
# threaded way:
PumaAfterReply.add_reply(threaded: true) { your_code }
- conditional reply adding:
reply(condition, threaded: false, &reply)
(threaded: false
by default);- when condition is
true
- your reply will be pushed to the reply queue; - when condition is
false
- your reply will be processed immediately; - condition can be represented as callable object (
#call
/Proc
);
# with a boolean value:
PumaAfterReply.cond_reply(!Rails.env.test?) { your_code }
# with a callabale value:
is_puma_request = proc { check_that_we_are_inside_a_request }
PumaAfterReply.cond_reply(is_puma_request) { your_code }
# add threaded reply:
PumaAfterReply.cond_reply(some_condition, threaded: true) { your_code }
# the count of the added replies:
PumaAfterReply.count # or .size
# replies collections:
PumaAfterReply.replies # all added replies
PumaAfterReply.inline_replies # all added non-threaded replies
PumaAfterReply.threaded_replies # all added threaded replies
# manual replies running:
PumaAfterReply.call # or .run
# clear replies collector:
PumaAfterReply.clear
# reset configs to default values:
PumaAfterReply.config.__reset!
In some cases and Rack applications you can have no "rack.after_reply"
key in your Rack env
(request environments in your tests, for example). For this cases you can use config.run_anyway = true
:
on each of your rack request all accumulated replies will be processed and cleared in the same way as for Puma.
- Fork it ( https://github.com/0exp/puma_after_reply )
- Create your feature branch (
git checkout -b feature/my-new-feature
) - Commit your changes (
git commit -am '[feature_context] Add some feature'
) - Push to the branch (
git push origin feature/my-new-feature
) - Create new Pull Request
Released under MIT License.