Merge pull request #522 from dry-rb/add-per-contract-macros

Add per-contract class macros
This commit is contained in:
Piotr Solnica 2019-05-05 09:12:03 +02:00 committed by GitHub
commit 4b9706c1d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 119 additions and 13 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -93,7 +93,7 @@ module Dry
instance_exec(_context, &block) if block
macros.each do |macro|
instance_exec(_context, &Macros[macro])
instance_exec(_context, &macro(macro))
end
end

View File

@ -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

View File

@ -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