Skip to content

Commit

Permalink
Arabic Integer Support + Tests
Browse files Browse the repository at this point in the history
  • Loading branch information
ahoshaiyan committed Mar 12, 2024
1 parent 5ba2ec2 commit d625dfd
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 4 deletions.
68 changes: 65 additions & 3 deletions lib/mini_defender/rules/integer.rb
Original file line number Diff line number Diff line change
@@ -1,21 +1,83 @@
# frozen_string_literal: true

class MiniDefender::Rules::Integer < MiniDefender::Rule
attr_reader :mode
attr_reader :parsed

DIGIT_MAP = {
# Arabic-Indic Digits
"\u0660" => '0',
"\u0661" => '1',
"\u0662" => '2',
"\u0663" => '3',
"\u0664" => '4',
"\u0665" => '5',
"\u0666" => '6',
"\u0667" => '7',
"\u0668" => '8',
"\u0669" => '9',

# Extended Arabic-Indic Digits
"\u06F0" => '0',
"\u06F1" => '1',
"\u06F2" => '2',
"\u06F3" => '3',
"\u06F4" => '4',
"\u06F5" => '5',
"\u06F6" => '6',
"\u06F7" => '7',
"\u06F8" => '8',
"\u06F9" => '9',
}

def initialize(mode = 'strict')
@mode = mode
end

def self.signature
'integer'
end

def self.make(args)
new(args[0] || 'strict')
end

def coerce(value)
Integer(value)
@parsed
end

def passes?(attribute, value, validator)
Integer(value.to_s)
# Avoid converting integers to string and back
if value.is_a?(Integer)
@parsed = value
return true
end

value = value.to_s

if @mode == 'relaxed'
value = normalize_digits(value)
end

@parsed = Integer(value)
rescue
false
end
end

def message(attribute, value, validator)
"The value must be an integer."
end

def normalize_digits(data)
# Check if arabic digits exist or avoid expensive string creation operation
unless data.match?(/[\u0660-\u0669\u06F0-\u06F9]/)
return data
end

DIGIT_MAP.each do |k, v|
data = data.gsub(k, v)
end

data
end
end
2 changes: 1 addition & 1 deletion lib/mini_defender/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module MiniDefender
VERSION = "0.5.8"
VERSION = "0.6.0"
end
35 changes: 35 additions & 0 deletions test/rules/expiry_month_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# frozen_string_literal: true

require 'test_helper'

class ExpiryMonthTest < Minitest::Test
def setup
@rule = MiniDefender::Rules::ExpiryMonth.new
end

def test_accept_integer_value
assert @rule.passes?('month', 1, nil)
end

def test_accept_string_digits
assert @rule.passes?('month', '2', nil)
end

def test_accept_single_integer_value
assert @rule.passes?('month', '3', nil)
end

def test_accept_string_with_leading_zero
assert @rule.passes?('month', '04', nil)
end

def test_accept_one_to_twelve
(1..12).each do |month|
assert @rule.passes?('month', month.to_s.rjust(2, '0'), nil)
end
end

def test_rejects_invalid_month
refute @rule.passes?('month', '13', nil)
end
end
20 changes: 20 additions & 0 deletions test/rules/integer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
class IntegerTest < Minitest::Test
def setup
@rule = MiniDefender::Rules::Integer.new
@rule_relax = MiniDefender::Rules::Integer.new('relaxed')
end

def test_passes_with_string_positive_integer
Expand Down Expand Up @@ -38,4 +39,23 @@ def test_fails_with_array
def test_fails_with_hash
refute(@rule.passes?("test", {}, nil))
end

def test_passes_with_integer
assert @rule.passes?('amount', 1, nil)
assert_equal 1, @rule.coerce(1)
end

def test_strict_fails_with_arabic_digits
refute @rule.passes?('amount', '٣', nil)
end

def test_relaxed_passes_with_arabic_digits
assert @rule_relax.passes?('amount', '٣', nil)
assert_equal 3, @rule_relax.coerce('٣')
end

def test_relaxed_passes_with_mixed_digits
assert @rule_relax.passes?('amount', '2٣', nil)
assert_equal 23, @rule_relax.coerce('2٣')
end
end

0 comments on commit d625dfd

Please sign in to comment.