Hello, I need to filter
field data before coercion, but for this logic, I need a custom predicate.
class User::Create < OperationBase
option :params, DryTypes::Strict::Hash
class Contract < ContractBase
option :record
params do
optional(:phone).filter(:phone_plausible?).filled(DryTypes::NormalizedPhone)
end
rule(:phone).validate(:unique)
end
def call
user = User.new
result = yield(validate(record: user))
user.assign_attributes(result.to_h)
user.save!
Success(user)
end
end
but I cant archive this
module DryCustomPredicates
include Dry::Logic::Predicates
predicate(:phone_plausible?) do |value|
Phony.plausible?(value)
end
end
class ContractBase < Dry::Validation::Contract
# config.predicates = Dry::Schema::PredicateRegistry.new(DryCustomPredicates)
config.messages.backend = :i18n
# config.messages.load_paths << 'config/locales/validation_errors.yml'
config.messages.default_locale = :ru
register_macro(:unique) do
key.failure(:non_unique_value) unless record.class
.where.not(id: record.id)
.where(keys.first => value).empty?
end
register_macro(:email_format) do
unless /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z\d\-]+)*\.[a-z]+\z/i.match?(value)
key.failure(:invalid_url_format)
end
end
end
suggest, please any true way for archive this logic, without monkey patching.
Currently I use non-strict coercion
NormalizedPhone = DryTypes::Strict::String.constructor do |phone|
Phone::Normalize.call(phone)
end
class Phone::Normalize
include Service
param :phone_string, DryTypes::Strict::String
def call
return @phone_string unless can_normalize?
Phony.format(
normalized_phone,
format: '+%<cc>s%<ndc>s%<local>s',
spaces: '',
local_spaces: ''
)
end
private
def can_normalize?
!@phone_string.blank? && Phony.plausible?(phone_string_without_trash)
end
def normalized_phone
Phony.normalize(phone_string_without_trash)
end
def phone_string_without_trash
@phone_string.gsub(/-/, '').gsub(/^8/, '7')
end
end
and then use rule
rule(:phone) do
key.failure(:invalid_phone_format) if key? && !Phony.plausible?(value)
end
but this method seems not beautiful