From 2b683817c109fb0f1c6a8bac1b84be1a6a8cf457 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Thu, 21 Mar 2024 23:43:37 -0400 Subject: [PATCH 1/4] Website integration (#365) --- .gitignore | 1 - docs/README.md | 83 +++++++++++++++++++++++++++++++++++++ mkdocs.yml | 31 ++++++++++++++ shard.yml | 2 +- src/athena-negotiation.cr | 87 --------------------------------------- 5 files changed, 115 insertions(+), 89 deletions(-) create mode 100644 docs/README.md create mode 100644 mkdocs.yml diff --git a/.gitignore b/.gitignore index 0bbd4a9..82f1ad5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -/docs/ /lib/ /bin/ /.shards/ diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c5ef2b6 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,83 @@ +The `Athena::Negotiation` component allows an application to support [content negotiation](https://tools.ietf.org/html/rfc7231#section-5.3). +The component has no dependencies and is framework agnostic; supporting various negotiators. + +## Installation + +First, install the component by adding the following to your `shard.yml`, then running `shards install`: + +```yaml +dependencies: + athena-negotiation: + github: athena-framework/negotiation + version: ~> 0.1.0 +``` + +## Usage + +The main type of [Athena::Negotiation][] is [ANG::AbstractNegotiator][] which is used to implement negotiators for each `Accept*` header. +`Athena::Negotiation` exposes class level getters for each negotiator; that return a lazily initialized singleton instance. +Each negotiator exposes two methods: [ANG::AbstractNegotiator#best][] and [ANG::AbstractNegotiator#ordered_elements][]. + +### Media Type + +```crystal +negotiator = ANG.negotiator + +accept_header = "text/html, application/xhtml+xml, application/xml;q=0.9" +priorities = ["text/html; charset=UTF-8", "application/json", "application/xml;q=0.5"] + +accept = negotiator.best(accept_header, priorities).not_nil! + +accept.media_range # => "text/html" +accept.parameters # => {"charset" => "UTF-8"} +``` + +The [ANG::Negotiator][] type returns an [ANG::Accept][], or `nil` if negotiating the best media type has failed. + +### Character Set + +```crystal +negotiator = ANG.charset_negotiator + +accept_header = "ISO-8859-1, UTF-8; q=0.9" +priorities = ["iso-8859-1;q=0.3", "utf-8;q=0.9", "utf-16;q=1.0"] + +accept = negotiator.best(accept_header, priorities).not_nil! + +accept.charset # => "utf-8" +accept.quality # => 0.9 +``` + +The [ANG::CharsetNegotiator][] type returns an [ANG::AcceptCharset][], or `nil` if negotiating the best character set has failed. + +### Encoding + +```crystal +negotiator = ANG.encoding_negotiator + +accept_header = "gzip;q=1.0, identity; q=0.5, *;q=0" +priorities = ["gzip", "foo"] + +accept = negotiator.best(accept_header, priorities).not_nil! + +accept.coding # => "gzip" +``` + +The [ANG::EncodingNegotiator][] type returns an [ANG::AcceptEncoding][], or `nil` if negotiating the best encoding has failed. + +### Language + +```crystal +negotiator = ANG.language_negotiator + +accept_header = "en; q=0.1, fr; q=0.4, zh-Hans-CN; q=0.9, de; q=0.2" +priorities = ["de", "zh-Hans-CN", "en"] + +accept = negotiator.best(accept_header, priorities).not_nil! + +accept.language # => "zh" +accept.region # => "cn" +accept.script # => "hans" +``` + +The [ANG::LanguageNegotiator][] type returns an [ANG::AcceptLanguage][], or `nil` if negotiating the best language has failed. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..08ced2d --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,31 @@ +INHERIT: ../../../mkdocs-common.yml + +site_name: Negotiation +site_url: https://athenaframework.org/Negotiation/ +repo_url: https://github.com/athena-framework/negotiation + +nav: + - Introduction: README.md + - Back to Manual: project://. + - API: + - Aliases: aliases.md + - Top Level: top_level.md + - '*' + +plugins: + - search + - section-index + - literate-nav + - gen-files: + scripts: + - ../../../gen_doc_stubs.py + - mkdocstrings: + default_handler: crystal + custom_templates: ../../../docs/templates + handlers: + crystal: + crystal_docs_flags: + - ./docs/index.cr + - ./lib/athena-negotiation/src/athena-negotiation.cr + source_locations: + lib/athena-negotiation: https://github.com/athena-framework/negotiation/blob/v{shard_version}/{file}#L{line} diff --git a/shard.yml b/shard.yml index 1902808..a69be44 100644 --- a/shard.yml +++ b/shard.yml @@ -14,4 +14,4 @@ description: | Framework agnostic content negotiation library. authors: - - George Dietrich + - George Dietrich diff --git a/src/athena-negotiation.cr b/src/athena-negotiation.cr index db3d0da..b348ef0 100644 --- a/src/athena-negotiation.cr +++ b/src/athena-negotiation.cr @@ -14,93 +14,6 @@ require "./exceptions/*" alias ANG = Athena::Negotiation # The `Athena::Negotiation` component allows an application to support [content negotiation](https://tools.ietf.org/html/rfc7231#section-5.3). -# The component has no dependencies and is framework agnostic; supporting various negotiators. -# -# ## Getting Started -# -# If using this component within the [Athena Framework][Athena::Framework], it is already installed and required for you. -# Checkout the [manual](../architecture/negotiation.md) for some additional information on how to use it within the framework. -# -# If using it outside of the framework, you will first need to add it as a dependency: -# -# ```yaml -# dependencies: -# athena-negotiation: -# github: athena-framework/negotiation -# version: ~> 0.1.0 -# ``` -# -# Then run `shards install`, being sure to require it via `require "athena-negotiation"`. -# -# ## Usage -# -# The main type of `Athena::Negotiation` is `ANG::AbstractNegotiator` which is used to implement negotiators for each `Accept*` header. -# `Athena::Negotiation` exposes class level getters for each negotiator; that return a lazily initialized singleton instance. -# Each negotiator exposes two methods: `ANG::AbstractNegotiator#best` and `ANG::AbstractNegotiator#ordered_elements`. -# -# ### Media Type -# -# ``` -# negotiator = ANG.negotiator -# -# accept_header = "text/html, application/xhtml+xml, application/xml;q=0.9" -# priorities = ["text/html; charset=UTF-8", "application/json", "application/xml;q=0.5"] -# -# accept = negotiator.best(accept_header, priorities).not_nil! -# -# accept.media_range # => "text/html" -# accept.parameters # => {"charset" => "UTF-8"} -# ``` -# -# The `ANG::Negotiator` type returns an `ANG::Accept`, or `nil` if negotiating the best media type has failed. -# -# ### Character Set -# -# ``` -# negotiator = ANG.charset_negotiator -# -# accept_header = "ISO-8859-1, UTF-8; q=0.9" -# priorities = ["iso-8859-1;q=0.3", "utf-8;q=0.9", "utf-16;q=1.0"] -# -# accept = negotiator.best(accept_header, priorities).not_nil! -# -# accept.charset # => "utf-8" -# accept.quality # => 0.9 -# ``` -# -# The `ANG::CharsetNegotiator` type returns an `ANG::AcceptCharset`, or `nil` if negotiating the best character set has failed. -# -# ### Encoding -# -# ``` -# negotiator = ANG.encoding_negotiator -# -# accept_header = "gzip;q=1.0, identity; q=0.5, *;q=0" -# priorities = ["gzip", "foo"] -# -# accept = negotiator.best(accept_header, priorities).not_nil! -# -# accept.coding # => "gzip" -# ``` -# -# The `ANG::EncodingNegotiator` type returns an `ANG::AcceptEncoding`, or `nil` if negotiating the best encoding has failed. -# -# ### Language -# -# ``` -# negotiator = ANG.language_negotiator -# -# accept_header = "en; q=0.1, fr; q=0.4, zh-Hans-CN; q=0.9, de; q=0.2" -# priorities = ["de", "zh-Hans-CN", "en"] -# -# accept = negotiator.best(accept_header, priorities).not_nil! -# -# accept.language # => "zh" -# accept.region # => "cn" -# accept.script # => "hans" -# ``` -# -# The `ANG::LanguageNegotiator` type returns an `ANG::AcceptLanguage`, or `nil` if negotiating the best language has failed. module Athena::Negotiation VERSION = "0.1.4" From f6a843890fce0c7ecaeaa11e35fe5bf91488fe01 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Tue, 9 Apr 2024 20:10:37 -0400 Subject: [PATCH 2/4] Release time! (#393) --- CHANGELOG.md | 7 +++++++ README.md | 18 ++---------------- shard.yml | 2 +- src/athena-negotiation.cr | 2 +- 4 files changed, 11 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a542e3e..d54ee29 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## [0.1.5] - 2024-04-09 + +### Changed + +- Integrate website into monorepo ([#365](https://github.com/athena-framework/athena/pull/365)) (George Dietrich) + ## [0.1.4] - 2023-10-09 _Administrative release, no functional changes_ @@ -37,6 +43,7 @@ _First release a part of the monorepo._ _Initial release._ +[0.1.5]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.5 [0.1.4]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.4 [0.1.3]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.3 [0.1.2]: https://github.com/athena-framework/negotiation/releases/tag/v0.1.2 diff --git a/README.md b/README.md index 51b81a6..6a1e9e4 100644 --- a/README.md +++ b/README.md @@ -6,23 +6,9 @@ Framework agnostic [content negotiation](https://tools.ietf.org/html/rfc7231#section-5.3) library based on [willdurand/Negotiation](https://github.com/willdurand/Negotiation). -## Installation +## Getting Started -1. Add the dependency to your `shard.yml`: - -```yaml -dependencies: - athena-negotiation: - github: athena-framework/negotiation - version: ~> 0.1.0 -``` - -2. Run `shards install` - -## Documentation - -If using the component on its own, checkout the [API documentation](https://athenaframework.org/Negotiation). -If using the component as part of Athena, also checkout the [external documentation](https://athenaframework.org/architecture/negotiation). +Checkout the [Documentation](https://athenaframework.org/Negotiation). ## Contributing diff --git a/shard.yml b/shard.yml index a69be44..b15ac1f 100644 --- a/shard.yml +++ b/shard.yml @@ -1,6 +1,6 @@ name: athena-negotiation -version: 0.1.4 +version: 0.1.5 crystal: ~> 1.4 diff --git a/src/athena-negotiation.cr b/src/athena-negotiation.cr index b348ef0..12ddf3a 100644 --- a/src/athena-negotiation.cr +++ b/src/athena-negotiation.cr @@ -15,7 +15,7 @@ alias ANG = Athena::Negotiation # The `Athena::Negotiation` component allows an application to support [content negotiation](https://tools.ietf.org/html/rfc7231#section-5.3). module Athena::Negotiation - VERSION = "0.1.4" + VERSION = "0.1.5" # Returns a lazily initialized `ANG::Negotiator` singleton instance. class_getter(negotiator) { ANG::Negotiator.new } From d821e338397adb92e4340b0d3eda325b169d0c9d Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Fri, 24 May 2024 10:12:32 -0400 Subject: [PATCH 3/4] Use `utf-8` as lowercase is preferred (#417) --- docs/README.md | 6 +++--- spec/negotiator_spec.cr | 6 +++--- src/accept.cr | 8 ++++---- src/base_accept.cr | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/README.md b/docs/README.md index c5ef2b6..94b03ef 100644 --- a/docs/README.md +++ b/docs/README.md @@ -24,12 +24,12 @@ Each negotiator exposes two methods: [ANG::AbstractNegotiator#best][] and [ANG:: negotiator = ANG.negotiator accept_header = "text/html, application/xhtml+xml, application/xml;q=0.9" -priorities = ["text/html; charset=UTF-8", "application/json", "application/xml;q=0.5"] +priorities = ["text/html; charset=utf-8", "application/json", "application/xml;q=0.5"] accept = negotiator.best(accept_header, priorities).not_nil! accept.media_range # => "text/html" -accept.parameters # => {"charset" => "UTF-8"} +accept.parameters # => {"charset" => "utf-8"} ``` The [ANG::Negotiator][] type returns an [ANG::Accept][], or `nil` if negotiating the best media type has failed. @@ -39,7 +39,7 @@ The [ANG::Negotiator][] type returns an [ANG::Accept][], or `nil` if negotiating ```crystal negotiator = ANG.charset_negotiator -accept_header = "ISO-8859-1, UTF-8; q=0.9" +accept_header = "ISO-8859-1, utf-8; q=0.9" priorities = ["iso-8859-1;q=0.3", "utf-8;q=0.9", "utf-16;q=1.0"] accept = negotiator.best(accept_header, priorities).not_nil! diff --git a/spec/negotiator_spec.cr b/spec/negotiator_spec.cr index f267d92..353942d 100644 --- a/spec/negotiator_spec.cr +++ b/spec/negotiator_spec.cr @@ -71,9 +71,9 @@ struct NegotiatorTest < NegotiatorTestCase {"image/png, text/plain, audio/ogg", {"baz/asdf"}, nil}, {"image/png, text/plain, audio/ogg", {"audio/ogg"}, {"audio/ogg", nil}}, {"image/png, text/plain, audio/ogg", {"YO/SuP"}, nil}, - {"text/html; charset=UTF-8, application/pdf", {"text/html; charset=UTF-8"}, {"text/html", {"charset" => "UTF-8"}}}, - {"text/html; charset=UTF-8, application/pdf", {"text/html"}, nil}, - {"text/html, application/pdf", {"text/html; charset=UTF-8"}, {"text/html", {"charset" => "UTF-8"}}}, + {"text/html; charset=utf-8, application/pdf", {"text/html; charset=utf-8"}, {"text/html", {"charset" => "utf-8"}}}, + {"text/html; charset=utf-8, application/pdf", {"text/html"}, nil}, + {"text/html, application/pdf", {"text/html; charset=utf-8"}, {"text/html", {"charset" => "utf-8"}}}, # PHP"s PEAR HTTP2 assertions I took from the other lib. {php_pear_header, {"image/gif", "image/png", "application/xhtml+xml", "application/xml", "text/html", "image/jpeg", "text/plain"}, {"image/png", nil}}, diff --git a/src/accept.cr b/src/accept.cr index 8a4b6df..d69e91a 100644 --- a/src/accept.cr +++ b/src/accept.cr @@ -3,11 +3,11 @@ require "./base_accept" # Represents an [Accept](https://tools.ietf.org/html/rfc7231#section-5.3.2) header media type. # # ``` -# accept = ANG::Accept.new "application/json; q = 0.75; charset = UTF-8" +# accept = ANG::Accept.new "application/json; q = 0.75; charset = utf-8" # -# accept.header # => "application/json; q = 0.75; charset = UTF-8" -# accept.normalized_header # => "application/json; charset=UTF-8" -# accept.parameters # => {"charset" => "UTF-8"} +# accept.header # => "application/json; q = 0.75; charset = utf-8" +# accept.normalized_header # => "application/json; charset=utf-8" +# accept.parameters # => {"charset" => "utf-8"} # accept.quality # => 0.75 # accept.type # => "application" # accept.sub_type # => "json" diff --git a/src/base_accept.cr b/src/base_accept.cr index f11d50e..b9e02b8 100644 --- a/src/base_accept.cr +++ b/src/base_accept.cr @@ -10,7 +10,7 @@ abstract struct Athena::Negotiation::BaseAccept getter normalized_header : String # Returns any extension parameters included in the header `self` represents. - # E.x. `charset=UTF-8` or `version=2`. + # E.x. `charset=utf-8` or `version=2`. getter parameters : Hash(String, String) = Hash(String, String).new # Returns the [quality value](https://tools.ietf.org/html/rfc7231#section-5.3.1) of the header `self` represents. From 94cf8e33d9604104334a5b6bc42812a359d3d961 Mon Sep 17 00:00:00 2001 From: George Dietrich Date: Mon, 15 Jul 2024 14:07:30 -0400 Subject: [PATCH 4/4] Bump typos version in CI & address errors (athena-framework/athena#430) --- src/abstract_negotiator.cr | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/abstract_negotiator.cr b/src/abstract_negotiator.cr index 0f67a51..1bddf8c 100644 --- a/src/abstract_negotiator.cr +++ b/src/abstract_negotiator.cr @@ -26,9 +26,9 @@ abstract class Athena::Negotiation::AbstractNegotiator(HeaderType) raise ex if strict end - accepted_priorties = priorities.map { |p| HeaderType.new p } + accepted_priorities = priorities.map { |p| HeaderType.new p } - matches = self.find_matches accepted_headers, accepted_priorties + matches = self.find_matches accepted_headers, accepted_priorities specific_matches = matches.reduce({} of Int32 => ANG::AcceptMatch) do |acc, match| ANG::AcceptMatch.reduce acc, match @@ -38,7 +38,7 @@ abstract class Athena::Negotiation::AbstractNegotiator(HeaderType) match = specific_matches.shift? - match.nil? ? nil : accepted_priorties[match.index] + match.nil? ? nil : accepted_priorities[match.index] end # Returns an array of `HeaderType` that the provided *header* allows, ordered so that the `#best` match is first.