Am I defining custom type right?


#1

I try to create a type which accepts decimal numbers in string with thousand separators
Here is the code I figured after digging the source:

# Usage
# In a controller...
def parsed_params
  ParsedParams.new(params.to_unsafe_h)
end

# Definition
class ParsedParams < ::Dry::Struct
  # This class requires hash with symbol keys...
  # or hash with indifferent access
  constructor_type(:schema)

  module Types
    include ::Dry::Types.module
  end


  class ListingAttributes < ::Dry::Struct
    # This class requires hash with symbol keys...
    # or hash with indifferent access
    constructor_type(:schema)

    module Types
      include ::Dry::Types.module

      SmartDecimal = Types::Decimal.constructor do |value|
        # `BigDecimal` method raise error for blank values
        next nil if value.blank?

        # `tr` is faster
        # https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code
        BigDecimal(value.is_a?(::String) ? value.tr("^0-9", "") : value)
      end
    end

    # Only attributes to be validated are defined here
    attribute(
      :city_id,
      Types::Coercible::String,
    )
    attribute(
      :is_rental,
      Types::Form::Bool,
    )

    attribute(
      :price,
      Types::SmartDecimal,
    )
  end

  attribute(
    :locale,
    Types::Coercible::String,
  )
  attribute(
    :listing_attributes,
    ListingAttributes,
  )


  # See predicates that can be used at
  # http://dry-rb.org/gems/dry-validation/
  InputSchema = ::Dry::Validation.Schema do
    required(:locale).filled
    required(:listing_attributes).schema do
      required(:city_id).filled
      required(:is_rental).filled

      # can be empty
      required(:price)
    end
  end
end

But I found a page about custom type from the doc recently
And wonder if there is an “official” way to define custom type (with custom constructor)


#2

I would say this doc is the official way of building types on top of arbitrary classes, e.g. your domain classes or some uncovered ruby classes such as Complex or Range. Since Decimal is built into dry-t feel free to use it as you do, it’s fine :100: I wouldn’t create multiple Types modules, for me it’s unnecessary, at least in this snippet you can to it just once because SmartDecimal will pick it up from the outer model.


#3

Thanks for the explanation

Multiple Types module <= Coz I just copy and paste some template code :stuck_out_tongue: