Merge pull request #4 from mtgrosser/ibantools

Merge ibantools
This commit is contained in:
Matthias Grosser 2025-06-04 00:23:38 +02:00 committed by GitHub
commit 94b602f2f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 124 additions and 48 deletions

View File

@ -13,7 +13,7 @@ jobs:
timeout-minutes: 5
strategy:
matrix:
ruby-version: ['2.7', '3.0', '3.1', '3.2', '3.3']
ruby-version: ['3.0', '3.1', '3.2', '3.3', '3.4']
steps:
- uses: actions/checkout@v2

View File

@ -1 +1 @@
3.0.2
3.4.3

View File

@ -1,3 +1,5 @@
## 0.2.0
## 1.0.0
- support Ruby 2.7+
- Drop bank-contact, use iban-tools (@Joerg-Seitz)
- Inline BIC validation from bank-contact
- Support Ruby 3.4

View File

@ -1,28 +1,30 @@
PATH
remote: .
specs:
girocode (0.2.0)
bank-contact
girocode (1.0.0)
bigdecimal
iban-tools
rqrcode
GEM
remote: https://rubygems.org/
specs:
bank-contact (0.0.6)
bigdecimal (3.2.1)
chunky_png (1.4.0)
docile (1.4.0)
minitest (5.14.4)
rake (13.0.6)
rqrcode (2.1.0)
docile (1.4.1)
iban-tools (1.2.1)
minitest (5.25.5)
rake (13.3.0)
rqrcode (3.1.0)
chunky_png (~> 1.0)
rqrcode_core (~> 1.0)
rqrcode_core (1.2.0)
simplecov (0.21.2)
rqrcode_core (~> 2.0)
rqrcode_core (2.0.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
simplecov_json_formatter (~> 0.1)
simplecov-html (0.12.3)
simplecov_json_formatter (0.1.3)
simplecov-html (0.13.1)
simplecov_json_formatter (0.1.4)
PLATFORMS
ruby
@ -32,6 +34,3 @@ DEPENDENCIES
minitest
rake
simplecov
BUNDLED WITH
2.2.22

View File

@ -16,6 +16,7 @@ Gem::Specification.new do |s|
s.files = Dir['{lib}/**/*.rb', 'LICENSE', 'README.md', 'CHANGELOG.md']
s.require_paths = ['lib']
s.add_dependency 'bigdecimal'
s.add_dependency 'rqrcode'
s.add_dependency 'bank-contact'
s.add_dependency 'iban-tools'
end

View File

@ -1,8 +1,9 @@
require 'bigdecimal'
require 'bank/contact'
require 'iban-tools'
require 'rqrcode'
require_relative 'girocode/version'
require_relative 'girocode/bic'
require_relative 'girocode/code'
module Girocode

67
lib/girocode/bic.rb Normal file
View File

@ -0,0 +1,67 @@
# derived from bank-contact/bic by Kevin
module Girocode
class BIC
REGEX = /\A([A-Z]{4})([A-Z]{2})([A-Z0-9]{2})([A-Z0-9]{3})?\z/.freeze
def self.valid?(code)
new(code).valid?
end
def initialize(code)
@code = code.to_s.gsub(/\s+/, '').upcase
end
def bank_code
@code[0..3]
end
def country_code
@code[4..5]
end
def location_code
@code[6..7]
end
def branch_code
@code[8..10]
end
def to_s(formatted = false)
formatted ? to_formatted_str : @code
end
def to_formatted_str
"#{bank_code} #{country_code} #{location_code} #{branch_code}".strip
end
def test?
location_code[1] == '0'
end
def passive?
location_code[1] == '1'
end
def reverse_billing?
location_code[1] == '2'
end
def valid?
valid_format? && valid_location_code? && valid_branch_code?
end
def valid_format?
REGEX.match?(@code)
end
def valid_location_code?
!location_code.start_with?('0', '1') && !location_code.end_with?('O')
end
def valid_branch_code?
branch_code.empty? || branch_code == 'XXX' || !branch_code.start_with?('X')
end
end
end

View File

@ -1,13 +1,13 @@
module Girocode
class Error < StandardError; end
class Code
ATTRIBUTES = %i[bic name iban currency amount purpose creditor_reference reference bto_info]
attr_reader *ATTRIBUTES
MAX_PAYLOAD_BYTES = 331
AMOUNT_RANGE = BigDecimal('0.01')..BigDecimal('999999999.99')
def initialize(**attrs)
if keys = attrs.keys - ATTRIBUTES and not keys.empty?
raise ArgumentError, "Illegal attributes #{keys.inspect}"
@ -19,17 +19,17 @@ module Girocode
raise ArgumentError, "either creditor reference or reference may be set" if creditor_reference? && reference?
raise ArgumentError, "payload too long" if payload.bytesize > MAX_PAYLOAD_BYTES
end
def bic=(value)
if value.nil?
@bic = nil
else
bic = Bank::BIC.new(value)
bic = BIC.new(value)
raise ArgumentError, "Invalid BIC #{value.inspect}" unless bic.valid?
@bic = bic.to_s
end
end
def name=(value)
value = value.strip
raise ArgumentError, 'name is required' unless value
@ -37,81 +37,81 @@ module Girocode
raise ArgumentError, 'Illegal name' if value.include?("\n") || value.include?("\r")
@name = value
end
def iban=(value)
iban = Bank::IBAN.new(value)
raise ArgumentError, "Invalid IBAN #{value.inspect}" unless iban.valid?
@iban = iban.to_s
raise ArgumentError, "Invalid IBAN #{value.inspect}" unless IBANTools::IBAN.valid?(value)
@iban = value
end
def currency=(value)
value = value.to_s.upcase
raise ArgumentError, "Invalid currency" unless value.match?(/\A[A-Z]{3}\z/)
@currency = value
end
def amount=(value)
raise ArgumentError, 'amount is required' unless value
value = BigDecimal(value, Float::DIG + 1)
raise ArgumentError, "invalid amount #{value.inspect}" unless AMOUNT_RANGE.cover?(value)
@amount = value
end
def purpose=(value)
unless value.nil?
raise ArgumentError, "invalid purpose #{value.inspect}" unless value.match?(/\A[A-z0-9]{0,4}\z/)
end
@purpose = value
end
def creditor_reference=(value)
unless value.nil?
raise ArgumentError, "invalid creditor reference #{value.inspect}" if value.include?("\n") || value.include?("\r") || value.size > 35
end
@creditor_reference = value
end
def reference=(value)
unless value.nil?
raise ArgumentError, "invalid reference #{value.inspect}" if value.include?("\n") || value.include?("\r") || value.size > 140
end
@reference = value
end
def bto_info=(value)
unless value.nil?
raise ArgumentError, "invalid bto_info #{value.inspect}" if value.include?("\n") || value.include?("\r") || value.size > 70
end
@bto_info = value
end
ATTRIBUTES.each do |attr|
define_method("#{attr}?") do
value = instance_variable_get("@#{attr}")
value.respond_to?(:empty?) ? !value.empty? : !!value
end
end
def payload
['BCD', '002', '1', 'SCT',
bic, name, iban,formatted_amount, purpose,
creditor_reference || reference, bto_info].map(&:to_s).join("\n")
end
def to_qrcode
RQRCode::QRCode.new(payload, level: :m, mode: :byte_8bit)
end
def to_ascii
to_qrcode.to_s
end
%i[png svg html ansi].each do |format|
define_method("to_#{format}") { |*args| to_qrcode.public_send("as_#{format}", *args) }
end
private
def formatted_amount
"#{currency}#{amount.round(2).to_s('F')}" if currency? && amount
end

View File

@ -1,3 +1,3 @@
module Girocode
VERSION = "0.2.0"
VERSION = "1.0.0"
end

View File

@ -2,7 +2,7 @@ require_relative 'test_helper'
class GirocodeTest < Minitest::Test
def test_that_it_has_a_version_number
refute_nil ::Girocode::VERSION
refute_nil Girocode::VERSION
end
def test_girocode
@ -10,9 +10,15 @@ class GirocodeTest < Minitest::Test
code = Girocode.new(**attrs)
assert_equal data(:data), code.to_ascii
end
def test_bic
assert_raises ArgumentError do
Girocode.new(bic: 'FOOBAR', name: 'Franz Mustermänn', iban: 'DE71110220330123456789', currency: :eur, amount: 12.3, purpose: 'GDDS', creditor_reference: 'RF18539007547034')
end
end
private
def data(name)
Pathname(__dir__).join("#{name}.txt").read
end