validate-rb 0.1.0.pre → 1.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.
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validate
4
+ module Constraints
5
+ class ValidationContext
6
+ def self.none
7
+ @none ||= None.new
8
+ end
9
+
10
+ def self.root(value, violations = [])
11
+ new(value, Path.new, violations)
12
+ end
13
+
14
+ def self.key(key, violations = [])
15
+ new(key, Path.new([KeyPath.new(key)]), violations)
16
+ end
17
+
18
+ attr_reader :value, :violations
19
+ protected :violations
20
+
21
+ def initialize(value, path = Path.new, violations = [])
22
+ @value = value
23
+ @path = path
24
+ @violations = violations
25
+ @keys = Hash.new do |hash, key|
26
+ unless @value.respond_to?(:[]) || @value.respond_to_missing?(:[])
27
+ raise Error::KeyError,
28
+ "#{key.inspect}: value doesn't respond to :[]"
29
+ end
30
+ begin
31
+ hash[key] = child_context(@value[key], KeyPath.new(key))
32
+ rescue => e
33
+ raise Error::KeyError,
34
+ "#{key.inspect}: #{e.message}",
35
+ cause: e
36
+ end
37
+ end
38
+ @attrs = Hash.new do |hash, attr|
39
+ unless @value.respond_to?(attr) || @value.respond_to_missing?(attr)
40
+ raise Error::NameError,
41
+ "#{attr.inspect}: value doesn't respond to #{attr.inspect}"
42
+ end
43
+ hash[attr] = child_context(@value.send(attr), AttrPath.new(attr))
44
+ end
45
+ end
46
+
47
+ def [](key)
48
+ @keys[key]
49
+ end
50
+
51
+ def attr(name)
52
+ @attrs[name]
53
+ end
54
+
55
+ def add_violation(constraint)
56
+ @violations << create_violation(constraint)
57
+ self
58
+ end
59
+
60
+ def clear_violations
61
+ @violations.clear
62
+ self
63
+ end
64
+
65
+ def has_violations?
66
+ !@violations.empty?
67
+ end
68
+
69
+ def to_err(backtrace = [])
70
+ err = Error::ConstraintViolationError.new(@violations.freeze)
71
+ err.set_backtrace(backtrace)
72
+ err
73
+ end
74
+
75
+ def merge(other)
76
+ other.violations.each do |violation|
77
+ @violations << Constraint::Violation.new(violation.value, @path.child(violation.path), violation.constraint)
78
+ end
79
+ self
80
+ end
81
+
82
+ private
83
+
84
+ def create_violation(constraint)
85
+ Constraint::Violation.new(@value, @path, constraint)
86
+ end
87
+
88
+ def child_context(value, path)
89
+ ValidationContext.new(value, @path.child(path), @violations)
90
+ end
91
+
92
+ class Path
93
+ extend Forwardable
94
+
95
+ def_delegators(:@paths, :empty?, :length, :size, :each)
96
+
97
+ include Enumerable
98
+
99
+ def initialize(paths = [])
100
+ @paths = paths
101
+ end
102
+
103
+ def child(path)
104
+ case path
105
+ when KeyPath, AttrPath
106
+ Path.new(@paths.dup << path)
107
+ when Path
108
+ Path.new(@paths.dup << path.to_a)
109
+ else
110
+ raise ArgumentError, 'invalid path'
111
+ end
112
+ end
113
+
114
+ def to_s
115
+ return '.' if @paths.empty?
116
+
117
+ @paths.join
118
+ end
119
+
120
+ def at(index)
121
+ raise Error::IndexError if index.negative?
122
+
123
+ return nil if index.zero?
124
+ @paths.fetch(index - 1)
125
+ end
126
+
127
+ def inspect
128
+ return "#<#{self.class.name} <root>>" if @paths.empty?
129
+
130
+ "#<#{self.class.name} #{to_s}>"
131
+ end
132
+ end
133
+
134
+ class KeyPath
135
+ def initialize(key)
136
+ @key = key
137
+ end
138
+
139
+ def to_s
140
+ "[#{@key.inspect}]"
141
+ end
142
+
143
+ def inspect
144
+ "#<#{self.class.name} #{@key.inspect}>"
145
+ end
146
+ end
147
+
148
+ class AttrPath
149
+ def initialize(attr)
150
+ @attr = attr
151
+ end
152
+
153
+ def to_s
154
+ ".#{@attr}"
155
+ end
156
+
157
+ def inspect
158
+ "#<#{self.class.name} #{@attr.inspect}>"
159
+ end
160
+ end
161
+
162
+ class None < ValidationContext
163
+ def initialize
164
+ end
165
+
166
+ def [](_)
167
+ self
168
+ end
169
+
170
+ def attr(_)
171
+ self
172
+ end
173
+
174
+ def add_violation(_)
175
+ self
176
+ end
177
+
178
+ def clear_violations
179
+ self
180
+ end
181
+
182
+ def has_violations?
183
+ false
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validate
4
+ module Error
5
+ class StandardError < ::StandardError
6
+ include Error
7
+ end
8
+
9
+ class ArgumentError < ::ArgumentError
10
+ include Error
11
+ end
12
+
13
+ class KeyError < ::KeyError
14
+ include Error
15
+ end
16
+
17
+ class IndexError < ::IndexError
18
+ include Error
19
+ end
20
+
21
+ class ValidationRuleError < StandardError
22
+ end
23
+
24
+ class ConstraintViolationError < StandardError
25
+ attr_reader :violations
26
+
27
+ def initialize(violations)
28
+ @violations = violations
29
+ super()
30
+ end
31
+
32
+ def message
33
+ @violations.group_by(&:path)
34
+ .transform_values { |violations| violations.map(&:message) }
35
+ .map { |path, messages| "#{path}: #{messages.join(', ')}" }
36
+ .join("\n")
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validate
4
+ module Helpers
5
+ module_function
6
+
7
+ def camelize(name)
8
+ name.to_s.split('_').collect(&:capitalize).join
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,48 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Validate
5
+ class Scope
6
+ def self.current
7
+ @current ||= Scope.new
8
+ end
9
+
10
+ def initialize
11
+ @constraints = {}
12
+ @validators = {}
13
+ end
14
+
15
+ def register_validator(name, validator)
16
+ if @validators.include?(name)
17
+ raise Error::ArgumentError,
18
+ "duplicate validator :#{name}"
19
+ end
20
+
21
+ @validators[name] = validator
22
+ end
23
+
24
+ def validator?(name)
25
+ @validators.include?(name)
26
+ end
27
+
28
+ def validator(name)
29
+ validator_name.assert(name,
30
+ message: "invalid validator #{name.inspect}",
31
+ error_class: KeyError)
32
+
33
+ @validators.fetch(name) { name.validator }
34
+ end
35
+
36
+ private
37
+
38
+ def validator_name
39
+ @validator_name ||= Assertions.create(@validators) do |validators|
40
+ not_nil(message: 'name must not be nil')
41
+ (one_of(values: validators,
42
+ message: '%{value.inspect} must be an existing validator name') |
43
+ respond_to(:validator,
44
+ message: '%{value.inspect} must respond to :validator'))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validate
4
+ module Validators
5
+ extend DSL
6
+ end
7
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Validate
4
+ module Validators
5
+ module DSL
6
+ include Arguments
7
+
8
+ arg(:name) { is_a(Module) | (is_a(Symbol) & not_blank) }
9
+ def define(name, &body)
10
+ Scope.current.register_validator(name, create(&body))
11
+ end
12
+
13
+ def create(&block)
14
+ Validator.new(&block)
15
+ end
16
+
17
+ def none
18
+ @none ||= Validator::None.new
19
+ end
20
+ end
21
+
22
+ class Validator
23
+ def initialize(&block)
24
+ @constraints = AST::DefinitionContext.create(&block)
25
+ end
26
+
27
+ def validate(ctx)
28
+ @constraints.evaluate(ctx)
29
+ end
30
+
31
+ private
32
+
33
+ class None < Validator
34
+ NO_VIOLATIONS = [].freeze
35
+
36
+ def initialize; end
37
+
38
+ def validate(*args)
39
+ NO_VIOLATIONS
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Validate
4
- VERSION = '0.1.0.pre'.freeze
4
+ VERSION = '1.1.0'
5
5
  end
metadata CHANGED
@@ -1,15 +1,113 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: validate-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bulat Shakirzyanov
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-13 00:00:00.000000000 Z
12
- dependencies: []
11
+ date: 2021-05-21 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: aruba
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2.1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: cucumber
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '4.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '4.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: sorbet
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
13
111
  description: Simple, powerful, and constraint-based validation
14
112
  email:
15
113
  - bulat.shakirzyanov@gusto.com
@@ -17,28 +115,31 @@ executables: []
17
115
  extensions: []
18
116
  extra_rdoc_files: []
19
117
  files:
20
- - ".gitignore"
21
- - ".rspec"
22
- - ".ruby-version"
23
- - ".travis.yml"
24
- - Gemfile
118
+ - LICENSE
25
119
  - README.md
26
- - Rakefile
27
- - bin/console
28
- - bin/setup
29
- - lib/validate-rb.rb
30
120
  - lib/validate.rb
121
+ - lib/validate/arguments.rb
122
+ - lib/validate/assertions.rb
123
+ - lib/validate/ast.rb
124
+ - lib/validate/compare.rb
125
+ - lib/validate/constraint.rb
126
+ - lib/validate/constraints.rb
127
+ - lib/validate/constraints/validation_context.rb
128
+ - lib/validate/errors.rb
129
+ - lib/validate/helpers.rb
130
+ - lib/validate/scope.rb
131
+ - lib/validate/validators.rb
132
+ - lib/validate/validators/dsl.rb
31
133
  - lib/validate/version.rb
32
- - validate-rb.gemspec
33
- homepage: https://github.com/Gusto/validaterb
134
+ homepage: https://github.com/Gusto/validate-rb
34
135
  licenses:
35
136
  - MIT
36
137
  metadata:
37
- homepage_uri: https://github.com/Gusto/validaterb
38
- changelog_uri: https://github.com/Gusto/validaterb/releases
39
- source_code_uri: https://github.com/Gusto/validaterb
40
- bug_tracker_uri: https://github.com/Gusto/validaterb/issues
41
- post_install_message:
138
+ homepage_uri: https://github.com/Gusto/validate-rb
139
+ changelog_uri: https://github.com/Gusto/validate-rb/releases
140
+ source_code_uri: https://github.com/Gusto/validate-rb
141
+ bug_tracker_uri: https://github.com/Gusto/validate-rb/issues
142
+ post_install_message:
42
143
  rdoc_options: []
43
144
  require_paths:
44
145
  - lib
@@ -49,12 +150,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
49
150
  version: 2.3.0
50
151
  required_rubygems_version: !ruby/object:Gem::Requirement
51
152
  requirements:
52
- - - ">"
153
+ - - ">="
53
154
  - !ruby/object:Gem::Version
54
- version: 1.3.1
155
+ version: '0'
55
156
  requirements: []
56
- rubygems_version: 3.0.3
57
- signing_key:
157
+ rubygems_version: 3.0.3.1
158
+ signing_key:
58
159
  specification_version: 4
59
160
  summary: Yummy constraint validations for Ruby
60
161
  test_files: []