Base code and test
This commit is contained in:
parent
3662a5feec
commit
64ac35efcd
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
||||
2.6.2
|
6
Gemfile
Normal file
6
Gemfile
Normal file
@ -0,0 +1,6 @@
|
||||
source "https://rubygems.org"
|
||||
|
||||
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
|
||||
|
||||
# Specify your gem's dependencies in girocode.gemspec
|
||||
gemspec
|
4
LICENSE
4
LICENSE
@ -1,4 +1,4 @@
|
||||
MIT License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Matthias Grosser
|
||||
|
||||
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
SOFTWARE.
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Girocode
|
||||
|
||||
Create QR codes for SEPA bank transfers.
|
||||
|
||||
## Installation
|
||||
|
||||
Add this line to your application's Gemfile:
|
||||
|
||||
```ruby
|
||||
gem 'girocode'
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```ruby
|
||||
code = Girocode.new(iban: 'DE02100500000054540402', name: 'Beispiel AG', currency: 'EUR', amount: 123.45, reference: 'RE 2019/05/445 744507')
|
||||
|
||||
code.to_svg
|
||||
code.to_png
|
||||
code.to_html
|
||||
|
||||
# in your console
|
||||
puts code.to_ansi
|
||||
```
|
10
Rakefile
Normal file
10
Rakefile
Normal file
@ -0,0 +1,10 @@
|
||||
require "bundler/gem_tasks"
|
||||
require "rake/testtask"
|
||||
|
||||
Rake::TestTask.new(:test) do |t|
|
||||
t.libs << "test"
|
||||
t.libs << "lib"
|
||||
t.test_files = FileList["test/**/*_test.rb"]
|
||||
end
|
||||
|
||||
task :default => :test
|
14
bin/console
Executable file
14
bin/console
Executable file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require "bundler/setup"
|
||||
require "girocode"
|
||||
|
||||
# You can add fixtures and/or initialization code here to make experimenting
|
||||
# with your gem easier. You can also use a different console, if you like.
|
||||
|
||||
# (If you use this, don't forget to add pry to your Gemfile!)
|
||||
# require "pry"
|
||||
# Pry.start
|
||||
|
||||
require "irb"
|
||||
IRB.start(__FILE__)
|
8
bin/setup
Executable file
8
bin/setup
Executable file
@ -0,0 +1,8 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
IFS=$'\n\t'
|
||||
set -vx
|
||||
|
||||
bundle install
|
||||
|
||||
# Do any other automated setup that you need to do here
|
25
girocode.gemspec
Normal file
25
girocode.gemspec
Normal file
@ -0,0 +1,25 @@
|
||||
lib = File.expand_path('../lib', __FILE__)
|
||||
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
||||
require 'girocode/version'
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = 'girocode'
|
||||
s.version = Girocode::VERSION
|
||||
s.authors = ['Matthias Grosser']
|
||||
s.email = ['mtgrosser@gmx.net']
|
||||
|
||||
s.summary = %q{Generate QR codes for SEPA credit transfers}
|
||||
s.description = %q{EPC QR code for SEPA payments in any format}
|
||||
s.homepage = 'https://github.com/mtgrosser/girocode'
|
||||
|
||||
s.files = Dir['{lib}/**/*.rb', 'LICENSE', 'README.md', 'CHANGELOG', 'Rakefile']
|
||||
s.require_paths = ['lib']
|
||||
|
||||
s.add_dependency 'rqrcode'
|
||||
s.add_dependency 'bank-contact'
|
||||
|
||||
s.add_development_dependency 'bundler', '~> 1.17'
|
||||
s.add_development_dependency 'rake', '~> 10.0'
|
||||
s.add_development_dependency 'minitest', '~> 5.0'
|
||||
s.add_development_dependency 'simplecov'
|
||||
end
|
12
lib/girocode.rb
Normal file
12
lib/girocode.rb
Normal file
@ -0,0 +1,12 @@
|
||||
require 'bigdecimal'
|
||||
require 'bank/contact'
|
||||
require 'rqrcode'
|
||||
|
||||
require_relative 'girocode/version'
|
||||
require_relative 'girocode/code'
|
||||
|
||||
module Girocode
|
||||
def self.new(**attrs)
|
||||
Girocode::Code.new(**attrs)
|
||||
end
|
||||
end
|
119
lib/girocode/code.rb
Normal file
119
lib/girocode/code.rb
Normal file
@ -0,0 +1,119 @@
|
||||
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}"
|
||||
end
|
||||
attrs.each { |attr, value| send("#{attr}=", value) }
|
||||
raise ArgumentError, "iban is required" unless iban?
|
||||
raise ArgumentError, "name is required" unless name?
|
||||
raise ArgumentError, 'currency is required for amount' if amount && !currency?
|
||||
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)
|
||||
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
|
||||
raise ArgumentError, 'name too long' if value.size > 70
|
||||
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
|
||||
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
|
||||
end
|
||||
end
|
3
lib/girocode/version.rb
Normal file
3
lib/girocode/version.rb
Normal file
@ -0,0 +1,3 @@
|
||||
module Girocode
|
||||
VERSION = "0.1.0"
|
||||
end
|
41
test/data.txt
Normal file
41
test/data.txt
Normal file
@ -0,0 +1,41 @@
|
||||
xxxxxxx xxx xx xx xx xxxx x x xxxxxxx
|
||||
x x xxxxx xxx xx xxx xx x x
|
||||
x xxx x x x xxxxx xxx x xxx x xxx x
|
||||
x xxx x xx xxx x xxxxxxx x xxx x
|
||||
x xxx x xx x xx x xxxxxx x x x x xxx x
|
||||
x x xxx xx xx x xxx x xx x x x
|
||||
xxxxxxx x x x x x x x x x x x x x xxxxxxx
|
||||
xx xxx xxx x
|
||||
x xxxxxx x x xxx xxxxx xxxx x xxx
|
||||
xx x xx xx xx x x x xxxx x x x
|
||||
xx xxx x xx xxxx xxxxx x x x x
|
||||
x x xxxx xxxx x xxx xxxxx
|
||||
xx xxx xx xx x x x x xxx xxxx
|
||||
x x x xx x xxxx xxxx xxx x xxx xxxxx
|
||||
x xxxxxx xx x xx xxxxx x x xx xxxx
|
||||
xx x xx xx xx x x xx x x x x xx x
|
||||
xxxxxxx xxx xx x x x x x x
|
||||
xx x xxxx x x xx x x xx x x xx x
|
||||
xxx x x x xxx x xx xxxxx x
|
||||
xxx xx x x x x xx x xx x
|
||||
x x x xx xxx x xxxx x xxxxx xx x x
|
||||
xxx xx xx x x xx x x x x xxx x
|
||||
x x x xxxx xx xxxxxx x x x xx
|
||||
x xxx x xx x xx x xx x x xx
|
||||
x xxx x xx x xxxx x xx x xxxx xx
|
||||
x x x x xx xx x x xxxx xxxx
|
||||
x x xxxxxx xxxxxxx x xx x xx x x x
|
||||
xxx x xxxx xx xx x x xx xxx xxx xxx
|
||||
xx x xx xx x xxx xxx xx x xxx
|
||||
xx x xx x xxx x xx xxx x x x x
|
||||
xx x xxxxx x x x x xxxx xx x xxx xxx
|
||||
xx xxx xxx xx xxxx x xx x x x
|
||||
xxx xxxx x x xxx xxx xx xxxxxxxxx
|
||||
xx xxx xxxx xxx xx xx x x x
|
||||
xxxxxxx x x x xx xx x x xx x xx
|
||||
x x x xxxx xx x xx x x
|
||||
x xxx x xx x xx x xxxxxx x xxxxxxx x
|
||||
x xxx x xx x xxx xxx x xxx xxx x
|
||||
x xxx x x xxxxxxxxx xx xxxxx x x
|
||||
x x x x x x x xx xxx x x x x x
|
||||
xxxxxxx x x xx x xxx x x x xx xx
|
19
test/girocode_test.rb
Normal file
19
test/girocode_test.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require_relative 'test_helper'
|
||||
|
||||
class GirocodeTest < Minitest::Test
|
||||
def test_that_it_has_a_version_number
|
||||
refute_nil ::Girocode::VERSION
|
||||
end
|
||||
|
||||
def test_girocode
|
||||
attrs = { bic: 'BHBLDEHHXXX', name: 'Franz Mustermänn', iban: 'DE71110220330123456789', currency: :eur, amount: 12.3, purpose: 'GDDS', creditor_reference: 'RF18539007547034' }
|
||||
code = Girocode.new(attrs)
|
||||
assert_equal data(:data), code.to_ascii
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def data(name)
|
||||
Pathname(__dir__).join("#{name}.txt").read
|
||||
end
|
||||
end
|
30
test/test_helper.rb
Normal file
30
test/test_helper.rb
Normal file
@ -0,0 +1,30 @@
|
||||
ENV["RAILS_ENV"] = "test"
|
||||
|
||||
require 'pathname'
|
||||
|
||||
if RUBY_VERSION >= '1.9'
|
||||
require 'simplecov'
|
||||
SimpleCov.start do
|
||||
if artifacts_dir = ENV['CC_BUILD_ARTIFACTS']
|
||||
coverage_dir Pathname.new(artifacts_dir).relative_path_from(Pathname.new(SimpleCov.root)).to_s
|
||||
end
|
||||
add_filter '/test/'
|
||||
add_filter 'vendor'
|
||||
end
|
||||
|
||||
SimpleCov.at_exit do
|
||||
SimpleCov.result.format!
|
||||
if result = SimpleCov.result
|
||||
File.open(File.join(SimpleCov.coverage_path, 'coverage_percent.txt'), 'w') { |f| f << result.covered_percent.to_s }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'rubygems'
|
||||
require 'bundler/setup'
|
||||
Bundler.require(:default)
|
||||
#require 'byebug'
|
||||
|
||||
require 'minitest/autorun'
|
||||
|
||||
require 'girocode'
|
Loading…
x
Reference in New Issue
Block a user