validate-rb 0.1.0.pre → 1.0.0.alpha.1

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,167 @@
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
+ attr_reader :value
15
+
16
+ def initialize(value, path = Path.new, violations = [])
17
+ @value = value
18
+ @path = path
19
+ @violations = violations
20
+ @keys = Hash.new do |hash, key|
21
+ unless @value.respond_to?(:[]) || @value.respond_to_missing?(:[])
22
+ raise Error::KeyError,
23
+ "#{key.inspect}: value doesn't respond to :[]"
24
+ end
25
+ begin
26
+ hash[key] = child_context(@value[key], KeyPath.new(key))
27
+ rescue => e
28
+ raise Error::KeyError,
29
+ "#{key.inspect}: #{e.message}",
30
+ cause: e
31
+ end
32
+ end
33
+ @attrs = Hash.new do |hash, attr|
34
+ unless @value.respond_to?(attr) || @value.respond_to_missing?(attr)
35
+ raise Error::NameError,
36
+ "#{attr.inspect}: value doesn't respond to #{attr.inspect}"
37
+ end
38
+ hash[attr] = child_context(@value.send(attr), AttrPath.new(attr))
39
+ end
40
+ end
41
+
42
+ def [](key)
43
+ @keys[key]
44
+ end
45
+
46
+ def attr(name)
47
+ @attrs[name]
48
+ end
49
+
50
+ def add_violation(constraint)
51
+ @violations << create_violation(constraint)
52
+ self
53
+ end
54
+
55
+ def clear_violations
56
+ @violations.clear
57
+ self
58
+ end
59
+
60
+ def has_violations?
61
+ !@violations.empty?
62
+ end
63
+
64
+ def to_err
65
+ Error::ConstraintViolationError.new(@violations.freeze)
66
+ end
67
+
68
+ private
69
+
70
+ def create_violation(constraint)
71
+ Constraint::Violation.new(@value, @path, constraint)
72
+ end
73
+
74
+ def child_context(value, path)
75
+ ValidationContext.new(value, @path.child(path), @violations)
76
+ end
77
+
78
+ class Path
79
+ extend Forwardable
80
+
81
+ def_delegators(:@paths, :empty?, :length, :size, :each)
82
+
83
+ include Enumerable
84
+
85
+ def initialize(paths = [])
86
+ @paths = paths
87
+ end
88
+
89
+ def child(path)
90
+ Path.new(@paths.dup << path)
91
+ end
92
+
93
+ def to_s
94
+ return '.' if @paths.empty?
95
+
96
+ @paths.join
97
+ end
98
+
99
+ def at(index)
100
+ raise Error::IndexError if index.negative?
101
+
102
+ return nil if index.zero?
103
+ @paths.fetch(index - 1)
104
+ end
105
+
106
+ def inspect
107
+ return "#<#{self.class.name} <root>>" if @paths.empty?
108
+
109
+ "#<#{self.class.name} #{to_s}>"
110
+ end
111
+ end
112
+
113
+ class KeyPath
114
+ def initialize(key)
115
+ @key = key
116
+ end
117
+
118
+ def to_s
119
+ "[#{@key.inspect}]"
120
+ end
121
+
122
+ def inspect
123
+ "#<#{self.class.name} #{@key.inspect}>"
124
+ end
125
+ end
126
+
127
+ class AttrPath
128
+ def initialize(attr)
129
+ @attr = attr
130
+ end
131
+
132
+ def to_s
133
+ ".#{@attr}"
134
+ end
135
+
136
+ def inspect
137
+ "#<#{self.class.name} #{@attr.inspect}>"
138
+ end
139
+ end
140
+
141
+ class None < ValidationContext
142
+ def initialize
143
+ end
144
+
145
+ def [](_)
146
+ self
147
+ end
148
+
149
+ def attr(_)
150
+ self
151
+ end
152
+
153
+ def add_violation(_)
154
+ self
155
+ end
156
+
157
+ def clear_violations
158
+ self
159
+ end
160
+
161
+ def has_violations?
162
+ false
163
+ end
164
+ end
165
+ end
166
+ end
167
+ 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.0.0.alpha.1'
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.0.0.alpha.1
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: 2020-07-22 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
@@ -54,7 +155,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
54
155
  version: 1.3.1
55
156
  requirements: []
56
157
  rubygems_version: 3.0.3
57
- signing_key:
158
+ signing_key:
58
159
  specification_version: 4
59
160
  summary: Yummy constraint validations for Ruby
60
161
  test_files: []