class Hanami::Action::Params::DefaultContract::Result
Attributes
@api private @since 2.2.0
Public Class Methods
# File lib/hanami/action/params.rb, line 41 def initialize(attrs) = @attrs = Utils::Hash.deep_symbolize(attrs) def to_h = @attrs def errors = {} end end # Params errors # # @since 1.1.0 class Errors < SimpleDelegator # @since 1.1.0 # @api private def initialize(errors = {}) super(errors.dup) end # Add an error to the param validations # # This has a semantic similar to `Hash#dig` where you use a set of keys # to get a nested value, here you use a set of keys to set a nested # value. # # @param args [Array<Symbol, String>] an array of arguments: the last # one is the message to add (String), while the beginning of the array # is made of keys to reach the attribute. # # @raise [ArgumentError] when try to add a message for a key that is # already filled with incompatible message type. # This usually happens with nested attributes: if you have a `:book` # schema and the input doesn't include data for `:book`, the messages # will be `["is missing"]`. In that case you can't add an error for a # key nested under `:book`. # # @since 1.1.0 # # @example Basic usage # require "hanami/controller" # # class MyAction < Hanami::Action # params do # required(:book).schema do # required(:isbn).filled(:str?) # end # end # # def handle(req, res) # # 1. Don't try to save the record if the params aren't valid # return unless req.params.valid? # # BookRepository.new.create(req.params[:book]) # rescue Hanami::Model::UniqueConstraintViolationError # # 2. Add an error in case the record wasn't unique # req.params.errors.add(:book, :isbn, "is not unique") # end # end # # @example Invalid argument # require "hanami/controller" # # class MyAction < Hanami::Action # params do # required(:book).schema do # required(:title).filled(:str?) # end # end # # def handle(req, *) # puts req.params.to_h # => {} # puts req.params.valid? # => false # puts req.params.error_messages # => ["Book is missing"] # puts req.params.errors # => {:book=>["is missing"]} # # req.params.errors.add(:book, :isbn, "is not unique") # => ArgumentError # end # end def add(*args) *keys, key, error = args _nested_attribute(keys, key) << error rescue TypeError raise ArgumentError.new("Can't add #{args.map(&:inspect).join(', ')} to #{inspect}") end private # @since 1.1.0 # @api private def _nested_attribute(keys, key) if keys.empty? self else keys.inject(self) { |result, k| result[k] ||= {} } dig(*keys) end[key] ||= [] end end # Defines validations for the params, using the `params` schema of a dry-validation contract. # # @param block [Proc] the schema definition # # @see https://dry-rb.org/gems/dry-validation/ # # @api public # @since 0.7.0 def self.params(&block) unless defined?(Dry::Validation::Contract) message = %(To use `.params`, please add the "hanami-validations" gem to your Gemfile) raise NoMethodError, message end @_contract = Class.new(Dry::Validation::Contract) { params(&block || -> {}) }.new end class << self # @api private # @since 2.2.0 attr_reader :_contract end # @attr_reader env [Hash] the Rack env # # @since 0.7.0 # @api private attr_reader :env # @attr_reader raw [Hash] the raw params from the request # # @since 0.7.0 # @api private attr_reader :raw # Returns structured error messages # # @return [Hash] # # @since 0.7.0 # # @example # params.errors # # => { # :email=>["is missing", "is in invalid format"], # :name=>["is missing"], # :tos=>["is missing"], # :age=>["is missing"], # :address=>["is missing"] # } attr_reader :errors # Initialize the params and freeze them. # # @param env [Hash] a Rack env or an hash of params. # # @return [Params] # # @since 0.1.0 # @api private def initialize(env:, contract: nil) @env = env @raw = _extract_params # Fall back to the default contract here, rather than in the `._contract` method itself. # This allows `._contract` to return nil when there is no user-defined contract, which is # important for the backwards compatibility behavior in `Validatable::ClassMethods#params`. contract ||= self.class._contract || DefaultContract validation = contract.call(raw) @params = validation.to_h @errors = Errors.new(validation.errors.to_h) freeze end # Returns the value for the given params key. # # @param key [Symbol] the key # # @return [Object,nil] the associated value, if found # # @since 0.7.0 # @api public def [](key) @params[key] end # Returns an value associated with the given params key. # # You can access nested attributes by listing all the keys in the path. This uses the same key # path semantics as `Hash#dig`. # # @param keys [Array<Symbol,Integer>] the key # # @return [Object,NilClass] return the associated value, if found # # @example # require "hanami/controller" # # module Deliveries # class Create < Hanami::Action # def handle(req, *) # req.params.get(:customer_name) # => "Luca" # req.params.get(:uknown) # => nil # # req.params.get(:address, :city) # => "Rome" # req.params.get(:address, :unknown) # => nil # # req.params.get(:tags, 0) # => "foo" # req.params.get(:tags, 1) # => "bar" # req.params.get(:tags, 999) # => nil # # req.params.get(nil) # => nil # end # end # end # # @since 0.7.0 # @api public def get(*keys) @params.dig(*keys) end # This is for compatibility with Hanami::Helpers::FormHelper::Values # # @api private # @since 0.8.0 alias_method :dig, :get # Returns flat collection of full error messages # # @return [Array] # # @since 0.7.0 # # @example # params.error_messages # # => [ # "Email is missing", # "Email is in invalid format", # "Name is missing", # "Tos is missing", # "Age is missing", # "Address is missing" # ] def error_messages(error_set = errors) error_set.each_with_object([]) do |(key, messages), result| k = Utils::String.titleize(key) msgs = if messages.is_a?(::Hash) error_messages(messages) else messages.map { |message| "#{k} #{message}" } end result.concat(msgs) end end # Returns true if no validation errors are found, # false otherwise. # # @return [TrueClass, FalseClass] # # @since 0.7.0 # # @example # params.valid? # => true def valid? errors.empty? end # Iterates over the params. # # Calls the given block with each param key-value pair; returns the full hash of params. # # @yieldparam key [Symbol] # @yieldparam value [Object] # # @return [to_h] # # @since 0.7.1 # @api public def each(&blk) to_h.each(&blk) end # Serialize validated params to Hash # # @return [::Hash] # # @since 0.3.0 def to_h @params end alias_method :to_hash, :to_h # Pattern-matching support # # @return [::Hash] # # @since 2.0.2 def deconstruct_keys(*) to_hash end private # @since 0.7.0 # @api private def _extract_params result = {} if env.key?(Action::RACK_INPUT) result.merge! ::Rack::Request.new(env).params result.merge! _router_params else result.merge! _router_params(env) env[Action::REQUEST_METHOD] ||= Action::DEFAULT_REQUEST_METHOD end result end # @since 0.7.0 # @api private def _router_params(fallback = {}) env.fetch(ROUTER_PARAMS) do if session = fallback.delete(Action::RACK_SESSION) fallback[Action::RACK_SESSION] = Utils::Hash.deep_symbolize(session) end fallback end end end
Defines validations for the params, using the ‘params` schema of a dry-validation contract.
@param block [Proc] the schema definition
@see dry-rb.org/gems/dry-validation/
@api public @since 0.7.0
# File lib/hanami/action/params.rb, line 145 def self.params(&block) unless defined?(Dry::Validation::Contract) message = %(To use `.params`, please add the "hanami-validations" gem to your Gemfile) raise NoMethodError, message end @_contract = Class.new(Dry::Validation::Contract) { params(&block || -> {}) }.new end
Public Instance Methods
Returns the value for the given params key.
@param key [Symbol] the key
@return [Object,nil] the associated value, if found
@since 0.7.0 @api public
# File lib/hanami/action/params.rb, line 221 def [](key) @params[key] end
@since 0.7.0 @api private
# File lib/hanami/action/params.rb, line 348 def _extract_params result = {} if env.key?(Action::RACK_INPUT) result.merge! ::Rack::Request.new(env).params result.merge! _router_params else result.merge! _router_params(env) env[Action::REQUEST_METHOD] ||= Action::DEFAULT_REQUEST_METHOD end result end
@since 0.7.0 @api private
# File lib/hanami/action/params.rb, line 364 def _router_params(fallback = {}) env.fetch(ROUTER_PARAMS) do if session = fallback.delete(Action::RACK_SESSION) fallback[Action::RACK_SESSION] = Utils::Hash.deep_symbolize(session) end fallback end end
Pattern-matching support
@return [::Hash]
@since 2.0.2
# File lib/hanami/action/params.rb, line 340 def deconstruct_keys(*) to_hash end
Iterates over the params.
Calls the given block with each param key-value pair; returns the full hash of params.
@yieldparam key [Symbol] @yieldparam value [Object]
@return [to_h]
@since 0.7.1 @api public
# File lib/hanami/action/params.rb, line 321 def each(&blk) to_h.each(&blk) end
Returns flat collection of full error messages
@return [Array]
@since 0.7.0
@example
params.error_messages # => [ "Email is missing", "Email is in invalid format", "Name is missing", "Tos is missing", "Age is missing", "Address is missing" ]
# File lib/hanami/action/params.rb, line 283 def error_messages(error_set = errors) error_set.each_with_object([]) do |(key, messages), result| k = Utils::String.titleize(key) msgs = if messages.is_a?(::Hash) error_messages(messages) else messages.map { |message| "#{k} #{message}" } end result.concat(msgs) end end
# File lib/hanami/action/params.rb, line 43 def errors = {} end
Returns an value associated with the given params key.
You can access nested attributes by listing all the keys in the path. This uses the same key path semantics as ‘Hash#dig`.
@param keys [Array<Symbol,Integer>] the key
@return [Object,NilClass] return the associated value, if found
@example
require "hanami/controller" module Deliveries class Create < Hanami::Action def handle(req, *) req.params.get(:customer_name) # => "Luca" req.params.get(:uknown) # => nil req.params.get(:address, :city) # => "Rome" req.params.get(:address, :unknown) # => nil req.params.get(:tags, 0) # => "foo" req.params.get(:tags, 1) # => "bar" req.params.get(:tags, 999) # => nil req.params.get(nil) # => nil end end end
@since 0.7.0 @api public
# File lib/hanami/action/params.rb, line 257 def get(*keys) @params.dig(*keys) end
# File lib/hanami/action/params.rb, line 42 def to_h = @attrs def errors = {} end end
Returns true if no validation errors are found, false otherwise.
@return [TrueClass, FalseClass]
@since 0.7.0
@example
params.valid? # => true
# File lib/hanami/action/params.rb, line 306 def valid? errors.empty? end