Forum https://discourse.hanamirb.org – Code of Conduct http://hanamirb.org/community/#code-of-conduct
depfu[bot] on check
depfu[bot] on check
Pin hanami-utils to version 2.0… (compare)
depfu[bot] on check
depfu[bot] on check
Pin hanami-utils to version 2.0… (compare)
I have a question about hanami v2.
I use hanami/interactor
now with hanami v1, but I found hanami/interactor
was removed from hanami v2.
https://github.com/hanami/utils/blob/main/CHANGELOG.md#v200beta1---2022-07-20
Does Anyone know why hanami/interactor
was removed?
And how we should change the old interactor? (For example we think we'll change expose
to attr_reader
and error!
method to raise
. Is it the right way in hanami v2? ref: https://docs.hanamirb.org/1.3.3/hanami/interactor)
@/all Hanami 2.0.0 is finally here! <3
Hi guys, I was trying Hanami 2.0 and faced an issue with the dev server port configuration. This is a new project, with no modifications after running hanami new bookshelf
.
$ bundle exec hanami version
v2.0.0
$ HANAMI_PORT=5000 bundle exec hanami server
17:39:02 - INFO - Using Guardfile at /bookshelf/Guardfile.
17:39:02 - INFO - Puma starting on port 2300 in development environment.
17:39:02 - INFO - Guard is now watching at '/bookshelf'
[1392505] Puma starting in cluster mode...
[1392505] * Puma version: 6.0.0 (ruby 3.1.2-p20) ("Sunflower")
[1392505] * Min threads: 5
[1392505] * Max threads: 5
[1392505] * Environment: development
[1392505] * Master PID: 1392505
[1392505] * Workers: 2
[1392505] * Restarts: (✔) hot (✖) phased
[1392505] * Preloading application
[1392505] * Listening on http://0.0.0.0:2300
$ HANAMI_PORT=5000 bundle exec puma -C config/puma.rb
[1392728] Puma starting in cluster mode...
[1392728] * Puma version: 6.0.0 (ruby 3.1.2-p20) ("Sunflower")
[1392728] * Min threads: 5
[1392728] * Max threads: 5
[1392728] * Environment: development
[1392728] * Master PID: 1392728
[1392728] * Workers: 2
[1392728] * Restarts: (✔) hot (✖) phased
[1392728] * Preloading application
[1392728] * Listening on http://0.0.0.0:5000
For some reason hanami server
doesn't react to changes in env variables or changes to the .env
file. While with puma
it's working as expected.
I am trying to add Rack::Static
middleware to my Hanami 2.0 app but am encountering error while starting server.
# config/app.rb
# frozen_string_literal: true
require "hanami"
module StartupTemplate
class App < Hanami::App
config.middleware.use Rack::Static, urls: ["/css", "/images"], root: "public"
end
end
Server logs
[10080] Puma starting in cluster mode...
[10080] * Puma version: 6.0.0 (ruby 3.1.2-p20) ("Sunflower")
[10080] * Min threads: 5
[10080] * Max threads: 5
[10080] * Environment: development
[10080] * Master PID: 10080
[10080] * Workers: 2
[10080] * Restarts: (✔) hot (✖) phased
[10080] * Preloading application
[10080] ! Unable to load application: ArgumentError: unknown keywords: :urls, :root
/home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/slice/routing/middleware/stack.rb:94:in `use': unknown keywords: :urls, :root (ArgumentError)
from /..../startup_template/config/app.rb:7:in `<class:App>'
from /..../startup_template/config/app.rb:6:in `<module:StartupTemplate>'
from /..../startup_template/config/app.rb:5:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami.rb:41:in `setup'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/setup.rb:6:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/boot.rb:3:in `require_relative'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/boot.rb:3:in `<top (required)>'
from config.ru:3:in `require'
from config.ru:3:in `block in <main>'
Checking the source code https://github.com/hanami/hanami/blob/beb60078bc9a5e84ba77946f2823ee12f7f6d0dc/lib/hanami/slice/routing/middleware/stack.rb#L94 the *args
is expected to be an optional Array. And the specs at https://github.com/hanami/hanami/blob/beb60078bc9a5e84ba77946f2823ee12f7f6d0dc/spec/integration/rack_app/middleware_spec.rb doesn't illustrate any middleware accepting keyword args as middleware's option. So is it that Hanami doesn't support middlewares accepting keyword args?
Hi! Congratulations to everyone on the 2.0 release, I've been waiting for it for a very long time!
I decided to test Hanami 2.0 and did everything according to the documentation from A to Z. I reached the configuration of middleware body_parser
.
I just wanted to add my own JSON body parser, like Oj. I looked and did as described in the documentation, but got an error in the RSpec tests.
NoMethodError:
undefined method `empty?' for #<OjParser:0x0000000101363df8>
return DEFAULT_BODY_PARSERS if parser_specs.empty?
^^^^^^^
I saw that the empty?
method is missing somewhere and added it to my custom parser. But I don't understand where it is being called from.
The working version of the code looks like this:
But I'm not sure if this fix is in the right place.
# config/app.rb
config.middleware.use :body_parser, OjParser.new
# lib/oj_parser.rb
class OjParser
def mime_types
['application/json']
end
def parse(body)
Oj.load(body) unless body.empty?
end
# dummy method
def empty?
false
end
end
I created pull request for guide, but we need to find a place in the code where to fix it:
hanami/guides#159
rules
is embedded in params
block
Params
with include Deps['utils.email_validator']
, but I notice in my spec, I just pass that dependency in on the initializer along with the param keys. What's to stop them conflicting and user input overriding a dependency?
# config/settings.rb
# frozen_string_literal: true
module StartupTemplate
class Settings < Hanami::Settings
# Define your app settings here, for example:
#
# setting :my_flag, default: false, constructor: Types::Params::Bool
setting :database_url, constructor: Types::String
def production_log_level
determine_production_log_level
end
private
def determine_production_log_level
@use_as_production_log_level ||= LogUtil::LOGGER_METHODS[0]
end
end
end
# lib/startup_template/log_util.rb
# auto_register: false
# frozen_string_literal: true
module StartupTemplate
module LogUtil
extend self
LOGGER_METHODS = [
:debug,
:info,
:warn,
:error,
:fatal
].freeze
end
end
# config/app.rb
# frozen_string_literal: true
require "hanami"
module StartupTemplate
class App < Hanami::App
config.actions.content_security_policy[:default_src] = "'self'"
# ======== STARTS: LOGGER CONFIG
config.logger.formatter = :json
config.logger.stream = root.join("log").join("#{Hanami.env}.log")
environment(:production) do
config.logger.level = settings.production_log_level
end
# ======== ENDS: LOGGER CONFIG
end
end
startup_template$ HANAMI_ENV=production bundle exec hanami c
/jwork/ruby/hanami_projects/StartupTemplateProject/startup_template/config/settings.rb:32:in `determine_production_log_level': uninitialized constant StartupTemplate::Settings::LogUtil (NameError)
log_util_clazz = LogUtil
^^^^^^^
from /..../startup_template/config/settings.rb:21:in `production_log_level'
from /..../startup_template/config/app.rb:15:in `block in <class:App>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/slice.rb:152:in `instance_eval'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/slice.rb:152:in `environment'
from /..../startup_template/config/app.rb:14:in `<class:App>'
from /..../startup_template/config/app.rb:6:in `<module:StartupTemplate>'
from /..../startup_template/config/app.rb:5:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami.rb:41:in `setup'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/setup.rb:6:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/prepare.rb:3:in `require_relative'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-2.0.0/lib/hanami/prepare.rb:3:in `<top (required)>'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/zeitwerk-2.6.6/lib/zeitwerk/kernel.rb:38:in `require'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-cli-2.0.0/lib/hanami/cli/commands/app/command.rb:60:in `app'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-cli-2.0.0/lib/hanami/cli/commands/app/console.rb:57:in `block in resolve_engine'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-cli-2.0.0/lib/hanami/cli/commands/app/console.rb:57:in `each'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/gems/hanami-cli-2.0.0/lib/hanami/cli/commands/app/console.rb:57:in `map'
from /home/jignesh/.rvm/gems/ruby-3.1.2@startup-template-hanami-2-0/g
Doing some trial and error I found the culprit to be following in StartupTemplate::App
environment(:production) do
config.logger.level = settings.production_log_level
end
If I replace settings.production_log_level
with a hardcoded value say :info
then the production console loads successfully.
Which makes me ask: Is it that the "determined settings" (as in custom method in StartupTemplate::Settings
involving some logic using other classes in lib/startup_template
folder) cannot be used for assigning to a configuration value in StartupTemplate::App
? If yes, then one question which puzzles me is why that same thing doesn't cause error when loading development env console?
Hey folks, thank you for the great framework! Got a question about a proper way of including development-only libraries for specific environments.
I want to use pry-byebug
library in development and test environments and have it available globally so I don't have to require it each time I wanna use binding.pry
e.g.:
# someplace in the app ...
# this
binding.pry
# not this
require 'pry-byebug'; binding.pry
Here's what I have now.
Gemfile
group :development, :test do
gem "pry-byebug"
end
config/app.rb
require "hanami"
require "pry-byebug" if Hanami.env?(:development, :test)
module Bookshelf
class App < Hanami::App
# ...
end
end
This works, but I was just wondering whether there is a more Hanami-idiomatic way of doing this? Having to specify in both the Gemfile
AND in the config/app.rb
that I want it only for those environments seems non-ideal.
Thank you.
Hi there!
I'm trying to get the endpoint
of a RecognizedRoute
as stated in: https://github.com/hanami/router#testing
But what I'm getting instead of a String is a Proc that when called with env
executes the actual request.
Am I doing something wrong?
Is there any other way of getting the human readable action/route from a Request?
Thank you!
Hey, I've added some scoped routes with nested params like so:
scope "secrets" do
get "/", to: "secrets.index"
post "/", to: "secrets.create"
patch ":id", to: "secrets.update"
delete ":id", to: "secrets.destroy"
scope ":secret_id/parts" do
post "/", to: "parts.create"
delete "/:id", to: "parts.destroy"
end
end
but I'm getting 404 when I call the DELETE /secrets/:secret_id/parts/:id
path. The hanami routes
output displays it correctly as DELETE /secrets/:secret_id/parts/:id parts.destroy
. I've disabled all additional headers but it still cannot be resolved, however if I specify the route outside the scope as delete "/secrets/:secret_id/parts/:id", to: "parts.destroy"
it is resolved successfully, how do I organize routes within scopes if I have multiple nested objects to query? Thanks!
Hi, I am giving Hanami a try and therefore am following the Getting Started tutorial. I am stuck at the "Listing books" stage. When running the last spec, I get the following error message:
1) GET /books returns a list of books
Failure/Error: let(:books) { app["persistence.rom"].relations[:books] }
Sequel::DatabaseError:
PG::UndefinedTable: ERROR: relation "books" does not exist
LINE 1: ..."."attnum" > 0) AND ("pg_class"."oid" = CAST(CAST('"books"' ...
# ./config/providers/persistence.rb:19:in `block (2 levels) in <top (required)>'
# ./spec/requests/books/index_spec.rb:2:in `block (2 levels) in <top (required)>'
# ./spec/requests/books/index_spec.rb:5:in `block (2 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:14:in `block (3 levels) in <top (required)>'
# ./spec/support/database_cleaner.rb:13:in `block (2 levels) in <top (required)>'
# ------------------
# --- Caused by: ---
# PG::UndefinedTable:
# ERROR: relation "books" does not exist
# LINE 1: ..."."attnum" > 0) AND ("pg_class"."oid" = CAST(CAST('"books"' ...
# ^
# ./config/providers/persistence.rb:19:in `block (2 levels) in <top (required)>'
The lib/bookshelf/persistence/relations/books.rb
does exist, though. Any idea? Thanks!
❯ ruby --version
ruby 3.2.0 (2022-12-25 revision a528908271) [arm64-darwin22]
❯ rbenv which hanami
/Users/krobayna/.rbenv/versions/3.2.0/bin/hanami
❯ gem list | pbcopy
abbrev (default: 0.1.1)
ast (2.4.2)
backport (1.2.0)
base64 (default: 0.1.1)
benchmark (default: 0.2.1, 0.2.0)
bigdecimal (default: 3.1.3)
bundler (default: 2.4.1, 2.3.26)
cgi (default: 0.3.6)
concurrent-ruby (1.1.10)
csv (default: 3.2.6)
date (default: 3.3.3)
debug (1.7.1)
delegate (default: 0.3.0)
did_you_mean (default: 1.6.3)
diff-lcs (1.5.0)
digest (default: 3.1.1)
drb (default: 2.1.1)
dry-auto_inject (1.0.0)
dry-cli (1.0.0)
dry-configurable (1.0.1)
dry-core (1.0.0)
dry-events (1.0.1)
dry-files (1.0.1)
dry-inflector (1.0.0)
dry-logger (1.0.3)
dry-monitor (1.0.1)
dry-system (1.0.1)
dry-transformer (1.0.1)
e2mmap (0.1.0)
english (default: 0.7.2)
erb (default: 4.0.2)
error_highlight (default: 0.5.1)
etc (default: 1.4.2)
fcntl (default: 1.0.2)
fiddle (default: 1.1.1)
fileutils (default: 1.7.0)
find (default: 0.1.1)
forwardable (default: 1.3.3)
getoptlong (default: 0.2.0)
hanami (2.0.2)
hanami-cli (2.0.2)
hanami-utils (2.0.2)
io-console (default: 0.6.0)
io-nonblock (default: 0.2.0)
io-wait (default: 0.3.0)
ipaddr (default: 1.2.5)
irb (default: 1.6.2)
jaro_winkler (1.5.4)
json (default: 2.6.3, 2.6.2)
kramdown (2.4.0)
kramdown-parser-gfm (1.1.0)
logger (default: 1.5.3)
matrix (0.4.2)
mini_portile2 (2.8.0)
minitest (5.16.3)
mutex_m (default: 0.1.2)
net-ftp (0.2.0)
net-http (default: 0.3.2)
net-imap (0.3.4)
net-pop (0.1.2)
net-protocol (default: 0.2.1)
net-smtp (0.3.3)
nkf (default: 0.1.2)
nokogiri (1.13.9)
observer (default: 0.1.1)
open-uri (default: 0.3.0)
open3 (default: 0.1.2)
openssl (default: 3.1.0)
optparse (default: 0.3.1)
ostruct (default: 0.5.5)
parallel (1.22.1)
parser (3.1.3.0)
pathname (default: 0.2.1)
power_assert (2.0.3)
pp (default: 0.4.0)
prettyprint (default: 0.1.1)
prime (0.1.2)
pstore (default: 0.1.2)
psych (default: 5.0.1)
racc (default: 1.6.2, 1.6.0)
rainbow (3.1.1)
rake (13.0.6)
rbs (2.8.2)
rdoc (default: 6.5.0)
readline (default: 0.0.3)
readline-ext (default: 0.1.5)
regexp_parser (2.6.1)
reline (default: 0.3.2)
resolv (default: 0.2.2)
resolv-replace (default: 0.1.1)
reverse_markdown (2.1.1)
rexml (3.2.5)
rinda (default: 0.1.1)
rss (0.2.9)
rubocop (1.39.0)
rubocop-ast (1.24.0)
ruby-progressbar (1.11.0)
ruby2_keywords (default: 0.0.5)
securerandom (default: 0.2.2)
set (default: 1.0.3)
shellwords (default: 0.1.0)
singleton (default: 0.1.1)
solargraph (0.47.2)
stringio (default: 3.0.4)
strscan (default: 3.0.5)
syntax_suggest (default: 1.0.2)
syslog (default: 0.1.1)
tempfile (default: 0.1.3)
test-unit (3.5.7)
thor (1.2.1)
tilt (2.0.11)
time (default: 0.2.1)
timeout (default: 0.3.1)
tmpdir (default: 0.1.3)
tsort (default: 0.1.1)
typeprof (0.21.3)
un (default: 0.2.1)
unicode-display_width (2.3.0)
uri (default: 0.12.0)
weakref (default: 0.1.2)
webrick (1.7.0)
yaml (default: 0.2.1)
yard (0.9.28)
zeitwerk (2.6.6)
zlib (default: 3.0.0)
❯ hanami new bookshelf
/Users/krobayna/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/hanami-cli-2.0.2/exe/hanami:5:in `require': cannot load such file -- hanami/cli (LoadError)
from /Users/krobayna/.rbenv/versions/3.2.0/lib/ruby/gems/3.2.0/gems/hanami-cli-2.0.2/exe/hanami:5:in `<top (required)>'
from /Users/krobayna/.rbenv/versions/3.2.0/bin/hanami:25:in `load'
from /Users/krobayna/.rbenv/versions/3.2.0/bin/hanami:25:in `<main>'
Hi community!
I have some great news for all of us that are using Hanami and Appsignal!
This autumn, my team started building a production application in Hanami 2. We wanted to use Appsignal for monitoring so we reached out to the Appsignal team to ask them to add support for Hanami. Yesterday I got these news from their team:
"Hi Hannes 👋 We released ruby gem version 3.3.0, which supports Hanami 2."
Changelog with mention about Hanami 2 support: https://github.com/appsignal/appsignal-ruby/blob/main/CHANGELOG.md#330
A getting started guide: https://docs.appsignal.com/ruby/integrations/hanami.html
Hello,
I am unable to use Custom Types defined in lib/types.rb
. For e.g. in my Hanami 2.0 application I modified that file in following manner:
require "dry/types"
module StartupTemplate
Types = Dry.Types
module Types
# Define your custom types here
# Reference: https://dry-rb.org/gems/dry-validation/1.8/schemas/#using-custom-types
StrippedString = Types::String.constructor(&:strip)
end
end
and then tried to access it from hanami console and encountered error:
startup_template$ hanami c
startup_template[development]> StartupTemplate::Types::StrippedString
NameError: uninitialized constant StartupTemplate::Types
from (pry):1:in `__pry__'
Then I tried to use that custom type in a dry-validation contract like following:
module StartupTemplate
module ServiceObject
module CreateDoctor
class Validate < Dry::Validation::Contract
params do
# TODO: Using a Custom type defined in lib/types is not working.
#required(:name).value(::StartupTemplate::Types::StrippedString)
#required(:qualification).value(::StartupTemplate::Types::StrippedString)
required(:name).filled(:string)
required(:qualification).filled(:string)
end
end
end
end
end
and accessing a page which should invoke that validation I encountered following error:
Puma caught this error: uninitialized constant StartupTemplate::Types
required(:name).value(::StartupTemplate::Types::StrippedString)
^^^^^^^ (NameError)
Can anybody help me in figuring out where is the problem?
On another note at https://dry-rb.org/gems/dry-types/1.2/getting-started/ following can be seen
module Types
include Dry.Types()
end
however in Hanami 2.0 default generated lib/types.rb
file looks like following:
require "dry/types"
module StartupTemplate
Types = Dry.Types
module Types
# Define your custom types here
end
end
Why this difference?
Also the type registration illustrated at https://dry-rb.org/gems/dry-schema/1.10/advanced/custom-types/ how can I leverage it in my Hanami 2.0 application?
select :client_id, clients_for_select, options: {prompt: (I18n.t :gm_contact_new_prompt_select_client)}
. The problem is that the prompt is not displayed as the "prompt". Rather the first value in client list is shown as selected:
clients_for_select
is just an ordinary hash. Any idea why it doesn't display the prompt?
Hello. :wave: What is the right way to render a partial from an action? Here's how I'm currently doing it:
...but what I'd really like to do is delete the view and template steps assuming I can render a partial directly from the action?
binding.pry
in an Action
... If I place a binding.pry
breakpoint inside of handle, the pry output is displayed in the hanami server
console output, but it does not provide an interactive repl, and the execution constinues without actually breaking. Any thoughts as to how I can get an interactive REPL from a breakpoint within an action?
Hello,
Is there any know issue with Hanami 2.0 about 405 Method Not allowed
error for routes defined with PATCH
and DELETE
? For e.g.
I defined following routes
scope "doctors" do
patch "/:id", to: "doctors.update", as: :update_doctor
delete "/:id", to: "doctors.remove", as: :remove_doctor
end
and submitting my Edit form with following hidden param in it:
<input type="hidden" name="_method" value="PATCH">
my request doesn't end up in my Update action and I see a 405 Method Not allowed
error.
Following is the output of Rack env I obtained by putting debug statement at https://github.com/hanami/router/blob/v2.0.1/lib/hanami/router.rb#L101.
It can be seen in that the REQUEST_METHOD
is received as POST instead of expected PATCH
for route doctors/1
.
>>>> env key: rack.version, value; [1, 6]
>>>> env key: rack.errors, value; #<IO:<STDERR>>
>>>> env key: rack.multithread, value; true
>>>> env key: rack.multiprocess, value; true
>>>> env key: rack.run_once, value; false
>>>> env key: rack.url_scheme, value; "http"
>>>> env key: SCRIPT_NAME, value; ""
>>>> env key: QUERY_STRING, value; ""
>>>> env key: SERVER_SOFTWARE, value; "puma 6.0.0 Sunflower"
>>>> env key: GATEWAY_INTERFACE, value; "CGI/1.2"
>>>> env key: REQUEST_METHOD, value; "POST" <----------------------
>>>> env key: REQUEST_PATH, value; "/doctors/1"
>>>> env key: REQUEST_URI, value; "/doctors/1"
>>>> env key: SERVER_PROTOCOL, value; "HTTP/1.1"
>>>> env key: HTTP_HOST, value; "localhost:2300"
>>>> env key: HTTP_USER_AGENT, value; "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0"
>>>> env key: HTTP_ACCEPT, value; "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8"
>>>> env key: HTTP_ACCEPT_LANGUAGE, value; "en-US,en;q=0.5"
>>>> env key: HTTP_ACCEPT_ENCODING, value; "gzip, deflate, br"
>>>> env key: CONTENT_TYPE, value; "application/x-www-form-urlencoded"
>>>> env key: CONTENT_LENGTH, value; "147"
>>>> env key: HTTP_ORIGIN, value; "http://localhost:2300"
>>>> env key: HTTP_CONNECTION, value; "keep-alive"
>>>> env key: HTTP_REFERER, value; "http://localhost:2300/doctors/1/edit"
>>>> env key: HTTP_COOKIE, value;
>>>> env key: HTTP_UPGRADE_INSECURE_REQUESTS, value; "1"
>>>> env key: HTTP_SEC_FETCH_DEST, value; "document"
>>>> env key: HTTP_SEC_FETCH_MODE, value; "navigate"
>>>> env key: HTTP_SEC_FETCH_SITE, value; "same-origin"
>>>> env key: HTTP_SEC_FETCH_USER, value; "?1"
>>>> env key: puma.request_body_wait, value; 0.03847099840641022
>>>> env key: SERVER_NAME, value; "localhost"
>>>> env key: SERVER_PORT, value; "2300"
>>>> env key: PATH_INFO, value; "/doctors/1"
>>>> env key: REMOTE_ADDR, value; "127.0.0.1"
>>>> env key: HTTP_VERSION, value; "HTTP/1.1"
>>>> env key: puma.socket, value; #<TCPSocket:fd 20, AF_INET, 127.0.0.1, 2300>
>>>> env key:
@/all :cherry_blossom: Hanami v2.0.3