Skip to content

Commit

Permalink
Refactor #path internals [#593]
Browse files Browse the repository at this point in the history
  • Loading branch information
ianwhite committed Oct 7, 2019
1 parent 1ca646b commit 37e272a
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 18 deletions.
74 changes: 58 additions & 16 deletions lib/dry/validation/extensions/composable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,33 @@ module Validation
#
# @api public
module Composable
Path = Dry::Schema::Path
class Path < Dry::Schema::Path
# @api private
def self.[](spec)
spec.nil? ? new([]) : super
end

# @api private
def +(other)
self.class.new(keys + self.class[other].keys)
end

# @api private
def empty?
keys.empty?
end
end

# class interface for Contract with Composable extension
#
# @api private
module ContractClassInterface
def self.extended(contract)
contract.class_eval do
include Dry::Equalizer(:schema, :rules, :messages, :contracts_at_paths, inspect: false)

# we allow contracts with no schemas if they have contracts,
# see option :contracts default block
# we handle missing schema in #default_contracts_at_paths

# @!attribute [r] schema
# @return [Dry::Schema::Params, Dry::Schema::JSON, Dry::Schema::Processor]
Expand All @@ -63,11 +80,7 @@ def self.extended(contract)
#
# @api public
def contract(contract, path: nil)
@current_path ||= nil
path = Path[path] if path
path = Path[[*@current_path, *path]] if @current_path || path

contracts_at_paths << [contract, path]
composable_builder.contract(contract, path: path)
end

# scope enclosed contracts to the specified path
Expand All @@ -82,13 +95,8 @@ def contract(contract, path: nil)
# end
#
# @api public
def path(path)
path = Path[path]
prev_path = @current_path
@current_path = Path[[*@current_path, *path]]
yield
ensure
@current_path = prev_path
def path(path, &block)
composable_builder.path(path, &block)
end

# Return contracts defined in this class
Expand All @@ -103,6 +111,40 @@ def contracts_at_paths
init.dup
end
end

private

def composable_builder
@composable_builder ||= Builder.new(contracts_at_paths)
end
end

# build the composition with #contract and #path methods
#
# @api private
class Builder
# @return [[Contract, Path]]
#
# @api private
attr_reader :contracts_at_paths

# @return Path
#
# @api private
attr_reader :prefix

def initialize(contracts_at_paths, prefix = nil)
@contracts_at_paths = contracts_at_paths
@prefix = Path[prefix]
end

def contract(contract, path: nil)
contracts_at_paths << [contract, prefix + Path[path]]
end

def path(path, &block)
Builder.new(contracts_at_paths, prefix + Path[path]).instance_eval(&block)
end
end

module ContractInterface
Expand Down Expand Up @@ -130,7 +172,7 @@ def inspect
return super if contracts_at_paths.empty?

contracts_str = contracts_at_paths.map do |(contract, path)|
path ? "#{path.to_a.join(DOT)}:#{contract}" : contract
path.empty? ? contract.to_s : "#{path.to_a.join(DOT)}:#{contract}"
end

super[0..-2] + " contracts=[#{contracts_str.join(', ')}]>"
Expand All @@ -140,7 +182,7 @@ def inspect

def contract_result(input, contract, path)
contract = contract.new unless contract.respond_to?(:call)
input = input.dig(*path) if path
input = input.dig(*path) unless path.empty?
contract.call(input)
end

Expand Down
13 changes: 11 additions & 2 deletions lib/dry/validation/extensions/composable/result_set.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def initialize(results = [])

# add a result to the set, optionally at the path
def add_result(result, path = nil)
result = ResultAtPath.new(result, path) if path
path = Path[path]
result = ResultAtPath.new(result, path) unless path.empty?

@values = nil
@success = nil
Expand Down Expand Up @@ -117,7 +118,15 @@ def merge_hashes(left, right)
#
# @api private
class ResultAtPath
attr_reader :result, :path
# @return Result
#
# @api private
attr_reader :result

# @return Array[Symbol]
#
# @api private
attr_reader :path

def initialize(result, path)
@message_set = nil
Expand Down

0 comments on commit 37e272a

Please sign in to comment.