diff --git a/exercises/luhn/.meta/.version b/exercises/luhn/.meta/.version new file mode 100644 index 0000000000..d00491fd7e --- /dev/null +++ b/exercises/luhn/.meta/.version @@ -0,0 +1 @@ +1 diff --git a/exercises/luhn/example.rb b/exercises/luhn/example.rb index 58e1242245..f53ac38d01 100644 --- a/exercises/luhn/example.rb +++ b/exercises/luhn/example.rb @@ -1,35 +1,30 @@ class Luhn - def self.create(number) - test_number = number * 10 - luhn = Luhn.new(test_number) - return test_number if luhn.valid? - test_number + 10 - (luhn.checksum % 10) - end + DOUBLE = [0, 2, 4, 6, 8, 1, 3, 5, 7, 9] + DOUBLE.freeze - attr_reader :number - def initialize(number) - @number = number + def self.valid?(string) + Luhn.new(string).valid? end - def addends - numbers = [] - number.to_s.reverse.split('').map(&:to_i).each_with_index do |n, i| - if i % 2 == 0 - numbers << n - else - value = n * 2 - value -= 9 if value > 9 - numbers << value - end - end - numbers.reverse + def initialize(string) + @string = string.tr(' ', '') end def checksum - addends.inject(0, :+) + @string. + reverse.each_char.with_index. + reduce(0) {|sum, (c, i)| sum + (i.odd? ? DOUBLE[c.to_i] : c.to_i) } end def valid? - checksum % 10 == 0 + clean? && (checksum % 10).zero? + end + + def clean? + @string.match(/^\d{2,}$/) end end + +module BookKeeping + VERSION = 1 +end diff --git a/exercises/luhn/example.tt b/exercises/luhn/example.tt new file mode 100644 index 0000000000..0770a41b04 --- /dev/null +++ b/exercises/luhn/example.tt @@ -0,0 +1,18 @@ +#!/usr/bin/env ruby +gem 'minitest', '>= 5.0.0' +require 'minitest/autorun' +require_relative 'luhn' + +# Common test data version: <%= abbreviated_commit_hash %> +class LuhnTest < Minitest::Test<% test_cases.each do |test_case| %> + def <%= test_case.name %> + <%= test_case.skipped %> + <%= test_case.workload %> + end +<% end %> +<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %> + def test_bookkeeping + skip + assert_equal <%= version %>, BookKeeping::VERSION + end +end diff --git a/exercises/luhn/luhn_test.rb b/exercises/luhn/luhn_test.rb index 52d5538cc1..1b7828f669 100755 --- a/exercises/luhn/luhn_test.rb +++ b/exercises/luhn/luhn_test.rb @@ -3,57 +3,90 @@ require 'minitest/autorun' require_relative 'luhn' +# Common test data version: c826372 class LuhnTest < Minitest::Test - def test_addends - luhn = Luhn.new(12_121) - assert_equal [1, 4, 1, 4, 1], luhn.addends + def test_single_digit_strings_can_not_be_valid + # skip + refute Luhn.valid?("1") end - def test_too_large_addend + def test_A_single_zero_is_invalid skip - luhn = Luhn.new(8631) - assert_equal [7, 6, 6, 1], luhn.addends + refute Luhn.valid?("0") end - def test_checksum + def test_a_simple_valid_SIN_that_remains_valid_if_reversed skip - luhn = Luhn.new(4913) - assert_equal 22, luhn.checksum + assert Luhn.valid?("059") end - def test_checksum_again + def test_a_simple_valid_SIN_that_becomes_invalid_if_reversed skip - luhn = Luhn.new(201_773) - assert_equal 21, luhn.checksum + assert Luhn.valid?("59") end - def test_invalid_number + def test_a_valid_Canadian_SIN skip - luhn = Luhn.new(738) - refute luhn.valid? + assert Luhn.valid?("055 444 285") end - def test_valid_number + def test_invalid_Canadian_SIN skip - luhn = Luhn.new(8_739_567) - assert luhn.valid? + refute Luhn.valid?("055 444 286") end - def test_create_valid_number + def test_invalid_credit_card skip - number = Luhn.create(123) - assert_equal 1230, number + refute Luhn.valid?("8273 1232 7352 0569") end - def test_create_other_valid_number + def test_valid_strings_with_a_non_digit_included_become_invalid skip - number = Luhn.create(873_956) - assert_equal 8_739_567, number + refute Luhn.valid?("055a 444 285") end - def test_create_yet_another_valid_number + def test_valid_strings_with_punctuation_included_become_invalid skip - number = Luhn.create(837_263_756) - assert_equal 8_372_637_564, number + refute Luhn.valid?("055-444-285") + end + + def test_valid_strings_with_symbols_included_become_invalid + skip + refute Luhn.valid?("055£ 444$ 285") + end + + def test_single_zero_with_space_is_invalid + skip + refute Luhn.valid?(" 0") + end + + def test_more_than_a_single_zero_is_valid + skip + assert Luhn.valid?("0000 0") + end + + def test_input_digit_9_is_correctly_converted_to_output_digit_9 + skip + assert Luhn.valid?("091") + end + # Problems in exercism evolve over time, as we find better ways to ask + # questions. + # The version number refers to the version of the problem you solved, + # not your solution. + # + # Define a constant named VERSION inside of the top level BookKeeping + # module, which may be placed near the end of your file. + # + # In your file, it will look like this: + # + # module BookKeeping + # VERSION = 1 # Where the version number matches the one in the test. + # end + # + # If you are curious, read more about constants on RubyDoc: + # http://ruby-doc.org/docs/ruby-doc-bundle/UsersGuide/rg/constants.html + def test_bookkeeping + skip + assert_equal 1, BookKeeping::VERSION end end diff --git a/lib/luhn_cases.rb b/lib/luhn_cases.rb new file mode 100644 index 0000000000..df608b4e23 --- /dev/null +++ b/lib/luhn_cases.rb @@ -0,0 +1,27 @@ +require 'exercise_cases' + +class LuhnCase < OpenStruct + def name + 'test_%s' % description.tr('- ', '__') + end + + def workload + %Q(#{assertion} Luhn.valid?(#{input.inspect})) + end + + def skipped + index.zero? ? '# skip' : 'skip' + end + + private + + def assertion + expected ? 'assert' : 'refute' + end +end + +LuhnCases = proc do |data| + JSON.parse(data)['cases'].map.with_index do |row, i| + LuhnCase.new(row.merge('index' => i)) + end +end