mini_defender 0.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.
Files changed (106) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +5 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +70 -0
  5. data/LICENSE.md +21 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +33 -0
  8. data/RULES.md +92 -0
  9. data/Rakefile +12 -0
  10. data/lib/mini_defender/extensions/enumerable.rb +34 -0
  11. data/lib/mini_defender/extensions/hash.rb +28 -0
  12. data/lib/mini_defender/rule.rb +77 -0
  13. data/lib/mini_defender/rules/accepted.rb +17 -0
  14. data/lib/mini_defender/rules/accepted_if.rb +26 -0
  15. data/lib/mini_defender/rules/alpha.rb +15 -0
  16. data/lib/mini_defender/rules/alpha_dash.rb +15 -0
  17. data/lib/mini_defender/rules/alpha_num.rb +15 -0
  18. data/lib/mini_defender/rules/array.rb +15 -0
  19. data/lib/mini_defender/rules/bail.rb +19 -0
  20. data/lib/mini_defender/rules/between.rb +29 -0
  21. data/lib/mini_defender/rules/boolean.rb +26 -0
  22. data/lib/mini_defender/rules/confirmed.rb +24 -0
  23. data/lib/mini_defender/rules/country_code.rb +23 -0
  24. data/lib/mini_defender/rules/credit_card.rb +13 -0
  25. data/lib/mini_defender/rules/date.rb +27 -0
  26. data/lib/mini_defender/rules/date_eq.rb +43 -0
  27. data/lib/mini_defender/rules/date_format.rb +38 -0
  28. data/lib/mini_defender/rules/date_gt.rb +24 -0
  29. data/lib/mini_defender/rules/date_gte.rb +24 -0
  30. data/lib/mini_defender/rules/date_lt.rb +24 -0
  31. data/lib/mini_defender/rules/date_lte.rb +24 -0
  32. data/lib/mini_defender/rules/declined.rb +17 -0
  33. data/lib/mini_defender/rules/declined_if.rb +26 -0
  34. data/lib/mini_defender/rules/default.rb +33 -0
  35. data/lib/mini_defender/rules/different.rb +31 -0
  36. data/lib/mini_defender/rules/digits.rb +26 -0
  37. data/lib/mini_defender/rules/digits_between.rb +29 -0
  38. data/lib/mini_defender/rules/distinct.rb +18 -0
  39. data/lib/mini_defender/rules/email.rb +19 -0
  40. data/lib/mini_defender/rules/ending_with.rb +31 -0
  41. data/lib/mini_defender/rules/equal.rb +9 -0
  42. data/lib/mini_defender/rules/excluded.rb +15 -0
  43. data/lib/mini_defender/rules/excluded_if.rb +28 -0
  44. data/lib/mini_defender/rules/excluded_unless.rb +28 -0
  45. data/lib/mini_defender/rules/excluded_with.rb +27 -0
  46. data/lib/mini_defender/rules/excluded_without.rb +27 -0
  47. data/lib/mini_defender/rules/exists.rb +29 -0
  48. data/lib/mini_defender/rules/expiry_date.rb +28 -0
  49. data/lib/mini_defender/rules/file.rb +17 -0
  50. data/lib/mini_defender/rules/filled.rb +22 -0
  51. data/lib/mini_defender/rules/greater_than.rb +34 -0
  52. data/lib/mini_defender/rules/greater_than_or_equal.rb +34 -0
  53. data/lib/mini_defender/rules/hash.rb +15 -0
  54. data/lib/mini_defender/rules/image.rb +19 -0
  55. data/lib/mini_defender/rules/in.rb +27 -0
  56. data/lib/mini_defender/rules/in_field.rb +40 -0
  57. data/lib/mini_defender/rules/integer.rb +19 -0
  58. data/lib/mini_defender/rules/ip.rb +43 -0
  59. data/lib/mini_defender/rules/ipv4.rb +43 -0
  60. data/lib/mini_defender/rules/ipv6.rb +43 -0
  61. data/lib/mini_defender/rules/json.rb +19 -0
  62. data/lib/mini_defender/rules/less_than.rb +34 -0
  63. data/lib/mini_defender/rules/less_than_or_equal.rb +34 -0
  64. data/lib/mini_defender/rules/luhn.rb +33 -0
  65. data/lib/mini_defender/rules/mac_address.rb +39 -0
  66. data/lib/mini_defender/rules/max.rb +9 -0
  67. data/lib/mini_defender/rules/max_digits.rb +32 -0
  68. data/lib/mini_defender/rules/mime_types.rb +37 -0
  69. data/lib/mini_defender/rules/min.rb +9 -0
  70. data/lib/mini_defender/rules/min_digits.rb +32 -0
  71. data/lib/mini_defender/rules/national_id.rb +13 -0
  72. data/lib/mini_defender/rules/not_ending_with.rb +31 -0
  73. data/lib/mini_defender/rules/not_in.rb +27 -0
  74. data/lib/mini_defender/rules/not_regex.rb +27 -0
  75. data/lib/mini_defender/rules/not_starting_with.rb +31 -0
  76. data/lib/mini_defender/rules/numeric.rb +19 -0
  77. data/lib/mini_defender/rules/present.rb +19 -0
  78. data/lib/mini_defender/rules/prohibited.rb +15 -0
  79. data/lib/mini_defender/rules/prohibited_if.rb +32 -0
  80. data/lib/mini_defender/rules/prohibited_unless.rb +32 -0
  81. data/lib/mini_defender/rules/regex.rb +27 -0
  82. data/lib/mini_defender/rules/required.rb +32 -0
  83. data/lib/mini_defender/rules/required_if.rb +26 -0
  84. data/lib/mini_defender/rules/required_unless.rb +26 -0
  85. data/lib/mini_defender/rules/required_with.rb +27 -0
  86. data/lib/mini_defender/rules/required_with_all.rb +27 -0
  87. data/lib/mini_defender/rules/required_without.rb +27 -0
  88. data/lib/mini_defender/rules/required_without_all.rb +27 -0
  89. data/lib/mini_defender/rules/size.rb +45 -0
  90. data/lib/mini_defender/rules/starting_with.rb +31 -0
  91. data/lib/mini_defender/rules/string.rb +15 -0
  92. data/lib/mini_defender/rules/timezone.rb +19 -0
  93. data/lib/mini_defender/rules/unique.rb +37 -0
  94. data/lib/mini_defender/rules/url.rb +17 -0
  95. data/lib/mini_defender/rules/uuid.rb +37 -0
  96. data/lib/mini_defender/rules.rb +8 -0
  97. data/lib/mini_defender/rules_expander.rb +19 -0
  98. data/lib/mini_defender/rules_factory.rb +36 -0
  99. data/lib/mini_defender/validates_input.rb +12 -0
  100. data/lib/mini_defender/validation_error.rb +10 -0
  101. data/lib/mini_defender/validator.rb +127 -0
  102. data/lib/mini_defender/version.rb +5 -0
  103. data/lib/mini_defender.rb +24 -0
  104. data/mini_defender.gemspec +38 -0
  105. data/sig/mini_defender.rbs +4 -0
  106. metadata +206 -0
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ class MiniDefender::Rules::Date < MiniDefender::Rule
6
+ def self.signature
7
+ 'date'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ parse_date(value).is_a?(Time)
12
+ rescue ArgumentError
13
+ false
14
+ end
15
+
16
+ def message(attribute, value, validator)
17
+ 'The given value is not a valid date.'
18
+ end
19
+
20
+ protected
21
+
22
+ def parse_date(date)
23
+ date = date.to_time if date.is_a?(Date)
24
+ date = Time.parse(date) unless date.is_a?(Time)
25
+ date
26
+ end
27
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ class MiniDefender::Rules::DateEq < MiniDefender::Rule
6
+ def initialize(target_date)
7
+ @target_date = parse_date(target_date)
8
+ @valid_value = false
9
+ end
10
+
11
+ def self.signature
12
+ 'date_eq'
13
+ end
14
+
15
+ def self.make(args)
16
+ raise ArgumentError, 'Target date is required for date rules.' unless args == 1
17
+
18
+ self.new(args[0])
19
+ end
20
+
21
+ def passes?(attribute, value, validator)
22
+ value = parse_date(value)
23
+ @valid_value = true
24
+
25
+ value == @target_date
26
+ rescue ArgumentError
27
+ false
28
+ end
29
+
30
+ def message(attribute, value, validator)
31
+ return "The given value is not a valid date." unless @valid_value
32
+
33
+ "The value must be equal to #{@target_date}."
34
+ end
35
+
36
+ protected
37
+
38
+ def parse_date(date)
39
+ date = date.to_time if date.is_a?(Date)
40
+ date = Time.parse(date) unless date.is_a?(Time)
41
+ date
42
+ end
43
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+
5
+ class MiniDefender::Rules::DateFormat < MiniDefender::Rule
6
+ def initialize(format)
7
+ raise ArgumentError, 'Format must be a string' unless format.is_a?(String)
8
+ raise ArgumentError, 'Format is required for this rule' if format.empty?
9
+
10
+ @format = format
11
+ end
12
+
13
+ def self.signature
14
+ 'date_format'
15
+ end
16
+
17
+ def self.make(args)
18
+ self.new(args[0])
19
+ end
20
+
21
+ def passes?(attribute, value, validator)
22
+ parse_date(value).is_a?(Time)
23
+ rescue ArgumentError
24
+ false
25
+ end
26
+
27
+ def message(attribute, value, validator)
28
+ 'The given value is not a valid date.'
29
+ end
30
+
31
+ protected
32
+
33
+ def parse_date(date)
34
+ date = date.to_time if date.is_a?(Date)
35
+ date = Time.strptime(date, @format) unless date.is_a?(Time)
36
+ date
37
+ end
38
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'date_eq'
4
+
5
+ class MiniDefender::Rules::DateGt < MiniDefender::Rules::DateEq
6
+ def self.signature
7
+ 'date_gt'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ value = parse_date(value)
12
+ @valid_value = true
13
+
14
+ value > @target_date
15
+ rescue ArgumentError
16
+ false
17
+ end
18
+
19
+ def message(attribute, value, validator)
20
+ return "The given value is not a valid date." unless @valid_value
21
+
22
+ "The value must be greater than #{@target_date}."
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'date_eq'
4
+
5
+ class MiniDefender::Rules::DateGte < MiniDefender::Rules::DateEq
6
+ def self.signature
7
+ 'date_gte'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ value = parse_date(value)
12
+ @valid_value = true
13
+
14
+ value >= @target_date
15
+ rescue ArgumentError
16
+ false
17
+ end
18
+
19
+ def message(attribute, value, validator)
20
+ return "The given value is not a valid date." unless @valid_value
21
+
22
+ "The value must be greater than or equal to #{@target_date}."
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'date_eq'
4
+
5
+ class MiniDefender::Rules::DateLt < MiniDefender::Rules::DateEq
6
+ def self.signature
7
+ 'date_lt'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ value = parse_date(value)
12
+ @valid_value = true
13
+
14
+ value < @target_date
15
+ rescue ArgumentError
16
+ false
17
+ end
18
+
19
+ def message(attribute, value, validator)
20
+ return "The given value is not a valid date." unless @valid_value
21
+
22
+ "The value must be less than #{@target_date}."
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'date_eq'
4
+
5
+ class MiniDefender::Rules::DateLte < MiniDefender::Rules::DateEq
6
+ def self.signature
7
+ 'date_lte'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ value = parse_date(value)
12
+ @valid_value = true
13
+
14
+ value <= @target_date
15
+ rescue ArgumentError
16
+ false
17
+ end
18
+
19
+ def message(attribute, value, validator)
20
+ return "The given value is not a valid date." unless @valid_value
21
+
22
+ "The value must be less than or equal to #{@target_date}."
23
+ end
24
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Declined < MiniDefender::Rule
4
+ ALLOWED_VALUES = ['no', 'off', 0, false]
5
+
6
+ def self.signature
7
+ 'declined'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ !value.nil? && ALLOWED_VALUES.include?(value)
12
+ end
13
+
14
+ def message(attribute, value, validator)
15
+ "Must be declined."
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'declined'
4
+
5
+ class MiniDefender::Rules::DeclinedIf < MiniDefender::Rules::Declined
6
+ def initialize(target, value)
7
+ raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
8
+
9
+ @target = target
10
+ @value = value
11
+ end
12
+
13
+ def self.signature
14
+ 'declined_if'
15
+ end
16
+
17
+ def self.make(args)
18
+ raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
19
+
20
+ self.new(args[0], args[1])
21
+ end
22
+
23
+ def active?(validator)
24
+ validator.data[@target] == @value
25
+ end
26
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Default < MiniDefender::Rule
4
+ def initialize(value)
5
+ @default = value
6
+ end
7
+
8
+ def self.signature
9
+ 'default'
10
+ end
11
+
12
+ def self.make(args)
13
+ raise ArgumentError, 'Expected exactly one argument.' unless args.length == 1
14
+
15
+ new(args[0])
16
+ end
17
+
18
+ def defaults?(validator)
19
+ true
20
+ end
21
+
22
+ def default_value(validator)
23
+ @default
24
+ end
25
+
26
+ def passes?(attribute, value, validator)
27
+ true
28
+ end
29
+
30
+ def message(attribute, value, validator)
31
+ "i can haz valuo?."
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Different < MiniDefender::Rule
4
+ def initialize(other_field)
5
+ raise ArgumentError, 'Other field must be a string' unless other_field.is_a?(String)
6
+
7
+ @found = false
8
+ @other_field = other_field
9
+ end
10
+
11
+ def self.signature
12
+ 'different'
13
+ end
14
+
15
+ def self.make(args)
16
+ self.new(args[0])
17
+ end
18
+
19
+ def passes?(attribute, value, validator)
20
+ @found, @other = validator.data.key?(@other_field), validator.data[@other_field]
21
+ @found && value == @other
22
+ end
23
+
24
+ def message(attribute, value, validator)
25
+ if @found
26
+ "The field does not match \"#{@other}\"."
27
+ else
28
+ "The field must match #{@other_field}."
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Digits < MiniDefender::Rule
4
+ def initialize(size)
5
+ raise ArgumentError, 'Size must be a positive integer' unless size.is_a?(Integer) && size > 0
6
+
7
+ @size = size
8
+ end
9
+
10
+ def self.signature
11
+ 'digits'
12
+ end
13
+
14
+ def self.make(args)
15
+ self.new(args[0].to_i)
16
+ end
17
+
18
+ def passes?(attribute, value, validator)
19
+ value = value&.to_s
20
+ value.is_a?(String) && /\A[0-9]+\z/.match?(value) && value.length == @size
21
+ end
22
+
23
+ def message(attribute, value, validator)
24
+ "The field must contain #{size} digits."
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::DigitsBetween < MiniDefender::Rule
4
+ def initialize(min, max)
5
+ raise ArgumentError, 'Min must be a positive integer' unless min.is_a?(Integer) && min > 0
6
+ raise ArgumentError, 'Max must be a positive integer' unless max.is_a?(Integer) && max > 0
7
+
8
+ @min = min
9
+ @max = max
10
+ end
11
+
12
+ def self.signature
13
+ 'digits_between'
14
+ end
15
+
16
+ def self.make(args)
17
+ self.new(args[0].to_i, args[1].to_i)
18
+ end
19
+
20
+ def passes?(attribute, value, validator)
21
+ value = value&.to_s
22
+
23
+ value.is_a?(String) && /\A[0-9]+\z/.match?(value) && value.length >= @min && value.length <= @max
24
+ end
25
+
26
+ def message(attribute, value, validator)
27
+ "The field must contain digits between #{min} and #{max}."
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Distinct < MiniDefender::Rule
4
+ def self.signature
5
+ 'distinct'
6
+ end
7
+
8
+ def passes?(attribute, value, validator)
9
+ validator
10
+ .neighbors(attribute)
11
+ .reject { |k, v| k == attribute }
12
+ .none? { |_, v| v == value }
13
+ end
14
+
15
+ def message(attribute, value, validator)
16
+ 'The value should be unique.'
17
+ end
18
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Email < MiniDefender::Rule
4
+ def self.signature
5
+ 'email'
6
+ end
7
+
8
+ def coerce(value)
9
+ value.strip.downcase
10
+ end
11
+
12
+ def passes?(attribute, value, validator)
13
+ value.to_s.match?(URI::MailTo::EMAIL_REGEXP)
14
+ end
15
+
16
+ def message(attribute, value, validator)
17
+ "The value should be a valid email address."
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::EndingWith < MiniDefender::Rule
4
+ def initialize(fragments)
5
+ unless fragments.is_a?(Array) && !fragments.empty? && fragments.all? { |f| f.is_a?(String) }
6
+ raise ArgumentError, 'Expected an array of strings.'
7
+ end
8
+
9
+ @fragments = fragments
10
+ end
11
+
12
+ def self.signature
13
+ 'ending_with'
14
+ end
15
+
16
+ def self.make(args)
17
+ new(args)
18
+ end
19
+
20
+ def passes?(attribute, value, validator)
21
+ @fragments.any? { |f| value.to_s.end_with?(f) }
22
+ end
23
+
24
+ def message(attribute, value, validator)
25
+ if @fragments.length == 1
26
+ "The value should end with #{@fragments[0]}."
27
+ else
28
+ "The value should end with one of the following #{@fragments.join(', ')}."
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'size'
4
+
5
+ class MiniDefender::Rules::Equal < MiniDefender::Rules::Size
6
+ def self.signature
7
+ 'equal'
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Excluded < MiniDefender::Rule
4
+ def self.signature
5
+ 'excluded'
6
+ end
7
+
8
+ def excluded?(validator)
9
+ true
10
+ end
11
+
12
+ def passes?(attribute, value, validator)
13
+ true
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::ExcludedIf < MiniDefender::Rule
4
+ def initialize(target, value)
5
+ raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
6
+
7
+ @target = target
8
+ @value = value
9
+ end
10
+
11
+ def self.signature
12
+ 'excluded_if'
13
+ end
14
+
15
+ def self.make(args)
16
+ raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
17
+
18
+ self.new(args[0], args[1])
19
+ end
20
+
21
+ def excluded?(validator)
22
+ validator.data.key?(@target) && validator.data[@target] == @value
23
+ end
24
+
25
+ def passes?(attribute, value, validator)
26
+ true
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::ExcludedUnless < MiniDefender::Rule
4
+ def initialize(target, value)
5
+ raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
6
+
7
+ @target = target
8
+ @value = value
9
+ end
10
+
11
+ def self.signature
12
+ 'excluded_unless'
13
+ end
14
+
15
+ def self.make(args)
16
+ raise ArgumentError, 'Target and expected value are required.' unless args.length == 2
17
+
18
+ self.new(args[0], args[1])
19
+ end
20
+
21
+ def excluded?(validator)
22
+ ! (validator.data.key?(@target) && validator.data[@target] == @value)
23
+ end
24
+
25
+ def passes?(attribute, value, validator)
26
+ true
27
+ end
28
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::ExcludedWith < MiniDefender::Rule
4
+ def initialize(target)
5
+ raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
6
+
7
+ @target = target
8
+ end
9
+
10
+ def self.signature
11
+ 'excluded_with'
12
+ end
13
+
14
+ def self.make(args)
15
+ raise ArgumentError, 'Target and expected value are required.' unless args.length == 1
16
+
17
+ self.new(args[0])
18
+ end
19
+
20
+ def excluded?(validator)
21
+ validator.data.key?(@target)
22
+ end
23
+
24
+ def passes?(attribute, value, validator)
25
+ true
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::ExcludedWithout < MiniDefender::Rule
4
+ def initialize(target)
5
+ raise ArgumentError, 'Target must be a string' unless target.is_a?(String)
6
+
7
+ @target = target
8
+ end
9
+
10
+ def self.signature
11
+ 'excluded_without'
12
+ end
13
+
14
+ def self.make(args)
15
+ raise ArgumentError, 'Target and expected value are required.' unless args.length == 1
16
+
17
+ self.new(args[0])
18
+ end
19
+
20
+ def excluded?(validator)
21
+ !validator.data.key?(@target)
22
+ end
23
+
24
+ def passes?(attribute, value, validator)
25
+ true
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::Exists < MiniDefender::Rule
4
+ def initialize(model, column)
5
+ raise ArgumentError, 'model name must be a string or ActiveRecord::Base' unless model.is_a?(String)
6
+ raise ArgumentError, 'Column name must be a string' unless column.is_a?(String)
7
+
8
+ @model = model.camelcase.constantize
9
+ @column = column
10
+ end
11
+
12
+ def self.signature
13
+ 'exists'
14
+ end
15
+
16
+ def self.make(args)
17
+ raise ArgumentError, 'Model and column are required.' unless args.length == 2
18
+
19
+ self.new(args[0], args[1])
20
+ end
21
+
22
+ def passes?(attribute, value, validator)
23
+ @model.where(@column => value).exists?
24
+ end
25
+
26
+ def message(attribute, value, validator)
27
+ "The value does not exists."
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MiniDefender::Rules::ExpiryDate < MiniDefender::Rule
4
+ def self.signature
5
+ 'expiry_date'
6
+ end
7
+
8
+ def coerce(value)
9
+ "#{@month}/#{@year}"
10
+ end
11
+
12
+ def passes?(attribute, value, validator)
13
+ return false unless value.is_a?(String)
14
+
15
+ matches = /(\d{2})\s*\/\s*(\d{2,4})/.match(value.strip)
16
+ return false unless matches
17
+
18
+ @month = matches[1].to_i
19
+ @year = matches[2].to_i
20
+ @year += 2000 if year < 100
21
+
22
+ @month >= 1 && @month <= 12 && @year >= 1900
23
+ end
24
+
25
+ def message(attribute, value, validator)
26
+ 'Invalid expiry date.'
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'action_dispatch'
4
+
5
+ class MiniDefender::Rules::File < MiniDefender::Rule
6
+ def self.signature
7
+ 'file'
8
+ end
9
+
10
+ def passes?(attribute, value, validator)
11
+ value.is_a?(ActionDispatch::Http::UploadedFile)
12
+ end
13
+
14
+ def message(attribute, value, validator)
15
+ "The field should be a file."
16
+ end
17
+ end