hanami-controller 2.0.0.beta4 → 2.0.0.rc1

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: a363029b7df3ddc560e2975862f72548788a2a2910c2d32519f073556b4bfbb2
4
- data.tar.gz: e7d67505e3b0161a656d24e1ff634bd02c11d4fad1e217a1ee482271be6dbbd9
3
+ metadata.gz: 0a5d623c09e8ecb8c267de856196333c5da74e066b55501af2acc706349d0d73
4
+ data.tar.gz: '0786076a81b7a11ca79aedba2ff0318f207683e71ba931293dcc1f585a191e0b'
5
5
  SHA512:
6
- metadata.gz: 0a9aeb4ea44db1fe6f137dee8f3e5e05bda318a8c53f1f57c30204a0077b5dcfd82aefab569fd7169c2c728de2331fcaa52f1fcdec2e3adb6d4679c38791edf6
7
- data.tar.gz: 726de07b87cd8055da9973ea0365c00bde1f8808a1593ef3830051e196c722f001f6dc13615182f067542c605c9437259d1d36028031867edf3abd8d9612e817
6
+ metadata.gz: 81f1cddefe11892d05ccb63e8649bc9fb6e50f2258eb02503044577e9c0e445c57cbf2318046a4002e3c9b107f9df530463cbeb10460bb3339bb52b747deb0eb
7
+ data.tar.gz: eb26f557543e0d345580b9160ec4123def85b4a4ec5a1ad0a17f8a8d15d05dc34f12e13705082e21b95a5cc3095d36677b099a319a1c2472c7180e413f5c5faf
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  Complete, fast and testable actions for Rack
4
4
 
5
+ ## v2.0.0.rc1 - 2022-11-08
6
+
7
+ ### Changed
8
+
9
+ - [Tim Riley] Simplify assignment of response format: `response.format = :json` (was `response.format = format(:json)`)
10
+
5
11
  ## v2.0.0.beta4 - 2022-10-24
6
12
 
7
13
  ### Added
data/README.md CHANGED
@@ -744,7 +744,7 @@ However, you can force this value:
744
744
  class Show < Hanami::Action
745
745
  def handle(*, res)
746
746
  # ...
747
- res.format = format(:json)
747
+ res.format = :json
748
748
  end
749
749
  end
750
750
 
@@ -820,7 +820,7 @@ response.format # => :custom
820
820
  class Show < Hanami::Action
821
821
  def handle(*, res)
822
822
  # ...
823
- res.format = format(:custom)
823
+ res.format = :custom
824
824
  end
825
825
  end
826
826
 
@@ -841,7 +841,7 @@ end
841
841
 
842
842
  class Csv < Hanami::Action
843
843
  def handle(*, res)
844
- res.format = format(:csv)
844
+ res.format = :csv
845
845
  res.body = Enumerator.new do |yielder|
846
846
  yielder << csv_header
847
847
 
@@ -21,8 +21,8 @@ Gem::Specification.new do |spec|
21
21
  spec.required_ruby_version = ">= 3.0"
22
22
 
23
23
  spec.add_dependency "rack", "~> 2.0"
24
- spec.add_dependency "hanami-utils", "~> 2.0.beta"
25
- spec.add_dependency "dry-configurable", "~> 0.13", ">= 0.13.0"
24
+ spec.add_dependency "hanami-utils", "~> 2.0.0.rc1"
25
+ spec.add_dependency "dry-configurable", "~> 1.0", "< 2"
26
26
 
27
27
  spec.add_development_dependency "bundler", ">= 1.6", "< 3"
28
28
  spec.add_development_dependency "rack-test", "~> 2.0"
@@ -5,6 +5,19 @@ require "hanami/utils/hash"
5
5
 
6
6
  module Hanami
7
7
  class Action
8
+ # Provides access to params included in a Rack request.
9
+ #
10
+ # Offers useful access to params via methods like {#[]}, {#get} and {#to_h}.
11
+ #
12
+ # These params are available via {Request#params}.
13
+ #
14
+ # This class is used by default when {Hanami::Action::Validatable} is not included, or when no
15
+ # {Validatable::ClassMethods#params params} validation schema is defined.
16
+ #
17
+ # @see Hanami::Action::Request#params
18
+ #
19
+ # @api private
20
+ # @since 0.7.0
8
21
  class BaseParams
9
22
  # @attr_reader env [Hash] the Rack env
10
23
  #
@@ -18,12 +31,10 @@ module Hanami
18
31
  # @api private
19
32
  attr_reader :raw
20
33
 
21
- # Initialize the params and freeze them.
34
+ # Returns a new frozen params object for the Rack env.
22
35
  #
23
36
  # @param env [Hash] a Rack env or an hash of params.
24
37
  #
25
- # @return [Params]
26
- #
27
38
  # @since 0.7.0
28
39
  # @api private
29
40
  def initialize(env)
@@ -33,26 +44,27 @@ module Hanami
33
44
  freeze
34
45
  end
35
46
 
36
- # Returns the object associated with the given key
47
+ # Returns the value for the given params key.
37
48
  #
38
49
  # @param key [Symbol] the key
39
50
  #
40
- # @return [Object,nil] return the associated object, if found
51
+ # @return [Object,nil] the associated value, if found
41
52
  #
42
53
  # @since 0.7.0
54
+ # @api public
43
55
  def [](key)
44
56
  @params[key]
45
57
  end
46
58
 
47
- # Get an attribute value associated with the given key.
48
- # Nested attributes are reached by listing all the keys to get to the value.
59
+ # Returns an value associated with the given params key.
60
+ #
61
+ # You can access nested attributes by listing all the keys in the path. This uses the same key
62
+ # path semantics as `Hash#dig`.
49
63
  #
50
64
  # @param keys [Array<Symbol,Integer>] the key
51
65
  #
52
66
  # @return [Object,NilClass] return the associated value, if found
53
67
  #
54
- # @since 0.7.0
55
- #
56
68
  # @example
57
69
  # require "hanami/controller"
58
70
  #
@@ -73,6 +85,9 @@ module Hanami
73
85
  # end
74
86
  # end
75
87
  # end
88
+ #
89
+ # @since 0.7.0
90
+ # @api public
76
91
  def get(*keys)
77
92
  @params.dig(*keys)
78
93
  end
@@ -83,32 +98,40 @@ module Hanami
83
98
  # @since 0.8.0
84
99
  alias_method :dig, :get
85
100
 
86
- # Provide a common interface with Params
101
+ # Returns true at all times, providing a common interface with {Params}.
87
102
  #
88
103
  # @return [TrueClass] always returns true
89
104
  #
90
- # @since 0.7.0
91
- #
92
105
  # @see Hanami::Action::Params#valid?
106
+ #
107
+ # @api public
108
+ # @since 0.7.0
93
109
  def valid?
94
110
  true
95
111
  end
96
112
 
97
- # Serialize params to Hash
113
+ # Returns a hash of the parsed request params.
98
114
  #
99
- # @return [::Hash]
115
+ # @return [Hash]
100
116
  #
101
117
  # @since 0.7.0
118
+ # @api public
102
119
  def to_h
103
120
  @params
104
121
  end
105
122
  alias_method :to_hash, :to_h
106
123
 
107
- # Iterates through params
124
+ # Iterates over the params.
125
+ #
126
+ # Calls the given block with each param key-value pair; returns the full hash of params.
127
+ #
128
+ # @yieldparam key [Symbol]
129
+ # @yieldparam value [Object]
108
130
  #
109
- # @param blk [Proc]
131
+ # @return [to_h]
110
132
  #
111
133
  # @since 0.7.1
134
+ # @api public
112
135
  def each(&blk)
113
136
  to_h.each(&blk)
114
137
  end
@@ -5,6 +5,12 @@ require_relative "mime"
5
5
 
6
6
  module Hanami
7
7
  class Action
8
+ # Config for `Hanami::Action` classes.
9
+ #
10
+ # @see Hanami::Action.config
11
+ #
12
+ # @api public
13
+ # @since 2.0.0
8
14
  class Config < Dry::Configurable::Config
9
15
  # Default MIME type to format mapping
10
16
  #
@@ -151,7 +157,7 @@ module Hanami
151
157
  #
152
158
  # The given format must be coercible to a symbol, and be a valid MIME
153
159
  # type alias. If it isn't, at runtime the framework will raise an
154
- # `Hanami::Controller::UnknownFormatError`.
160
+ # `Hanami::Action::UnknownFormatError`.
155
161
  #
156
162
  # By default, this value is nil.
157
163
  #
@@ -168,7 +174,7 @@ module Hanami
168
174
  #
169
175
  # The given format must be coercible to a symbol, and be a valid MIME
170
176
  # type alias. If it isn't, at the runtime the framework will raise an
171
- # `Hanami::Controller::UnknownFormatError`.
177
+ # `Hanami::Action::UnknownFormatError`.
172
178
  #
173
179
  # By default, this value is nil.
174
180
  #
@@ -116,12 +116,6 @@ module Hanami
116
116
  # @api private
117
117
  HTTP_ACCEPT = "HTTP_ACCEPT"
118
118
 
119
- # The header key to set the mime type of the response
120
- #
121
- # @since 0.1.0
122
- # @api private
123
- CONTENT_TYPE = ::Rack::CONTENT_TYPE
124
-
125
119
  # The default mime type for an incoming HTTP request
126
120
  #
127
121
  # @since 0.1.0
@@ -1,19 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "hanami/utils/blank"
4
- require "hanami/controller/error"
5
4
  require "rack/utils"
6
5
  require "securerandom"
6
+ require_relative "errors"
7
7
 
8
8
  module Hanami
9
9
  # @api private
10
10
  class Action
11
- # Invalid CSRF Token
12
- #
13
- # @since 0.4.0
14
- class InvalidCSRFTokenError < Controller::Error
15
- end
16
-
17
11
  # CSRF Protection
18
12
  #
19
13
  # This security mechanism is enabled automatically if sessions are turned on.
@@ -2,22 +2,44 @@
2
2
 
3
3
  module Hanami
4
4
  class Action
5
+ # Base class for all Action errors.
6
+ #
7
+ # @api public
5
8
  # @since 2.0.0
6
9
  class Error < ::StandardError
7
10
  end
8
11
 
9
- # Missing session error
12
+ # Unknown format error
10
13
  #
11
- # This error is raised when `session` or `flash` is accessed/set on request/response objects
12
- # in actions which do not include `Hanami::Action::Session`.
14
+ # This error is raised when a action sets a format that it isn't recognized
15
+ # both by `Hanami::Action::Configuration` and the list of Rack mime types
13
16
  #
14
17
  # @since 2.0.0
15
18
  #
19
+ # @see Hanami::Action::Mime#format=
20
+ class UnknownFormatError < Error
21
+ # @since 2.0.0
22
+ # @api private
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
25
+ end
26
+ end
27
+
28
+ # Error raised when session is accessed but not enabled.
29
+ #
30
+ # This error is raised when `session` or `flash` is accessed/set on request/response objects
31
+ # in actions which do not include `Hanami::Action::Session`.
32
+ #
16
33
  # @see Hanami::Action::Session
17
34
  # @see Hanami::Action::Request#session
18
35
  # @see Hanami::Action::Response#session
19
36
  # @see Hanami::Action::Response#flash
37
+ #
38
+ # @api public
39
+ # @since 2.0.0
20
40
  class MissingSessionError < Error
41
+ # @api private
42
+ # @since 2.0.0
21
43
  def initialize(session_method)
22
44
  super(<<~TEXT)
23
45
  Sessions are not enabled. To use `#{session_method}`:
@@ -37,5 +59,11 @@ module Hanami
37
59
  TEXT
38
60
  end
39
61
  end
62
+
63
+ # Invalid CSRF Token
64
+ #
65
+ # @since 0.4.0
66
+ class InvalidCSRFTokenError < Error
67
+ end
40
68
  end
41
69
  end
@@ -1,19 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # The Hanami::Action::Flash implementation is derived from Roda's FlashHash, also released under the
4
+ # MIT Licence:
5
+ #
6
+ # Copyright (c) 2014-2020 Jeremy Evans
7
+ # Copyright (c) 2010-2014 Michel Martens, Damian Janowski and Cyril David
8
+ # Copyright (c) 2008-2009 Christian Neukirchen
9
+
3
10
  module Hanami
4
11
  class Action
5
- # A container to transport data with the HTTP session, with a lifespan of
6
- # just one HTTP request or redirect.
7
- #
8
- # Behaves like a hash, returning entries for the current request, except for
9
- # {#[]=}, which updates the hash for the next request.
12
+ # A container to transport data with the HTTP session, with a lifespan of just one HTTP request
13
+ # or redirect.
10
14
  #
11
- # This implementation is derived from Roda's FlashHash, also released under
12
- # the MIT Licence:
13
- #
14
- # Copyright (c) 2014-2020 Jeremy Evans
15
- # Copyright (c) 2010-2014 Michel Martens, Damian Janowski and Cyril David
16
- # Copyright (c) 2008-2009 Christian Neukirchen
15
+ # Behaves like a hash, returning entries for the current request, except for {#[]=}, which
16
+ # updates the hash for the next request.
17
17
  #
18
18
  # @since 0.3.0
19
19
  # @api public
@@ -30,9 +30,9 @@ module Hanami
30
30
  # @api public
31
31
  attr_reader :next
32
32
 
33
- # Initializes a new flash instance
33
+ # Returns a new flash object.
34
34
  #
35
- # @param hash [Hash, nil] the flash hash for the current request. nil will become an empty hash.
35
+ # @param hash [Hash, nil] the flash hash for the current request; `nil` will become an empty hash.
36
36
  #
37
37
  # @since 0.3.0
38
38
  # @api public
@@ -41,7 +41,9 @@ module Hanami
41
41
  @next = {}
42
42
  end
43
43
 
44
- # @return [Hash] The flash hash for the current request
44
+ # Returns the flash hash for the current request.
45
+ #
46
+ # @return [Hash] the flash hash for the current request
45
47
  #
46
48
  # @since 2.0.0
47
49
  # @api public
@@ -49,7 +51,7 @@ module Hanami
49
51
  @flash
50
52
  end
51
53
 
52
- # Returns the value for the given key in the current hash
54
+ # Returns the value for the given key in the current hash.
53
55
  #
54
56
  # @param key [Object] the key
55
57
  #
@@ -61,7 +63,7 @@ module Hanami
61
63
  @flash[key]
62
64
  end
63
65
 
64
- # Updates the next hash with the given key and value
66
+ # Updates the next hash with the given key and value.
65
67
  #
66
68
  # @param key [Object] the key
67
69
  # @param value [Object] the value
@@ -72,9 +74,12 @@ module Hanami
72
74
  @next[key] = value
73
75
  end
74
76
 
75
- # Calls the given block once for each element in the current hash
77
+ # Calls the given block once for each element in the current hash.
76
78
  #
77
- # @param block [Proc]
79
+ # @yieldparam element [Array<(Object, Object)>] array containing the key and value from the
80
+ # hash
81
+ #
82
+ # @return [now]
78
83
  #
79
84
  # @since 1.2.0
80
85
  # @api public
@@ -82,10 +87,12 @@ module Hanami
82
87
  @flash.each(&block)
83
88
  end
84
89
 
85
- # Returns a new array with the results of running block once for every
86
- # element in the current hash
90
+ # Returns an array of objects returned by the block, called once for each element in the
91
+ # current hash.
92
+ #
93
+ # @yieldparam element [Array<(Object, Object)>] array containing the key and value from the
94
+ # hash
87
95
  #
88
- # @param block [Proc]
89
96
  # @return [Array]
90
97
  #
91
98
  # @since 1.2.0
@@ -114,7 +121,7 @@ module Hanami
114
121
  @flash.key?(key)
115
122
  end
116
123
 
117
- # Removes entries from the next hash
124
+ # Removes entries from the next hash.
118
125
  #
119
126
  # @overload discard(key)
120
127
  # Removes the given key from the next hash
@@ -4,9 +4,11 @@ require "hanami/http/status"
4
4
 
5
5
  module Hanami
6
6
  class Action
7
+ # @api private
8
+ # @since 2.0.0
7
9
  module Halt
8
- # @since 2.0.0
9
10
  # @api private
11
+ # @since 2.0.0
10
12
  def self.call(status, body = nil)
11
13
  body ||= Http::Status.message_for(status)
12
14
  throw :halt, [status, body]
@@ -3,6 +3,7 @@
3
3
  require "hanami/utils"
4
4
  require "rack/utils"
5
5
  require "rack/mime"
6
+ require_relative "errors"
6
7
 
7
8
  module Hanami
8
9
  class Action
@@ -117,7 +118,7 @@ module Hanami
117
118
  return if format.nil?
118
119
 
119
120
  config.mime_type_for(format) ||
120
- TYPES.fetch(format) { raise Hanami::Controller::UnknownFormatError.new(format) }
121
+ TYPES.fetch(format) { raise Hanami::Action::UnknownFormatError.new(format) }
121
122
  end
122
123
 
123
124
  # Transforms MIME Types to symbol
@@ -144,13 +145,26 @@ module Hanami
144
145
  TYPES.key(content_type)
145
146
  end
146
147
 
148
+ # @since 2.0.0
149
+ # @api private
150
+ def self.detect_format_and_content_type(value, config)
151
+ case value
152
+ when Symbol
153
+ [value, format_to_mime_type(value, config)]
154
+ when String
155
+ [detect_format(value, config), value]
156
+ else
157
+ raise UnknownFormatError.new(value)
158
+ end
159
+ end
160
+
147
161
  # Transforms symbols to MIME Types
148
162
  # @example
149
163
  # restrict_mime_types(config, [:json]) #=> ["application/json"]
150
164
  #
151
165
  # @return [Array<String>, nil]
152
166
  #
153
- # @raise [Hanami::Controller::UnknownFormatError] if the format is invalid
167
+ # @raise [Hanami::Action::UnknownFormatError] if the format is invalid
154
168
  #
155
169
  # @since 2.0.0
156
170
  # @api private
@@ -223,8 +237,6 @@ module Hanami
223
237
 
224
238
  # Use for setting the content_type and charset if the response
225
239
  #
226
- # @see Hanami::Action::Mime#call
227
- #
228
240
  # @return [String]
229
241
  #
230
242
  # @since 2.0.0
@@ -4,13 +4,17 @@ require "rack/file"
4
4
 
5
5
  module Hanami
6
6
  class Action
7
+ # Rack extensions for actions.
8
+ #
9
+ # @api private
10
+ # @since 0.4.3
7
11
  module Rack
8
12
  # File to be sent
9
13
  #
14
+ # @see Hanami::Action::Response#send_file
15
+ #
10
16
  # @since 0.4.3
11
17
  # @api private
12
- #
13
- # @see Hanami::Action::Rack#send_file
14
18
  class File
15
19
  # @param path [String,Pathname] file path
16
20
  #
@@ -5,18 +5,31 @@ require "rack/mime"
5
5
  require "rack/request"
6
6
  require "rack/utils"
7
7
  require "securerandom"
8
+ require_relative "errors"
8
9
 
9
10
  module Hanami
10
11
  class Action
11
- # An HTTP request based on top of Rack::Request.
12
- # This guarantees backwards compatibility with with Rack.
12
+ # The HTTP request for an action, given to {Action#handle}.
13
13
  #
14
- # @since 0.3.1
14
+ # Inherits from `Rack::Request`, providing compatibility with Rack functionality.
15
15
  #
16
16
  # @see http://www.rubydoc.info/gems/rack/Rack/Request
17
+ #
18
+ # @since 0.3.1
17
19
  class Request < ::Rack::Request
20
+ # Returns the request's params.
21
+ #
22
+ # For an action with {Validatable} included, this will be a {Params} instance, otherwise a
23
+ # {BaseParams}.
24
+ #
25
+ # @return [BaseParams,Params]
26
+ #
27
+ # @since 2.0.0
28
+ # @api public
18
29
  attr_reader :params
19
30
 
31
+ # @since 2.0.0
32
+ # @api private
20
33
  def initialize(env:, params:, sessions_enabled: false)
21
34
  super(env)
22
35
 
@@ -24,11 +37,27 @@ module Hanami
24
37
  @sessions_enabled = sessions_enabled
25
38
  end
26
39
 
40
+ # Returns the request's ID
41
+ #
42
+ # @return [String]
43
+ #
44
+ # @since 2.0.0
45
+ # @api public
27
46
  def id
28
47
  # FIXME: make this number configurable and document the probabilities of clashes
29
48
  @id ||= @env[Action::REQUEST_ID] = SecureRandom.hex(Action::DEFAULT_ID_LENGTH)
30
49
  end
31
50
 
51
+ # Returns the session for the request.
52
+ #
53
+ # @return [Hash] the session object
54
+ #
55
+ # @raise [MissingSessionError] if sessions are not enabled
56
+ #
57
+ # @see Response#session
58
+ #
59
+ # @since 2.0.0
60
+ # @api public
32
61
  def session
33
62
  unless @sessions_enabled
34
63
  raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#session")
@@ -37,6 +66,16 @@ module Hanami
37
66
  super
38
67
  end
39
68
 
69
+ # Returns the flash for the request.
70
+ #
71
+ # @return [Flash]
72
+ #
73
+ # @raise [MissingSessionError] if sessions are not enabled
74
+ #
75
+ # @see Response#flash
76
+ #
77
+ # @since 2.0.0
78
+ # @api public
40
79
  def flash
41
80
  unless @sessions_enabled
42
81
  raise Hanami::Action::MissingSessionError.new("Hanami::Action::Request#flash")
@@ -45,12 +84,16 @@ module Hanami
45
84
  @flash ||= Flash.new(session[Flash::KEY])
46
85
  end
47
86
 
87
+ # @since 2.0.0
88
+ # @api private
48
89
  def accept?(mime_type)
49
90
  !!::Rack::Utils.q_values(accept).find do |mime, _|
50
91
  ::Rack::Mime.match?(mime_type, mime)
51
92
  end
52
93
  end
53
94
 
95
+ # @since 2.0.0
96
+ # @api private
54
97
  def accept_header?
55
98
  accept != Action::DEFAULT_ACCEPT
56
99
  end