Merge pull request #522 from dry-rb/add-per-contract-macros
Add per-contract class macros
This commit is contained in:
commit
4b9706c1d2
|
@ -1,10 +1,18 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'dry/schema/config'
|
||||
require 'dry/validation/macros'
|
||||
|
||||
module Dry
|
||||
module Validation
|
||||
# @api private
|
||||
class Config < Schema::Config
|
||||
setting :macros, Macros::Container.new, &:dup
|
||||
|
||||
# @api private
|
||||
def macros
|
||||
config.macros
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,6 +77,11 @@ module Dry
|
|||
# @api private
|
||||
option :rules, default: -> { self.class.rules }
|
||||
|
||||
# @!attribute [r] macros
|
||||
# @return [Hash]
|
||||
# @api private
|
||||
option :macros, default: -> { config.macros }
|
||||
|
||||
# @!attribute [r] message_resolver
|
||||
# @return [Messages::Resolver]
|
||||
# @api private
|
||||
|
@ -115,6 +120,15 @@ module Dry
|
|||
path = Schema::Path[key]
|
||||
result.error?(path) || path.map.with_index { |k, i| result.error?(path.keys[0..i-2]) }.any?
|
||||
end
|
||||
|
||||
# Get a registered macro
|
||||
#
|
||||
# @return [Proc,#to_proc]
|
||||
#
|
||||
# @api private
|
||||
def macro(name)
|
||||
macros.key?(name) ? macros[name] : Macros[name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -27,6 +27,15 @@ module Dry
|
|||
@config ||= Validation::Config.new
|
||||
end
|
||||
|
||||
# Macros
|
||||
#
|
||||
# @return [Macros::Container]
|
||||
#
|
||||
# @api public
|
||||
def macros
|
||||
config.macros
|
||||
end
|
||||
|
||||
# Define a params schema for your contract
|
||||
#
|
||||
# This type of schema is suitable for HTTP parameters
|
||||
|
|
|
@ -93,7 +93,7 @@ module Dry
|
|||
instance_exec(_context, &block) if block
|
||||
|
||||
macros.each do |macro|
|
||||
instance_exec(_context, &Macros[macro])
|
||||
instance_exec(_context, ¯o(macro))
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'dry/container/mixin'
|
||||
require 'dry/container'
|
||||
|
||||
module Dry
|
||||
module Validation
|
||||
|
@ -8,19 +8,39 @@ module Dry
|
|||
#
|
||||
# @api public
|
||||
module Macros
|
||||
extend Container::Mixin
|
||||
|
||||
# @api public
|
||||
def self.register(name, &block)
|
||||
super(name, block, call: false)
|
||||
end
|
||||
|
||||
# Acceptance macro
|
||||
# Registry for macros
|
||||
#
|
||||
# @api public
|
||||
register(:acceptance) do
|
||||
key.failure(:acceptance, key: key_name) unless values[key_name].equal?(true)
|
||||
# @api private
|
||||
class Container
|
||||
include Dry::Container::Mixin
|
||||
|
||||
# @api private
|
||||
def register(name, &block)
|
||||
super(name, block, call: false)
|
||||
end
|
||||
end
|
||||
|
||||
# @api public
|
||||
def self.[](name)
|
||||
container[name]
|
||||
end
|
||||
|
||||
# @api public
|
||||
def self.register(*args, &block)
|
||||
container.register(*args, &block)
|
||||
end
|
||||
|
||||
# @api private
|
||||
def self.container
|
||||
@container ||= Container.new
|
||||
end
|
||||
end
|
||||
|
||||
# Acceptance macro
|
||||
#
|
||||
# @api public
|
||||
Macros.register(:acceptance) do
|
||||
key.failure(:acceptance, key: key_name) unless values[key_name].equal?(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe 'Defining custom macros' do
|
||||
subject(:contract) do
|
||||
Class.new(Test::BaseContract) do
|
||||
schema do
|
||||
required(:numbers).array(:integer)
|
||||
end
|
||||
|
||||
rule(:numbers).validate(:even_numbers)
|
||||
end.new
|
||||
end
|
||||
|
||||
before do
|
||||
class Test::BaseContract < Dry::Validation::Contract; end
|
||||
end
|
||||
|
||||
shared_context 'a contract with a custom macro' do
|
||||
it 'succeeds with valid input' do
|
||||
expect(contract.(numbers: [2, 4, 6])).to be_success
|
||||
end
|
||||
|
||||
it 'fails with invalid input' do
|
||||
expect(contract.(numbers: [1, 2, 3]).errors.to_h).to eql(numbers: ['all numbers must be even'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'using macro from the global registry' do
|
||||
include_context 'a contract with a custom macro' do
|
||||
before do
|
||||
Dry::Validation::Macros.register(:even_numbers) do
|
||||
key.failure('all numbers must be even') unless values[key_name].all?(&:even?)
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Dry::Validation::Macros.container._container.delete('even_numbers')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'using macro from contract itself' do
|
||||
include_context 'a contract with a custom macro' do
|
||||
before do
|
||||
Test::BaseContract.macros.register(:even_numbers) do
|
||||
key.failure('all numbers must be even') unless values[key_name].all?(&:even?)
|
||||
end
|
||||
end
|
||||
|
||||
after do
|
||||
Test::BaseContract.macros._container.delete('even_numbers')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue