Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Luhn with generator #559

Merged
merged 5 commits into from
Apr 21, 2017
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions exercises/luhn/.meta/.version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1
41 changes: 18 additions & 23 deletions exercises/luhn/example.rb
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions exercises/luhn/example.tt
Original file line number Diff line number Diff line change
@@ -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.work_load %>
end
<% end %>
<%= IO.read(XRUBY_LIB + '/bookkeeping.md') %>
def test_bookkeeping
skip
assert_equal <%= version %>, BookKeeping::VERSION
end
end
87 changes: 60 additions & 27 deletions exercises/luhn/luhn_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
27 changes: 27 additions & 0 deletions lib/luhn_cases.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require 'exercise_cases'

class LuhnCase < OpenStruct
def name
'test_%s' % description.tr('- ', '__')
end

def work_load
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be workload

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've updated the documentation to help clarify this: #560

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a style guide somewhere (other than the README)? Because some of the exercises use work_load and others use workload. In fact, as long as it is the same in both example.tt and <exercise>_cases.rb it should work fine.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, just the README.
I've made a PR to rename the old occurrences to try and clear this up: #561

%Q(#{assertion} Luhn.valid?("#{input}"))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"#{input}"

This will work, but it's a good habit to use input.inspect which will handle input that contains quotes.

Nice usage of %Q though. 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea!

end

def skipped
index.zero? ? '# skip' : 'skip'
end

private

def assertion
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be made private as it is only needed by workload

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