mini_defender 0.4.1 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 56cc42b9b0ce4c44e30dc4bcd8dfcc31bcccc012388f52a076320ee926feec11
4
- data.tar.gz: 7e5aaf99490b42b14956fda14843065d3cd906b6568da7ff4df90059f965b75b
3
+ metadata.gz: 36f0ca2e9d174da94e991c9eed6300ae0f0315ab57b3d9372921726261318c67
4
+ data.tar.gz: a4f29b8f5558008c1f5b992722a8ab9dd07dbc93d51d7ec288bd3be5e9d1d91b
5
5
  SHA512:
6
- metadata.gz: f3a25a2655ed8889345d101bca863cd72a605825ae2dd99a0b6527f73c33572cf5771e8aba28fdb81b1311ac3cc22845a868c5edeb3ddcb1f9ee2572e5e91010
7
- data.tar.gz: 9ee05f959b9289c6ea0d9b1f88278bc9617df76cef267a362163fef02f6056dc8443ee3700c86308b54e6608edfd3c34ad7cfabd01d6b01e86d37772d028abe5
6
+ metadata.gz: 885a3ff5f6a337246c9b47e6ac21b0e9dc21067a11c49d7af2d9135d66f6ab878a620b9f73cafa5b1316d5edc2d74f709f924e37309bbe74bb596500abe8348b
7
+ data.tar.gz: 3d6a38389f37816a4b6ebbadd7700b6c628e7011c42dfbe0a07ee907f0d354b9b7aaed18179ad97c8350ea01948966ee47182b40e5630370761a5487c58fbe97
data/Gemfile CHANGED
@@ -1,10 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- source "https://rubygems.org"
3
+ source 'https://rubygems.org'
4
4
 
5
- # Specify your gem's dependencies in mini_defender.gemspec
6
5
  gemspec
7
6
 
8
- gem "rake", "~> 13.0"
9
-
10
- gem "minitest", "~> 5.0"
7
+ gem 'rake', '~> 13.0'
8
+ gem 'minitest', '~> 5.0'
@@ -61,6 +61,12 @@ class MiniDefender::Rule
61
61
  value
62
62
  end
63
63
 
64
+ # This method is used to change the value
65
+ # regardless if coercion is required
66
+ def force_coerce?
67
+ false
68
+ end
69
+
64
70
  # @param [Object] attribute
65
71
  # @param [Object] value
66
72
  # @param [MiniDefender::Validator] validator
@@ -5,6 +5,22 @@ class MiniDefender::Rules::Array < MiniDefender::Rule
5
5
  'array'
6
6
  end
7
7
 
8
+ def initialize(data_mode = 'none')
9
+ @data_mode = data_mode
10
+ end
11
+
12
+ def self.make(args)
13
+ new(args[0] || 'none')
14
+ end
15
+
16
+ def coerce(value)
17
+ @data_mode == 'all' ? value : []
18
+ end
19
+
20
+ def force_coerce?
21
+ true
22
+ end
23
+
8
24
  def passes?(attribute, value, validator)
9
25
  value.is_a?(Array)
10
26
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'countries'
3
+ require 'money'
4
4
 
5
5
  class MiniDefender::Rules::Currency < MiniDefender::Rule
6
6
  CURRENCIES = Money::Currency.map(&:iso_code).map(&:upcase).uniq
@@ -5,11 +5,60 @@ class MiniDefender::Rules::Hash < MiniDefender::Rule
5
5
  'hash'
6
6
  end
7
7
 
8
+ def initialize(data_mode = 'none', key_type = 'any', value_type = 'any')
9
+ @data_mode = data_mode
10
+ @key_type = key_type
11
+ @value_type = value_type
12
+ end
13
+
14
+ def self.make(args)
15
+ new(
16
+ args[0] || 'none',
17
+ args[1] || 'any',
18
+ args[2] || 'any',
19
+ )
20
+ end
21
+
22
+ def coerce(value)
23
+ @data_mode == 'all' ? value : {}
24
+ end
25
+
26
+ def force_coerce?
27
+ true
28
+ end
29
+
8
30
  def passes?(attribute, value, validator)
9
- value.is_a?(Hash)
31
+ passes = value.is_a?(Hash)
32
+
33
+ if @key_type == 'string'
34
+ passes &= value.all? { |k, _| k.is_a?(String) }
35
+ end
36
+
37
+ case @value_type
38
+ when 'string'
39
+ passes &= value.all? { |_, v| v.is_a?(String) }
40
+ when 'integer'
41
+ passes &= value.all? { |_, v| v.is_a?(Integer) }
42
+ when 'float'
43
+ passes &= value.all? { |_, v| v.is_a?(Float) }
44
+ else
45
+ # None
46
+ end
47
+
48
+ passes
10
49
  end
11
50
 
12
51
  def message(attribute, value, validator)
13
- "The field must be an object."
52
+ additional = ''
53
+
54
+ if @key_type != 'any'
55
+ additional += ", key must be #{@key_type}"
56
+ end
57
+
58
+ if @value_type != 'any'
59
+ additional += ", value must be #{@value_type}"
60
+ end
61
+
62
+ "The field must be an object#{additional}."
14
63
  end
15
64
  end
@@ -6,14 +6,36 @@ module MiniDefender
6
6
  # @param [Hash] flat_data
7
7
  # @return [Hash]
8
8
  def expand(rules, flat_data)
9
+ result = {}
10
+
11
+ rules.each do |rule_key, rule_set|
12
+ unless rule_key.include?('*')
13
+ result[rule_key] = rule_set
14
+ next
15
+ end
16
+
17
+ k_pattern = Regexp.compile('\A' + rule_key.gsub(/\*/, '\d+') + '\Z')
18
+ k_pattern_result = {}
19
+
20
+ flat_data.each do |value_key, _|
21
+ next unless k_pattern.match?(value_key)
22
+ k_pattern_result[value_key] = rule_set
23
+ end
24
+
25
+ if k_pattern_result.empty?
26
+ k_pattern_result[k_pattern.source.gsub(/\\[AZ]/, '').gsub('\d+', '0')] = rule_set
27
+ end
28
+
29
+ result.merge!(k_pattern_result)
30
+ end
31
+
32
+ result
33
+ end
34
+
35
+ def array_patterns(rules)
9
36
  rules
10
- .map { |k, v| [Regexp.compile('\A' + k.gsub(/\*/, '\d+') + '\Z'), v] }.to_h
11
- .map { |p, set|
12
- data_rules = flat_data.filter { |k, _| p.match? k }.map { |k, _| [k, set] }
13
- data_rules.length > 0 ? data_rules : [[p.source.gsub(/\\[AZ]/, '').gsub('\d+', '0'), set]]
14
- }
15
- .flatten(1)
16
- .to_h
37
+ .filter { |key, _| key.include?('*') }
38
+ .map { |key, _| Regexp.compile('\A' + key.gsub(/\*/, '\d+') + '\Z') }
17
39
  end
18
40
  end
19
41
  end
@@ -12,10 +12,13 @@ module MiniDefender
12
12
  @coerced = {}
13
13
  @expander = RulesExpander.new
14
14
  @factory = RulesFactory.new
15
+ @array_patterns = @expander.array_patterns(rules)
15
16
  end
16
17
 
17
18
  def validate
18
- return unless @errors.nil?
19
+ unless @errors.nil?
20
+ return
21
+ end
19
22
 
20
23
  @errors = {}
21
24
 
@@ -23,6 +26,7 @@ module MiniDefender
23
26
  [k, @factory.init_set(set)]
24
27
  end
25
28
 
29
+ # Set default values for missing data key compared to rules
26
30
  data_rules.each do |k, set|
27
31
  if !@data.key?(k) || @data[k].blank?
28
32
  set.filter{ |r| r.defaults?(self) }.each do |r|
@@ -51,33 +55,40 @@ module MiniDefender
51
55
  end
52
56
 
53
57
  value = coerced = @data[k]
58
+ force_coerce = false
59
+
54
60
  rule_set.each do |rule|
55
61
  next unless rule.active?(self)
56
62
 
57
63
  value_included &= !rule.excluded?(self)
58
64
 
59
- if rule.passes?(k, coerced, self)
65
+ if rule.passes?(k, value, self)
60
66
  coerced = rule.coerce(coerced)
67
+ force_coerce = rule.force_coerce?
61
68
  else
62
69
  @errors[k] << rule.error_message(k, value, self)
63
70
  end
64
71
  end
65
72
 
73
+ if force_coerce
74
+ value = coerced
75
+ end
76
+
66
77
  if @errors[k].empty? && value_included
67
78
  @validated[k] = value
68
79
  @coerced[k] = coerced
69
80
  end
70
81
  end
71
82
 
72
- @validated = @validated.expand
73
- @coerced = @coerced.expand
74
-
75
83
  @errors.reject! { |_, v| v.empty? }
76
84
  end
77
85
 
78
86
  def validate!
79
87
  validate if @errors.nil?
80
- raise ValidationError.new('Data validation failed', @errors) unless @errors.empty?
88
+
89
+ unless @errors.empty?
90
+ raise ValidationError.new('Data validation failed', @errors)
91
+ end
81
92
  end
82
93
 
83
94
  def passes?
@@ -91,12 +102,12 @@ module MiniDefender
91
102
 
92
103
  def validated
93
104
  validate! if @errors.nil?
94
- @validated
105
+ @validated_compacted ||= compact_expanded(@validated)
95
106
  end
96
107
 
97
108
  def coerced
98
109
  validate! if @errors.nil?
99
- @coerced
110
+ @coerced_compacted = compact_expanded(@coerced)
100
111
  end
101
112
 
102
113
  # @return [Hash]
@@ -125,5 +136,57 @@ module MiniDefender
125
136
 
126
137
  Regexp.compile("\\A#{search_key.reverse.join('\.')}\\z")
127
138
  end
139
+
140
+ def compact_expanded(hash)
141
+ expanded = {}
142
+
143
+ hash.each do |k, v|
144
+ keys = k.split('.')
145
+ node = expanded
146
+
147
+ while keys.length > 1
148
+ next_key = keys.shift
149
+ next_node = (node[next_key] ||= {})
150
+
151
+ if next_node.is_a?(Array)
152
+ node[next_key] = next_node = next_node.each_with_index.to_h do |value, index|
153
+ [index.to_s, value]
154
+ end
155
+ end
156
+
157
+ node = next_node
158
+ end
159
+
160
+ node[keys.shift] = v
161
+ end
162
+
163
+ compact_keys(expanded)
164
+ end
165
+
166
+ def compact_keys(hash, current_key = '')
167
+ current_key_suffixed = if current_key == ''
168
+ ''
169
+ else
170
+ "#{current_key}."
171
+ end
172
+
173
+ result = hash.to_h do |k, v|
174
+ [k, v.is_a?(Hash) ? compact_keys(v, current_key_suffixed + k) : v]
175
+ end
176
+
177
+ if result.empty?
178
+ return result
179
+ end
180
+
181
+ unless @array_patterns.any? { |p| p.match?("#{current_key_suffixed}#{result.keys.first}") }
182
+ return result
183
+ end
184
+
185
+ if result.all? { |k, _v| k.match?(/\A\d+\z/) }
186
+ return result.values
187
+ end
188
+
189
+ result
190
+ end
128
191
  end
129
192
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MiniDefender
4
- VERSION = "0.4.1"
4
+ VERSION = "0.5.1"
5
5
  end
data/lib/mini_defender.rb CHANGED
@@ -13,7 +13,6 @@ require_relative 'mini_defender/validation_helpers'
13
13
 
14
14
  # Extensions to Ruby Core
15
15
  require_relative 'mini_defender/extensions/enumerable'
16
- require_relative 'mini_defender/extensions/hash'
17
16
 
18
17
  module MiniDefender
19
18
  end
@@ -14,8 +14,6 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
  spec.required_ruby_version = ">= 2.6.0"
16
16
 
17
- # spec.metadata["allowed_push_host"] = "TODO: Set to your gem server 'https://example.com'"
18
-
19
17
  spec.metadata["homepage_uri"] = spec.homepage
20
18
  spec.metadata["source_code_uri"] = spec.homepage
21
19
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mini_defender
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ali Alhoshaiyan
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-16 00:00:00.000000000 Z
11
+ date: 2024-01-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -97,7 +97,6 @@ files:
97
97
  - Rakefile
98
98
  - lib/mini_defender.rb
99
99
  - lib/mini_defender/extensions/enumerable.rb
100
- - lib/mini_defender/extensions/hash.rb
101
100
  - lib/mini_defender/handles_validation_errors.rb
102
101
  - lib/mini_defender/rule.rb
103
102
  - lib/mini_defender/rules.rb
@@ -1,28 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Hash
4
- def expand
5
- expanded = {}
6
-
7
- reject{ |_, v| v.is_a?(Hash) || v.is_a?(Array) }.each do |k, v|
8
- keys = k.split('.')
9
- node = expanded
10
- node = (node[keys.shift] ||= {}) while keys.length > 1
11
- node[keys.shift] = v
12
- end
13
-
14
- expanded.compact_keys
15
- end
16
-
17
- def compact_keys
18
- result = to_h do |k, v|
19
- [k, v.is_a?(Hash) ? v.compact_keys : v]
20
- end
21
-
22
- if !result.empty? && result.all? { |k, _v| k.match?(/\A\d+\z/) }
23
- result.values
24
- else
25
- result
26
- end
27
- end
28
- end