2

I wish to coerce the form input

"1,3,5"

into:

[1,3,5]

I am using dry-types gem for other coercions and constraints. I need to know:

  • Is this possible via any built-in mechanism in rails or dry-types ?

  • If not, how do I define a custom coercion for it using dry-types ?

Zuhaib Ali
  • 3,344
  • 3
  • 20
  • 32

3 Answers3

3

I'd consider two ways of solving this:

  • converting a string with comma-separated values into an array of numbers and then feed it to dry-types (what as far as I understand you're currently doing)
  • Define a custom construction type for such a string which is convertable to an array here's an article about it
Igor Drozdov
  • 14,690
  • 5
  • 37
  • 53
3

You can patch dry-types

app/config/initializers/dry_type_patch.rb

module Dry
  module Types
    class Array < Definition
      class Member < Array
        alias old_try, try
        def try(input, &block)
          input = input.split(',') if input.is_a?(::String)
          old_try(input, &block)
        end
      end
    end
  end
end
SteveTurczyn
  • 36,057
  • 6
  • 41
  • 53
  • 3
    Interesting, Steve, but keep in mind that one day the OP may have to hand over the code to someone else. Not only will that person have to be (or become) familiar with `dry-types`, but they will have to understand this customization, something that wouldn't be necessary if the OP simply used `arr.split(',').map(&:to_i)`. – Cary Swoveland Jul 23 '17 at 20:12
  • 1
    @CarySwoveland I hear what you're saying, but the whole point of using the coersion feature of dry types is so that you don't have to check everywhere in the code what data type the target is expecting and what data type is the input. The OP doesn't want to do a test for every field to check (a) are we expecting an array? and (b) is the input a string?. Having said that, I think dry-type has a custom coercion feature that would be way better than my solution. – SteveTurczyn Jul 24 '17 at 07:55
1

I was using dry-validation, which uses dry-types under the hood. You can pre-process the input using a custom type that transforms it as you’d like:

NumberArrayAsString =
  Dry::Types::Definition
  .new(Array)
  .constructor { |input| input.split(',').map { |v| Integer(v) } }

In complete context, using dry-validation:

# frozen_string_literal: true

require 'dry-validation'

NumberArrayAsString =
  Dry::Types::Definition
  .new(Array)
  .constructor { |input| input.split(',').map { |v| Integer(v) } }

ExampleContract = Dry::Validation.Params do
  configure do
    config.type_specs = true
  end

  required(:ids, NumberArrayAsString)
end

puts ExampleContract.call(ids: '1,3,5').inspect
#<Dry::Validation::Result output={:ids=>[1, 3, 5]} errors={}>

This works with dry-validation 0.13, but similar code should work for 1.0.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366