An open API service indexing awesome lists of open source software.

https://github.com/0exp/puma_after_reply

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.
https://github.com/0exp/puma_after_reply

puma-after-reply rack-after-reply

Last synced: 12 months ago
JSON representation

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.

Awesome Lists containing this project

README

          

# puma_after_reply · ![Gem Version](https://img.shields.io/gem/v/puma_after_reply) ![build](https://github.com/0exp/puma_after_reply/actions/workflows/build.yml/badge.svg??branch=master)

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](#requirements)
- [Installation](#installation)
- [Usage](#usage)
- [Algorithm](#algorithm)
- [Configuration](#configuration)
- [Adding replies](#adding-replies-add_replycond_reply)
- [Some debugging methods](#some-debugging-methods)
- [Test environments](#test-environments-and-other-rack-apps)
- [Contributing](#contributing)
- [License](#license)
- [Authors](#authors)

---

### Requirements

\[[back to top](#table-of-contents)\]

- `concurrent-ruby` (`~> 1.3`)

---

### Installation

\[[back to top](#table-of-contents)\]

```ruby
gem 'puma_after_reply'
```

```shell
bundle install
# --- or ---
gem install puma_after_reply
```

```ruby
require 'puma_after_reply'
```

---

### Usage

\[[back to top](#table-of-contents)\]

- [Algorithm](#algorithm)
- [Configuration](#configuration)
- [Adding replies](#adding-replies-add_replycond_reply)
- [Some debugging methods](#some-debugging-methods)
- [Test environments](#test-environments-and-other-rack-apps)

---

#### 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](#adding-replies-add_replycond_reply));
- using `PumaAfterReply.cond_reply` ([doc](#adding-replies-add_replycond_reply));
- 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](#usage)\]

```ruby
PumaAfterReply.configure do |config|
# default values:
config.fail_on_error = false # expects:
config.log_error = nil # expects: (receives: error object)
config.on_error = nil # expects: (receives: error object)
config.before_reply = nil # expects: (receives: nothing)
config.after_reply = nil # expects: (receives: nothing)
config.run_anyway = false # expects:
config.thread_pool_size = 10 # expects:
end

# get configs as a hash:
PumaAfterReply.cofnig.to_h

# get configs directly:
PumaAfterRepy.config.fail_on_error # and etc;
```

```ruby
# (IMPORTANT): register the middleware (Rails example)
Rails.configuration.middleware.use(PumaAfterReply::Middleware)
```

---

#### Adding replies (`add_reply`/`cond_reply`)

\[[back to top](#usage)\]

- non-threaded way (this reply will be processed sequentially):

```ruby
# non-threaded way:
PumaAfterReply.add_reply { your_code }
```

- threaded-way (this reply will be processed in a separated thread):

```ruby
# 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`);

```ruby
# with a boolean value:
PumaAfterReply.cond_reply(!Rails.env.test?) { your_code }
```

```ruby
# with a callabale value:
is_puma_request = proc { check_that_we_are_inside_a_request }
PumaAfterReply.cond_reply(is_puma_request) { your_code }
```

```ruby
# add threaded reply:
PumaAfterReply.cond_reply(some_condition, threaded: true) { your_code }
```

---

#### Some debugging methods

\[[back to top](#usage)\]

```ruby
# the count of the added replies:
PumaAfterReply.count # or .size
```

```ruby
# replies collections:
PumaAfterReply.replies # all added replies
PumaAfterReply.inline_replies # all added non-threaded replies
PumaAfterReply.threaded_replies # all added threaded replies
```

```ruby
# manual replies running:
PumaAfterReply.call # or .run
```

```ruby
# clear replies collector:
PumaAfterReply.clear
```

```ruby
# reset configs to default values:
PumaAfterReply.config.__reset!
```

---

#### Test environments (and other Rack apps)

\[[back to top](#usage)\]

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](#table-of-contents)\]

- 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](#table-of-contents)\]

Released under MIT License.

## Authors

\[[back to top](#table-of-contents)\]

[Rustam Ibragimov](https://github.com/0exp)