hanami-controller 2.2.0 → 2.3.0.beta2

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.
@@ -11,35 +11,57 @@ module Hanami
11
11
  # @since 2.0.0
12
12
  # @api private
13
13
  class Formats
14
- include Dry.Equalizer(:values, :mapping)
14
+ include Dry.Equalizer(:accepted, :mapping)
15
15
 
16
- # Default MIME type to format mapping
17
- #
18
16
  # @since 2.0.0
19
17
  # @api private
20
- DEFAULT_MAPPING = {
21
- "application/octet-stream" => :all,
22
- "*/*" => :all
23
- }.freeze
18
+ attr_reader :mapping
24
19
 
20
+ # The array of formats to accept requests by.
21
+ #
22
+ # @example
23
+ # config.formats.accepted = [:html, :json]
24
+ # config.formats.accepted # => [:html, :json]
25
+ #
25
26
  # @since 2.0.0
26
- # @api private
27
- attr_reader :mapping
27
+ # @api public
28
+ attr_reader :accepted
29
+
30
+ # @see #accepted
31
+ #
32
+ # @since 2.0.0
33
+ # @api public
34
+ def values
35
+ msg = <<~TEXT
36
+ Hanami::Action `config.formats.values` is deprecated and will be removed in Hanami 2.4.
37
+
38
+ Please use `config.formats.accepted` instead.
39
+
40
+ See https://guides.hanamirb.org/v2.3/actions/formats-and-mime-types/ for details.
41
+ TEXT
42
+ warn(msg, category: :deprecated)
43
+
44
+ accepted
45
+ end
28
46
 
29
- # The array of enabled formats.
47
+ # Returns the default format name.
48
+ #
49
+ # When a request is received that cannot
50
+ #
51
+ # @return [Symbol, nil] the default format name, if any
30
52
  #
31
53
  # @example
32
- # config.formats.values = [:html, :json]
33
- # config.formats.values # => [:html, :json]
54
+ # @config.formats.default # => :json
34
55
  #
35
56
  # @since 2.0.0
36
57
  # @api public
37
- attr_reader :values
58
+ attr_reader :default
38
59
 
39
60
  # @since 2.0.0
40
61
  # @api private
41
- def initialize(values: [], mapping: DEFAULT_MAPPING.dup)
42
- @values = values
62
+ def initialize(accepted: [], default: nil, mapping: {})
63
+ @accepted = accepted
64
+ @default = default
43
65
  @mapping = mapping
44
66
  end
45
67
 
@@ -47,15 +69,74 @@ module Hanami
47
69
  # @api private
48
70
  private def initialize_copy(original) # rubocop:disable Style/AccessModifierDeclarations
49
71
  super
50
- @values = original.values.dup
72
+ @accepted = original.accepted.dup
73
+ @default = original.default
51
74
  @mapping = original.mapping.dup
52
75
  end
53
76
 
77
+ # !@attribute [w] accepted
78
+ # @since 2.3.0
79
+ # @api public
80
+ def accepted=(formats)
81
+ @accepted = formats.map { |f| Hanami::Utils::Kernel.Symbol(f) }
82
+ end
83
+
54
84
  # !@attribute [w] values
55
85
  # @since 2.0.0
56
86
  # @api public
57
- def values=(formats)
58
- @values = formats.map { |f| Utils::Kernel.Symbol(f) }
87
+ alias_method :values=, :accepted=
88
+
89
+ # @since 2.3.0
90
+ def accept(*formats)
91
+ self.default = formats.first if default.nil?
92
+ self.accepted = accepted | formats
93
+ end
94
+
95
+ # @api private
96
+ def accepted_formats(standard_formats = {})
97
+ accepted.to_h { |format|
98
+ [
99
+ format,
100
+ mapping.fetch(format) { standard_formats[format] }
101
+ ]
102
+ }
103
+ end
104
+
105
+ # @since 2.3.0
106
+ def default=(format)
107
+ @default = format.to_sym
108
+ end
109
+
110
+ # Registers a format and its associated media types.
111
+ #
112
+ # @param format [Symbol] the format name
113
+ # @param media_type [String] the format's media type
114
+ # @param accept_types [Array<String>] media types to accept in request `Accept` headers
115
+ # @param content_types [Array<String>] media types to accept in request `Content-Type` headers
116
+ #
117
+ # @example
118
+ # config.formats.register(:scim, media_type: "application/json+scim")
119
+ #
120
+ # config.formats.register(
121
+ # :jsonapi,
122
+ # "application/vnd.api+json",
123
+ # accept_types: ["application/vnd.api+json", "application/json"],
124
+ # content_types: ["application/vnd.api+json", "application/json"]
125
+ # )
126
+ #
127
+ # @return [self]
128
+ #
129
+ # @since 2.3.0
130
+ # @api public
131
+ def register(format, media_type, accept_types: [media_type], content_types: [media_type])
132
+ mapping[format] = Mime::Format.new(
133
+ name: format.to_sym,
134
+ media_type: media_type,
135
+ accept_types: accept_types,
136
+ content_types: content_types
137
+ )
138
+
139
+ self
59
140
  end
60
141
 
61
142
  # @overload add(format)
@@ -83,20 +164,30 @@ module Hanami
83
164
  # @param mime_types [Array<String>]
84
165
  #
85
166
  # @example
86
- # config.formats.add(:json, ["application/json+scim", "application/json"])
167
+ # config.formats.add(:json, ["application/json+scim"])
87
168
  #
88
169
  # @return [self]
89
170
  #
90
171
  # @since 2.0.0
91
172
  # @api public
92
- def add(format, mime_types = [])
93
- format = Utils::Kernel.Symbol(format)
173
+ def add(format, mime_types)
174
+ msg = <<~TEXT
175
+ Hanami::Action `config.formats.add` is deprecated and will be removed in Hanami 2.4.
176
+
177
+ Please use `config.formats.register` instead.
178
+
179
+ See https://guides.hanamirb.org/v2.3/actions/formats-and-mime-types/ for details.
180
+ TEXT
181
+ warn(msg, category: :deprecated)
182
+
183
+ mime_type = Array(mime_types).first
94
184
 
95
- Array(mime_types).each do |mime_type|
96
- @mapping[Utils::Kernel.String(mime_type)] = format
97
- end
185
+ # The old behaviour would have subsequent mime types _replacing_ previous ones
186
+ mapping.reject! { |_, format| format.media_type == mime_type }
98
187
 
99
- @values << format unless @values.include?(format)
188
+ register(format, Array(mime_types).first, accept_types: mime_types)
189
+
190
+ accept(format) unless @accepted.include?(format)
100
191
 
101
192
  self
102
193
  end
@@ -104,31 +195,19 @@ module Hanami
104
195
  # @since 2.0.0
105
196
  # @api private
106
197
  def empty?
107
- @values.empty?
198
+ accepted.empty?
108
199
  end
109
200
 
110
201
  # @since 2.0.0
111
202
  # @api private
112
203
  def any?
113
- @values.any?
204
+ @accepted.any?
114
205
  end
115
206
 
116
207
  # @since 2.0.0
117
208
  # @api private
118
209
  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
210
+ @accepted.map(&blk)
132
211
  end
133
212
 
134
213
  # Clears any previously added mappings and format values.
@@ -138,15 +217,24 @@ module Hanami
138
217
  # @since 2.0.0
139
218
  # @api public
140
219
  def clear
141
- @mapping = DEFAULT_MAPPING.dup
142
- @values = []
220
+ @accepted = []
221
+ @default = nil
222
+ @mapping = {}
143
223
 
144
224
  self
145
225
  end
146
226
 
147
- # Retrieve the format name associated with the given MIME Type
227
+ # Returns an array of all accepted media types.
228
+ #
229
+ # @since 2.3.0
230
+ # @api public
231
+ def accept_types
232
+ accepted.map { |format| mapping[format]&.accept_types }.flatten(1).compact
233
+ end
234
+
235
+ # Retrieve the format name associated with the given media type
148
236
  #
149
- # @param mime_type [String] the MIME Type
237
+ # @param media_type [String] the media Type
150
238
  #
151
239
  # @return [Symbol,NilClass] the associated format name, if any
152
240
  #
@@ -157,59 +245,46 @@ module Hanami
157
245
  #
158
246
  # @since 2.0.0
159
247
  # @api public
160
- def format_for(mime_type)
161
- @mapping[mime_type]
248
+ def format_for(media_type)
249
+ mapping.values.reverse.find { |format| format.media_type == media_type }&.name
162
250
  end
163
251
 
164
- # Returns the primary MIME type associated with the given format.
252
+ # Returns the media type associated with the given format.
165
253
  #
166
254
  # @param format [Symbol] the format name
167
255
  #
168
- # @return [String, nil] the associated MIME type, if any
256
+ # @return [String, nil] the associated media type, if any
169
257
  #
170
258
  # @example
171
- # @config.formats.mime_type_for(:json) # => "application/json"
259
+ # @config.formats.media_type_for(:json) # => "application/json"
172
260
  #
173
261
  # @see #format_for
174
262
  #
175
- # @since 2.0.0
263
+ # @since 2.3.0
176
264
  # @api public
177
- def mime_type_for(format)
178
- @mapping.key(format)
265
+ def media_type_for(format)
266
+ mapping[format]&.media_type
179
267
  end
180
268
 
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 }
269
+ # @api private
270
+ def accept_types_for(format)
271
+ mapping[format]&.accept_types || []
193
272
  end
194
273
 
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
- #
274
+ # @api private
275
+ def content_types_for(format)
276
+ mapping[format]&.content_types || []
277
+ end
278
+
279
+ # @see #media_type_for
202
280
  # @since 2.0.0
203
281
  # @api public
204
- def default
205
- @values.first
206
- end
282
+ alias_method :mime_type_for, :media_type_for
207
283
 
284
+ # @see #media_type_for
208
285
  # @since 2.0.0
209
- # @api private
210
- def keys
211
- @mapping.keys
212
- end
286
+ # @api public
287
+ alias_method :mime_types_for, :accept_types_for
213
288
  end
214
289
  end
215
290
  end
@@ -68,7 +68,8 @@ module Hanami
68
68
 
69
69
  # Sets the format (or formats) for the action.
70
70
  #
71
- # To configure custom formats and MIME type mappings, call {Formats#add formats.add} first.
71
+ # To configure custom formats and MIME type mappings, call {Formats#register formats.register}
72
+ # first.
72
73
  #
73
74
  # @example
74
75
  # config.format :html, :json
@@ -82,10 +83,20 @@ module Hanami
82
83
  # @since 2.0.0
83
84
  # @api public
84
85
  def format(*formats)
86
+ msg = <<~TEXT
87
+ Hanami::Action `config.format` is deprecated and will be removed in Hanami 2.4.
88
+
89
+ Please use `config.formats.register` and/or `config.formats.accept` instead.
90
+
91
+ See https://guides.hanamirb.org/v2.3/actions/formats-and-mime-types/ for details.
92
+ TEXT
93
+ warn(msg, category: :deprecated)
94
+
85
95
  if formats.empty?
86
96
  self.formats.values
87
97
  else
88
98
  self.formats.values = formats
99
+ self.formats.default = formats.first
89
100
  end
90
101
  end
91
102
 
@@ -117,6 +128,24 @@ module Hanami
117
128
  #
118
129
  # @since 0.4.0
119
130
 
131
+ # @!attribute [rw] default_tld_length
132
+ #
133
+ # Sets the default TLD length for host names. It is used to extract the
134
+ # subdomain(s) in `Request#subdomains`.
135
+ #
136
+ # Defaults to 1.
137
+ #
138
+ # @example
139
+ # # For *.example.com
140
+ # config.default_tld_length = 1
141
+ #
142
+ # # Or for *.example.co.uk
143
+ # config.default_tld_length = 2
144
+ #
145
+ # @return [Integer] the number of subdomains
146
+ #
147
+ # @since 2.3.0
148
+
120
149
  # @!attribute [rw] cookies
121
150
  #
122
151
  # Sets default cookie options for all responses.
@@ -50,14 +50,14 @@ module Hanami
50
50
  # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.5
51
51
  # @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html
52
52
  ENTITY_HEADERS = {
53
- "Allow" => true,
54
- "Content-Encoding" => true,
55
- "Content-Language" => true,
56
- "Content-Location" => true,
57
- "Content-MD5" => true,
58
- "Content-Range" => true,
59
- "Expires" => true,
60
- "Last-Modified" => true,
53
+ "allow" => true,
54
+ "content-encoding" => true,
55
+ "content-language" => true,
56
+ "content-location" => true,
57
+ "content-md5" => true,
58
+ "content-range" => true,
59
+ "expires" => true,
60
+ "last-modified" => true,
61
61
  "extension-header" => true
62
62
  }.freeze
63
63
 
@@ -82,7 +82,7 @@ module Hanami
82
82
  module MyApp
83
83
  class App < Hanami::App
84
84
  # See Rack::Session::Cookie for options
85
- config.sessions = :cookie, {**cookie_session_options}
85
+ config.actions.sessions = :cookie, {**cookie_session_options}
86
86
  end
87
87
  end
88
88