dry-validation 0.1.0 → 1.8.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.
Files changed (82) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +969 -1
  3. data/LICENSE +1 -1
  4. data/README.md +19 -286
  5. data/config/errors.yml +4 -35
  6. data/dry-validation.gemspec +38 -22
  7. data/lib/dry/validation/config.rb +24 -0
  8. data/lib/dry/validation/constants.rb +43 -0
  9. data/lib/dry/validation/contract/class_interface.rb +230 -0
  10. data/lib/dry/validation/contract.rb +173 -0
  11. data/lib/dry/validation/evaluator.rb +233 -0
  12. data/lib/dry/validation/extensions/hints.rb +67 -0
  13. data/lib/dry/validation/extensions/monads.rb +34 -0
  14. data/lib/dry/validation/extensions/predicates_as_macros.rb +75 -0
  15. data/lib/dry/validation/failures.rb +70 -0
  16. data/lib/dry/validation/function.rb +43 -0
  17. data/lib/dry/validation/macro.rb +38 -0
  18. data/lib/dry/validation/macros.rb +104 -0
  19. data/lib/dry/validation/message.rb +100 -0
  20. data/lib/dry/validation/message_set.rb +97 -0
  21. data/lib/dry/validation/messages/resolver.rb +129 -0
  22. data/lib/dry/validation/result.rb +206 -38
  23. data/lib/dry/validation/rule.rb +116 -106
  24. data/lib/dry/validation/schema_ext.rb +19 -0
  25. data/lib/dry/validation/values.rb +108 -0
  26. data/lib/dry/validation/version.rb +3 -1
  27. data/lib/dry/validation.rb +55 -7
  28. data/lib/dry-validation.rb +3 -1
  29. metadata +80 -106
  30. data/.gitignore +0 -8
  31. data/.rspec +0 -3
  32. data/.rubocop.yml +0 -16
  33. data/.rubocop_todo.yml +0 -7
  34. data/.travis.yml +0 -29
  35. data/Gemfile +0 -11
  36. data/Rakefile +0 -12
  37. data/examples/basic.rb +0 -21
  38. data/examples/nested.rb +0 -30
  39. data/examples/rule_ast.rb +0 -33
  40. data/lib/dry/validation/error.rb +0 -43
  41. data/lib/dry/validation/error_compiler.rb +0 -116
  42. data/lib/dry/validation/messages.rb +0 -71
  43. data/lib/dry/validation/predicate.rb +0 -39
  44. data/lib/dry/validation/predicate_set.rb +0 -22
  45. data/lib/dry/validation/predicates.rb +0 -88
  46. data/lib/dry/validation/rule_compiler.rb +0 -57
  47. data/lib/dry/validation/schema/definition.rb +0 -15
  48. data/lib/dry/validation/schema/key.rb +0 -39
  49. data/lib/dry/validation/schema/rule.rb +0 -28
  50. data/lib/dry/validation/schema/value.rb +0 -31
  51. data/lib/dry/validation/schema.rb +0 -74
  52. data/rakelib/rubocop.rake +0 -18
  53. data/spec/fixtures/errors.yml +0 -4
  54. data/spec/integration/custom_error_messages_spec.rb +0 -35
  55. data/spec/integration/custom_predicates_spec.rb +0 -57
  56. data/spec/integration/validation_spec.rb +0 -118
  57. data/spec/shared/predicates.rb +0 -31
  58. data/spec/spec_helper.rb +0 -18
  59. data/spec/unit/error_compiler_spec.rb +0 -165
  60. data/spec/unit/predicate_spec.rb +0 -37
  61. data/spec/unit/predicates/empty_spec.rb +0 -38
  62. data/spec/unit/predicates/eql_spec.rb +0 -21
  63. data/spec/unit/predicates/exclusion_spec.rb +0 -35
  64. data/spec/unit/predicates/filled_spec.rb +0 -38
  65. data/spec/unit/predicates/format_spec.rb +0 -21
  66. data/spec/unit/predicates/gt_spec.rb +0 -40
  67. data/spec/unit/predicates/gteq_spec.rb +0 -40
  68. data/spec/unit/predicates/inclusion_spec.rb +0 -35
  69. data/spec/unit/predicates/int_spec.rb +0 -34
  70. data/spec/unit/predicates/key_spec.rb +0 -29
  71. data/spec/unit/predicates/lt_spec.rb +0 -40
  72. data/spec/unit/predicates/lteq_spec.rb +0 -40
  73. data/spec/unit/predicates/max_size_spec.rb +0 -49
  74. data/spec/unit/predicates/min_size_spec.rb +0 -49
  75. data/spec/unit/predicates/nil_spec.rb +0 -28
  76. data/spec/unit/predicates/size_spec.rb +0 -49
  77. data/spec/unit/predicates/str_spec.rb +0 -32
  78. data/spec/unit/rule/each_spec.rb +0 -20
  79. data/spec/unit/rule/key_spec.rb +0 -27
  80. data/spec/unit/rule/set_spec.rb +0 -32
  81. data/spec/unit/rule/value_spec.rb +0 -42
  82. data/spec/unit/rule_compiler_spec.rb +0 -86
@@ -1,71 +0,0 @@
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
@@ -1,39 +0,0 @@
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
@@ -1,22 +0,0 @@
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
@@ -1,88 +0,0 @@
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
@@ -1,57 +0,0 @@
1
- require 'dry/validation/rule'
2
-
3
- module Dry
4
- module Validation
5
- class RuleCompiler
6
- attr_reader :predicates
7
-
8
- def initialize(predicates)
9
- @predicates = predicates
10
- end
11
-
12
- def call(ast)
13
- ast.map { |node| visit(node) }
14
- end
15
-
16
- def visit(node)
17
- name, nodes = node
18
- send(:"visit_#{name}", nodes)
19
- end
20
-
21
- def visit_key(node)
22
- name, predicate = node
23
- Rule::Key.new(name, visit(predicate))
24
- end
25
-
26
- def visit_val(node)
27
- name, predicate = node
28
- Rule::Value.new(name, visit(predicate))
29
- end
30
-
31
- def visit_set(node)
32
- name, rules = node
33
- Rule::Set.new(name, call(rules))
34
- end
35
-
36
- def visit_each(node)
37
- name, rule = node
38
- Rule::Each.new(name, visit(rule))
39
- end
40
-
41
- def visit_predicate(node)
42
- name, args = node
43
- predicates[name].curry(*args)
44
- end
45
-
46
- def visit_and(node)
47
- left, right = node
48
- visit(left) & visit(right)
49
- end
50
-
51
- def visit_or(node)
52
- left, right = node
53
- visit(left) | visit(right)
54
- end
55
- end
56
- end
57
- end
@@ -1,15 +0,0 @@
1
- module Dry
2
- module Validation
3
- class Schema
4
- module Definition
5
- def key(name, &block)
6
- Key.new(name, rules).key?(&block)
7
- end
8
- end
9
- end
10
- end
11
- end
12
-
13
- require 'dry/validation/schema/rule'
14
- require 'dry/validation/schema/value'
15
- require 'dry/validation/schema/key'
@@ -1,39 +0,0 @@
1
- require 'dry/validation/rule'
2
-
3
- module Dry
4
- module Validation
5
- class Schema
6
- class Key
7
- attr_reader :name, :rules
8
-
9
- def initialize(name, rules, &block)
10
- @name = name
11
- @rules = rules
12
- end
13
-
14
- private
15
-
16
- def method_missing(meth, *args, &block)
17
- key_rule = [:key, [name, [:predicate, [meth, args]]]]
18
-
19
- if block
20
- val_rule = yield(Value.new(name))
21
-
22
- rules <<
23
- if val_rule.is_a?(Array)
24
- Definition::Rule.new([:and, [key_rule, [:set, [name, val_rule.map(&:to_ary)]]]])
25
- else
26
- Definition::Rule.new([:and, [key_rule, val_rule.to_ary]])
27
- end
28
- else
29
- Definition::Rule.new(key_rule)
30
- end
31
- end
32
-
33
- def respond_to_missing?(meth, _include_private = false)
34
- true
35
- end
36
- end
37
- end
38
- end
39
- end
@@ -1,28 +0,0 @@
1
- module Dry
2
- module Validation
3
- class Schema
4
- module Definition
5
- class Rule
6
- attr_reader :node
7
-
8
- def initialize(node)
9
- @node = node
10
- end
11
-
12
- def to_ary
13
- node
14
- end
15
- alias_method :to_a, :to_ary
16
-
17
- def &(other)
18
- self.class.new([:and, [node, other.to_ary]])
19
- end
20
-
21
- def |(other)
22
- self.class.new([:or, [node, other.to_ary]])
23
- end
24
- end
25
- end
26
- end
27
- end
28
- end
@@ -1,31 +0,0 @@
1
- module Dry
2
- module Validation
3
- class Schema
4
- class Value
5
- include Schema::Definition
6
-
7
- attr_reader :name, :rules
8
-
9
- def initialize(name)
10
- @name = name
11
- @rules = []
12
- end
13
-
14
- def each(&block)
15
- rule = yield(self).to_ary
16
- Definition::Rule.new([:each, [name, rule]])
17
- end
18
-
19
- private
20
-
21
- def method_missing(meth, *args, &block)
22
- Definition::Rule.new([:val, [name, [:predicate, [meth, args]]]])
23
- end
24
-
25
- def respond_to_missing?(meth, _include_private = false)
26
- true
27
- end
28
- end
29
- end
30
- end
31
- end
@@ -1,74 +0,0 @@
1
- require 'dry/validation/schema/definition'
2
- require 'dry/validation/predicates'
3
- require 'dry/validation/error'
4
- require 'dry/validation/rule_compiler'
5
- require 'dry/validation/messages'
6
- require 'dry/validation/error_compiler'
7
-
8
- module Dry
9
- module Validation
10
- class Schema
11
- extend Dry::Configurable
12
- extend Definition
13
-
14
- setting :predicates, Predicates
15
- setting :messages, Messages.default
16
- setting :messages_file
17
- setting :namespace
18
-
19
- def self.predicates
20
- config.predicates
21
- end
22
-
23
- def self.error_compiler
24
- ErrorCompiler.new(messages)
25
- end
26
-
27
- def self.messages
28
- default = config.messages
29
-
30
- if config.messages_file && config.namespace
31
- default.merge(config.messages_file).namespaced(config.namespace)
32
- elsif config.messages_file
33
- default.merge(config.messages_file)
34
- elsif config.namespace
35
- default.namespaced(config.namespace)
36
- else
37
- default
38
- end
39
- end
40
-
41
- def self.rules
42
- @__rules__ ||= []
43
- end
44
-
45
- attr_reader :rules
46
-
47
- attr_reader :error_compiler
48
-
49
- def initialize(error_compiler = self.class.error_compiler)
50
- @rules = RuleCompiler.new(self).(self.class.rules.map(&:to_ary))
51
- @error_compiler = error_compiler
52
- end
53
-
54
- def call(input)
55
- rules.each_with_object(Error::Set.new) do |rule, errors|
56
- result = rule.(input)
57
- errors << Error.new(result) if result.failure?
58
- end
59
- end
60
-
61
- def messages(input)
62
- error_compiler.call(call(input).map(&:to_ary))
63
- end
64
-
65
- def [](name)
66
- if methods.include?(name)
67
- Predicate.new(name, &method(name))
68
- else
69
- self.class.predicates[name]
70
- end
71
- end
72
- end
73
- end
74
- end
data/rakelib/rubocop.rake DELETED
@@ -1,18 +0,0 @@
1
- begin
2
- require 'rubocop/rake_task'
3
-
4
- Rake::Task[:default].enhance [:rubocop]
5
-
6
- RuboCop::RakeTask.new do |task|
7
- task.options << '--display-cop-names'
8
- end
9
-
10
- namespace :rubocop do
11
- desc 'Generate a configuration file acting as a TODO list.'
12
- task :auto_gen_config do
13
- exec 'bundle exec rubocop --auto-gen-config'
14
- end
15
- end
16
-
17
- rescue LoadError
18
- end
@@ -1,4 +0,0 @@
1
- user:
2
- attributes:
3
- email:
4
- filled?: "%{name} can't be blank"
@@ -1,35 +0,0 @@
1
- RSpec.describe Dry::Validation, 'with custom messages' do
2
- subject(:validation) { schema.new }
3
-
4
- describe 'defining schema' do
5
- let(:schema) do
6
- Class.new(Dry::Validation::Schema) do
7
- configure do |config|
8
- config.messages_file = SPEC_ROOT.join('fixtures/errors.yml')
9
- config.namespace = :user
10
- end
11
-
12
- key(:email) { |email| email.filled? }
13
- end
14
- end
15
-
16
- let(:attrs) do
17
- {
18
- email: 'jane@doe.org',
19
- age: 19,
20
- address: { city: 'NYC', street: 'Street 1/2', country: { code: 'US', name: 'USA' } },
21
- phone_numbers: [
22
- '123456', '234567'
23
- ]
24
- }.freeze
25
- end
26
-
27
- describe '#messages' do
28
- it 'returns compiled error messages' do
29
- expect(validation.messages(attrs.merge(email: ''))).to eql([
30
- [:email, ["email can't be blank"]]
31
- ])
32
- end
33
- end
34
- end
35
- end
@@ -1,57 +0,0 @@
1
- RSpec.describe Dry::Validation do
2
- subject(:validation) { schema.new }
3
-
4
- shared_context 'uses custom predicates' do
5
- it 'uses provided custom predicates' do
6
- expect(validation.(email: 'jane@doe')).to be_empty
7
-
8
- expect(validation.(email: nil)).to match_array([
9
- [:error, [:input, [:email, nil, [[:val, [:email, [:predicate, [:filled?, []]]]]]]]]
10
- ])
11
-
12
- expect(validation.(email: 'jane')).to match_array([
13
- [:error, [:input, [:email, 'jane', [[:val, [:email, [:predicate, [:email?, []]]]]]]]]
14
- ])
15
- end
16
- end
17
-
18
- describe 'defining schema with custom predicates container' do
19
- let(:schema) do
20
- Class.new(Dry::Validation::Schema) do
21
- configure do |config|
22
- config.predicates = Test::Predicates
23
- end
24
-
25
- key(:email) { |value| value.filled? & value.email? }
26
- end
27
- end
28
-
29
- before do
30
- module Test
31
- module Predicates
32
- include Dry::Validation::Predicates
33
-
34
- predicate(:email?) do |input|
35
- input.include?('@') # for the lols
36
- end
37
- end
38
- end
39
- end
40
-
41
- include_context 'uses custom predicates'
42
- end
43
-
44
- describe 'defining schema with custom predicate methods' do
45
- let(:schema) do
46
- Class.new(Dry::Validation::Schema) do
47
- key(:email) { |value| value.filled? & value.email? }
48
-
49
- def email?(value)
50
- value.include?('@')
51
- end
52
- end
53
- end
54
-
55
- include_context 'uses custom predicates'
56
- end
57
- end