Skip to content

Commit

Permalink
Allow direct reuse of dry-validation contracts in actions (#454)
Browse files Browse the repository at this point in the history
Allow dry-validation contract classes to be provided directly to actions.

First, via the contract class method, instead of expecting `Hanami::Action::Params` subclasses:

```ruby
class MyAction < Hanami::Action
  contract MyContract # a Dry::Validation::Contract subclass
end
```

Second, via a `contract` dependency of the action:

```ruby
# Either manually
class MyAction < Hanami::Action
end

action = MyAction.new(contract: MyContract.new)
```
```ruby
# Or in Hanami apps, via Deps
class MyAction
  include Deps[contract: "my_contract"]
end
```

This is a more flexible arrangement than requiring these contracts to inherit from `Hanami::Action::Params`, since it will allow contracts to be used across both actions and other non-action classes within Hanami apps.

While here, begin to "soft deprecate" the idea of using `Hanami::Action::Params` subclasses. We do this by updating `Hanami::Actions.contract` such that it **does not** support being given `Hanami::Action::Params` subclasses. Given that `.contract` is the new and full-powered incarnation of action validation, this hopefully will nudge people in the right direction.
  • Loading branch information
timriley authored Sep 17, 2024
1 parent d446cd8 commit 0a7a746
Show file tree
Hide file tree
Showing 12 changed files with 292 additions and 338 deletions.
38 changes: 13 additions & 25 deletions lib/hanami/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def self.gem_loader
loader.ignore(
"#{root}/hanami-controller.rb",
"#{root}/hanami/controller/version.rb",
"#{root}/hanami/action/{constants,errors,params,validatable}.rb"
"#{root}/hanami/action/{constants,errors,validatable}.rb"
)
loader.inflector.inflect("csrf_protection" => "CSRFProtection")
end
Expand Down Expand Up @@ -72,6 +72,7 @@ def self.gem_loader
setting :public_directory, default: Config::DEFAULT_PUBLIC_DIRECTORY
setting :before_callbacks, default: Utils::Callbacks::Chain.new, mutable: true
setting :after_callbacks, default: Utils::Callbacks::Chain.new, mutable: true
setting :contract_class

# @!scope class

Expand Down Expand Up @@ -105,24 +106,6 @@ def self.inherited(subclass)
include Validatable if defined?(Validatable)
end
end

if instance_variable_defined?(:@params_class)
subclass.instance_variable_set(:@params_class, @params_class)
end
end

# Returns the class which defines the params
#
# Returns the class which has been provided to define the
# params. By default this will be Hanami::Action::Params.
#
# @return [Class] A params class (when whitelisted) or
# Hanami::Action::Params
#
# @api private
# @since 0.7.0
def self.params_class
@params_class || BaseParams
end

# Placeholder for the `.params` method. Raises an error when the hanami-validations gem is not
Expand Down Expand Up @@ -298,12 +281,21 @@ def self.handle_exception(...)
config.handle_exception(...)
end

# @since 2.0.0
# @api private
private attr_reader :config

# @since 2.2.0
# @api private
private attr_reader :contract

# Returns a new action
#
# @since 2.0.0
# @api public
def initialize(config: self.class.config)
def initialize(config: self.class.config, contract: nil)
@config = config
@contract = contract || config.contract_class&.new # TODO: tests showing this overridden by a dep
freeze
end

Expand All @@ -316,7 +308,7 @@ def call(env)
response = nil

halted = catch :halt do
params = self.class.params_class.new(env)
params = Params.new(env: env, contract: contract)
request = build_request(
env: env,
params: params,
Expand Down Expand Up @@ -412,10 +404,6 @@ def _requires_no_body?(res)

private

# @since 2.0.0
# @api private
attr_reader :config

# @since 2.0.0
# @api private
def enforce_accepted_mime_types(request)
Expand Down
170 changes: 0 additions & 170 deletions lib/hanami/action/base_params.rb

This file was deleted.

4 changes: 2 additions & 2 deletions lib/hanami/action/csrf_protection.rb
Original file line number Diff line number Diff line change
Expand Up @@ -131,14 +131,14 @@ def invalid_csrf_token?(req, res)
return false unless verify_csrf_token?(req, res)

missing_csrf_token?(req, res) ||
!::Rack::Utils.secure_compare(req.session[CSRF_TOKEN], req.params[CSRF_TOKEN])
!::Rack::Utils.secure_compare(req.session[CSRF_TOKEN], req.params.raw[CSRF_TOKEN.to_s])
end

# Verify the CSRF token was passed in params.
#
# @api private
def missing_csrf_token?(req, *)
Hanami::Utils::Blank.blank?(req.params[CSRF_TOKEN])
Hanami::Utils::Blank.blank?(req.params.raw[CSRF_TOKEN.to_s])
end

# Generates a random CSRF Token
Expand Down
Loading

0 comments on commit 0a7a746

Please sign in to comment.