itax_code 0.1.2
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 +7 -0
- data/.gitignore +12 -0
- data/.rubocop.yml +30 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +9 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +73 -0
- data/README.md +84 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/itax_code.gemspec +36 -0
- data/lib/itax_code.rb +68 -0
- data/lib/itax_code/data/countries.json +278 -0
- data/lib/itax_code/data/municipalities.json +13833 -0
- data/lib/itax_code/encoder.rb +97 -0
- data/lib/itax_code/parser.rb +114 -0
- data/lib/itax_code/utils.rb +112 -0
- data/lib/itax_code/validator.rb +44 -0
- data/lib/itax_code/version.rb +3 -0
- metadata +192 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
module ItaxCode
|
2
|
+
##
|
3
|
+
# This class handles the tax code generation logic.
|
4
|
+
#
|
5
|
+
# @param [String] surname The citizen first name
|
6
|
+
# @param [String] name The citizen last name
|
7
|
+
# @param [String] gender The citizen gender
|
8
|
+
# @param [String, Date] birthdate The citizen birthdate
|
9
|
+
# @param [String] birthplace The citizen birthplace
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# ItaxCode::Encoder.new(
|
14
|
+
# surname: "Rossi",
|
15
|
+
# name: "Matteo",
|
16
|
+
# gender: "M",
|
17
|
+
# birthdate: "1990-08-23",
|
18
|
+
# birthplace: "Milano"
|
19
|
+
# ).encode
|
20
|
+
#
|
21
|
+
# @return [String] The encoded tax code
|
22
|
+
|
23
|
+
class Encoder
|
24
|
+
def initialize(data = {}, utils = Utils.new)
|
25
|
+
@surname = data[:surname]
|
26
|
+
@name = data[:name]
|
27
|
+
@gender = data[:gender].try :upcase
|
28
|
+
@birthdate = parsed_date data[:birthdate]
|
29
|
+
@birthplace = data[:birthplace]
|
30
|
+
@utils = utils
|
31
|
+
end
|
32
|
+
|
33
|
+
##
|
34
|
+
# This method calculates the tax code.
|
35
|
+
#
|
36
|
+
# @return [String] The calculated tax code
|
37
|
+
|
38
|
+
def encode
|
39
|
+
code = encode_surname
|
40
|
+
code += encode_name
|
41
|
+
code += encode_birthdate
|
42
|
+
code += encode_birthplace
|
43
|
+
code += utils.encode_cin code
|
44
|
+
code
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_accessor :surname,
|
50
|
+
:name,
|
51
|
+
:gender,
|
52
|
+
:birthdate,
|
53
|
+
:birthplace,
|
54
|
+
:utils
|
55
|
+
|
56
|
+
def encode_surname
|
57
|
+
str = utils.slugged(surname).chars
|
58
|
+
consonants = utils.extract_consonants str
|
59
|
+
vowels = utils.extract_vowels str
|
60
|
+
"#{consonants[0..2]}#{vowels[0..2]}XXX"[0..2].upcase
|
61
|
+
end
|
62
|
+
|
63
|
+
def encode_name
|
64
|
+
str = utils.slugged(name).chars
|
65
|
+
consonants = utils.extract_consonants str
|
66
|
+
vowels = utils.extract_vowels str
|
67
|
+
|
68
|
+
if consonants.length > 3
|
69
|
+
arr = consonants.dup.chars
|
70
|
+
arr.delete_at 1
|
71
|
+
consonants = arr.join
|
72
|
+
end
|
73
|
+
|
74
|
+
"#{consonants[0..2]}#{vowels[0..2]}XXX"[0..2].upcase
|
75
|
+
end
|
76
|
+
|
77
|
+
def encode_birthdate
|
78
|
+
year = birthdate.year.to_s[2..-1]
|
79
|
+
month = utils.months[birthdate.month - 1]
|
80
|
+
day = format "%02d", (birthdate.day + (gender == "F" ? 40 : 0))
|
81
|
+
"#{year}#{month}#{day}"
|
82
|
+
end
|
83
|
+
|
84
|
+
def encode_birthplace
|
85
|
+
utils.municipalities.find do |m|
|
86
|
+
utils.slugged(m["name"]) == utils.slugged(birthplace)
|
87
|
+
end.try(:[], "code")
|
88
|
+
end
|
89
|
+
|
90
|
+
def parsed_date(date)
|
91
|
+
case date.class.name
|
92
|
+
when "Date", "Time", "DateTime" then date
|
93
|
+
else Date.parse(date)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module ItaxCode
|
2
|
+
##
|
3
|
+
# This class handles the parsing logic for TaxCode module.
|
4
|
+
#
|
5
|
+
# @param [String] tax_code
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
#
|
9
|
+
# ItaxCode::Parser.new("RSSMRA70A01L726S").decode
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
|
13
|
+
class Parser
|
14
|
+
def initialize(tax_code, utils = Utils.new)
|
15
|
+
@tax_code = (tax_code || "").upcase
|
16
|
+
@utils = utils
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# This method decodes the tax code.
|
21
|
+
#
|
22
|
+
# @return [Hash]
|
23
|
+
|
24
|
+
def decode
|
25
|
+
year = decode_year
|
26
|
+
month = utils.months.find_index(raw[:birthdate_month]) + 1
|
27
|
+
day, gender = decode_day_and_gender
|
28
|
+
birthplace = decode_birthplace
|
29
|
+
|
30
|
+
{
|
31
|
+
code: tax_code,
|
32
|
+
gender: gender,
|
33
|
+
birthdate: [year, month, day].join("-"),
|
34
|
+
birthplace: birthplace,
|
35
|
+
omocodes: omocodes,
|
36
|
+
raw: raw
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_accessor :tax_code, :utils
|
43
|
+
|
44
|
+
def raw
|
45
|
+
matches = tax_code.scan(utils.regex).flatten
|
46
|
+
|
47
|
+
{
|
48
|
+
surname: matches[0],
|
49
|
+
name: matches[1],
|
50
|
+
birthdate: matches[2],
|
51
|
+
birthdate_year: matches[3],
|
52
|
+
birthdate_month: matches[4],
|
53
|
+
birthdate_day: matches[5],
|
54
|
+
birthplace: matches[6],
|
55
|
+
cin: matches[7]
|
56
|
+
}
|
57
|
+
end
|
58
|
+
|
59
|
+
def decode_year
|
60
|
+
year = utils.omocodia_decode raw[:birthdate_year]
|
61
|
+
year = (Date.today.year.to_s[0..1] + year).to_i
|
62
|
+
year -= 100 if year > Date.today.year
|
63
|
+
year
|
64
|
+
end
|
65
|
+
|
66
|
+
def decode_day_and_gender
|
67
|
+
day = utils.omocodia_decode(raw[:birthdate_day]).to_i
|
68
|
+
|
69
|
+
if day > 40
|
70
|
+
day -= 40
|
71
|
+
[day, "F"]
|
72
|
+
else
|
73
|
+
[day, "M"]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def decode_birthplace
|
78
|
+
places = utils.municipalities
|
79
|
+
.select { |m| m["code"] == municipality_code }
|
80
|
+
place = places.find { |m| !m["name"].include? "soppresso" }
|
81
|
+
place = (place.presence || places.last).deep_symbolize_keys
|
82
|
+
place[:name] = place[:name].gsub(" (soppresso)", "")
|
83
|
+
place
|
84
|
+
end
|
85
|
+
|
86
|
+
def municipality_code
|
87
|
+
raw[:birthplace][0] + utils.omocodia_decode(raw[:birthplace][1..-1])
|
88
|
+
end
|
89
|
+
|
90
|
+
def omocodes
|
91
|
+
code_chars = tax_code[0..14].chars
|
92
|
+
codes = []
|
93
|
+
|
94
|
+
utils.omocodia_subs_indexes.reverse_each do |i|
|
95
|
+
code_chars[i] = utils.omocodia_decode(code_chars[i])
|
96
|
+
end
|
97
|
+
|
98
|
+
code = code_chars.join
|
99
|
+
code_cin = utils.encode_cin(code)
|
100
|
+
code += code_cin
|
101
|
+
codes.push(code)
|
102
|
+
|
103
|
+
utils.omocodia_subs_indexes.reverse_each do |i|
|
104
|
+
code_chars[i] = utils.omocodia_encode(code_chars[i])
|
105
|
+
code = code_chars.join
|
106
|
+
code_cin = utils.encode_cin(code)
|
107
|
+
code += code_cin
|
108
|
+
codes.push(code)
|
109
|
+
end
|
110
|
+
|
111
|
+
codes
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module ItaxCode
|
2
|
+
class Utils
|
3
|
+
def regex
|
4
|
+
/^([A-Z]{3})([A-Z]{3})
|
5
|
+
(([A-Z\d]{2})([ABCDEHLMPRST]{1})([A-Z\d]{2}))
|
6
|
+
([A-Z]{1}[A-Z\d]{3})
|
7
|
+
([A-Z]{1})$/x
|
8
|
+
end
|
9
|
+
|
10
|
+
def slugged(str, separator = "-")
|
11
|
+
str.gsub!(/\s*@\s*/, " at ")
|
12
|
+
str.gsub!(/\s*&\s*/, " and ")
|
13
|
+
str.parameterize(separator: separator)
|
14
|
+
end
|
15
|
+
|
16
|
+
def months
|
17
|
+
%w[A B C D E H L M P R S T]
|
18
|
+
end
|
19
|
+
|
20
|
+
def consonants
|
21
|
+
%w[b c d f g h j k l m n p q r s t v w x y z]
|
22
|
+
end
|
23
|
+
|
24
|
+
def vowels
|
25
|
+
%w[a e i o u]
|
26
|
+
end
|
27
|
+
|
28
|
+
def cin_odds
|
29
|
+
{
|
30
|
+
"0": 1, "1": 0, "2": 5, "3": 7, "4": 9, "5": 13,
|
31
|
+
"6": 15, "7": 17, "8": 19, "9": 21, "A": 1, "B": 0,
|
32
|
+
"C": 5, "D": 7, "E": 9, "F": 13, "G": 15, "H": 17,
|
33
|
+
"I": 19, "J": 21, "K": 2, "L": 4, "M": 18, "N": 20,
|
34
|
+
"O": 11, "P": 3, "Q": 6, "R": 8, "S": 12, "T": 14,
|
35
|
+
"U": 16, "V": 10, "W": 22, "X": 25, "Y": 24, "Z": 23
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def cin_evens
|
40
|
+
{
|
41
|
+
"0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5,
|
42
|
+
"6": 6, "7": 7, "8": 8, "9": 9, "A": 0, "B": 1,
|
43
|
+
"C": 2, "D": 3, "E": 4, "F": 5, "G": 6, "H": 7,
|
44
|
+
"I": 8, "J": 9, "K": 10, "L": 11, "M": 12, "N": 13,
|
45
|
+
"O": 14, "P": 15, "Q": 16, "R": 17, "S": 18, "T": 19,
|
46
|
+
"U": 20, "V": 21, "W": 22, "X": 23, "Y": 24, "Z": 25
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def cin_remainders
|
51
|
+
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
52
|
+
end
|
53
|
+
|
54
|
+
def omocodia
|
55
|
+
{
|
56
|
+
"0": "L", "1": "M", "2": "N", "3": "P", "4": "Q",
|
57
|
+
"5": "R", "6": "S", "7": "T", "8": "U", "9": "V"
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
def omocodia_digits
|
62
|
+
omocodia.keys.join
|
63
|
+
end
|
64
|
+
|
65
|
+
def omocodia_letters
|
66
|
+
omocodia.values.join
|
67
|
+
end
|
68
|
+
|
69
|
+
def omocodia_subs_indexes
|
70
|
+
[6, 7, 9, 10, 12, 13, 14]
|
71
|
+
end
|
72
|
+
|
73
|
+
def omocodia_encode(val)
|
74
|
+
val.tr omocodia_digits, omocodia_letters
|
75
|
+
end
|
76
|
+
|
77
|
+
def omocodia_decode(val)
|
78
|
+
val.tr omocodia_letters, omocodia_digits
|
79
|
+
end
|
80
|
+
|
81
|
+
def extract_consonants(str)
|
82
|
+
str.select { |c| consonants.include? c }.join
|
83
|
+
end
|
84
|
+
|
85
|
+
def extract_vowels(str)
|
86
|
+
str.select { |c| vowels.include? c }.join
|
87
|
+
end
|
88
|
+
|
89
|
+
def encode_cin(code)
|
90
|
+
tot = 0
|
91
|
+
|
92
|
+
code.chars.each_with_index do |char, index|
|
93
|
+
tot += cin_odds[char.to_sym] if (index + 1).odd?
|
94
|
+
tot += cin_evens[char.to_sym] if (index + 1).even?
|
95
|
+
end
|
96
|
+
|
97
|
+
cin_remainders[tot % 26]
|
98
|
+
end
|
99
|
+
|
100
|
+
def municipalities
|
101
|
+
@municipalities ||= JSON.parse(
|
102
|
+
File.read("#{__dir__}/data/municipalities.json")
|
103
|
+
)
|
104
|
+
end
|
105
|
+
|
106
|
+
# def countries
|
107
|
+
# @countries ||= JSON.parse(
|
108
|
+
# File.read("#{__dir__}/data/countries.json")
|
109
|
+
# )
|
110
|
+
# end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module ItaxCode
|
2
|
+
##
|
3
|
+
# This class holds the TaxCode validation logic.
|
4
|
+
#
|
5
|
+
# @param [Hash] data The citizen input data
|
6
|
+
|
7
|
+
class Validator
|
8
|
+
LENGTH = 16
|
9
|
+
|
10
|
+
def initialize(data = {})
|
11
|
+
@encoded = Encoder.new(data).encode
|
12
|
+
end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
##
|
16
|
+
# This method checks tax code standard length
|
17
|
+
# against citizen and business fical code standards.
|
18
|
+
#
|
19
|
+
# @param [String] code The tax code
|
20
|
+
#
|
21
|
+
# @return [true, false]
|
22
|
+
|
23
|
+
def standard_length?(code)
|
24
|
+
code.length == LENGTH
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
##
|
29
|
+
# This method check pre calculated tax code validity
|
30
|
+
# against newly encoded tax code.
|
31
|
+
#
|
32
|
+
# @param [String] tax_code The pre calculated tax code
|
33
|
+
#
|
34
|
+
# @return [true, false]
|
35
|
+
|
36
|
+
def valid?(tax_code)
|
37
|
+
encoded[0..10] == tax_code[0..10]
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
attr_accessor :encoded
|
43
|
+
end
|
44
|
+
end
|
metadata
ADDED
@@ -0,0 +1,192 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: itax_code
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matteo Rossi
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-11-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activesupport
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubocop-minitest
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rubocop-performance
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: simplecov
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ">="
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ">="
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
description:
|
140
|
+
email:
|
141
|
+
- mttrss5@gmail.com
|
142
|
+
executables: []
|
143
|
+
extensions: []
|
144
|
+
extra_rdoc_files: []
|
145
|
+
files:
|
146
|
+
- ".gitignore"
|
147
|
+
- ".rubocop.yml"
|
148
|
+
- ".travis.yml"
|
149
|
+
- CHANGELOG.md
|
150
|
+
- CODE_OF_CONDUCT.md
|
151
|
+
- Gemfile
|
152
|
+
- Gemfile.lock
|
153
|
+
- README.md
|
154
|
+
- Rakefile
|
155
|
+
- bin/console
|
156
|
+
- bin/setup
|
157
|
+
- itax_code.gemspec
|
158
|
+
- lib/itax_code.rb
|
159
|
+
- lib/itax_code/data/countries.json
|
160
|
+
- lib/itax_code/data/municipalities.json
|
161
|
+
- lib/itax_code/encoder.rb
|
162
|
+
- lib/itax_code/parser.rb
|
163
|
+
- lib/itax_code/utils.rb
|
164
|
+
- lib/itax_code/validator.rb
|
165
|
+
- lib/itax_code/version.rb
|
166
|
+
homepage: https://github.com/matteoredz/itax-code
|
167
|
+
licenses:
|
168
|
+
- MIT
|
169
|
+
metadata:
|
170
|
+
homepage_uri: https://github.com/matteoredz/itax-code
|
171
|
+
source_code_uri: https://github.com/matteoredz/itax-code
|
172
|
+
changelog_uri: https://github.com/matteoredz/itax-code/CHANGELOG.md
|
173
|
+
post_install_message:
|
174
|
+
rdoc_options: []
|
175
|
+
require_paths:
|
176
|
+
- lib
|
177
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
178
|
+
requirements:
|
179
|
+
- - ">="
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: 2.5.0
|
182
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
183
|
+
requirements:
|
184
|
+
- - ">="
|
185
|
+
- !ruby/object:Gem::Version
|
186
|
+
version: '0'
|
187
|
+
requirements: []
|
188
|
+
rubygems_version: 3.1.2
|
189
|
+
signing_key:
|
190
|
+
specification_version: 4
|
191
|
+
summary: Encode and decode Italian tax code (Codice Fiscale).
|
192
|
+
test_files: []
|