Module: Fend::Plugins::Coercions

Defined in:
lib/fend/plugins/coercions.rb

Overview

‘coercions` plugin provides a way to coerce validaiton input. First, the plugin needs to be loaded

plugin :coercions

Because of Fend’s dynamic nature, coercion is separated from validation. As such, coercion needs to be done before the actual validation. In order to make this work, type schema must be passed to ‘coerce` method.

coerce username: :string, age: :integer, admin: :boolean

As you can see, type schema is just a hash containing param names and types to which the values need to be converted. Here are some examples:

# coerce username value to string
coerce(username: :string)

# coerce address value to hash
coerce(address: :hash)

# coerce address value to hash
# coerce address[:city] value to string
# coerce address[:street] value to string
coerce(address: { city: :string, street: :string })

# coerce tags to an array
coerce(tags: :array)

# coerce tags to an array of strings
coerce(tags: [:string])

# coerce tags to an array of hashes, each containing `id` and `name` of the tag
coerce(tags: [{ id: :integer, name: :string }])

Coerced data will also serve as result output:

result = UserValidation.call(username: 1234, age: "18", admin: 0)
result.output #=> { username: "1234", age: 18, admin: false }

## Built-in coercions

General rules:

* If input value **cannot** be coerced to specified type, it is returned
  unmodified.

* `nil` is returned if input value is an empty string, except for `:hash`
  and `:array` coercions.

:any : Returns input

:string : Returns ‘input.to_s` if input is `Numeric` or `Symbol`

:symbol : Returns ‘input.to_sym` if `input.respond_to?(:to_sym)`

:integer : Uses ‘Kernel.Integer(input)`

:float : Uses ‘Kernel.Float(input)`

:decimal : Uses ‘Kernel.Float(input).to_d`

:date : Uses ‘Date.parse(input)`

:date_time : Uses ‘DateTime.parse(input)`

:time : Uses ‘Time.parse(input)`

:boolean : Returns ‘true` if input is one of: `1, “1”, “t”, “true”, :true “y”,“yes”, “on”` (case insensitive)

: Returns ‘false` if input is one of: `0, “0”, “f”, “false”, :false, “n”, “no”, “off”` (case insensitive)

:array : Returns ‘[]` if input is an empty string.

: Returns input if input is an array

:hash : Returns ‘{}` if input is an empty string.

: Returns input if input is a hash

## Strict coercions

Adding ‘strict_` prefix to type name will cause error to be raised when input is incoercible:

coerce username: :strict_string

UserValidation.call(username: Hash.new)
#=> Fend::Plugins::Coercions::CoercionError: cannot coerce {} to string

Custom error message can be defined by setting ‘:strict_error_message` option when loading the plugin:

plugin :coercions, strict_error_message: "Incoercible input encountered"

# or

plugin :coercions, strict_error_message: ->(value, type) { "#{value.inspect} cannot become #{type}" }

## Defining custom coercions and overriding built-in ones

You can define your own coercion method or override the built-in one by passing a block and using ‘coerce_to` method, when loading the plugin:

plugin :coercions do
  # add new
  coerce_to(:positive_integer) do |input|
    Kernel.Integer(input).abs
  end

  # override existing
  coerce_to(:integer) do |input|
    # ...
  end
end

### Handling incoercible input

If input value cannot be coerced, either ‘ArgumentError` or `TypeError` should be raised.

class PostValidation < Fend
  plugin :coercions do
    coerce_to(:user) do |input|
      raise ArgumentError unless input.is_a?(Integer)

      User.find(input)
    end
  end

  # ...

end

‘ArgumentError` and `TypeError` are rescued on a higher level and input is returned as is.

‘coerce(modified_by: :user)`

result = PostValidation.call(modified_by: "invalid_id")

result.input #=> { modified_by: "invalid_id" }
result.output #=> { modified_by: "invalid_id" }

If strict coercion is specified, errors are re-raised as ‘CoercionError`.

‘coerce(modified_by: :strict_user)`

result = PostValidation.call(modified_by: "invalid_id")
#=> Fend::Plugins::Coercions::CoercionError: cannot coerce invalid_id to user

### Handling empty strings

In order to check if input is an empty string, you can take advantange of ‘empty_string?` helper method. It takes input as an argument:

coerce_to(:user) do |input|
  return if empty_string?(input)

  # ...
end

Defined Under Namespace

Modules: ClassMethods, InstanceMethods Classes: Coerce, Coercer, CoercionError

Class Method Summary collapse

Class Method Details

.configure(validation, opts = {}, &block) ⇒ Object



188
189
190
191
192
193
194
195
# File 'lib/fend/plugins/coercions.rb', line 188

def self.configure(validation, opts = {}, &block)
  validation.const_set(:Coerce, Class.new(Fend::Plugins::Coercions::Coerce)) unless validation.const_defined?(:Coerce)
  validation::Coerce.class_eval(&block) if block_given?
  validation.opts[:coercions_strict_error_message] = opts.fetch(:strict_error_message, validation.opts[:coercions_strict_error_message])
  validation::Coerce.fend_class = validation

  validation.const_set(:Coercer, Coercer) unless validation.const_defined?(:Coercer)
end