Проверка IBAN в Рубин


Ищу советы о том, как улучшить мой код и местах, что я могу лучше придерживаться объектно-ориентированный принципы. Это структура класса ОК? Как код в целом?

Проблема: есть два стандарта ОКОНХ: ОКОНХ-10 и IBAN-13. Ибан-10 состоит из 9 цифр плюс контрольная цифра. Ибан-13 состоит из 12 цифр плюс контрольная цифра. Пробелы и дефисы могут быть включены в код, но не значительные.

Проверка на Ибан-10 рассчитывается путем умножения каждой цифры на своей позиции, суммируя эти продукты вместе и принимая модуль 11 результата. Подставляя X, если в результате 10.

Контрольную цифру по ОКОНХ-13 рассчитывается путем умножения каждой цифры попеременно по 1 или 3, просуммировав эти продукты вместе и принимая модуль 10 результата и вычитая это значение из 10. Потом с этим результатом воспользоваться модулем из десяти, так как к Reduct число к одной цифре.

Возвратите true, если это правильный Ибан-10/ОКОНХ-13

class IBAN

  def initialize(number)
    puts "number: #{number}"

    @number = number
    @number.gsub!(/[^0-9]/i, '')
    return @number
  end

  def number
    @number
  end

  # # check IBAN length and if valid proceed to process IBAN type
  def check_number_format
    iban_length = self.number.length

    unless iban_length == 10 || iban_length == 13
      puts "Not a valid IBAN - incorrect length"
      return false
    else
      self.validate_iban_type
    end
  end

  def validate_iban_type
    case self.number.length
    when 10
      @iban_ten = IBANTEN.new(self.number)
      @iban_ten.process_ten_digit_iban
    when 13
      @iban_thirteen = IBANTHIRTEEN.new(self.number)
      @iban_thirteen.process_thirteen_digit_iban
    else
      return false
    end
  end
end


class IBANTEN

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def iban_number
    @iban_number
  end

  def process_ten_digit_iban
    number_split = self.iban_number.split('')
    final_digit = number_split[-1]
    running_total = 0

    number_split[0, (number_split.length - 1)].each_with_index do |num, index|
      # multiply each digit by its position
      sum = num.to_i * (index.to_i + 1)
      running_total += sum.to_i
    end
    # get the modulus of the running total if it exuals 10 replace it with X otherwise return the result
    digit_sum_total = running_total % 11
    calculated_result = digit_sum_total == 10 ? "X" : digit_sum_total

    if calculated_result == final_digit.to_i
      puts "Valid IBAN-10 Number"
      return true
    else
      puts "Invalid IBAN-10 Number"
      return false
    end
  end

end


class IBANTHIRTEEN

  def initialize(iban_number)
    @iban_number = iban_number
  end

  def iban_number
    @iban_number
  end

  def process_thirteen_digit_iban
    number_split = self.iban_number.split('')
    final_digit = number_split[-1]
    running_total = 0

    number_split[0, (number_split.length - 1)].each_with_index do |num, index|
      # if the remainder equals zero multiply by 1 otherwise multiplu by 3
      sum = (index % 2 == 0 ) ? num.to_i * 1 : num.to_i * 3
      running_total += sum.to_i
    end
    # modulo 10 of the result and subtracting this value from 10, and then taking the modulo 10 of the result again to produce a single digit
    calculated_result = ( (10 - (running_total % 10) ) % 10)

    if calculated_result == final_digit.to_i
      puts "Valid IBAN-13 Number"
      return true
    else
      puts "Invalid IBAN-13 Number"
      return false
    end
  end
end


@iban_v13_valid = IBAN.new("978 0 471 48648 0")
@iban_v13_valid.check_number_format

@iban_v13_invalid = IBAN.new("9780470059021")
@iban_v13_valid.check_number_format

@iban_v10_valid = IBAN.new("0 471 60695 2")
@iban_v10_valid.check_number_format

@iban_v10_invalid = IBAN.new("0-470-84525-6")
@iban_v10_invalid.check_number_format


@iban_invalid_length = IBAN.new("0-470-84525-618423")
@iban_invalid_length.check_number_format


234
3
задан 21 марта 2018 в 07:03 Источник Поделиться
Комментарии
2 ответа

Если вы предпочитаете использовать 3 различных классов;


  • Использовать Rubocop камень.


    • Предпочитают защитные предложения.

    • Предпочитаю одинарные кавычки на двойные.

    • Предпочитаю локальные переменные, когда переменные не нужны.

    • Нет необходимости для явного возвращения.


  • Понять разницу между gsub! и gsub.

  • Не нужно беспрекословно писать самостоятельно.

  • Предпочитаю attr_ вместо того, чтобы писать методы.

  • Лично я не большой поклонник используя инициализаторы для изменения данных. Если вы делаете, держать его минимальным.

  • Используйте более короткие методы. Читать единый принцип ответственности (СРП).

  • Проще использовать имена переменных.

  • Не рефакторинг длинные методами, как это может зависеть от личных обращений. Тем не менее:


    • Вы можете извлечь обе #process_ten_digit_iban и #process_thirteen_digit_iban так как они очень похожи.

    • Вы можете извлечь puts valid/invalid в случае, если условия, чтобы сохранить ваш код сухой.



class IBAN
REQUIRED_LENGTH = [10, 13].freeze

attr_reader :number

def initialize(number)
@number = number.gsub(/[^0-9]/i, '')
end

def validate_iban_type
return if check_number_format?

case number.length
when 10
IBANTEN.new(number).process_ten_digit_iban
when 13
IBANTHIRTEEN.new(number).process_thirteen_digit_iban
else
false
end
end

# check IBAN length and if valid proceed to process IBAN type
def check_number_format?
iban_length = number.length
raise 'Incorrect length' unless REQUIRED_LENGTH.include?(iban_length)
end
end

class IBANTEN
attr_reader :iban_number

def initialize(iban_number)
@iban_number = iban_number
end

def process_ten_digit_iban
numbers_array = iban_number.split('')
total = 0

numbers_array[0, (numbers_array.length - 1)].each_with_index do |num, index|
# multiply each digit by its position
sum = num.to_i * (index.to_i + 1)
total += sum
end

# get the modulus of the running total if it exuals 10 replace it with X
# otherwise return the result
digit_sum_total = total % 11
result = digit_sum_total == 10 ? 'X' : digit_sum_total

if result == numbers_array[-1].to_i
puts 'Valid IBAN-10 Number'
true
else
puts 'Invalid IBAN-10 Number'
false
end
end
end

class IBANTHIRTEEN
attr_reader :iban_number

def initialize(iban_number)
@iban_number = iban_number
end

def process_thirteen_digit_iban
numbers_array = iban_number.split('')
total = 0

numbers_array[0, (numbers_array.length - 1)].each_with_index do |num, index|
# if the remainder equals zero multiply by 1 otherwise multiply by 3
sum = index.even? ? num.to_i * 1 : num.to_i * 3
total += sum
end

# modulo 10 of the result and subtracting this value from 10, and then
# taking the modulo 10 of the result again to produce a single digit
result = ((10 - (total % 10)) % 10)

if result == numbers_array[-1].to_i
puts 'Valid IBAN-13 Number'
true
else
puts 'Invalid IBAN-13 Number'
false
end
end
end

iban_v13_valid = IBAN.new('978 0 471 48648 0')
iban_v13_valid.validate_iban_type

iban_v13_invalid = IBAN.new('9780470059021')
iban_v13_invalid.validate_iban_type

iban_v10_valid = IBAN.new('0 471 60695 2')
iban_v10_valid.validate_iban_type

iban_v10_invalid = IBAN.new('0-470-84525-6')
iban_v10_invalid.validate_iban_type

iban_invalid_length = IBAN.new('0-470-84525-618423')
iban_invalid_length.validate_iban_type

1
ответ дан 23 марта 2018 в 04:03 Источник Поделиться

Я бы посмотрела, чтобы реализовать структуры с отдельной реализации для трех случаев:


  1. IBAN13

  2. IBAN10

  3. Ни один из вышеперечисленных

Вы можете представить что-то вдоль этих линий, в которых IBAN#new переопределяется для определения количества целых чисел, при условии, и начать правильный объект для обработки.

Реальный трюк здесь является обеспечение Invalid модуль, который реагирует на все, что вы ожидаете Thirteen и Ten объекты реагировать, в реализации, похож на нулевой объект шаблона – в этом случае недопустимый объект.

Я опустил реализации #valid? и т. д. классы, но хотел бы указать вам на Lisbn камень, в котором мы внедрили очень быстрый алгоритм https://github.com/ragalie/lisbn/blob/master/lib/lisbn/lisbn.rb#L111. В сравнении с перечисляемую на основе методов, которые часто выглядят весьма элегантно, этот алгоритм невероятно быстро – 40 раз быстрее, чем та же логика реализована в некоторых ИСБН драгоценные камни.

Вот набросок кода:

module IBAN
def self.new(string)
numeric = string.gsub(/[^0-9]/,"")
case numeric.size
when 13
then Thirteen.new(string)
when 10
then Ten.new(string)
else
Invalid
end
end

class Thirteen
def initialize(number)
@number = number
end

def checksum
# TODO
end

def valid?
# TODO
end

attr_reader :number
end

class Ten
def initialize(number)
@number = number
end

def checksum
# TODO
end

def valid?
# TODO
end

attr_reader :number
end

module Invalid
def self.valid?
false
end

def self.checksum
nil
end
end
end

ПС. Вы могли бы предпочесть, чтобы реализовать Invalid как одиночный элемент, а не как модуль.

    ...
else
Invalid.instance
end
...

class Invalid
include Singleton

def valid?
false
end

def checksum
nil
end
end
end

2
ответ дан 1 апреля 2018 в 02:04 Источник Поделиться