From 06b79ed475e2a6b8b7d93b6fb377593a11d71f59 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Sat, 11 Oct 2025 22:13:15 +1100 Subject: [PATCH 1/4] Update action format docs for new 2.3 API --- .../v2.3/actions/formats-and-mime-types.md | 80 +++++++++++++------ 1 file changed, 54 insertions(+), 26 deletions(-) diff --git a/content/v2.3/actions/formats-and-mime-types.md b/content/v2.3/actions/formats-and-mime-types.md index 51906948..066d8cc0 100644 --- a/content/v2.3/actions/formats-and-mime-types.md +++ b/content/v2.3/actions/formats-and-mime-types.md @@ -7,35 +7,36 @@ Hanami maps [over 50 of the most common MIME types][built-in-formats] to simple [built-in-formats]: https://github.com/hanami/controller/blob/dc5bb2a1db48b0ccf3faf52aac20eaef0fd135a3/lib/hanami/action/mime.rb#L15-L69 -Configuring one or more formats for your actions will: +Configuring your actions to accept one or more formats will: -- Ensure the actions accept only appropriate requests based on their `Accept` or `Content-Type` headers -- Set an appropriate `Content-Type` header on responses +- Ensure the actions accept only matching requests based on their `Accept` or `Content-Type` headers +- Default to an appropriate `Content-Type` header on responses - For certain formats, enable automatic parsing of request bodies -## Configuring a format for all actions +## Accepting a format across all actions -To configure a format for all actions, use `config.actions.format` in your app class. +To accept a format for all actions, use `config.actions.format` in your app class. ```ruby # config/app.rb module Bookshelf class App < Hanami::App - config.actions.format :json + config.actions.formats.accept :json end end ``` -You can also configure actions to use multiple formats: +You can also configure actions to accept multiple formats: ```ruby -config.actions.format :json, :html +config.actions.formats.accept :json, :html ``` -## Configuring a format for particular actions +## Accepting a format for particular actions -You can also configure a format on any action class. `format` in an action class is analogous to `config.actions.format` in your app class, just as `config` in an action is analogous to `config.actions` in your app. +You can also accept formats on any individual action class. `config.formats` in an action class is +analogous to `config.actions.formats` in your app class. ```ruby # app/actions/books/index.rb @@ -44,7 +45,7 @@ module Bookshelf module Actions module Books class Index < Bookshelf::Action - format :json # or `config.format :json` + config.formats.accept :json def handle(request, response) # ... @@ -55,21 +56,21 @@ module Bookshelf end ``` -If you configure a format on a base action class, then it will be inherited by all its subclasses. +If you accept a format on a base action class, then it will be inherited by all its subclasses. ```ruby # app/action.rb module Bookshelf class Action < Hanami::Action - config.format :json + config.formats.accept :json end end ``` ## Request acceptance -Once you've configured a format, your actions will reject certain requests that do not match the format. +Once your actions accept a format, they will reject requests that do not match the format. The following kinds of requests will be accepted: @@ -82,7 +83,7 @@ Whereas these kinds of requests will be rejected: - `Accept` does not include the format's MIME type, rejected as `406 Not acceptable` - No `Accept` header, but a `Content-Type` header is present and does not match the format's MIME type, rejected as `415 Unsupported media type` -For example, if you configure `format :json`, then requests with these headers will be accepted: +For example, if you set `config.formats.accept :json`, then requests with these headers will be accepted: - `Accept: application/json` - `Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"` (courtesy of the `*/*`) @@ -96,14 +97,14 @@ While requests with these headers will be rejected: ## Parsing JSON request bodies -If you configure `:json` as your action format in the app, then any requests with `Content-Type: application/json` will have their request bodies parsed and made available as request params. +If you configure `:json` as an accepted format in your app class, then any requests with `Content-Type: application/json` will have their request bodies parsed and made available as request params. ```ruby # config/app.rb module Bookshelf class App < Hanami::App - config.actions.format :json + config.actions.formats.accept :json end end ``` @@ -122,9 +123,22 @@ end ## Response format -Actions set a `Content-Type` response header based on your configured formats along with the MIME type and charset of the incoming request. +Actions set a default `Content-Type` response header, based on your accepted formats along with the MIME type and charset of the request. -For example, if a request's `Accept` header is `"text/html,application/xhtml+xml,application/xml;q=0.9"`, the action will return a content type of `"text/html; charset=utf-8"`, assuming that the action is configured with the `:html` format. +For example, if a request's `Accept` header is `"text/html,application/xhtml+xml,application/xml;q=0.9"`, the action will return a content type of `"text/html; charset=utf-8"`, assuming that the action is configured to accept the `:html` format. + +When handling a request _without_ an `Accept` header, the response's `Content-Type` will be set to the action's _default format_. You can configure this directly: + +```ruby +config.formats.default = :json +``` + +If you do not directly configure a default format, then the first of your accepted formats will be the default: + +```ruby +config.formats.accept :json, :html +config.formats.default # => :json +``` You can also assign a particular format directly on the response inside your action. @@ -145,6 +159,7 @@ module Bookshelf end ``` + ## Default character set The default character set for actions is `utf-8`. This is included in your response's `Content-Type` header: @@ -172,7 +187,7 @@ module Bookshelf module Actions module Books class Index < Bookshelf::Action - config.default_charset "koi8-r" + config.default_charset = koi8-r" end end end @@ -181,30 +196,43 @@ end ## Registering additional MIME Types -If you need your actions to work with additional MIME types, you can configure these like so: +If you need your actions to work with additional MIME types, you can register these as formats: ```ruby # config/app.rb module Bookshelf class App < Hanami::App - config.actions.formats.add :custom, "application/custom" + config.actions.formats.register custom: "application/custom" end end ``` -This will add the `:custom` format for the `"application/custom"` MIME type and also configure your actions to use this format. +This will add the `:custom` format for the `"application/custom"` MIME type. -You can also configure a format to map to multiple MIME types: +You can also configure multiple MIME types to correspond to a format: ```ruby # config/app.rb module Bookshelf class App < Hanami::App - config.actions.formats.add :json, ["application/json+scim", "application/json"] + config.actions.formats.register json: ["application/json+scim", "application/json"] end end ``` -In this case, requests for both these MIME types will be accepted. +In this case, requests for either of these MIME types be considered as `:json`. + +For your actions to accept requests with these registered MIME types, you must still configure your acctions to accept the corresponding format: + +```ruby +# config/app.rb + +module Bookshelf + class App < Hanami::App + config.actions.formats.register json: ["application/json+scim", "application/json"] + config.actions.formats.accept :json + end +end +``` From 85e866b62c119d71841d2a315289d792911f1957 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Sat, 11 Oct 2025 23:17:19 +1100 Subject: [PATCH 2/4] Remove blank line --- content/v2.3/actions/formats-and-mime-types.md | 1 - 1 file changed, 1 deletion(-) diff --git a/content/v2.3/actions/formats-and-mime-types.md b/content/v2.3/actions/formats-and-mime-types.md index 066d8cc0..26487e09 100644 --- a/content/v2.3/actions/formats-and-mime-types.md +++ b/content/v2.3/actions/formats-and-mime-types.md @@ -159,7 +159,6 @@ module Bookshelf end ``` - ## Default character set The default character set for actions is `utf-8`. This is included in your response's `Content-Type` header: From 383075c7987872c9e4302814ec956d419f568623 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Sat, 11 Oct 2025 23:18:20 +1100 Subject: [PATCH 3/4] Use correct version number in 2.2 upgrade guide MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was accidentally changed in a mass find/replace of “2.2” to “2.3” when we created the 2.3 guides. --- content/v2.3/upgrade-notes/v2.2.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/content/v2.3/upgrade-notes/v2.2.md b/content/v2.3/upgrade-notes/v2.2.md index def29b3b..e91c6d82 100644 --- a/content/v2.3/upgrade-notes/v2.2.md +++ b/content/v2.3/upgrade-notes/v2.2.md @@ -1,13 +1,13 @@ --- -title: Upgrade to 2.3 +title: Upgrade to 2.2 order: 20 --- -These notes cover an upgrade from 2.1 to 2.3. +These notes cover an upgrade from 2.1 to 2.2. ## Update the app -- Edit `Gemfile` and update the versions of the Hanami gems to `"~> 2.3"`. +- Edit `Gemfile` and update the versions of the Hanami gems to `"~> 2.2"`. - Remove `gem "guard-puma"` from your `Gemfile` (this is now a dependency of hanami-reloader). @@ -31,7 +31,7 @@ Certain steps below apply to each of your app and slices. Where the code example - Add the following gems to your `Gemfile`: ```ruby -gem "hanami-db", "~> 2.3" +gem "hanami-db", "~> 2.2" # If you want a SQLite database gem "sqlite3" From 51af87930b7df0440b632ec1a0c4475cc3a2b022 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Sat, 11 Oct 2025 23:18:50 +1100 Subject: [PATCH 4/4] Add beginning of 2.3 upgrade guide, covering action format changes --- content/v2.3/upgrade-notes/v2.3.md | 57 ++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 content/v2.3/upgrade-notes/v2.3.md diff --git a/content/v2.3/upgrade-notes/v2.3.md b/content/v2.3/upgrade-notes/v2.3.md new file mode 100644 index 00000000..6e893867 --- /dev/null +++ b/content/v2.3/upgrade-notes/v2.3.md @@ -0,0 +1,57 @@ +--- +title: Upgrade to 2.3 +order: 30 +--- + +These notes cover an upgrade from 2.2 to 2.3. + +## Use new formats config for actions + +`Hanami::Action.format`, `Hanami::Action::Config#format`, `Hanami::Action::Config::Formats#values`, and `Hanami::Action::Config::Formats#add` are now deprecated and will be removed in Hanami 2.4. + +These methods are deprecated because they did not make it clear that configuring a format would immediately restrict requests based on that format, and conflated the registration of custom MIME types with immediately restricting based on those types, and did not allow for the default response format to be independently configured. This led to unexpected behaviors and made it difficult configure formats in the app or base action classes. + +The new format config API is documented in [Formats and MIME types](/v2.3/actions/formats-and-mime-types). To use it, make the following changes wherever you configure your actions (in your app class using `config.actions`, or directly in your action classes using `config` or `format`). + +Change `format` and `config.format` to `config.formats.accept`. For example: + +```ruby +# In app or slice classes: +# +# Before: +# config.actions.format :json +config.actions.formats.accept :json + +# In an action class: +# +# Before: +# format :json +# Or: +# config.format :json +config.formats.accept :json +``` + +Change `config.formats.add` to `config.formats.register`. For example: + +```ruby +# In app or slice classes: +# +# Before: +# config.actions.formats.add :custom, "application/custom" +config.actions.formats.register custom: "application/custom" + +# In an action class: +# +# Before: +# config.formats.add :custom, "application/custom" +config.formats.register custom: "application/custom" +``` + +With these changes, calling `register` no longer adds the format to the list of accepted formats. You must now do this separately: + +```ruby +config.formats.register custom: "application/custom" +config.formats.accept :custom +``` + +The benefit of this change is that you can now more easily `register` custom MIME types in an app class or base action class, and then choose to `accept` them only in specific action classes.