module Dixon module Validators class Taiwan INVALID_MESSAGE = 'Invalid ID number.' LETTER_ISSUED_BY = { ## # Active Letters 'A' => 'Taipei City' , 'B' => 'Taichung City', 'C' => 'Keelung City' , 'D' => 'Tainan City', 'E' => 'Kaohsiung City' , 'F' => 'New Taipei City', 'G' => 'Yilan County' , 'H' => 'Taoyuan County', 'I' => 'Chiayi City' , 'J' => 'Hsinchu County', 'K' => 'Miaoli County' , 'M' => 'Nantou County', 'N' => 'Changhua County' , 'O' => 'Hsinchu City', 'P' => 'Yunlin County' , 'Q' => 'Chiayi County', 'T' => 'Pingtung County' , 'U' => 'Hualien County', 'V' => 'Taitung County' , 'W' => 'Kinmen County', 'X' => 'Penghu County' , 'Z' => 'Lienchiang County', ## # Letters no longer issued 'L' => 'Taichung County' , 'R' => 'Tainan County' , 'S' => 'Kaohsiung County', 'Y' => 'Yangmingshan Management Bureau' } LETTER_NUMBERS = { 'A' => '10', 'B' => '11', 'C' => '12', 'D' => '13', 'E' => '14', 'F' => '15', 'G' => '16', 'H' => '17', 'J' => '18', 'K' => '19', 'L' => '20', 'M' => '21', 'N' => '22', 'P' => '23', 'Q' => '24', 'R' => '25', 'S' => '26', 'T' => '27', 'U' => '28', 'V' => '29', 'X' => '30', 'Y' => '31', 'W' => '32', 'Z' => '33', 'I' => '34', 'O' => '35' } RULE_NUMBERS = [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1] ## # Check if a given ID is valid. # `id` must be a string, case is insensitive. # First convert ID into all numbers, then multiply to RULE_NUMBERS # element by element. Sum up and take a 10 modulo then check # if remainder is 0. If it's, return true, false otherwise. def checks(id) # "F127337575" => [1, 5, 1, 2, 7, 3, 3, 7, 5, 7, 5] converted = convert id.to_s # [1, 5, 1, 2, 7, 3, 3, 7, 5, 7, 5] # * [1, 9, 8, 7, 6, 5, 4, 3, 2, 1, 1] # -------------------------------------------- # sum 1, 45, 8, 14, 43, 15, 12, 21, 10, 7, 5 # => 180 # 180 mod 10, is the remainder equal to zero? If so, return true. return (mapcar(-> (a,b) { a * b }, converted, RULE_NUMBERS).reduce(:+).divmod 10)[1] == 0 end ## # Returns gender for given ID. # By check the second digit of ID. # 1 is male, 2 is female. def gender(id) case id[1] when '1' then 'male' when '2' then 'female' else puts INVALID_MESSAGE end end ## # Returns local agencies who issued your id. def issued_by(id) LETTER_ISSUED_BY[id[0]] end private ## # Convert array of strings into integer array. def to_i_array(str_arr) str_arr.map(&:to_i) end ## # Convert an identification number to array of strings # First letter conversion rules is defined in LETTER_NUMBERS # `convert 'F127337575'` will result in # [1, 5, 1, 2, 7, 3, 3, 7, 5, 7, 5] def convert(id) to_i_array((LETTER_NUMBERS[id[0]] + id[1..-1]).split('')) end ## # mapcar operates on successive elements of the arrays. # fn is applied to the first element of each array, # then to the secondf element of each array, and so on. # The iteration terminates when the shortest array runs out, # and excess elements in other arrays are ignored. # The value returned by mapcar is a array of the results of # successive calls to function. def mapcar(fn, *arrs) return [] if arrs.empty? or arrs.include? [] transposed = if arrs.element_of_same_size arrs.transpose else arrs[0].zip(*arrs.drop(1)) end transposed.map do |e| e.reduce(&fn) unless e.include? nil end end end end end module ValidateFormat ## # A123456789 TAIWAN_ID_REGEXP = /[a-zA-Z][1|2][0-9]+/i ## # Check if given id's format is valid. # The current ID number has exact 10 digits # The first digit is one capital English letter and # is followed by nine Arabic numerals. # returns # 'Empty ID.' for empty string # 'Valid ID.' if the format is correct. # otherwise, 'Invalid ID number.' def validate_id_format(id) id_str = id.to_s return 'Empty ID.' if id_str.empty? return 'Valid ID.' if id_str =~ TAIWAN_ID_REGEXP and id_str.size == 10 return Dixon::Validators::Taiwan::INVALID_MESSAGE end ## # Add a before_hook for methods that # defined in literal symbol array. %i(checks gender issued_by convert).each do |m| define_method(m) do |id| is_it_valid = validate_id_format id if is_it_valid == 'Valid ID.' super(id) else return is_it_valid end end end private :convert end module Dixon module Validators class Taiwan prepend ValidateFormat end end end