hanami-controller 2.0.0.rc1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0a5d623c09e8ecb8c267de856196333c5da74e066b55501af2acc706349d0d73
4
- data.tar.gz: '0786076a81b7a11ca79aedba2ff0318f207683e71ba931293dcc1f585a191e0b'
3
+ metadata.gz: '03586fe42cbe2496c8433bb50cf8b176d1edd75ecd2848d89e28e9d6b2cd735e'
4
+ data.tar.gz: 3a5e3a900db6918d9c3f74e49509efb5e33afcf9843ac76aa837fa193f6eb748
5
5
  SHA512:
6
- metadata.gz: 81f1cddefe11892d05ccb63e8649bc9fb6e50f2258eb02503044577e9c0e445c57cbf2318046a4002e3c9b107f9df530463cbeb10460bb3339bb52b747deb0eb
7
- data.tar.gz: eb26f557543e0d345580b9160ec4123def85b4a4ec5a1ad0a17f8a8d15d05dc34f12e13705082e21b95a5cc3095d36677b099a319a1c2472c7180e413f5c5faf
6
+ metadata.gz: de6142be19fc0a9d18e8a27260ab94729d2103f72589e9c36db8226d9cf9bb19b9cec9193e0c7e00659afd5f8aee775abda2af0ed21e1962315d77c6b789bd89
7
+ data.tar.gz: 98927780e4bfd2e31de03b3d2bb01441aad6405743ff68991d80a76c92647191bd929e2a345554cf5146097cf3dd222717451b7f93d326cf0fb49ef5ce7905f8
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  Complete, fast and testable actions for Rack
4
4
 
5
+ ## v2.0.0 - 2022-11-22
6
+
7
+ ### Added
8
+
9
+ - [Tim Riley] Use Zeitwerk to autoload the gem
10
+ - [Tim Riley] Introduce `Hanami::Action::Config#formats`. Use `config.actions.formats.add(:json)`. Custom formats can use `config.actions.formats.add(:graphql, ["application/graphql"])`
11
+
12
+ ### Changed
13
+
14
+ - [Tim Riley] Changed `Hanami::Action::Config#format` semantic: it's no longer used to add custom MIME Types, but as a macro to setup the wanted format for action(s)
15
+ - [Tim Riley] Removed `Hanami::Action::Config#default_request_format` and `#default_response_format`, use `#format` for both
16
+ - [Tim Riley] Removed `Hanami::Action::Config#accept`, use `#format`
17
+
5
18
  ## v2.0.0.rc1 - 2022-11-08
6
19
 
7
20
  ### Changed
data/README.md CHANGED
@@ -928,12 +928,6 @@ configuration = Hanami::Controller::Configuration.new do |config|
928
928
  #
929
929
  config.format custom: "application/custom"
930
930
 
931
- # Define a fallback format to detect in case of HTTP request with `Accept: */*`
932
- # If not defined here, it will return Rack's default: `application/octet-stream`
933
- # Argument: symbol, it should be already known. defaults to `nil`
934
- #
935
- config.default_request_format = :html
936
-
937
931
  # Define a default format to set as `Content-Type` header for response,
938
932
  # unless otherwise specified.
939
933
  # If not defined here, it will return Rack's default: `application/octet-stream`
@@ -20,9 +20,11 @@ Gem::Specification.new do |spec|
20
20
  spec.metadata["rubygems_mfa_required"] = "true"
21
21
  spec.required_ruby_version = ">= 3.0"
22
22
 
23
- spec.add_dependency "rack", "~> 2.0"
24
- spec.add_dependency "hanami-utils", "~> 2.0.0.rc1"
23
+ spec.add_dependency "rack", "~> 2.0"
24
+ spec.add_dependency "hanami-utils", "~> 2.0"
25
25
  spec.add_dependency "dry-configurable", "~> 1.0", "< 2"
26
+ spec.add_dependency "dry-core", "~> 1.0"
27
+ spec.add_dependency "zeitwerk", "~> 2.6"
26
28
 
27
29
  spec.add_development_dependency "bundler", ">= 1.6", "< 3"
28
30
  spec.add_development_dependency "rack-test", "~> 2.0"
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/action/cache/directives"
4
-
5
3
  module Hanami
6
4
  class Action
7
5
  module Cache
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/action/cache/cache_control"
4
-
5
3
  module Hanami
6
4
  class Action
7
5
  module Cache
@@ -1,9 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "hanami/action/cache/cache_control"
4
- require "hanami/action/cache/expires"
5
- require "hanami/action/cache/conditional_get"
6
-
7
3
  module Hanami
8
4
  class Action
9
5
  # Cache type API
@@ -0,0 +1,216 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "hanami/utils/kernel"
4
+ require "dry/core"
5
+
6
+ module Hanami
7
+ class Action
8
+ class Config
9
+ # Action format configuration.
10
+ #
11
+ # @since 2.0.0
12
+ # @api private
13
+ class Formats
14
+ include Dry.Equalizer(:values, :mapping)
15
+
16
+ # Default MIME type to format mapping
17
+ #
18
+ # @since 2.0.0
19
+ # @api private
20
+ DEFAULT_MAPPING = {
21
+ "application/octet-stream" => :all,
22
+ "*/*" => :all
23
+ }.freeze
24
+
25
+ # @since 2.0.0
26
+ # @api private
27
+ attr_reader :mapping
28
+
29
+ # The array of enabled formats.
30
+ #
31
+ # @example
32
+ # config.formats.values = [:html, :json]
33
+ # config.formats.values # => [:html, :json]
34
+ #
35
+ # @since 2.0.0
36
+ # @api public
37
+ attr_reader :values
38
+
39
+ # @since 2.0.0
40
+ # @api private
41
+ def initialize(values: [], mapping: DEFAULT_MAPPING.dup)
42
+ @values = values
43
+ @mapping = mapping
44
+ end
45
+
46
+ # @since 2.0.0
47
+ # @api private
48
+ private def initialize_copy(original) # rubocop:disable Style/AccessModifierDeclarations
49
+ super
50
+ @values = original.values.dup
51
+ @mapping = original.mapping.dup
52
+ end
53
+
54
+ # !@attribute [w] values
55
+ # @since 2.0.0
56
+ # @api public
57
+ def values=(formats)
58
+ @values = formats.map { |f| Utils::Kernel.Symbol(f) }
59
+ end
60
+
61
+ # @overload add(format)
62
+ # Adds and enables a format.
63
+ #
64
+ # @param format [Symbol]
65
+ #
66
+ # @example
67
+ # config.formats.add(:json)
68
+ #
69
+ # @overload add(format, mime_type)
70
+ # Adds a custom format to MIME type mapping and enables the format.
71
+ # Adds a format mapping to a single MIME type.
72
+ #
73
+ # @param format [Symbol]
74
+ # @param mime_type [String]
75
+ #
76
+ # @example
77
+ # config.formats.add(:json, "application/json")
78
+ #
79
+ # @overload add(format, mime_types)
80
+ # Adds a format mapping to multiple MIME types.
81
+ #
82
+ # @param format [Symbol]
83
+ # @param mime_types [Array<String>]
84
+ #
85
+ # @example
86
+ # config.formats.add(:json, ["application/json+scim", "application/json"])
87
+ #
88
+ # @return [self]
89
+ #
90
+ # @since 2.0.0
91
+ # @api public
92
+ def add(format, mime_types = [])
93
+ format = Utils::Kernel.Symbol(format)
94
+
95
+ Array(mime_types).each do |mime_type|
96
+ @mapping[Utils::Kernel.String(mime_type)] = format
97
+ end
98
+
99
+ @values << format unless @values.include?(format)
100
+
101
+ self
102
+ end
103
+
104
+ # @since 2.0.0
105
+ # @api private
106
+ def empty?
107
+ @values.empty?
108
+ end
109
+
110
+ # @since 2.0.0
111
+ # @api private
112
+ def any?
113
+ @values.any?
114
+ end
115
+
116
+ # @since 2.0.0
117
+ # @api private
118
+ def map(&blk)
119
+ @values.map(&blk)
120
+ end
121
+
122
+ # @since 2.0.0
123
+ # @api private
124
+ def mapping=(mappings)
125
+ @mapping = {}
126
+
127
+ mappings.each do |format_name, mime_types|
128
+ Array(mime_types).each do |mime_type|
129
+ add(format_name, mime_type)
130
+ end
131
+ end
132
+ end
133
+
134
+ # Clears any previously added mappings and format values.
135
+ #
136
+ # @return [self]
137
+ #
138
+ # @since 2.0.0
139
+ # @api public
140
+ def clear
141
+ @mapping = DEFAULT_MAPPING.dup
142
+ @values = []
143
+
144
+ self
145
+ end
146
+
147
+ # Retrieve the format name associated with the given MIME Type
148
+ #
149
+ # @param mime_type [String] the MIME Type
150
+ #
151
+ # @return [Symbol,NilClass] the associated format name, if any
152
+ #
153
+ # @example
154
+ # @config.formats.format_for("application/json") # => :json
155
+ #
156
+ # @see #mime_type_for
157
+ #
158
+ # @since 2.0.0
159
+ # @api public
160
+ def format_for(mime_type)
161
+ @mapping[mime_type]
162
+ end
163
+
164
+ # Returns the primary MIME type associated with the given format.
165
+ #
166
+ # @param format [Symbol] the format name
167
+ #
168
+ # @return [String, nil] the associated MIME type, if any
169
+ #
170
+ # @example
171
+ # @config.formats.mime_type_for(:json) # => "application/json"
172
+ #
173
+ # @see #format_for
174
+ #
175
+ # @since 2.0.0
176
+ # @api public
177
+ def mime_type_for(format)
178
+ @mapping.key(format)
179
+ end
180
+
181
+ # Returns an array of all MIME types associated with the given format.
182
+ #
183
+ # Returns an empty array if no such format is configured.
184
+ #
185
+ # @param format [Symbol] the format name
186
+ #
187
+ # @return [Array<String>] the associated MIME types
188
+ #
189
+ # @since 2.0.0
190
+ # @api public
191
+ def mime_types_for(format)
192
+ @mapping.each_with_object([]) { |(mime_type, f), arr| arr << mime_type if format == f }
193
+ end
194
+
195
+ # Returns the default format name
196
+ #
197
+ # @return [Symbol, nil] the default format name, if any
198
+ #
199
+ # @example
200
+ # @config.formats.default # => :json
201
+ #
202
+ # @since 2.0.0
203
+ # @api public
204
+ def default
205
+ @values.first
206
+ end
207
+
208
+ # @since 2.0.0
209
+ # @api private
210
+ def keys
211
+ @mapping.keys
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "dry/configurable"
4
- require_relative "mime"
5
4
 
6
5
  module Hanami
7
6
  class Action
@@ -12,16 +11,6 @@ module Hanami
12
11
  # @api public
13
12
  # @since 2.0.0
14
13
  class Config < Dry::Configurable::Config
15
- # Default MIME type to format mapping
16
- #
17
- # @since 0.2.0
18
- # @api private
19
- DEFAULT_FORMATS = {
20
- "application/octet-stream" => :all,
21
- "*/*" => :all,
22
- "text/html" => :html
23
- }.freeze
24
-
25
14
  # Default public directory
26
15
  #
27
16
  # This serves as the root directory for file downloads
@@ -69,121 +58,37 @@ module Hanami
69
58
  .to_h
70
59
  end
71
60
 
72
- # @!attribute [rw] formats
73
- #
74
- # Specifies the MIME type to format mapping
75
- #
76
- # @return [Hash{String=>Symbol}] MIME type strings as keys and format symbols as values
77
- #
78
- # @see format
79
- # @see Hanami::Action::Mime
61
+ # @!attribute [r] formats
62
+ # Returns the format config for the action.
80
63
  #
81
- # @example
82
- # config.formats = {"text/html" => :html}
64
+ # @return [Config::Formats]
83
65
  #
84
- # @since 0.2.0
66
+ # @since 2.0.0
67
+ # @api public
85
68
 
86
- # Registers a MIME type to format mapping
69
+ # Sets the format (or formats) for the action.
87
70
  #
88
- # @param hash [Hash{Symbol=>String}] format symbols as keys and the MIME
89
- # type strings must as values
90
- #
91
- # @return [void]
92
- #
93
- # @see formats
94
- # @see Hanami::Action::Mime
71
+ # To configure custom formats and MIME type mappings, call {Formats#add formats.add} first.
95
72
  #
96
73
  # @example
97
- # config.format html: "text/html"
98
- #
99
- # @since 0.2.0
100
- def format(hash)
101
- symbol, mime_type = *Utils::Kernel.Array(hash)
102
- formats[Utils::Kernel.String(mime_type)] = Utils::Kernel.Symbol(symbol)
103
- end
104
-
105
- # Returns the configured format for the given MIME type
106
- #
107
- # @param mime_type [#to_s,#to_str] A mime type
108
- #
109
- # @return [Symbol,nil] the corresponding format, nil if not found
110
- #
111
- # @see format
112
- #
113
- # @since 0.2.0
114
- # @api private
115
- def format_for(mime_type)
116
- formats[mime_type]
117
- end
118
-
119
- # Returns the configured format's MIME types
120
- #
121
- # @return [Array<String>] the format's MIME types
122
- #
123
- # @see formats=
124
- # @see format
74
+ # config.format :html, :json
125
75
  #
126
- # @since 0.8.0
76
+ # @param formats [Array<Symbol>] the format names
127
77
  #
128
- # @api private
129
- def mime_types
130
- # FIXME: this isn't efficient. speed it up!
131
- ((formats.keys - DEFAULT_FORMATS.keys) +
132
- Hanami::Action::Mime::TYPES.values).freeze
133
- end
134
-
135
- # Returns a MIME type for the given format
136
- #
137
- # @param format [#to_sym] a format
78
+ # @return [Array<Symbol>] the given format names
138
79
  #
139
- # @return [String,nil] the corresponding MIME type, if present
80
+ # @see #formats
140
81
  #
141
- # @since 0.2.0
142
- # @api private
143
- def mime_type_for(format)
144
- formats.key(format)
145
- end
146
-
147
82
  # @since 2.0.0
148
- # @api private
149
- def accepted_mime_types
150
- accepted_formats.any? ? Mime.restrict_mime_types(self) : mime_types
83
+ # @api public
84
+ def format(*formats)
85
+ if formats.empty?
86
+ self.formats.values
87
+ else
88
+ self.formats.values = formats
89
+ end
151
90
  end
152
91
 
153
- # @!attribute [rw] default_request_format
154
- #
155
- # Sets a format as default fallback for all the requests without a strict
156
- # requirement for the MIME type.
157
- #
158
- # The given format must be coercible to a symbol, and be a valid MIME
159
- # type alias. If it isn't, at runtime the framework will raise an
160
- # `Hanami::Action::UnknownFormatError`.
161
- #
162
- # By default, this value is nil.
163
- #
164
- # @return [Symbol]
165
- #
166
- # @see Hanami::Action::Mime
167
- #
168
- # @since 0.5.0
169
-
170
- # @!attribute [rw] default_response_format
171
- #
172
- # Sets a format to be used for all responses regardless of the request
173
- # type.
174
- #
175
- # The given format must be coercible to a symbol, and be a valid MIME
176
- # type alias. If it isn't, at the runtime the framework will raise an
177
- # `Hanami::Action::UnknownFormatError`.
178
- #
179
- # By default, this value is nil.
180
- #
181
- # @return [Symbol]
182
- #
183
- # @see Hanami::Action::Mime
184
- #
185
- # @since 0.5.0
186
-
187
92
  # @!attribute [rw] default_charset
188
93
  #
189
94
  # Sets a charset (character set) as default fallback for all the requests
@@ -21,7 +21,14 @@ module Hanami
21
21
  # @since 2.0.0
22
22
  # @api private
23
23
  def initialize(format)
24
- super("Cannot find a corresponding Mime type for '#{format}'. Please configure it with Hanami::Controller::Configuration#format.") # rubocop:disable Layout/LineLength
24
+ msg =
25
+ if format.to_s != "" # rubocop:disable Style/NegatedIfElseCondition
26
+ "Cannot find a corresponding MIME type for format #{format.inspect}. Configure one via `config.formats.add(#{format}: \"MIME_TYPE_HERE\")`." # rubocop:disable Layout/LineLength
27
+ else
28
+ "Cannot find a corresponding MIME type for `nil` format."
29
+ end
30
+
31
+ super(msg)
25
32
  end
26
33
  end
27
34
 
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Hanami
4
+ class Action
5
+ module Mime
6
+ # @since 1.0.1
7
+ # @api private
8
+ class RequestMimeWeight
9
+ # @since 2.0.0
10
+ # @api private
11
+ MIME_SEPARATOR = "/"
12
+ private_constant :MIME_SEPARATOR
13
+
14
+ # @since 2.0.0
15
+ # @api private
16
+ MIME_WILDCARD = "*"
17
+ private_constant :MIME_WILDCARD
18
+
19
+ include Comparable
20
+
21
+ # @since 1.0.1
22
+ # @api private
23
+ attr_reader :quality
24
+
25
+ # @since 1.0.1
26
+ # @api private
27
+ attr_reader :index
28
+
29
+ # @since 1.0.1
30
+ # @api private
31
+ attr_reader :mime
32
+
33
+ # @since 1.0.1
34
+ # @api private
35
+ attr_reader :format
36
+
37
+ # @since 1.0.1
38
+ # @api private
39
+ attr_reader :priority
40
+
41
+ # @since 1.0.1
42
+ # @api private
43
+ def initialize(mime, quality, index, format = mime)
44
+ @quality, @index, @format = quality, index, format
45
+ calculate_priority(mime)
46
+ end
47
+
48
+ # @since 1.0.1
49
+ # @api private
50
+ def <=>(other)
51
+ return priority <=> other.priority unless priority == other.priority
52
+
53
+ other.index <=> index
54
+ end
55
+
56
+ private
57
+
58
+ # @since 1.0.1
59
+ # @api private
60
+ def calculate_priority(mime)
61
+ @priority ||= (mime.split(MIME_SEPARATOR, 2).count(MIME_WILDCARD) * -10) + quality
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end