iso-iban 0.0.4 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8dab7b32e738763d4edd921bb3a9f32555105f51
4
- data.tar.gz: 8d98494b70cb198d78130acf0dc259cb5b9c3a4e
3
+ metadata.gz: 72a53aeb4de7eb495706e51a08f34c861e706c03
4
+ data.tar.gz: e3af74706241ab8bff72986d8ed79aa34d1b2231
5
5
  SHA512:
6
- metadata.gz: da2844427c7e2766c23f510604495ac90d9700b43874c45e91df9bce081aac5eb3e179f5c5a52880f85d655a9df36204c284b39906d0f6f889104760101abe54
7
- data.tar.gz: 607166d6cb2757c9ca185460e7842c28eaa655a70b8cc2a5af45f29d100a74c6cfb449859f44826ebd467eaa154375d86125ef0d8ba8f0adfce7dc1e231324df
6
+ metadata.gz: cb22f4ea028de0f22100dfefba44860e0dcaec1cb3619b64659b0b22562a8aba3c3570577944363e22d4331123609d93b6c6a38f747874303adb0a15ecc4675f
7
+ data.tar.gz: 495fa573e6734141cc389101ed4bb83048e5619064bfe62b33548b230f01f649bb9898833c07a5e5a3a428e84ff77a5db1a442b7d67dee7964ccfd1380f87b22
@@ -1,4 +1,4 @@
1
- Copyright (c) 2012, Stefan Rusterholz <stefan.rusterholz@gmail.com>
1
+ Copyright (c) 2012-2014, Stefan Rusterholz <stefan.rusterholz@gmail.com>
2
2
  All rights reserved.
3
3
 
4
4
  Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
@@ -4,22 +4,36 @@ README
4
4
 
5
5
  Summary
6
6
  -------
7
- ISO::IBAN implements IBAN (International Bank Account Number) specification as per ISO 13616-1.
7
+
8
+ ISO::IBAN implements the IBAN (International Bank Account Number) specification as per ISO 13616-1.
8
9
  It provides methods to generate valid IBAN numbers from components, or to validate a given IBAN.
9
10
 
10
11
 
11
12
  Installation
12
13
  ------------
13
- `gem install iso-iban`
14
+
15
+ ### Via rubygems
16
+
17
+ gem install iso-iban
18
+
19
+ ### From github
20
+
21
+ git clone https://github.com/apeiros/iso-iban.git
22
+ cd iso-iban
23
+ rm -r *.gem
24
+ gem build *.gemspec
25
+ gem install *.gem
14
26
 
15
27
 
16
28
  Usage
17
29
  -----
18
30
 
19
- ISO::IBAN.valid?('CH35 1234 5987 6543 2109 A') # => true
20
- ISO::IBAN.validate('CH37 1234 5987 6543 2109 A') # => [:invalid_checksum]
21
- ISO::IBAN.generate('CH', '12345', '987') # => #<ISO::IBAN CH76 1234 5000 0000 0098 7>
22
- iban = ISO::IBAN.new('CH35 1234 5987 6543 2109 A') # => #<ISO::IBAN CH35 1234 5987 6543 2109 A>
31
+ require 'iso/iban'
32
+ ISO::IBAN.valid?('CH35 1234 5987 6543 2109 A') # => true
33
+ ISO::IBAN.validate('CH37 1234 5987 6543 2109 A') # => [:invalid_checksum]
34
+ ISO::IBAN.generate('CH', '12345', '987') # => #<ISO::IBAN CH76 1234 5000 0000 0098 7>
35
+ iban = ISO::IBAN.parse('CH35 1234 5987 6543 2109 A') # => #<ISO::IBAN CH35 1234 5987 6543 2109 A>
36
+ iban = ISO::IBAN.new('CH351234598765432109A') # => #<ISO::IBAN CH35 1234 5987 6543 2109 A>
23
37
  iban.formatted # => "CH35 1234 5987 6543 2109 A"
24
38
  iban.compact # => "CH351234598765432109A"
25
39
  iban.country # => "CH"
@@ -29,6 +43,18 @@ Usage
29
43
  iban.valid? # => true
30
44
  iban.validate # => []
31
45
 
46
+ **Note:** iso/iban automatically loads the IBAN specifications delivered with the gem. If you do not wish
47
+ those to be loaded, `require 'iso/iban/no_autoload'` instead.
48
+
49
+
50
+ ENV
51
+ ---
52
+
53
+ ISO::IBAN.load_specifications (which is automatically called when you require 'iso/iban') uses the
54
+ ENV variable `IBAN_SPECIFICATIONS` to determine where to look for IBAN specifications. If that
55
+ variable is not set, it will default to the datafile delivered with the gem.
56
+
57
+
32
58
  Links
33
59
  -----
34
60
 
@@ -42,4 +68,4 @@ License
42
68
  -------
43
69
 
44
70
  You can use this code under the {file:LICENSE.txt BSD-2-Clause License}, free of charge.
45
- If you need a different license, please ask the author.
71
+ If you need a different license, please ask the author.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "iso-iban"
5
- s.version = "0.0.4"
5
+ s.version = "0.1.0"
6
6
  s.authors = "Stefan Rusterholz"
7
7
  s.email = "stefan.rusterholz@gmail.com"
8
8
  s.homepage = "https://github.com/apeiros/iso-iban"
@@ -16,6 +16,18 @@ Gem::Specification.new do |s|
16
16
  Utilities for International Bank Account Numbers (IBAN) as per ISO 13616-1.
17
17
  SUMMARY
18
18
 
19
+ s.post_install_message = <<-POST_INSTALL_MESSAGE.gsub(/^ /, '').chomp
20
+ IMPORTANT!
21
+
22
+ As of 0.1.0, there are 2 backward incompatible changes:
23
+
24
+ * `require 'iso/iban/autoload'` is deprecated. Please use plain `require 'iso/iban'`.
25
+ To load ISO::IBAN without loading the specifications, please use
26
+ `require 'iso/iban/no_autoload'`.
27
+ * ISO::IBAN.new no longer accepts formatted input.
28
+ Use ISO::IBAN.parse if your input is potentially not in the compact format.
29
+ POST_INSTALL_MESSAGE
30
+
19
31
  s.files =
20
32
  Dir['bin/**/*'] +
21
33
  Dir['data/**/*'] +
@@ -1,373 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'iso/iban/specification'
4
- require 'iso/iban/version'
5
- require 'yaml'
3
+ # Actual implementation is in iso/iban/no_autoload.rb
4
+ require 'iso/iban/no_autoload'
6
5
 
7
- module ISO
8
-
9
- # IBAN - ISO 13616-1
10
- #
11
- # General IBAN Information
12
- # ========================
13
- #
14
- # * What is an IBAN?
15
- # IBAN stands for International Bank Account Number. It is the ISO 13616
16
- # international standard for numbering bank accounts. In 2006, the
17
- # International Organization for Standardization (ISO) designated SWIFT as
18
- # the Registration Authority for ISO 13616.
19
- #
20
- # * Use
21
- # The IBAN facilitates the communication and processing of cross-border
22
- # transactions. It allows exchanging account identification details in a
23
- # machine-readable form.
24
- #
25
- #
26
- # The ISO 13616 IBAN Standard
27
- # ===========================
28
- #
29
- # * Structure
30
- # The IBAN structure is defined in ISO 13616-1 and consists of a two-letter
31
- # ISO 3166-1 country code, followed by two check digits and up to thirty
32
- # alphanumeric characters for a BBAN (Basic Bank Account Number) which has a
33
- # fixed length per country and, included within it, a bank identifier with a
34
- # fixed position and a fixed length per country. The check digits are
35
- # calculated based on the scheme defined in ISO/IEC 7064 (MOD97-10).
36
- #
37
- # * Terms and definitions
38
- # Bank identifier: The identifier that uniquely identifies the financial
39
- # institution and, when appropriate, the branch of that financial institution
40
- # servicing an account
41
- #
42
- # `In this registry, the branch identifier format is shown specifically, when
43
- # present.`
44
- #
45
- # *BBAN*: basic bank account number: The identifier that uniquely identifies
46
- # an individual account, at a specific financial institution, in a particular
47
- # country. The BBAN includes a bank identifier of the financial institution
48
- # servicing that account.
49
- # *IBAN*: international bank account number: The expanded version of the
50
- # basic bank account number (BBAN), intended for use internationally. The
51
- # IBAN uniquely identifies an individual account, at a specific financial
52
- # institution, in a particular country.
53
- #
54
- # * Submitters
55
- # Nationally-agreed, ISO13616-compliant IBAN formats are submitted to the
56
- # registration authority exclusively by the National Standards Body or the
57
- # National Central Bank of the country.
58
- class IBAN
59
- include Comparable
60
-
61
- # Character code translation used to convert an IBAN into its numeric
62
- # (digits-only) form
63
- CharacterCodes = Hash[('0'..'9').zip('0'..'9')+('a'..'z').zip(10..35)+('A'..'Z').zip(10..35)]
64
-
65
- # All uppercase letters
66
- UpperAlpha = [*'A'..'Z']
67
-
68
- # All lowercase letters
69
- LowerAlpha = [*'a'..'z']
70
-
71
- # All digits
72
- Digits = [*'0'..'9']
73
-
74
- # All uppercase letters, lowercase letters and digits
75
- AlphaNumeric = [*'A'..'Z', *'a'..'z', *'0'..'9']
76
-
77
- # All specifications, see ISO::IBAN::Specification
78
- @specifications = nil
79
-
80
- # Load the IBAN specifications file, which determines how the IBAN
81
- # for any given country looks like.
82
- #
83
- # It will use the following sources in this order (first one which exists wins)
84
- #
85
- # * Path passed as spec_file parameter
86
- # * Path provided by the env variable IBAN_SPECIFICATIONS
87
- # * The file ../data/iso-iban/specs.yaml relative to the lib dir
88
- # * The Gem datadir path
89
- #
90
- # @param [String] spec_file
91
- # Override the default specifications file path.
92
- #
93
- # @return [self]
94
- def self.load_specifications(spec_file=nil)
95
- if spec_file then
96
- # do nothing
97
- elsif ENV['IBAN_SPECIFICATIONS'] then
98
- spec_file = ENV['IBAN_SPECIFICATIONS']
99
- else
100
- spec_file = File.expand_path('../../../data/iso-iban/specs.yaml', __FILE__)
101
- if !File.file?(spec_file) && defined?(Gem) && Gem.datadir('iso-iban')
102
- spec_file = Gem.datadir('iso-iban')+'/specs.yaml'
103
- end
104
- end
105
-
106
- if spec_file && File.file?(spec_file)
107
- @specifications = ISO::IBAN::Specification.load_yaml(spec_file)
108
- else
109
- raise "Could not load IBAN specifications, no specs file found."
110
- end
111
-
112
- self
113
- end
114
-
115
- # @return [Hash<String => ISO::IBAN::Specification>]
116
- # A hash with the country (ISO3166 2-letter) as key and the specification for that country as value
117
- def self.specifications
118
- @specifications || raise("No specifications have been loaded yet.")
119
- end
120
-
121
- # @param [String] a2_country_code
122
- # The country (ISO3166 2-letter), e.g. 'CH' or 'DE'.
123
- #
124
- # @return [ISO::IBAN::Specification]
125
- # The specification for the given country
126
- def self.specification(a2_country_code, *default, &default_block)
127
- specifications.fetch(a2_country_code, *default, &default_block)
128
- end
129
-
130
- # @param [String] iban
131
- # An IBAN number, either in compact or human format.
132
- #
133
- # @return [true, false]
134
- # Whether the IBAN is valid.
135
- # See {#validate} for details.
136
- def self.valid?(iban)
137
- new(iban).valid?
138
- end
139
-
140
- # @param [String] iban
141
- # An IBAN number, either in compact or human format.
142
- #
143
- # @return [Array<Symbol>]
144
- # An array with a code of all validation errors, empty if valid.
145
- # See {#validate} for details.
146
- def self.validate(iban)
147
- new(iban).validate
148
- end
149
-
150
- # @param [String] iban
151
- # The IBAN in either compact or human readable form.
152
- #
153
- # @return [String]
154
- # The IBAN in compact form.
155
- def self.strip(iban)
156
- iban.tr(' -', '')
157
- end
158
-
159
- # Generate an IBAN from country code and components, automatically filling in the checksum.
160
- #
161
- # @example Generate an IBAN for UBS Switzerland with account number '12345'
162
- # ISO::IBAN.generate('CH', '216', '12345') # => #<ISO::IBAN CH92 0021 6000 0000 1234 5>
163
- #
164
- # @param [String] country
165
- # The ISO-3166 2-letter country code.
166
- #
167
- def self.generate(country, *components)
168
- spec = specification(country)
169
- justified = spec.component_lengths.zip(components).map { |length, component| component.rjust(length, "0") }
170
- iban = new(country+'??'+justified.join(''))
171
- iban.update_checksum!
172
-
173
- iban
174
- end
175
-
176
- # @param [String] countries
177
- # A list of 2 letter country codes. If empty, all countries in
178
- # ISO::IBAN::specifications are used.
179
- #
180
- # @return [ISO::IBAN] A random, valid IBAN
181
- def self.random(*countries)
182
- countries = specifications.keys if countries.empty?
183
- country = countries.sample
184
- account = specification(country).iban_structure.scan(/([A-Z]+)|(\d+)(!?)([nac])/).map { |exact, length, fixed, code|
185
- if exact
186
- exact
187
- elsif code == 'a'
188
- Array.new(length.to_i) { UpperAlpha.sample }.join('')
189
- elsif code == 'c'
190
- Array.new(length.to_i) { AlphaNumeric.sample }.join('')
191
- elsif code == 'e'
192
- ' '*length.to_i
193
- elsif code == 'n'
194
- Array.new(length.to_i) { Digits.sample }.join('')
195
- end
196
- }.join('')
197
- account[2,2] = '??'
198
- iban = new(account)
199
- iban.update_checksum!
200
-
201
- iban
202
- end
203
-
204
- # Converts a String into its digits-only form, i.e. all characters a-z are replaced with their corresponding
205
- # digit sequences, according to the IBAN specification.
206
- #
207
- # @param [String] string
208
- # The string to convert into its numeric form.
209
- #
210
- # @return [String] The string in its numeric, digits-only form.
211
- def self.numerify(string)
212
- string.downcase.gsub(/\D/) { |char|
213
- CharacterCodes.fetch(char) {
214
- raise ArgumentError, "The string contains an invalid character #{char.inspect}"
215
- }
216
- }.to_i
217
- end
218
-
219
- # @return [String] The standard form of the IBAN for machine communication, without spaces.
220
- attr_reader :compact
221
-
222
- # @return [String] The ISO-3166 2-letter country code.
223
- attr_reader :country
224
-
225
- # @return [ISO::IBAN::Specification] The specification for this IBAN (determined by its country).
226
- attr_reader :specification
227
-
228
- # @param [String] iban
229
- # The IBAN number, either in formatted, human readable or in compact form.
230
- def initialize(iban)
231
- raise ArgumentError, "String expected for iban, but got #{iban.class}" unless iban.is_a?(String)
232
-
233
- @compact = self.class.strip(iban)
234
- @country = iban[0,2]
235
- @specification = self.class.specification(@country, nil)
236
- end
237
-
238
- # @example Formatted IBAN
239
- #
240
- # ISO::IBAN.new('CH')
241
- #
242
- # @return [String] The IBAN in its formatted form, which is more human readable than the compact form.
243
- def formatted
244
- @_formatted ||= @compact.gsub(/.{4}(?=.)/, '\0 ')
245
- end
246
-
247
- # @return [String]
248
- # IBAN in its numeric form, i.e. all characters a-z are replaced with their corresponding
249
- # digit sequences.
250
- def numeric
251
- @compact.size < 5 ? nil : self.class.numerify(@compact[4..-1]+@compact[0,4])
252
- end
253
-
254
- # @return [true, false]
255
- # Whether the IBAN is valid.
256
- # See {#validate} for details.
257
- def valid?
258
- validate.empty?
259
- end
260
-
261
- # Validation error codes:
262
- #
263
- # * :invalid_country
264
- # * :invalid_checksum
265
- # * :invalid_length
266
- # * :invalid_format
267
- #
268
- # Invalid country means the country is unknown (char 1 & 2 in the IBAN).
269
- # Invalid checksum means the two check digits (char 3 & 4 in the IBAN).
270
- # Invalid length means the IBAN does not comply with the length specified for the country of that IBAN.
271
- # Invalid format means the IBAN does not comply with the format specified for the country of that IBAN.
272
- #
273
- # @return [Array<Symbol>] An array with a code of all validation errors, empty if valid.
274
- def validate
275
- errors = []
276
- errors << :invalid_country unless valid_country?
277
- errors << :invalid_checksum unless valid_checksum?
278
- errors << :invalid_length unless valid_length?
279
- errors << :invalid_format unless valid_format?
280
-
281
- errors
282
- end
283
-
284
- # @return [String] The checksum digits in the IBAN.
285
- def checksum_digits
286
- @compact[2,2]
287
- end
288
-
289
- # @return [String] The BBAN of the IBAN.
290
- def bban
291
- @compact[4..-1]
292
- end
293
-
294
- # @return [String, nil] The bank code part of the IBAN, nil if not applicable.
295
- def bank_code
296
- if @specification && @specification.bank_position_from && @specification.bank_position_to
297
- @compact[@specification.bank_position_from..@specification.bank_position_to]
298
- else
299
- nil
300
- end
301
- end
302
-
303
- # @return [String, nil] The branch code part of the IBAN, nil if not applicable.
304
- def branch_code
305
- if @specification && @specification.branch_position_from && @specification.branch_position_to
306
- @compact[@specification.branch_position_from..@specification.branch_position_to]
307
- else
308
- nil
309
- end
310
- end
311
-
312
- # @return [String] The account code part of the IBAN.
313
- def account_code
314
- @compact[((@specification.branch_position_to || @specification.bank_position_to || 3)+1)..-1]
315
- end
316
-
317
- # @return [true, false] Whether the country of the IBAN is valid.
318
- def valid_country?
319
- @specification ? true : false
320
- end
321
-
322
- # @return [true, false] Whether the format of the IBAN is valid.
323
- def valid_format?
324
- specification && specification.iban_regex =~ @compact ? true : false
325
- end
326
-
327
- # @return [true, false] Whether the length of the IBAN is valid.
328
- def valid_length?
329
- specification && @compact.size == specification.iban_length ? true : false
330
- end
331
-
332
- # @return [true, false] Whether the checksum of the IBAN is valid.
333
- def valid_checksum?
334
- numerified = numeric()
335
-
336
- numerified && (numerified % 97 == 1)
337
- end
338
-
339
- # See Object#<=>
340
- #
341
- # @return [-1, 0, 1, nil]
342
- def <=>(other)
343
- other.respond_to?(:compact) ? @compact <=> other.compact : nil
344
- end
345
-
346
- # Requires that the checksum digits were left as '??', replaces them with
347
- # the proper checksum.
348
- #
349
- # @return [self]
350
- def update_checksum!
351
- raise "Checksum digit placeholders missing" unless @compact[2,2] == '??'
352
-
353
- @compact[2,2] = calculated_check_digits
354
-
355
- self
356
- end
357
-
358
- # @return [String] The check-digits as calculated from the IBAN.
359
- def calculated_check_digits
360
- "%02d" % (98-(self.class.numerify(bban+@country)*100)%97)
361
- end
362
-
363
- # See Object#inspect
364
- def inspect
365
- sprintf "#<%p %s>", self.class, formatted
366
- end
367
-
368
- # @return [String] The compact form of the IBAN as a String.
369
- def to_s
370
- @compact.dup
371
- end
372
- end
373
- end
6
+ ISO::IBAN.load_specifications
@@ -1,5 +1,9 @@
1
- # This file automatically loads all specifications. It's recommended that you require this file instead of iso/iban.
1
+ # encoding: utf-8
2
2
 
3
- require 'iso/iban'
3
+ # This file used automatically load all specifications.
4
+ # Now this is the default behavior of require 'iso/iban', and this file only
5
+ # exists to inform about the move.
6
+ # The file will be removed (at the earlier of) either when reaching 1.0 or in
7
+ # february 2015 (1 year from now).
4
8
 
5
- ISO::IBAN.load_specifications
9
+ raise "Deprecated - use just `require 'iso/iban'` instead.\nIf you wish to suppress autoloading, use `require 'iso/iban/no_autoload'`."
@@ -0,0 +1,17 @@
1
+ # encoding: utf-8
2
+
3
+ # Port some newer ruby methods back
4
+ unless String.method_defined?(:b)
5
+
6
+ # Backports from ruby 2.0 used in ISO::IBAN
7
+ class String
8
+
9
+ # @example
10
+ # str.b -> str
11
+ #
12
+ # Returns a copied string whose encoding is ASCII-8BIT.
13
+ def b
14
+ dup.force_encoding(Encoding::BINARY)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ module ISO
4
+ class IBAN
5
+
6
+ # Raised by ISO::IBAN::parse!
7
+ class Invalid < ArgumentError
8
+
9
+ # @return [Array<Symbol>] The errors in the IBAN.
10
+ # @see ISO::IBAN#validate
11
+ attr_reader :errors
12
+
13
+ # @return [ISO::IBAN] The faulty IBAN.
14
+ attr_reader :iban
15
+
16
+ # @param [ISO::IBAN] iban
17
+ # The faulty IBAN.
18
+ def initialize(iban)
19
+ @iban = iban
20
+ @errors = iban.validate
21
+ super("The IBAN #{@iban.formatted} is invalid (#{@errors.join(', ')})")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,481 @@
1
+ # encoding: utf-8
2
+
3
+ require 'iso/iban/backports'
4
+ require 'iso/iban/invalid'
5
+ require 'iso/iban/specification'
6
+ require 'iso/iban/version'
7
+ require 'yaml'
8
+
9
+ # Container for classes which implement ISO standards.
10
+ module ISO
11
+
12
+ # IBAN - ISO 13616-1
13
+ #
14
+ # Usage
15
+ # =====
16
+ #
17
+ # require 'iso/iban'
18
+ # ISO::IBAN.valid?('CH35 1234 5987 6543 2109 A') # => true
19
+ # ISO::IBAN.validate('CH37 1234 5987 6543 2109 A') # => [:invalid_checksum]
20
+ # ISO::IBAN.generate('CH', '12345', '987') # => #<ISO::IBAN CH76 1234 5000 0000 0098 7>
21
+ # iban = ISO::IBAN.parse('CH35 1234 5987 6543 2109 A') # => #<ISO::IBAN CH35 1234 5987 6543 2109 A>
22
+ # iban = ISO::IBAN.new('CH351234598765432109A') # => #<ISO::IBAN CH35 1234 5987 6543 2109 A>
23
+ # iban.formatted # => "CH35 1234 5987 6543 2109 A"
24
+ # iban.compact # => "CH351234598765432109A"
25
+ # iban.country # => "CH"
26
+ # iban.checksum_digits # => "35"
27
+ # iban.bank_code # => "12345"
28
+ # iban.account_code # => "98765432109A"
29
+ # iban.valid? # => true
30
+ # iban.validate # => []
31
+ #
32
+ # General IBAN Information
33
+ # ========================
34
+ #
35
+ # * **What is an IBAN?**
36
+ # IBAN stands for International Bank Account Number. It is the ISO 13616
37
+ # international standard for numbering bank accounts. In 2006, the
38
+ # International Organization for Standardization (ISO) designated SWIFT as
39
+ # the Registration Authority for ISO 13616.
40
+ #
41
+ # * **Use**
42
+ # The IBAN facilitates the communication and processing of cross-border
43
+ # transactions. It allows exchanging account identification details in a
44
+ # machine-readable form.
45
+ #
46
+ #
47
+ # The ISO 13616 IBAN Standard
48
+ # ===========================
49
+ #
50
+ # * **Structure**
51
+ # The IBAN structure is defined in ISO 13616-1 and consists of a two-letter
52
+ # ISO 3166-1 country code, followed by two check digits and up to thirty
53
+ # alphanumeric characters for a BBAN (Basic Bank Account Number) which has a
54
+ # fixed length per country and, included within it, a bank identifier with a
55
+ # fixed position and a fixed length per country. The check digits are
56
+ # calculated based on the scheme defined in ISO/IEC 7064 (MOD97-10).
57
+ #
58
+ # * **Terms and definitions**
59
+ # * *Bank identifier:* The identifier that uniquely identifies the financial
60
+ # institution and, when appropriate, the branch of that financial institution
61
+ # servicing an account.
62
+ # * *BBAN:* basic bank account number: The identifier that uniquely identifies
63
+ # an individual account, at a specific financial institution, in a particular
64
+ # country. The BBAN includes a bank identifier of the financial institution
65
+ # servicing that account.
66
+ # * *IBAN:* international bank account number: The expanded version of the
67
+ # basic bank account number (BBAN), intended for use internationally. The
68
+ # IBAN uniquely identifies an individual account, at a specific financial
69
+ # institution, in a particular country.
70
+ #
71
+ # * **Submitters**
72
+ # Nationally-agreed, ISO13616-compliant IBAN formats are submitted to the
73
+ # registration authority exclusively by the National Standards Body or the
74
+ # National Central Bank of the country.
75
+ class IBAN
76
+ include Comparable
77
+
78
+ # Character code translation used to convert an IBAN into its numeric
79
+ # (digits-only) form
80
+ CharacterCodes = Hash[('0'..'9').zip('0'..'9')+('a'..'z').zip(10..35)+('A'..'Z').zip(10..35)]
81
+
82
+ # All uppercase letters
83
+ UpperAlpha = [*'A'..'Z']
84
+
85
+ # All lowercase letters
86
+ LowerAlpha = [*'a'..'z']
87
+
88
+ # All digits
89
+ Digits = [*'0'..'9']
90
+
91
+ # All uppercase letters, lowercase letters and digits
92
+ AlphaNumeric = [*'A'..'Z', *'a'..'z', *'0'..'9']
93
+
94
+ # All specifications, see ISO::IBAN::Specification
95
+ @specifications = nil
96
+
97
+ # @note
98
+ # Using `require 'iso/iban'` will automatically invoke this method.
99
+ # If you do not wish this behavior, `require 'iso/iban/no_autoload'` instead.
100
+ #
101
+ # Load the IBAN specifications file, which determines how the IBAN
102
+ # for any given country looks.
103
+ #
104
+ # It will use the following sources in this order (first one which exists wins)
105
+ #
106
+ # * Path passed as `spec_file` parameter
107
+ # * Path provided by the env variable `IBAN_SPECIFICATIONS`
108
+ # * The file `../data/iso-iban/specs.yaml` relative to the lib dir
109
+ # * The Gem datadir path
110
+ #
111
+ # @param [String] spec_file
112
+ # Override the default specifications file path.
113
+ #
114
+ # @return [self]
115
+ def self.load_specifications(spec_file=nil)
116
+ if spec_file then
117
+ # do nothing
118
+ elsif ENV['IBAN_SPECIFICATIONS'] then
119
+ spec_file = ENV['IBAN_SPECIFICATIONS']
120
+ else
121
+ spec_file = File.expand_path('../../../../data/iso-iban/specs.yaml', __FILE__)
122
+ if !File.file?(spec_file) && defined?(Gem) && Gem.datadir('iso-iban')
123
+ spec_file = Gem.datadir('iso-iban')+'/specs.yaml'
124
+ end
125
+ end
126
+
127
+ if spec_file && File.file?(spec_file)
128
+ @specifications = ISO::IBAN::Specification.load_yaml(spec_file)
129
+ elsif spec_file
130
+ raise "Could not load IBAN specifications, specs file #{spec_file.inspect} does not exist or can't be read."
131
+ else
132
+ raise "Could not load IBAN specifications, no specs file found."
133
+ end
134
+
135
+ self
136
+ end
137
+
138
+ # @return [Hash<String => ISO::IBAN::Specification>]
139
+ # A hash with the country (ISO3166 2-letter) as key and the specification for that country as value.
140
+ def self.specifications
141
+ @specifications || raise("No specifications have been loaded yet - Check the docs for ISO::IBAN::load_specifications.")
142
+ end
143
+
144
+ # @param [String] a2_country_code
145
+ # The country (ISO3166 2-letter), e.g. 'CH' or 'DE'.
146
+ #
147
+ # @return [ISO::IBAN::Specification]
148
+ # The specification for the given country.
149
+ def self.specification(a2_country_code, *default, &default_block)
150
+ specifications.fetch(a2_country_code, *default, &default_block)
151
+ end
152
+
153
+ # @param [String] iban
154
+ # An IBAN number, either in compact or human format.
155
+ #
156
+ # @return [true, false]
157
+ # Whether the IBAN is valid.
158
+ # See {#validate} for details.
159
+ def self.valid?(iban)
160
+ parse(iban).valid?
161
+ end
162
+
163
+ # @param [String] iban
164
+ # An IBAN number, either in compact or human format.
165
+ #
166
+ # @return [Array<Symbol>]
167
+ # An array with a code of all validation errors, empty if valid.
168
+ # See {#validate} for details.
169
+ def self.validate(iban)
170
+ parse(iban).validate
171
+ end
172
+
173
+ # @param [String] iban
174
+ # The IBAN in either compact or human readable form.
175
+ #
176
+ # @return [String]
177
+ # The IBAN in compact form, all whitespace and dashes stripped.
178
+ def self.strip(iban)
179
+ iban.delete("\n\r\t -")
180
+ end
181
+
182
+ # Like ISO::IBAN.parse, but raises an ISO::IBAN::Invalid exception if the IBAN is invalid.
183
+ #
184
+ # @param [String] iban_number
185
+ # The IBAN in either compact or human readable form.
186
+ #
187
+ # @return [ISO::IBAN]
188
+ # An IBAN instance representing the passed IBAN number.
189
+ def self.parse!(iban_number)
190
+ iban = parse(iban_number)
191
+ raise Invalid.new(iban) unless iban.valid?
192
+
193
+ iban
194
+ end
195
+
196
+ # @param [String] iban_number
197
+ # The IBAN in either compact or human readable form.
198
+ #
199
+ # @return [ISO::IBAN]
200
+ # An IBAN instance representing the passed IBAN number.
201
+ def self.parse(iban_number)
202
+ new(strip(iban_number))
203
+ end
204
+
205
+ # Generate an IBAN from country code and components, automatically filling in the checksum.
206
+ #
207
+ # @note
208
+ # `generate` will pad all segments with zeros, which means it will generate invalid
209
+ # IBANs if you provide too short segments which are alphabetic only.
210
+ # For example, `ISO::IBAN.generate('BG', 'A', '2', 'C')` generates an invalid IBAN.
211
+ #
212
+ # @example Generate an IBAN for UBS Switzerland with account number '12345'
213
+ # ISO::IBAN.generate('CH', '216', '12345') # => #<ISO::IBAN CH92 0021 6000 0000 1234 5>
214
+ #
215
+ # @param [String] country
216
+ # The ISO-3166 2-letter country code.
217
+ #
218
+ def self.generate(country, *components)
219
+ spec = specification(country)
220
+ justified = spec.component_lengths.zip(components).map { |length, component| component.rjust(length, "0") }
221
+ iban = new(country+'??'+justified.join(''))
222
+ iban.update_checksum!
223
+
224
+ iban
225
+ end
226
+
227
+ # @param [String] countries
228
+ # A list of 2 letter country codes. If empty, all countries in ISO::IBAN::specifications are used.
229
+ #
230
+ # @return [ISO::IBAN] A random, valid IBAN.
231
+ def self.random(*countries)
232
+ countries = specifications.keys if countries.empty?
233
+ country = countries.sample
234
+ account = specification(country).iban_structure.scan(/([A-Z]+)|(\d+)(!?)([nac])/).map { |exact, length, fixed, code|
235
+ if exact
236
+ exact
237
+ elsif code == 'a'
238
+ Array.new(length.to_i) { UpperAlpha.sample }.join('')
239
+ elsif code == 'c'
240
+ Array.new(length.to_i) { AlphaNumeric.sample }.join('')
241
+ elsif code == 'e'
242
+ ' '*length.to_i
243
+ elsif code == 'n'
244
+ Array.new(length.to_i) { Digits.sample }.join('')
245
+ end
246
+ }.join('')
247
+ account[2,2] = '??'
248
+ iban = new(account)
249
+ iban.update_checksum!
250
+
251
+ iban
252
+ end
253
+
254
+ # Converts a String into its digits-only form, i.e. all characters a-z are replaced with their corresponding
255
+ # digit sequences, according to the IBAN specification.
256
+ #
257
+ # @param [String] string
258
+ # The string to convert into its numeric form.
259
+ #
260
+ # @return [String] The string in its numeric, digits-only form.
261
+ def self.numerify(string)
262
+ string.downcase.gsub(/\D/) { |char|
263
+ CharacterCodes.fetch(char) {
264
+ raise ArgumentError, "The string contains an invalid character #{char.inspect}"
265
+ }
266
+ }.to_i
267
+ end
268
+
269
+ # @return [String] The standard form of the IBAN for machine communication, without spaces, encoded in Encoding::BINARY.
270
+ attr_reader :compact
271
+
272
+ # @return [String] The ISO-3166 2-letter country code (first and second character).
273
+ attr_reader :country
274
+
275
+ # @return [ISO::IBAN::Specification] The specification for this IBAN (determined by its country).
276
+ attr_reader :specification
277
+
278
+ # @param [String] iban
279
+ # The IBAN number, must be in compact form. Use ISO::IBAN::parse for formatted IBANs.
280
+ def initialize(iban)
281
+ raise ArgumentError, "String expected for iban, but got #{iban.class}" unless iban.is_a?(String)
282
+
283
+ @compact = iban.b
284
+ @country = iban[0,2]
285
+ @specification = self.class.specification(@country, nil)
286
+ end
287
+
288
+ # @example Formatted IBAN
289
+ #
290
+ # ISO::IBAN.new('CH')
291
+ #
292
+ # @return [String] The IBAN in its formatted form, which is more human readable than the compact form.
293
+ def formatted
294
+ @_formatted ||= @compact.gsub(/.{4}(?=.)/, '\0 ')
295
+ end
296
+
297
+ # @return [Integer, nil]
298
+ # IBAN in its numeric form, i.e. all characters a-z are replaced with their corresponding
299
+ # digit sequences.
300
+ # Will return nil if the IBAN is shorter than 5 characters (which makes it invalid).
301
+ def numeric
302
+ @compact.size < 5 ? nil : self.class.numerify(@compact[4..-1]+@compact[0,4])
303
+ end
304
+
305
+ # @return [true, false]
306
+ # Whether the IBAN is valid.
307
+ # See {#validate} for details.
308
+ def valid?
309
+ valid_country? && valid_checksum? && valid_length? && valid_format?
310
+ end
311
+
312
+ # @note
313
+ # The class method {ISO::IBAN.validate} uses {ISO::IBAN.parse}, which means it will strip whitespace and
314
+ # dashes from the IBAN.
315
+ # {ISO::IBAN#initialize} on the other hand expects the IBAN in compact format and will not strip
316
+ # those characters.
317
+ #
318
+ # Error codes:
319
+ #
320
+ # * :invalid_characters
321
+ # * :invalid_country
322
+ # * :invalid_checksum
323
+ # * :invalid_length
324
+ # * :invalid_format
325
+ #
326
+ # Invalid characters means that the IBAN contains characters which are not in the set of A-Za-z0-9. See {#invalid_characters}.
327
+ # Invalid country means the country is unknown (character 1 & 2 in the IBAN).
328
+ # Invalid checksum means the two check digits (character 3 & 4 in the IBAN).
329
+ # Invalid length means the IBAN does not comply with the length specified for the country of that IBAN.
330
+ # Invalid format means the IBAN does not comply with the format specified for the country of that IBAN.
331
+ #
332
+ # @return [Array<Symbol>] An array with a code of all validation errors, empty if valid.
333
+ def validate
334
+ errors = []
335
+ errors << :invalid_characters unless valid_characters?
336
+ errors << :invalid_country unless valid_country?
337
+ errors << :invalid_checksum unless valid_characters? && valid_checksum?
338
+ errors << :invalid_length unless valid_length?
339
+ errors << :invalid_format unless valid_format?
340
+
341
+ errors
342
+ end
343
+
344
+ # @return [String] The checksum digits in the IBAN (character 3 & 4).
345
+ def checksum_digits
346
+ @compact[2,2]
347
+ end
348
+
349
+ # @return [String] The BBAN of the IBAN (everything except the country code and check digits).
350
+ def bban
351
+ @compact[4..-1]
352
+ end
353
+
354
+ # @return [String, nil] The bank code part of the IBAN, nil if not applicable.
355
+ def bank_code
356
+ if @specification && @specification.bank_position_from && @specification.bank_position_to
357
+ @compact[@specification.bank_position_from..@specification.bank_position_to]
358
+ else
359
+ nil
360
+ end
361
+ end
362
+
363
+ # @return [String, nil] The branch code part of the IBAN, nil if not applicable.
364
+ def branch_code
365
+ if @specification && @specification.branch_position_from && @specification.branch_position_to
366
+ @compact[@specification.branch_position_from..@specification.branch_position_to]
367
+ else
368
+ nil
369
+ end
370
+ end
371
+
372
+ # @return [String] The account code part of the IBAN.
373
+ def account_code
374
+ @compact[((@specification.branch_position_to || @specification.bank_position_to || 3)+1)..-1]
375
+ end
376
+
377
+ # @example
378
+ # invalid = "hägar"
379
+ # invalid.encoding # => #<Encoding:UTF-8>
380
+ # ISO::IBAN.new(invalid).invalid_characters # => ["\xC3", "\xA4"]
381
+ # ISO::IBAN.new(invalid).invalid_characters('utf-8') # => ["ä"]
382
+ #
383
+ # @param [String, Encoding, nil] input_encoding
384
+ # ISO::IBAN::new interprets the passed IBAN as binary.
385
+ # If you got the IBAN from a source which is not binary, you should provide that encoding.
386
+ # Otherwise an invalid character may be split into multiple bytes.
387
+ #
388
+ # @return [Array] An Array with all invalid characters.
389
+ def invalid_characters(input_encoding=nil)
390
+ iban = input_encoding ? @compact.dup.force_encoding(input_encoding) : @compact
391
+
392
+ iban.gsub(/[A-Z0-9?]*/i, '').chars.to_a.uniq # to_a is for ruby <= 2.0 where String#chars returns an Enumerator
393
+ end
394
+
395
+ # @return [true, false] Whether IBAN consists only of valid characters.
396
+ def valid_characters?
397
+ @compact =~ /\A[A-Z]{2}(?:\d\d|\?\?)[A-Z0-9]*\z/in ? true : false
398
+ end
399
+
400
+ # @return [true, false] Whether the country of the IBAN is valid.
401
+ def valid_country?
402
+ @specification ? true : false
403
+ end
404
+
405
+ # @return [true, false] Whether the format of the IBAN is valid.
406
+ def valid_format?
407
+ @specification && @specification.iban_regex =~ @compact ? true : false
408
+ end
409
+
410
+ # @return [true, false] Whether the length of the IBAN is valid.
411
+ def valid_length?
412
+ @specification && @compact.size == @specification.iban_length ? true : false
413
+ end
414
+
415
+ # @return [true, false] Whether the checksum of the IBAN is valid.
416
+ def valid_checksum?
417
+ numerified = numeric()
418
+
419
+ numerified && (numerified % 97 == 1)
420
+ end
421
+
422
+ # See Object#<=>
423
+ #
424
+ # @return [-1, 0, 1, nil]
425
+ def <=>(other)
426
+ other.respond_to?(:compact) ? @compact <=> other.compact : nil
427
+ end
428
+
429
+ # Requires that the checksum digits were left as '??', replaces them with
430
+ # the proper checksum.
431
+ #
432
+ # @return [self]
433
+ def update_checksum!
434
+ raise "Checksum digit placeholders missing" unless @compact[2,2] == '??'
435
+
436
+ @compact[2,2] = calculated_check_digits
437
+
438
+ self
439
+ end
440
+
441
+ # @return [String] The check-digits as calculated from the IBAN.
442
+ def calculated_check_digits
443
+ "%02d" % (98-(self.class.numerify(bban+@country)*100)%97)
444
+ end
445
+
446
+ # @return [true, false]
447
+ # Whether two ISO::IBANs are equal.
448
+ # Comparison is based on class and IBAN number
449
+ def eql?(other)
450
+ self.class.equal?(other.class) && self == other
451
+ end
452
+
453
+ # @return [Integer]
454
+ # A hash value, see Object#hash
455
+ def hash
456
+ [self.class, @compact].hash
457
+ end
458
+
459
+ # See Object#inspect
460
+ def inspect
461
+ sprintf "#<%p %s>", self.class, formatted
462
+ end
463
+
464
+ # @return [String] The compact form of the IBAN as a String.
465
+ def to_s
466
+ @compact.dup
467
+ end
468
+
469
+ # @note
470
+ # This method is experimental. It might change or be removed in future versions!
471
+ #
472
+ # @return [Array]
473
+ # The individual IBAN components as defined by the SWIFT specification.
474
+ # An empty array if this IBAN does not have a specification.
475
+ def to_a
476
+ @_components ||= @specification ? @compact.match(@specification.iban_regex).captures : []
477
+
478
+ @_components.dup
479
+ end
480
+ end
481
+ end
@@ -68,9 +68,9 @@ module ISO
68
68
  else
69
69
  StructureCodes[code]+(fixed ? "{#{length}}" : "{,#{length}}")
70
70
  end
71
- }.join('')
71
+ }.join(')(')
72
72
 
73
- anchored ? /\A#{source}\z/ : /#{source}/
73
+ anchored ? /\A(#{source})\z/ : /(#{source})/n
74
74
  end
75
75
 
76
76
  attr_reader :country_name,
@@ -99,7 +99,7 @@ module ISO
99
99
 
100
100
  # @return [Regexp] A regex to verify the structure of the IBAN.
101
101
  def iban_regex
102
- @iban_regex ||= self.class.structure_regex(@iban_structure)
102
+ @_iban_regex ||= self.class.structure_regex(@iban_structure)
103
103
  end
104
104
 
105
105
  # @return [Regexp] A regex to identify the structure of the IBAN, without anchors.
@@ -109,7 +109,7 @@ module ISO
109
109
 
110
110
  # @return [Array<Integer>] An array with the lengths of all components.
111
111
  def component_lengths
112
- [bank_code_length, branch_code_length, account_code_lenght].tap { |lengths| lengths.delete(0) }
112
+ [bank_code_length, branch_code_length, account_code_length].tap { |lengths| lengths.delete(0) }
113
113
  end
114
114
 
115
115
  # @return [Fixnum]
@@ -126,7 +126,7 @@ module ISO
126
126
 
127
127
  # @return [Fixnum]
128
128
  # The length of the account code in the IBAN.
129
- def account_code_lenght
129
+ def account_code_length
130
130
  bban_length-bank_code_length-branch_code_length
131
131
  end
132
132
 
@@ -13,6 +13,6 @@ module ISO
13
13
  # @note
14
14
  # require 'iso/iban' loads the version.
15
15
  #
16
- Version = Gem::Version.new("0.0.4")
16
+ Version = Gem::Version.new("0.1.0")
17
17
  end
18
18
  end
@@ -1,6 +1,6 @@
1
1
  # encoding: utf-8
2
2
 
3
- require 'iso/iban'
3
+ require 'iso/iban/no_autoload'
4
4
 
5
5
  suite "ISO::IBAN" do
6
6
  setup do
@@ -28,7 +28,7 @@ suite "ISO::IBAN" do
28
28
  end
29
29
  end
30
30
 
31
- test 'ISO::IBAN::generate problem, TODO' do
31
+ test 'ISO::IBAN::generate problem, TODO (this test is expected to fail at the moment)' do
32
32
  ISO::IBAN.instance_variable_set(:@specifications, {'BG' => ISO::IBAN::Specification.new("Bulgaria", "BG", "BG2!n4!a4!n2!n8!c", 22, "4!a4!n2!n8!c", 18, 4, 7, 8, 11)})
33
33
  assert ISO::IBAN.generate('BG', 'AAAA', '2', 'C').valid? # this works now
34
34
  assert ISO::IBAN.generate('BG', 'A', '2', 'C').valid? # this still fails, because ISO::IBAN::generate can't pad 'a' format fields
@@ -73,73 +73,89 @@ suite "ISO::IBAN" do
73
73
  assert_equal [:invalid_length, :invalid_format], ISO::IBAN.validate('CH83 X234 5987 6543 2109 AB')
74
74
  assert_equal [:invalid_checksum, :invalid_length, :invalid_format], ISO::IBAN.validate('CH99 X234 5987 6543 2109 AB')
75
75
  assert_equal [:invalid_country, :invalid_checksum, :invalid_length, :invalid_format], ISO::IBAN.validate('XX35 1234 5987 6543 2109 A')
76
+ assert_equal [:invalid_characters, :invalid_checksum, :invalid_length, :invalid_format], ISO::IBAN.validate('CH35 1234 5987 6543 2109 Ä')
77
+ assert_equal [:invalid_characters, :invalid_country, :invalid_checksum, :invalid_length, :invalid_format], ISO::IBAN.validate('XX35 1234 5987 6543 2109 Ä')
78
+ end
79
+
80
+ test "ISO::IBAN::parse" do
81
+ assert_kind_of ISO::IBAN, ISO::IBAN.parse('CH35 1234 5987 6543 2109 A')
82
+ assert_equal 'CH351234598765432109A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').compact
76
83
  end
77
84
 
78
85
  test "ISO::IBAN::new" do
79
- assert_kind_of ISO::IBAN, ISO::IBAN.new('CH35 1234 5987 6543 2109 A')
80
86
  assert_kind_of ISO::IBAN, ISO::IBAN.new('CH351234598765432109A')
81
87
  end
82
88
 
83
89
  test "ISO::IBAN#formatted" do
84
- assert_equal 'CH35 1234 5987 6543 2109 A', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').formatted
85
- assert_equal 'CH35 1234 5987 6543 2109 A', ISO::IBAN.new('CH351234598765432109A').formatted
90
+ assert_equal 'CH35 1234 5987 6543 2109 A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').formatted
91
+ assert_equal 'CH35 1234 5987 6543 2109 A', ISO::IBAN.parse('CH351234598765432109A').formatted
86
92
  end
87
93
 
88
94
  test "ISO::IBAN#compact" do
89
- assert_equal 'CH351234598765432109A', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').compact
90
- assert_equal 'CH351234598765432109A', ISO::IBAN.new('CH351234598765432109A').compact
95
+ assert_equal 'CH351234598765432109A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').compact
96
+ assert_equal 'CH351234598765432109A', ISO::IBAN.parse('CH351234598765432109A').compact
91
97
  end
92
98
 
93
99
  test "ISO::IBAN#to_s" do
94
- assert_equal 'CH351234598765432109A', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').to_s
95
- assert_equal 'CH351234598765432109A', ISO::IBAN.new('CH351234598765432109A').to_s
100
+ assert_equal 'CH351234598765432109A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').to_s
101
+ assert_equal 'CH351234598765432109A', ISO::IBAN.parse('CH351234598765432109A').to_s
96
102
  end
97
103
 
98
104
  test "ISO::IBAN#country" do
99
- assert_equal 'CH', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').country
105
+ assert_equal 'CH', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').country
100
106
  end
101
107
 
102
108
  test "ISO::IBAN#checksum_digits" do
103
- assert_equal '35', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').checksum_digits
109
+ assert_equal '35', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').checksum_digits
104
110
  end
105
111
 
106
112
  test "ISO::IBAN#bban" do
107
- assert_equal '1234598765432109A', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').bban
113
+ assert_equal '1234598765432109A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').bban
108
114
  end
109
115
 
110
116
  test "ISO::IBAN#bank_code" do
111
- assert_equal '12345', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').bank_code
117
+ assert_equal '12345', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').bank_code
112
118
  end
113
119
 
114
120
  test "ISO::IBAN#branch_code" do
115
121
  ISO::IBAN.instance_variable_set(:@specifications, {'BG' => ISO::IBAN::Specification.new("Bulgaria", "BG", "BG2!n4!a4!n2!n8!c", 22, "4!a4!n2!n8!c", 18, 4, 7, 8, 11)})
116
- assert_equal "0002", ISO::IBAN.new('BG69 0001 0002 0300 0000 04').branch_code
122
+ assert_equal "0002", ISO::IBAN.parse('BG69 0001 0002 0300 0000 04').branch_code
117
123
  end
118
124
 
119
125
  test "ISO::IBAN#account_code" do
120
- assert_equal '98765432109A', ISO::IBAN.new('CH35 1234 5987 6543 2109 A').account_code
126
+ assert_equal '98765432109A', ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').account_code
121
127
  end
122
128
 
123
129
  test "ISO::IBAN#valid?" do
124
- assert_equal true, ISO::IBAN.new('CH35 1234 5987 6543 2109 A').valid?
125
- assert_equal false, ISO::IBAN.new('CH99 1234 5987 6543 2109 A').valid?
130
+ assert_equal true, ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').valid?
131
+ assert_equal false, ISO::IBAN.parse('CH99 1234 5987 6543 2109 A').valid?
126
132
  end
127
133
 
128
134
  test "ISO::IBAN#validate" do
129
- assert_equal [], ISO::IBAN.new('CH35 1234 5987 6543 2109 A').validate
135
+ assert_equal [], ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').validate
136
+ end
137
+
138
+ test 'ISO::IBAN#invalid_characters' do
139
+ assert_equal ["\xC3".b, "\x84".b], ISO::IBAN.parse('Ä').invalid_characters
140
+ assert_equal ['Ä'], ISO::IBAN.parse('Ä').invalid_characters('utf-8')
130
141
  end
131
142
 
132
143
  test "ISO::IBAN#<=>" do
133
144
  iban0 = ISO::IBAN.generate('CH', '0', '0')
134
145
  iban1 = ISO::IBAN.generate('CH', '0', '97') # 97 to have the same checksum
135
- assert_equal -1, iban0 <=> iban1
136
- assert_equal 0, iban0 <=> iban0
137
- assert_equal 1, iban1 <=> iban0
138
- assert_equal nil, iban0 <=> "incomparable"
139
- assert_equal nil, "incomparable" <=> iban0
146
+ assert_equal( -1, iban0 <=> iban1 )
147
+ assert_equal( 0, iban0 <=> iban0 )
148
+ assert_equal( 1, iban1 <=> iban0 )
149
+ assert_equal(nil, iban0 <=> "incomparable")
150
+ assert_equal(nil, "incomparable" <=> iban0)
151
+ end
152
+
153
+ test "ISO::IBAN#to_a" do
154
+ assert_equal %w[CH 35 12345 98765432109A], ISO::IBAN.parse("CH351234598765432109A").to_a
155
+ assert_equal [], ISO::IBAN.parse("XX351234598765432109A").to_a # no specification
140
156
  end
141
157
 
142
158
  test "ISO::IBAN#inspect" do
143
- assert_equal "#<ISO::IBAN CH35 1234 5987 6543 2109 A>", ISO::IBAN.new('CH35 1234 5987 6543 2109 A').inspect
159
+ assert_equal "#<ISO::IBAN CH35 1234 5987 6543 2109 A>", ISO::IBAN.parse('CH35 1234 5987 6543 2109 A').inspect
144
160
  end
145
161
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iso-iban
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stefan Rusterholz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-10-23 00:00:00.000000000 Z
11
+ date: 2014-02-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |-
14
14
  ISO::IBAN implements IBAN (International Bank Account Number) specification as per ISO 13616-1.
@@ -18,42 +18,54 @@ executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
20
  files:
21
+ - ".yardopts"
22
+ - LICENSE.txt
23
+ - README.markdown
24
+ - Rakefile
21
25
  - data/iso-iban/specs.yaml
26
+ - iso-iban.gemspec
27
+ - lib/iso/iban.rb
22
28
  - lib/iso/iban/autoload.rb
29
+ - lib/iso/iban/backports.rb
30
+ - lib/iso/iban/invalid.rb
31
+ - lib/iso/iban/no_autoload.rb
23
32
  - lib/iso/iban/specification.rb
24
33
  - lib/iso/iban/version.rb
25
- - lib/iso/iban.rb
26
34
  - test/data/test_spec.yaml
27
35
  - test/lib/helper.rb
28
36
  - test/runner.rb
29
37
  - test/tmp/test_spec.yaml
30
38
  - test/unit/lib/iso/iban.rb
31
- - iso-iban.gemspec
32
- - .yardopts
33
- - LICENSE.txt
34
- - Rakefile
35
- - README.markdown
36
39
  homepage: https://github.com/apeiros/iso-iban
37
40
  licenses:
38
41
  - BSD 2-Clause
39
42
  metadata: {}
40
- post_install_message:
43
+ post_install_message: |-
44
+ IMPORTANT!
45
+
46
+ As of 0.1.0, there are 2 backward incompatible changes:
47
+
48
+ * `require 'iso/iban/autoload'` is deprecated. Please use plain `require 'iso/iban'`.
49
+ To load ISO::IBAN without loading the specifications, please use
50
+ `require 'iso/iban/no_autoload'`.
51
+ * ISO::IBAN.new no longer accepts formatted input.
52
+ Use ISO::IBAN.parse if your input is potentially not in the compact format.
41
53
  rdoc_options: []
42
54
  require_paths:
43
55
  - lib
44
56
  required_ruby_version: !ruby/object:Gem::Requirement
45
57
  requirements:
46
- - - '>='
58
+ - - ">="
47
59
  - !ruby/object:Gem::Version
48
60
  version: 1.9.2
49
61
  required_rubygems_version: !ruby/object:Gem::Requirement
50
62
  requirements:
51
- - - '>'
63
+ - - ">"
52
64
  - !ruby/object:Gem::Version
53
65
  version: 1.3.1
54
66
  requirements: []
55
67
  rubyforge_project:
56
- rubygems_version: 2.1.5
68
+ rubygems_version: 2.2.1
57
69
  signing_key:
58
70
  specification_version: 3
59
71
  summary: Utilities for International Bank Account Numbers (IBAN) as per ISO 13616-1.