diff --git a/README.md b/README.md index 264ac7f..f585417 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,17 @@ Faker::Business.credit_card_type #=> "visa" ``` +### Faker::Code + +```crystal + +Faker::Code.isbn #=> "640354399-7" + +Faker::Code.imei #=> "531691246033652" + +``` + + ### Faker::Commerce ```crystal diff --git a/spec/code_spec.cr b/spec/code_spec.cr new file mode 100644 index 0000000..ce3c710 --- /dev/null +++ b/spec/code_spec.cr @@ -0,0 +1,22 @@ +require "./spec_helper" + +describe Faker::Code do + it "base 10 isbn" do + Faker::Code.isbn.match(/^\d{9}-[\d|X]$/).should_not eq nil + end + + it "base 13 isbn" do + Faker::Code.isbn(13).match(/^\d{12}-\d$/).should_not eq nil + end + + it "imei" do + Faker::Code.imei.match(/\A[\d.:\-\s]+\z/i).should_not eq nil + end + + it "should return deterministic results when seeded" do + Faker.seed 123456 + Faker::Code.isbn.should eq "394314441-0" + Faker::Code.isbn(13).should eq "205982563728-9" + Faker::Code.imei.should eq "860839366575918" + end +end diff --git a/src/faker/code.cr b/src/faker/code.cr new file mode 100644 index 0000000..fe520ae --- /dev/null +++ b/src/faker/code.cr @@ -0,0 +1,66 @@ +module Faker + class Code < Base + RBI = %w[01 10 30 33 35 44 45 49 50 51 52 53 54 86 91 98 99] + + def self.isbn(base : Int32 = 10) + case base + when 10 then generate_base10_isbn + when 13 then generate_base13_isbn + else raise ArgumentError.new("base must be 10 or 13") + end + end + + def self.imei + str = Array.new(15, 0) + sum = 0 + len = 15 + + # Fill in the first two values of the string based with the specified prefix. + arr = RBI.sample(Faker.rng) + str[0] = arr[0].to_i + str[1] = arr[1].to_i + pos = 2 + + # Fill all the remaining numbers except for the last one with random values. + while pos < (len - 1) + str[pos] = Faker.rng.rand(0..9) + pos += 1 + end + + # Calculate the Luhn checksum of the values thus far + len_offset = (len + 1) % 2 + (0..(len - 1)).each do |position| + if (position + len_offset).odd? + t = str[position] * 2 + + t -= 9 if t > 9 + + sum += t + else + sum += str[position] + end + end + str[len - 1] = (10 - (sum % 10)) % 10 + + str.join("") + end + + private def self.generate_base10_isbn + values = Faker.regexify(/\d{9}/) + remainder = sum(values) { |value, index| (index + 1) * value.to_i } % 11 + values += "-#{remainder == 10 ? 'X' : remainder}" + end + + private def self.generate_base13_isbn + values = Faker.regexify(/\d{12}/) + remainder = sum(values) { |value, index| index.even? ? value.to_i : value.to_i * 3 } % 10 + values += "-#{(10 - remainder) % 10}" + end + + private def self.sum(values) + values.split(//).each_with_index.reduce(0) do |sum, (value, index)| + sum + yield(value, index) + end + end + end +end