dry-validation 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
 - data/.gitignore +8 -0
 - data/.rspec +3 -0
 - data/.rubocop.yml +16 -0
 - data/.rubocop_todo.yml +7 -0
 - data/.travis.yml +29 -0
 - data/CHANGELOG.md +3 -0
 - data/Gemfile +11 -0
 - data/LICENSE +20 -0
 - data/README.md +297 -0
 - data/Rakefile +12 -0
 - data/config/errors.yml +35 -0
 - data/dry-validation.gemspec +25 -0
 - data/examples/basic.rb +21 -0
 - data/examples/nested.rb +30 -0
 - data/examples/rule_ast.rb +33 -0
 - data/lib/dry-validation.rb +1 -0
 - data/lib/dry/validation.rb +12 -0
 - data/lib/dry/validation/error.rb +43 -0
 - data/lib/dry/validation/error_compiler.rb +116 -0
 - data/lib/dry/validation/messages.rb +71 -0
 - data/lib/dry/validation/predicate.rb +39 -0
 - data/lib/dry/validation/predicate_set.rb +22 -0
 - data/lib/dry/validation/predicates.rb +88 -0
 - data/lib/dry/validation/result.rb +64 -0
 - data/lib/dry/validation/rule.rb +125 -0
 - data/lib/dry/validation/rule_compiler.rb +57 -0
 - data/lib/dry/validation/schema.rb +74 -0
 - data/lib/dry/validation/schema/definition.rb +15 -0
 - data/lib/dry/validation/schema/key.rb +39 -0
 - data/lib/dry/validation/schema/rule.rb +28 -0
 - data/lib/dry/validation/schema/value.rb +31 -0
 - data/lib/dry/validation/version.rb +5 -0
 - data/rakelib/rubocop.rake +18 -0
 - data/spec/fixtures/errors.yml +4 -0
 - data/spec/integration/custom_error_messages_spec.rb +35 -0
 - data/spec/integration/custom_predicates_spec.rb +57 -0
 - data/spec/integration/validation_spec.rb +118 -0
 - data/spec/shared/predicates.rb +31 -0
 - data/spec/spec_helper.rb +18 -0
 - data/spec/unit/error_compiler_spec.rb +165 -0
 - data/spec/unit/predicate_spec.rb +37 -0
 - data/spec/unit/predicates/empty_spec.rb +38 -0
 - data/spec/unit/predicates/eql_spec.rb +21 -0
 - data/spec/unit/predicates/exclusion_spec.rb +35 -0
 - data/spec/unit/predicates/filled_spec.rb +38 -0
 - data/spec/unit/predicates/format_spec.rb +21 -0
 - data/spec/unit/predicates/gt_spec.rb +40 -0
 - data/spec/unit/predicates/gteq_spec.rb +40 -0
 - data/spec/unit/predicates/inclusion_spec.rb +35 -0
 - data/spec/unit/predicates/int_spec.rb +34 -0
 - data/spec/unit/predicates/key_spec.rb +29 -0
 - data/spec/unit/predicates/lt_spec.rb +40 -0
 - data/spec/unit/predicates/lteq_spec.rb +40 -0
 - data/spec/unit/predicates/max_size_spec.rb +49 -0
 - data/spec/unit/predicates/min_size_spec.rb +49 -0
 - data/spec/unit/predicates/nil_spec.rb +28 -0
 - data/spec/unit/predicates/size_spec.rb +49 -0
 - data/spec/unit/predicates/str_spec.rb +32 -0
 - data/spec/unit/rule/each_spec.rb +20 -0
 - data/spec/unit/rule/key_spec.rb +27 -0
 - data/spec/unit/rule/set_spec.rb +32 -0
 - data/spec/unit/rule/value_spec.rb +42 -0
 - data/spec/unit/rule_compiler_spec.rb +86 -0
 - metadata +230 -0
 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # coding: utf-8
         
     | 
| 
      
 2 
     | 
    
         
            +
            require File.expand_path('../lib/dry/validation/version', __FILE__)
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            Gem::Specification.new do |spec|
         
     | 
| 
      
 5 
     | 
    
         
            +
              spec.name          = 'dry-validation'
         
     | 
| 
      
 6 
     | 
    
         
            +
              spec.version       = Dry::Validation::VERSION
         
     | 
| 
      
 7 
     | 
    
         
            +
              spec.authors       = ['Andy Holland', 'Piotr Solnica']
         
     | 
| 
      
 8 
     | 
    
         
            +
              spec.email         = ['andyholland1991@aol.com', 'piotr.solnica@gmail.com']
         
     | 
| 
      
 9 
     | 
    
         
            +
              spec.summary       = 'A simple validation library'
         
     | 
| 
      
 10 
     | 
    
         
            +
              spec.homepage      = 'https://github.com/dryrb/dry-validation'
         
     | 
| 
      
 11 
     | 
    
         
            +
              spec.license       = 'MIT'
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              spec.files         = `git ls-files -z`.split("\x0")
         
     | 
| 
      
 14 
     | 
    
         
            +
              spec.executables   = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
         
     | 
| 
      
 15 
     | 
    
         
            +
              spec.test_files    = spec.files.grep(%r{^(test|spec|features)/})
         
     | 
| 
      
 16 
     | 
    
         
            +
              spec.require_paths = ['lib']
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
         
     | 
| 
      
 19 
     | 
    
         
            +
              spec.add_runtime_dependency 'dry-container', '~> 0.2', '>= 0.2.6'
         
     | 
| 
      
 20 
     | 
    
         
            +
              spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              spec.add_development_dependency 'bundler'
         
     | 
| 
      
 23 
     | 
    
         
            +
              spec.add_development_dependency 'rake'
         
     | 
| 
      
 24 
     | 
    
         
            +
              spec.add_development_dependency 'rspec'
         
     | 
| 
      
 25 
     | 
    
         
            +
            end
         
     | 
    
        data/examples/basic.rb
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry-validation'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Schema < Dry::Validation::Schema
         
     | 
| 
      
 4 
     | 
    
         
            +
              key(:email) { |email| email.filled? }
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
              key(:age) do |age|
         
     | 
| 
      
 7 
     | 
    
         
            +
                age.int? & age.gt?(18)
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
            end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            schema = Schema.new
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            errors = schema.messages(email: 'jane@doe.org', age: 19)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            puts errors.inspect
         
     | 
| 
      
 16 
     | 
    
         
            +
            # []
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            errors = schema.messages(email: nil, age: 19)
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            puts errors.inspect
         
     | 
| 
      
 21 
     | 
    
         
            +
            # [[:email, ["email must be filled"]]]
         
     | 
    
        data/examples/nested.rb
    ADDED
    
    | 
         @@ -0,0 +1,30 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry-validation'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class Schema < Dry::Validation::Schema
         
     | 
| 
      
 4 
     | 
    
         
            +
              key(:address) do |address|
         
     | 
| 
      
 5 
     | 
    
         
            +
                address.key(:city) do |city|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  city.min_size?(3)
         
     | 
| 
      
 7 
     | 
    
         
            +
                end
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                address.key(:street) do |street|
         
     | 
| 
      
 10 
     | 
    
         
            +
                  street.filled?
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                address.key(:country) do |country|
         
     | 
| 
      
 14 
     | 
    
         
            +
                  country.key(:name, &:filled?)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  country.key(:code, &:filled?)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
            schema = Schema.new
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            errors = schema.messages({})
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            puts errors.inspect
         
     | 
| 
      
 25 
     | 
    
         
            +
            #<Dry::Validation::Error::Set:0x007fc4f89c4360 @errors=[#<Dry::Validation::Error:0x007fc4f89c4108 @result=#<Dry::Validation::Result::Value success?=false input=nil rule=#<Dry::Validation::Rule::Key name=:address predicate=#<Dry::Validation::Predicate id=:key?>>>>]>
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
            errors = schema.messages(address: { city: 'NYC' })
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
            puts errors.inspect
         
     | 
| 
      
 30 
     | 
    
         
            +
            #<Dry::Validation::Error::Set:0x007fd151189b18 @errors=[#<Dry::Validation::Error:0x007fd151188e20 @result=#<Dry::Validation::Result::Set success?=false input={:city=>"NYC"} rule=#<Dry::Validation::Rule::Set name=:address predicate=[#<Dry::Validation::Rule::Conjunction left=#<Dry::Validation::Rule::Key name=:city predicate=#<Dry::Validation::Predicate id=:key?>> right=#<Dry::Validation::Rule::Value name=:city predicate=#<Dry::Validation::Predicate id=:min_size?>>>, #<Dry::Validation::Rule::Conjunction left=#<Dry::Validation::Rule::Key name=:street predicate=#<Dry::Validation::Predicate id=:key?>> right=#<Dry::Validation::Rule::Value name=:street predicate=#<Dry::Validation::Predicate id=:filled?>>>, #<Dry::Validation::Rule::Conjunction left=#<Dry::Validation::Rule::Key name=:country predicate=#<Dry::Validation::Predicate id=:key?>> right=#<Dry::Validation::Rule::Set name=:country predicate=[#<Dry::Validation::Rule::Conjunction left=#<Dry::Validation::Rule::Key name=:name predicate=#<Dry::Validation::Predicate id=:key?>> right=#<Dry::Validation::Rule::Value name=:name predicate=#<Dry::Validation::Predicate id=:filled?>>>, #<Dry::Validation::Rule::Conjunction left=#<Dry::Validation::Rule::Key name=:code predicate=#<Dry::Validation::Predicate id=:key?>> right=#<Dry::Validation::Rule::Value name=:code predicate=#<Dry::Validation::Predicate id=:filled?>>>]>>]>>>]>
         
     | 
| 
         @@ -0,0 +1,33 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry-validation'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            ast = [
         
     | 
| 
      
 4 
     | 
    
         
            +
              [
         
     | 
| 
      
 5 
     | 
    
         
            +
                :and,
         
     | 
| 
      
 6 
     | 
    
         
            +
                [
         
     | 
| 
      
 7 
     | 
    
         
            +
                  [:key, [:age, [:predicate, [:key?, []]]]],
         
     | 
| 
      
 8 
     | 
    
         
            +
                  [
         
     | 
| 
      
 9 
     | 
    
         
            +
                    :and,
         
     | 
| 
      
 10 
     | 
    
         
            +
                    [
         
     | 
| 
      
 11 
     | 
    
         
            +
                      [:val, [:age, [:predicate, [:filled?, []]]]],
         
     | 
| 
      
 12 
     | 
    
         
            +
                      [:val, [:age, [:predicate, [:gt?, [18]]]]]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 14 
     | 
    
         
            +
                  ]
         
     | 
| 
      
 15 
     | 
    
         
            +
                ]
         
     | 
| 
      
 16 
     | 
    
         
            +
              ]
         
     | 
| 
      
 17 
     | 
    
         
            +
            ]
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
            compiler = Dry::Validation::RuleCompiler.new(Dry::Validation::Predicates)
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            rules = compiler.call(ast)
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
            puts rules.inspect
         
     | 
| 
      
 24 
     | 
    
         
            +
            # [
         
     | 
| 
      
 25 
     | 
    
         
            +
            #   #<Dry::Validation::Rule::Conjunction
         
     | 
| 
      
 26 
     | 
    
         
            +
            #     left=#<Dry::Validation::Rule::Key name=:age predicate=#<Dry::Validation::Predicate id=:key?>>
         
     | 
| 
      
 27 
     | 
    
         
            +
            #     right=#<Dry::Validation::Rule::Conjunction
         
     | 
| 
      
 28 
     | 
    
         
            +
            #       left=#<Dry::Validation::Rule::Value name=:age predicate=#<Dry::Validation::Predicate id=:filled?>>
         
     | 
| 
      
 29 
     | 
    
         
            +
            #       right=#<Dry::Validation::Rule::Value name=:age predicate=#<Dry::Validation::Predicate id=:gt?>>>>
         
     | 
| 
      
 30 
     | 
    
         
            +
            # ]
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
            puts rules.map(&:to_ary).inspect
         
     | 
| 
      
 33 
     | 
    
         
            +
            # [[:and, [:key, [:age, [:predicate, [:key?, [:age]]]]], [[:and, [:val, [:age, [:predicate, [:filled?, []]]]], [[:val, [:age, [:predicate, [:gt?, [18]]]]]]]]]]
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/validation'
         
     | 
| 
         @@ -0,0 +1,43 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 3 
     | 
    
         
            +
                class Error
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class Set
         
     | 
| 
      
 5 
     | 
    
         
            +
                    include Enumerable
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                    attr_reader :errors
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                    def initialize
         
     | 
| 
      
 10 
     | 
    
         
            +
                      @errors = []
         
     | 
| 
      
 11 
     | 
    
         
            +
                    end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                    def each(&block)
         
     | 
| 
      
 14 
     | 
    
         
            +
                      errors.each(&block)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    end
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
                    def empty?
         
     | 
| 
      
 18 
     | 
    
         
            +
                      errors.empty?
         
     | 
| 
      
 19 
     | 
    
         
            +
                    end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                    def <<(error)
         
     | 
| 
      
 22 
     | 
    
         
            +
                      errors << error
         
     | 
| 
      
 23 
     | 
    
         
            +
                    end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                    def to_ary
         
     | 
| 
      
 26 
     | 
    
         
            +
                      errors.map { |error| error.to_ary }
         
     | 
| 
      
 27 
     | 
    
         
            +
                    end
         
     | 
| 
      
 28 
     | 
    
         
            +
                    alias_method :to_a, :to_ary
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  attr_reader :result
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def initialize(result)
         
     | 
| 
      
 34 
     | 
    
         
            +
                    @result = result
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def to_ary
         
     | 
| 
      
 38 
     | 
    
         
            +
                    [:error, result.to_ary]
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  alias_method :to_a, :to_ary
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,116 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 3 
     | 
    
         
            +
                class ErrorCompiler
         
     | 
| 
      
 4 
     | 
    
         
            +
                  attr_reader :messages
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  def initialize(messages)
         
     | 
| 
      
 7 
     | 
    
         
            +
                    @messages = messages
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  def call(ast)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    ast.map { |node| visit(node) }
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  def visit(node, *args)
         
     | 
| 
      
 15 
     | 
    
         
            +
                    __send__(:"visit_#{node[0]}", node[1], *args)
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  def visit_error(error)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    visit(error)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  def visit_input(input, *args)
         
     | 
| 
      
 23 
     | 
    
         
            +
                    name, value, rules = input
         
     | 
| 
      
 24 
     | 
    
         
            +
                    [name, rules.map { |rule| visit(rule, name, value) }]
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  def visit_key(rule, name, value)
         
     | 
| 
      
 28 
     | 
    
         
            +
                    _, predicate = rule
         
     | 
| 
      
 29 
     | 
    
         
            +
                    visit(predicate, value, name)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                  def visit_val(rule, name, value)
         
     | 
| 
      
 33 
     | 
    
         
            +
                    name, predicate = rule
         
     | 
| 
      
 34 
     | 
    
         
            +
                    visit(predicate, value, name)
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                  def visit_predicate(predicate, value, name)
         
     | 
| 
      
 38 
     | 
    
         
            +
                    messages.lookup(predicate[0], name, predicate[1][0]) % visit(predicate, value).merge(name: name)
         
     | 
| 
      
 39 
     | 
    
         
            +
                  end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                  def visit_key?(*args, value)
         
     | 
| 
      
 42 
     | 
    
         
            +
                    { name: args[0][0] }
         
     | 
| 
      
 43 
     | 
    
         
            +
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def visit_empty?(*args, value)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    { value: value }
         
     | 
| 
      
 47 
     | 
    
         
            +
                  end
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                  def visit_exclusion?(*args, value)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    { list: args[0][0].join(', ') }
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def visit_inclusion?(*args, value)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    { list: args[0][0].join(', ') }
         
     | 
| 
      
 55 
     | 
    
         
            +
                  end
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                  def visit_gt?(*args, value)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 59 
     | 
    
         
            +
                  end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                  def visit_gteq?(*args, value)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 63 
     | 
    
         
            +
                  end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                  def visit_lt?(*args, value)
         
     | 
| 
      
 66 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 67 
     | 
    
         
            +
                  end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  def visit_lteq?(*args, value)
         
     | 
| 
      
 70 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 71 
     | 
    
         
            +
                  end
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  def visit_int?(*args, value)
         
     | 
| 
      
 74 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 75 
     | 
    
         
            +
                  end
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
                  def visit_max_size?(*args, value)
         
     | 
| 
      
 78 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 79 
     | 
    
         
            +
                  end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  def visit_min_size?(*args, value)
         
     | 
| 
      
 82 
     | 
    
         
            +
                    { num: args[0][0], value: value }
         
     | 
| 
      
 83 
     | 
    
         
            +
                  end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                  def visit_eql?(*args, value)
         
     | 
| 
      
 86 
     | 
    
         
            +
                    { eql_value: args[0][0], value: value }
         
     | 
| 
      
 87 
     | 
    
         
            +
                  end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                  def visit_size?(*args, value)
         
     | 
| 
      
 90 
     | 
    
         
            +
                    num = args[0][0]
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                    if num.is_a?(Range)
         
     | 
| 
      
 93 
     | 
    
         
            +
                      { left: num.first, right: num.last, value: value }
         
     | 
| 
      
 94 
     | 
    
         
            +
                    else
         
     | 
| 
      
 95 
     | 
    
         
            +
                      { num: args[0][0], value: value }
         
     | 
| 
      
 96 
     | 
    
         
            +
                    end
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
                  def visit_str?(*args, value)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    { value: value }
         
     | 
| 
      
 101 
     | 
    
         
            +
                  end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                  def visit_format?(*args, value)
         
     | 
| 
      
 104 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 105 
     | 
    
         
            +
                  end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                  def visit_nil?(*args, value)
         
     | 
| 
      
 108 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 109 
     | 
    
         
            +
                  end
         
     | 
| 
      
 110 
     | 
    
         
            +
             
     | 
| 
      
 111 
     | 
    
         
            +
                  def visit_filled?(*args)
         
     | 
| 
      
 112 
     | 
    
         
            +
                    {}
         
     | 
| 
      
 113 
     | 
    
         
            +
                  end
         
     | 
| 
      
 114 
     | 
    
         
            +
                end
         
     | 
| 
      
 115 
     | 
    
         
            +
              end
         
     | 
| 
      
 116 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'yaml'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'pathname'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 5 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 6 
     | 
    
         
            +
                class Messages
         
     | 
| 
      
 7 
     | 
    
         
            +
                  DEFAULT_PATH = Pathname(__dir__).join('../../../config/errors.yml').freeze
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                  attr_reader :data
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                  def self.default
         
     | 
| 
      
 12 
     | 
    
         
            +
                    load(DEFAULT_PATH)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  end
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def self.load(path)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    new(load_yaml(path))
         
     | 
| 
      
 17 
     | 
    
         
            +
                  end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                  def self.load_yaml(path)
         
     | 
| 
      
 20 
     | 
    
         
            +
                    symbolize_keys(YAML.load_file(path))
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                  def self.symbolize_keys(hash)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    hash.each_with_object({}) do |(k, v), r|
         
     | 
| 
      
 25 
     | 
    
         
            +
                      r[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v
         
     | 
| 
      
 26 
     | 
    
         
            +
                    end
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  class Namespaced
         
     | 
| 
      
 30 
     | 
    
         
            +
                    attr_reader :namespace, :fallback
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                    def initialize(namespace, fallback)
         
     | 
| 
      
 33 
     | 
    
         
            +
                      @namespace = namespace
         
     | 
| 
      
 34 
     | 
    
         
            +
                      @fallback = fallback
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                    def lookup(*args)
         
     | 
| 
      
 38 
     | 
    
         
            +
                      namespace.lookup(*args) { fallback.lookup(*args) }
         
     | 
| 
      
 39 
     | 
    
         
            +
                    end
         
     | 
| 
      
 40 
     | 
    
         
            +
                  end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                  def initialize(data)
         
     | 
| 
      
 43 
     | 
    
         
            +
                    @data = data
         
     | 
| 
      
 44 
     | 
    
         
            +
                  end
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                  def merge(overrides)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if overrides.is_a?(Hash)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      self.class.new(data.merge(overrides))
         
     | 
| 
      
 49 
     | 
    
         
            +
                    else
         
     | 
| 
      
 50 
     | 
    
         
            +
                      self.class.new(data.merge(Messages.load_yaml(overrides)))
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
                  end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                  def namespaced(namespace)
         
     | 
| 
      
 55 
     | 
    
         
            +
                    Namespaced.new(Messages.new(data[namespace]), self)
         
     | 
| 
      
 56 
     | 
    
         
            +
                  end
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                  def lookup(identifier, key, arg, &block)
         
     | 
| 
      
 59 
     | 
    
         
            +
                    message = data.fetch(:attributes, {}).fetch(key, {}).fetch(identifier) do
         
     | 
| 
      
 60 
     | 
    
         
            +
                      data.fetch(identifier, &block)
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                    if message.is_a?(Hash)
         
     | 
| 
      
 64 
     | 
    
         
            +
                      message.fetch(arg.class.name.downcase.to_sym, message.fetch(:default))
         
     | 
| 
      
 65 
     | 
    
         
            +
                    else
         
     | 
| 
      
 66 
     | 
    
         
            +
                      message
         
     | 
| 
      
 67 
     | 
    
         
            +
                    end
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,39 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 3 
     | 
    
         
            +
                def self.Predicate(block)
         
     | 
| 
      
 4 
     | 
    
         
            +
                  case block
         
     | 
| 
      
 5 
     | 
    
         
            +
                  when Method then Predicate.new(block.name, &block)
         
     | 
| 
      
 6 
     | 
    
         
            +
                  else raise ArgumentError, 'predicate needs an :id'
         
     | 
| 
      
 7 
     | 
    
         
            +
                  end
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                class Predicate
         
     | 
| 
      
 11 
     | 
    
         
            +
                  include Dry::Equalizer(:id)
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  attr_reader :id, :args, :fn
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                  def initialize(id, *args, &block)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    @id = id
         
     | 
| 
      
 17 
     | 
    
         
            +
                    @fn = block
         
     | 
| 
      
 18 
     | 
    
         
            +
                    @args = args
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                  def call(*args)
         
     | 
| 
      
 22 
     | 
    
         
            +
                    fn.(*args)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                  def negation
         
     | 
| 
      
 26 
     | 
    
         
            +
                    self.class.new(:"not_#{id}") { |input| !fn.(input) }
         
     | 
| 
      
 27 
     | 
    
         
            +
                  end
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                  def curry(*args)
         
     | 
| 
      
 30 
     | 
    
         
            +
                    self.class.new(id, *args, &fn.curry.(*args))
         
     | 
| 
      
 31 
     | 
    
         
            +
                  end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                  def to_ary
         
     | 
| 
      
 34 
     | 
    
         
            +
                    [:predicate, [id, args]]
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                  alias_method :to_a, :to_ary
         
     | 
| 
      
 37 
     | 
    
         
            +
                end
         
     | 
| 
      
 38 
     | 
    
         
            +
              end
         
     | 
| 
      
 39 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/validation/predicate'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 5 
     | 
    
         
            +
                module PredicateSet
         
     | 
| 
      
 6 
     | 
    
         
            +
                  module Methods
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def predicate(name, &block)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      register(name) { Predicate.new(name, &block) }
         
     | 
| 
      
 9 
     | 
    
         
            +
                    end
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
                    def import(predicate_set)
         
     | 
| 
      
 12 
     | 
    
         
            +
                      merge(predicate_set)
         
     | 
| 
      
 13 
     | 
    
         
            +
                    end
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                  def self.extended(other)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    super
         
     | 
| 
      
 18 
     | 
    
         
            +
                    other.extend(Methods, Dry::Container::Mixin)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  end
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,88 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'dry/validation/predicate_set'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module Dry
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Validation
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Predicates
         
     | 
| 
      
 6 
     | 
    
         
            +
                  extend PredicateSet
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  def self.included(other)
         
     | 
| 
      
 9 
     | 
    
         
            +
                    super
         
     | 
| 
      
 10 
     | 
    
         
            +
                    other.extend(PredicateSet)
         
     | 
| 
      
 11 
     | 
    
         
            +
                    other.import(self)
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  predicate(:nil?) do |input|
         
     | 
| 
      
 15 
     | 
    
         
            +
                    input.nil?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                  predicate(:key?) do |name, input|
         
     | 
| 
      
 19 
     | 
    
         
            +
                    input.key?(name)
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  predicate(:empty?) do |input|
         
     | 
| 
      
 23 
     | 
    
         
            +
                    case input
         
     | 
| 
      
 24 
     | 
    
         
            +
                    when String, Array, Hash then input.empty?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    when nil then true
         
     | 
| 
      
 26 
     | 
    
         
            +
                    else
         
     | 
| 
      
 27 
     | 
    
         
            +
                      false
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
                  predicate(:filled?) do |input|
         
     | 
| 
      
 32 
     | 
    
         
            +
                    !self[:empty?].(input)
         
     | 
| 
      
 33 
     | 
    
         
            +
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
                  predicate(:int?) do |input|
         
     | 
| 
      
 36 
     | 
    
         
            +
                    input.is_a?(Fixnum)
         
     | 
| 
      
 37 
     | 
    
         
            +
                  end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                  predicate(:str?) do |input|
         
     | 
| 
      
 40 
     | 
    
         
            +
                    input.is_a?(String)
         
     | 
| 
      
 41 
     | 
    
         
            +
                  end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                  predicate(:lt?) do |num, input|
         
     | 
| 
      
 44 
     | 
    
         
            +
                    input < num
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                  predicate(:gt?) do |num, input|
         
     | 
| 
      
 48 
     | 
    
         
            +
                    input > num
         
     | 
| 
      
 49 
     | 
    
         
            +
                  end
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                  predicate(:lteq?) do |num, input|
         
     | 
| 
      
 52 
     | 
    
         
            +
                    !self[:gt?].(num, input)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                  predicate(:gteq?) do |num, input|
         
     | 
| 
      
 56 
     | 
    
         
            +
                    !self[:lt?].(num, input)
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  predicate(:size?) do |num, input|
         
     | 
| 
      
 60 
     | 
    
         
            +
                    input.size == num
         
     | 
| 
      
 61 
     | 
    
         
            +
                  end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
      
 63 
     | 
    
         
            +
                  predicate(:min_size?) do |num, input|
         
     | 
| 
      
 64 
     | 
    
         
            +
                    input.size >= num
         
     | 
| 
      
 65 
     | 
    
         
            +
                  end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                  predicate(:max_size?) do |num, input|
         
     | 
| 
      
 68 
     | 
    
         
            +
                    input.size <= num
         
     | 
| 
      
 69 
     | 
    
         
            +
                  end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                  predicate(:inclusion?) do |list, input|
         
     | 
| 
      
 72 
     | 
    
         
            +
                    list.include?(input)
         
     | 
| 
      
 73 
     | 
    
         
            +
                  end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                  predicate(:exclusion?) do |list, input|
         
     | 
| 
      
 76 
     | 
    
         
            +
                    !self[:inclusion?].(list, input)
         
     | 
| 
      
 77 
     | 
    
         
            +
                  end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  predicate(:eql?) do |left, right|
         
     | 
| 
      
 80 
     | 
    
         
            +
                    left.eql?(right)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                  predicate(:format?) do |regex, input|
         
     | 
| 
      
 84 
     | 
    
         
            +
                    !regex.match(input).nil?
         
     | 
| 
      
 85 
     | 
    
         
            +
                  end
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
            end
         
     |