Skip to content

Puma's "rack.after_reply" integration for your Rack-applications. Provides #call-able reply abstraction and configurable invocation flow with before/on_error/after hooks for each added reply.

License

Notifications You must be signed in to change notification settings

0exp/puma_after_reply

Repository files navigation

puma_after_reply · Gem Version build

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.


Table of Contents


Requirements

[back to top]

  • concurrent-ruby (~> 1.3)

Installation

[back to top]

gem 'puma_after_reply'
bundle install
# --- or ---
gem install puma_after_reply
require 'puma_after_reply'

Usage

[back to top]


Algorithm

  • every Puma's worker gets own reply collector;
  • during the Puma's request your logic adds replies to the worker's reply collector:
    • using PumaAfterReply.add_reply (doc);
    • using PumaAfterReply.cond_reply (doc);
  • 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)
  • (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).


Configuration

[back to top]

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)

Adding replies (add_reply/cond_reply)

[back to top]

  • 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 }

Some debugging methods

[back to top]

# 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!

Test environments (and other Rack apps)

[back to top]

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.


Contributing

[back to top]

  • 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

License

[back to top]

Released under MIT License.

Authors

[back to top]

Rustam Ibragimov

About

Puma's "rack.after_reply" integration for your Rack-applications. Provides #call-able reply abstraction and configurable invocation flow with before/on_error/after hooks for each added reply.

Topics

Resources

License

Code of conduct

Stars

Watchers

Forks

Packages

No packages published