hashy_validator 0.1.2 → 0.1.5
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 +4 -4
- data/CHANGELOG.md +18 -1
- data/README.md +8 -5
- data/hashy_validator.gemspec +4 -1
- data/lib/hashy_validator/hashy_array_validator.rb +18 -44
- data/lib/hashy_validator/hashy_object_validator.rb +49 -0
- data/lib/hashy_validator/hashy_value_validator.rb +71 -41
- data/lib/hashy_validator/version.rb +7 -7
- data/lib/hashy_validator.rb +7 -4
- metadata +49 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 65bbe593f0a313de084f7bd4a7ab23075422a068a7bea5dc6903f55a7243b09e
|
4
|
+
data.tar.gz: b86b243a6e39958a38b8c51bd1d2ce2644e0581a6929956faf74d8c83cab2a34
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: af7fd7055e2bf2462ae5784668f7c42d502abfd293e72c4ded6d17d5694182b92342ffb3f22a45063534d81ad53b15c0652092e66490abe46a8428a8acfba166
|
7
|
+
data.tar.gz: 74a8d08f9409cd379a2be4af5a82118323ed36dbfb097cddac2fc9b8860348efb37d401c18da6040dbd04bc70adde05483dd72d600fc716d1c58af1d5c7fbd2b
|
data/CHANGELOG.md
CHANGED
@@ -5,9 +5,26 @@ All notable changes to this project will be documented in this file.
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
7
7
|
|
8
|
-
### [0.1.
|
8
|
+
### [0.1.5](https://www.github.com/flecto-io/hashy-validator/compare/v0.1.4...v0.1.5) (2024-09-05)
|
9
9
|
|
10
10
|
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* add hashy_object ([35e1ccf](https://www.github.com/flecto-io/hashy-validator/commit/35e1ccf1cff9d95df069961c8ac1b3fec6d23645))
|
14
|
+
|
15
|
+
### [0.1.4](https://www.github.com/flecto-io/hashy-validator/compare/v0.1.3...v0.1.4) (2024-08-24)
|
16
|
+
|
17
|
+
|
18
|
+
### Bug Fixes
|
19
|
+
|
20
|
+
* Accepts dependencies version of activerecord lower than 8 ([eab4692](https://www.github.com/flecto-io/hashy-validator/commit/eab46925196ada0db0c408037cdf90b9ce4dacca))
|
21
|
+
|
22
|
+
### [0.1.2](https://www.github.com/flecto-io/hashy-validator/compare/v0.1.2...v0.1.3) (2024-01-13)
|
23
|
+
|
24
|
+
### Changes
|
25
|
+
|
26
|
+
* Production ready
|
27
|
+
|
11
28
|
### Bug Fixes
|
12
29
|
|
13
30
|
* changelog ([6b8c151](https://www.github.com/flecto-io/hashy-validator/commit/6b8c15105bd085dafa6358f27f780ad4826a42e7))
|
data/README.md
CHANGED
@@ -8,7 +8,6 @@
|
|
8
8
|
HashyValidator is a custom Ruby on Rails validator designed to validate an array of hashes based on [HashValidator](https://github.com/jamesbrooks/hash_validator) criteria but also the following new criteria:
|
9
9
|
- `unique`: A value within each hash that has to be unique across the whole array
|
10
10
|
|
11
|
-
|
12
11
|
## Installation
|
13
12
|
|
14
13
|
Add this line to your application's Gemfile:
|
@@ -17,8 +16,6 @@ Add this line to your application's Gemfile:
|
|
17
16
|
gem 'hashy_validator'
|
18
17
|
```
|
19
18
|
|
20
|
-
<b>THIS GEM IS NOT READY FOR PRODUCTION YET!</b>
|
21
|
-
|
22
19
|
And then execute:
|
23
20
|
|
24
21
|
```bash
|
@@ -42,14 +39,20 @@ To leverage HashyValidator in your Rails model, follow these steps:
|
|
42
39
|
```ruby
|
43
40
|
class YourModel < ApplicationRecord
|
44
41
|
validates :pricing, hashy_array: {
|
45
|
-
minutes: HashValidator.multiple('
|
46
|
-
price_cents: HashValidator.multiple('
|
42
|
+
minutes: HashValidator.multiple('integer', 'unique'),
|
43
|
+
price_cents: HashValidator.multiple('integer')
|
47
44
|
}
|
48
45
|
end
|
49
46
|
```
|
50
47
|
|
51
48
|
Customize each entry validators according to [HashValidator](https://github.com/jamesbrooks/hash_validator) criteria
|
52
49
|
|
50
|
+
# Testing
|
51
|
+
|
52
|
+
```bash
|
53
|
+
rake test
|
54
|
+
```
|
55
|
+
|
53
56
|
## Contributing
|
54
57
|
|
55
58
|
Bug reports and pull requests are welcome. This project is intended to be a safe, welcoming space for collaboration. To ease up contribution we provide a VSCode _devcontainer_ to run the project in a container.
|
data/hashy_validator.gemspec
CHANGED
@@ -14,12 +14,15 @@ Gem::Specification.new do |spec|
|
|
14
14
|
spec.require_paths = ["lib"]
|
15
15
|
spec.files = Dir["{lib}/**/*"] + ["README.md", "CHANGELOG.md", "hashy_validator.gemspec"]
|
16
16
|
|
17
|
-
spec.add_dependency "activerecord", ">= 6.0.0", "
|
17
|
+
spec.add_dependency "activerecord", ">= 6.0.0", "< 8.0.0"
|
18
18
|
spec.add_dependency "hash_validator", "~> 1.1"
|
19
19
|
|
20
20
|
spec.add_development_dependency 'sqlite3', '~> 1.4'
|
21
21
|
spec.add_development_dependency 'rake', '~> 13.1.0'
|
22
22
|
spec.add_development_dependency 'simplecov', '0.17.1'
|
23
|
+
spec.add_development_dependency 'rubocop', '~> 1.59'
|
24
|
+
spec.add_development_dependency 'rubocop-shopify', '~> 2.14'
|
25
|
+
spec.add_development_dependency 'pry', '~> 0.14.1'
|
23
26
|
|
24
27
|
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new("3")
|
25
28
|
spec.add_development_dependency 'minitest', '>= 5.15.0', '< 5.16'
|
@@ -1,63 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class HashyArrayValidator < ActiveModel::EachValidator
|
2
4
|
def validate_each(record, attribute, value)
|
3
|
-
instance_value = HashyValueValidator.new(value)
|
4
|
-
|
5
|
+
instance_value = HashyValueValidator.new(value, options)
|
6
|
+
|
7
|
+
unless instance_value.valid?
|
5
8
|
record.errors.add(attribute, instance_value.reason)
|
6
9
|
return false
|
7
10
|
end
|
8
11
|
|
9
|
-
|
12
|
+
unless instance_value.value.is_a?(Array)
|
13
|
+
record.errors.add(attribute, :not_an_array)
|
14
|
+
return false
|
15
|
+
end
|
10
16
|
|
11
|
-
|
12
|
-
unique_attrs = {}
|
13
|
-
boolean_attrs = []
|
14
|
-
validations =
|
15
|
-
# force validator keys to be strings
|
16
|
-
options.stringify_keys.map do |val_attr,val|
|
17
|
-
if (val.is_a?(HashValidator::Validations::Multiple) && val.validations.include?('boolean')) || (val.is_a?(String) && val == 'boolean')
|
18
|
-
boolean_attrs << val_attr
|
19
|
-
[val_attr, val]
|
20
|
-
elsif val.is_a?(HashValidator::Validations::Multiple) && val.validations.include?('unique')
|
21
|
-
# if unique key present, then remove that entry (since its not from HashValidator standard) and keep its history
|
22
|
-
unique_attrs[val_attr] ||= []
|
23
|
-
# we have to make a new object to remove the unique entry,
|
24
|
-
# because deleting it directly from the original object (val) would result into deleting the verification forever
|
25
|
-
new_val = HashValidator::Validations::Multiple.new(val.validations.reject{|v| v == 'unique'})
|
26
|
-
# return the value
|
27
|
-
val.validations.blank? ? nil : [val_attr, new_val]
|
28
|
-
elsif val.is_a?(String) && val == 'unique'
|
29
|
-
# same as above but substring
|
30
|
-
unique_attrs[val_attr] ||= []
|
31
|
-
nil
|
32
|
-
else
|
33
|
-
[val_attr, val]
|
34
|
-
end
|
35
|
-
end.compact.to_h
|
17
|
+
value = instance_value.value
|
36
18
|
|
37
19
|
# force all array entries to have string keys
|
38
20
|
# discard keys that do not have validators
|
39
|
-
value = value.map{ |e| e.stringify_keys.slice(*validations.keys) }
|
21
|
+
value = value.map { |e| e.stringify_keys.slice(*instance_value.validations.keys) }
|
40
22
|
|
41
23
|
# we validate each object in the array
|
42
24
|
value.each do |t|
|
43
25
|
# if boolean found as any of the validations we force value to boolean - if present
|
44
|
-
boolean_attrs.each do |boolean_attr|
|
45
|
-
t[boolean_attr] = get_boolean_value(t[boolean_attr]) if t.key?(boolean_attr)
|
26
|
+
instance_value.boolean_attrs.each do |boolean_attr|
|
27
|
+
t[boolean_attr] = HashyValueValidator.get_boolean_value(t[boolean_attr]) if t.key?(boolean_attr)
|
46
28
|
end
|
47
29
|
|
48
30
|
# keep track of unique values and add error if needed
|
49
|
-
unique_attrs.
|
50
|
-
if unique_attrs[unique_attr].include?(t[unique_attr])
|
31
|
+
instance_value.unique_attrs.each_key do |unique_attr|
|
32
|
+
if instance_value.unique_attrs[unique_attr].include?(t[unique_attr])
|
51
33
|
record.errors.add(attribute, "'#{unique_attr}' not unique")
|
52
34
|
else
|
53
|
-
unique_attrs[unique_attr] << t[unique_attr]
|
35
|
+
instance_value.unique_attrs[unique_attr] << t[unique_attr]
|
54
36
|
end
|
55
37
|
end
|
56
38
|
|
57
39
|
# use default hash validator
|
58
|
-
validator = HashValidator.validate(t, validations)
|
40
|
+
validator = HashValidator.validate(t, instance_value.validations)
|
59
41
|
unless validator.valid?
|
60
|
-
validator.errors.each { |k,v| record.errors.add(attribute, "'#{k
|
42
|
+
validator.errors.each { |k, v| record.errors.add(attribute, "'#{k}' #{v}") }
|
61
43
|
end
|
62
44
|
end
|
63
45
|
|
@@ -65,12 +47,4 @@ class HashyArrayValidator < ActiveModel::EachValidator
|
|
65
47
|
# we use send write param so we also support attr_accessor attributes
|
66
48
|
record.send("#{attribute}=", value)
|
67
49
|
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def get_boolean_value(value)
|
72
|
-
return true if value == true || value == 'true'
|
73
|
-
return false if value == false || value == 'false'
|
74
|
-
nil
|
75
|
-
end
|
76
50
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class HashyObjectValidator < ActiveModel::EachValidator
|
4
|
+
def validate_each(record, attribute, value)
|
5
|
+
instance_value = HashyValueValidator.new(value, options)
|
6
|
+
|
7
|
+
# Do not validate empty hash
|
8
|
+
return if instance_value.value.keys.empty?
|
9
|
+
|
10
|
+
unless instance_value.valid?
|
11
|
+
record.errors.add(attribute, instance_value.reason)
|
12
|
+
return false
|
13
|
+
end
|
14
|
+
|
15
|
+
if instance_value.value.is_a?(Array)
|
16
|
+
record.errors.add(attribute, :not_an_object)
|
17
|
+
return false
|
18
|
+
end
|
19
|
+
|
20
|
+
# force all array entries to have string keys
|
21
|
+
# discard keys that do not have validators
|
22
|
+
value = instance_value.value.stringify_keys.slice(*instance_value.validations.keys)
|
23
|
+
|
24
|
+
# we validate each object in the array
|
25
|
+
# if boolean found as any of the validations we force value to boolean - if present
|
26
|
+
instance_value.boolean_attrs.each do |boolean_attr|
|
27
|
+
value[boolean_attr] = HashyValueValidator.get_boolean_value(t[boolean_attr]) if value.key?(boolean_attr)
|
28
|
+
end
|
29
|
+
|
30
|
+
# keep track of unique values and add error if needed
|
31
|
+
instance_value.unique_attrs.each_key do |unique_attr|
|
32
|
+
if instance_value.unique_attrs[unique_attr].include?(value[unique_attr])
|
33
|
+
record.errors.add(attribute, "'#{unique_attr}' not unique")
|
34
|
+
else
|
35
|
+
instance_value.unique_attrs[unique_attr] << t[unique_attr]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# use default hash validator
|
40
|
+
validator = HashValidator.validate(value, instance_value.validations)
|
41
|
+
unless validator.valid?
|
42
|
+
validator.errors.each { |k, v| record.errors.add(attribute, "'#{k}' #{v}") }
|
43
|
+
end
|
44
|
+
|
45
|
+
# update the value even if errors found
|
46
|
+
# we use send write param so we also support attr_accessor attributes
|
47
|
+
record.send("#{attribute}=", value)
|
48
|
+
end
|
49
|
+
end
|
@@ -1,42 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
class HashyValueValidator
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
4
|
+
def initialize(value, options = {})
|
5
|
+
@value = value.blank? ? [] : value
|
6
|
+
@valid = true
|
7
|
+
@reason = nil
|
8
|
+
@validations = {}
|
9
|
+
@unique_attrs = {}
|
10
|
+
@boolean_attrs = []
|
11
|
+
|
12
|
+
check_parse_value
|
13
|
+
define_validations(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def valid?
|
17
|
+
@valid
|
18
|
+
end
|
19
|
+
|
20
|
+
attr_reader :value, :reason, :validations, :unique_attrs, :boolean_attrs
|
21
|
+
|
22
|
+
def self.get_boolean_value(value)
|
23
|
+
return true if [true, "true"].include?(value)
|
24
|
+
return false if [false, "false"].include?(value)
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def check_parse_value
|
32
|
+
@value = JSON.parse(@value) if @value.is_a?(String)
|
33
|
+
rescue JSON::ParserError
|
34
|
+
@valid = false
|
35
|
+
@reason = :invalid
|
36
|
+
end
|
37
|
+
|
38
|
+
def define_validations(options)
|
39
|
+
# look for boolean and unique validator entries
|
40
|
+
unique_attrs = {}
|
41
|
+
boolean_attrs = []
|
42
|
+
validations =
|
43
|
+
# force validator keys to be strings
|
44
|
+
options.stringify_keys.map do |val_attr, val|
|
45
|
+
is_multiple = val.is_a?(HashValidator::Validations::Multiple)
|
46
|
+
if (is_multiple && val.validations.include?("boolean")) || (val.is_a?(String) && val == "boolean")
|
47
|
+
boolean_attrs << val_attr
|
48
|
+
[val_attr, val]
|
49
|
+
elsif is_multiple && val.validations.include?("unique")
|
50
|
+
# if unique key present, then remove that entry
|
51
|
+
# (since its not from HashValidator standard) and keep its history
|
52
|
+
unique_attrs[val_attr] ||= []
|
53
|
+
# we have to make a new object to remove the unique entry,
|
54
|
+
# because deleting it directly from the original object
|
55
|
+
# (val) would result into deleting the verification forever
|
56
|
+
new_val = HashValidator::Validations::Multiple.new(val.validations.reject { |v| v == "unique" })
|
57
|
+
# return the value
|
58
|
+
val.validations.blank? ? nil : [val_attr, new_val]
|
59
|
+
elsif val.is_a?(String) && val == "unique"
|
60
|
+
# same as above but substring
|
61
|
+
unique_attrs[val_attr] ||= []
|
62
|
+
nil
|
63
|
+
else
|
64
|
+
[val_attr, val]
|
65
|
+
end
|
66
|
+
end.compact.to_h
|
67
|
+
|
68
|
+
@validations = validations
|
69
|
+
@unique_attrs = unique_attrs
|
70
|
+
@boolean_attrs = boolean_attrs
|
71
|
+
end
|
72
|
+
end
|
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module HashyValidator
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
4
|
+
module Version
|
5
|
+
MAJOR = 0
|
6
|
+
MINOR = 1
|
7
|
+
PATCH = 5
|
8
|
+
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
|
9
|
+
end
|
10
|
+
end
|
data/lib/hashy_validator.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_record"
|
4
|
+
require "hash_validator"
|
5
|
+
require_relative "hashy_validator/hashy_value_validator"
|
6
|
+
require_relative "hashy_validator/hashy_array_validator"
|
7
|
+
require_relative "hashy_validator/hashy_object_validator"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hashy_validator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flecto Team
|
@@ -17,9 +17,9 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 6.0.0
|
20
|
-
- - "
|
20
|
+
- - "<"
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
22
|
+
version: 8.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -27,9 +27,9 @@ dependencies:
|
|
27
27
|
- - ">="
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 6.0.0
|
30
|
-
- - "
|
30
|
+
- - "<"
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: 8.0.0
|
33
33
|
- !ruby/object:Gem::Dependency
|
34
34
|
name: hash_validator
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,6 +86,48 @@ dependencies:
|
|
86
86
|
- - '='
|
87
87
|
- !ruby/object:Gem::Version
|
88
88
|
version: 0.17.1
|
89
|
+
- !ruby/object:Gem::Dependency
|
90
|
+
name: rubocop
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - "~>"
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '1.59'
|
96
|
+
type: :development
|
97
|
+
prerelease: false
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
99
|
+
requirements:
|
100
|
+
- - "~>"
|
101
|
+
- !ruby/object:Gem::Version
|
102
|
+
version: '1.59'
|
103
|
+
- !ruby/object:Gem::Dependency
|
104
|
+
name: rubocop-shopify
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - "~>"
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.14'
|
110
|
+
type: :development
|
111
|
+
prerelease: false
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
113
|
+
requirements:
|
114
|
+
- - "~>"
|
115
|
+
- !ruby/object:Gem::Version
|
116
|
+
version: '2.14'
|
117
|
+
- !ruby/object:Gem::Dependency
|
118
|
+
name: pry
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - "~>"
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 0.14.1
|
124
|
+
type: :development
|
125
|
+
prerelease: false
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - "~>"
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: 0.14.1
|
89
131
|
- !ruby/object:Gem::Dependency
|
90
132
|
name: minitest
|
91
133
|
requirement: !ruby/object:Gem::Requirement
|
@@ -114,6 +156,7 @@ files:
|
|
114
156
|
- hashy_validator.gemspec
|
115
157
|
- lib/hashy_validator.rb
|
116
158
|
- lib/hashy_validator/hashy_array_validator.rb
|
159
|
+
- lib/hashy_validator/hashy_object_validator.rb
|
117
160
|
- lib/hashy_validator/hashy_value_validator.rb
|
118
161
|
- lib/hashy_validator/version.rb
|
119
162
|
homepage:
|
@@ -134,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
177
|
- !ruby/object:Gem::Version
|
135
178
|
version: '0'
|
136
179
|
requirements: []
|
137
|
-
rubygems_version: 3.
|
180
|
+
rubygems_version: 3.4.19
|
138
181
|
signing_key:
|
139
182
|
specification_version: 4
|
140
183
|
summary: Custom Active Model validator for validating arrays of hashes
|