iso-iban 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.