kennitala 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +2 -0
- data/.rspec +3 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +61 -0
- data/LICENSE.txt +21 -0
- data/README.md +123 -0
- data/Rakefile +12 -0
- data/lib/kennitala.rb +303 -0
- data/lib/kennitala_string.rb +25 -0
- data/sig/kennitala.rbs +4 -0
- metadata +115 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d35f02ba753cae3e03263f8fb33fa11bd425855d49dbead29c5661edd2eef1ac
|
4
|
+
data.tar.gz: aded0d8ea8337bd0a4d7d639b24d36c166f0e2100111cf94f6680de18040f7a8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 74f549441ebda6b238140412b8eb4175e0bdb6a6ea6a4e2d3e1b142adfbe70c03c62cee9e0709082535d3beb72fd7e213fccc83f4bf6290dadcda48c7ce34dfb
|
7
|
+
data.tar.gz: fbadc0f4bcce4f941a23d699be2aaeac1b4afd720e6ad3ae87c151d9caa7183ff835066a8a589e0c4bfcedceb924d2a0f2023cd71f9e104822f813123da089cb
|
data/.editorconfig
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
3.1.2
|
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
|
6
|
+
|
7
|
+
We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community.
|
8
|
+
|
9
|
+
## Our Standards
|
10
|
+
|
11
|
+
Examples of behavior that contributes to a positive environment for our community include:
|
12
|
+
|
13
|
+
* Demonstrating empathy and kindness toward other people
|
14
|
+
* Being respectful of differing opinions, viewpoints, and experiences
|
15
|
+
* Giving and gracefully accepting constructive feedback
|
16
|
+
* Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience
|
17
|
+
* Focusing on what is best not just for us as individuals, but for the overall community
|
18
|
+
|
19
|
+
Examples of unacceptable behavior include:
|
20
|
+
|
21
|
+
* The use of sexualized language or imagery, and sexual attention or
|
22
|
+
advances of any kind
|
23
|
+
* Trolling, insulting or derogatory comments, and personal or political attacks
|
24
|
+
* Public or private harassment
|
25
|
+
* Publishing others' private information, such as a physical or email
|
26
|
+
address, without their explicit permission
|
27
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
28
|
+
professional setting
|
29
|
+
|
30
|
+
## Enforcement Responsibilities
|
31
|
+
|
32
|
+
Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful.
|
33
|
+
|
34
|
+
Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate.
|
35
|
+
|
36
|
+
## Scope
|
37
|
+
|
38
|
+
This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
|
39
|
+
|
40
|
+
## Enforcement
|
41
|
+
|
42
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at aldavigdis@aldavigdis.is. All complaints will be reviewed and investigated promptly and fairly.
|
43
|
+
|
44
|
+
All community leaders are obligated to respect the privacy and security of the reporter of any incident.
|
45
|
+
|
46
|
+
## Enforcement Guidelines
|
47
|
+
|
48
|
+
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
|
49
|
+
|
50
|
+
### 1. Correction
|
51
|
+
|
52
|
+
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
|
53
|
+
|
54
|
+
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
|
55
|
+
|
56
|
+
### 2. Warning
|
57
|
+
|
58
|
+
**Community Impact**: A violation through a single incident or series of actions.
|
59
|
+
|
60
|
+
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
|
61
|
+
|
62
|
+
### 3. Temporary Ban
|
63
|
+
|
64
|
+
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
|
65
|
+
|
66
|
+
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban.
|
67
|
+
|
68
|
+
### 4. Permanent Ban
|
69
|
+
|
70
|
+
**Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals.
|
71
|
+
|
72
|
+
**Consequence**: A permanent ban from any sort of public interaction within the community.
|
73
|
+
|
74
|
+
## Attribution
|
75
|
+
|
76
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0,
|
77
|
+
available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
78
|
+
|
79
|
+
Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity).
|
80
|
+
|
81
|
+
[homepage]: https://www.contributor-covenant.org
|
82
|
+
|
83
|
+
For answers to common questions about this code of conduct, see the FAQ at
|
84
|
+
https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
kennitala (0.1.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.2)
|
10
|
+
diff-lcs (1.5.0)
|
11
|
+
json (2.6.3)
|
12
|
+
parallel (1.22.1)
|
13
|
+
parser (3.2.0.0)
|
14
|
+
ast (~> 2.4.1)
|
15
|
+
rainbow (3.1.1)
|
16
|
+
rake (13.0.6)
|
17
|
+
regexp_parser (2.6.2)
|
18
|
+
rexml (3.2.5)
|
19
|
+
rspec (3.12.0)
|
20
|
+
rspec-core (~> 3.12.0)
|
21
|
+
rspec-expectations (~> 3.12.0)
|
22
|
+
rspec-mocks (~> 3.12.0)
|
23
|
+
rspec-core (3.12.0)
|
24
|
+
rspec-support (~> 3.12.0)
|
25
|
+
rspec-expectations (3.12.2)
|
26
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
27
|
+
rspec-support (~> 3.12.0)
|
28
|
+
rspec-mocks (3.12.3)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.12.0)
|
31
|
+
rspec-support (3.12.0)
|
32
|
+
rubocop (1.44.1)
|
33
|
+
json (~> 2.3)
|
34
|
+
parallel (~> 1.10)
|
35
|
+
parser (>= 3.2.0.0)
|
36
|
+
rainbow (>= 2.2.2, < 4.0)
|
37
|
+
regexp_parser (>= 1.8, < 3.0)
|
38
|
+
rexml (>= 3.2.5, < 4.0)
|
39
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
40
|
+
ruby-progressbar (~> 1.7)
|
41
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
42
|
+
rubocop-ast (1.24.1)
|
43
|
+
parser (>= 3.1.1.0)
|
44
|
+
ruby-progressbar (1.11.0)
|
45
|
+
unicode-display_width (2.4.2)
|
46
|
+
webrick (1.7.0)
|
47
|
+
yard (0.9.28)
|
48
|
+
webrick (~> 1.7.0)
|
49
|
+
|
50
|
+
PLATFORMS
|
51
|
+
x86_64-linux
|
52
|
+
|
53
|
+
DEPENDENCIES
|
54
|
+
kennitala!
|
55
|
+
rake (~> 13.0)
|
56
|
+
rspec (~> 3.0)
|
57
|
+
rubocop (~> 1.21)
|
58
|
+
yard (~> 0.9)
|
59
|
+
|
60
|
+
BUNDLED WITH
|
61
|
+
2.3.7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2023 Alda Vigdís Skarphéðinsdóttir
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# Kennitala
|
2
|
+
|
3
|
+
Process Icelandic kenntiala identification numbers.
|
4
|
+
|
5
|
+
The Gem provides a class to handle "kennitala" identifier codes and monkey patched the String class to that strings can be cast to Kennitala objects. The class can be used to sanitize the identifiers and read information like the date of birth (or date of registration in the case of companies and organization), age and the type of entity.
|
6
|
+
|
7
|
+
The class does not access external APIs or databases such as the National Registry or the Company Registry, so names and status (sex/gender, death, bankruptcy, credit rating etc.) cannot be accessed using the class. However, it can be used to sanitize and validate such data before being sent to external APIs, as such services are provided by private companies, which often charge a specific amount for each query.
|
8
|
+
|
9
|
+
## Uses of kennitala
|
10
|
+
|
11
|
+
Unlike the US Social Security number and equivalents, the kennitala is only used for identification of persons and companies (as well as other registered organizations) — and is often used internally by educational institutions, companies and other organization as a primary identifier for persons (e.g. school, employee, customer and frequent flyer ID). It is not to be used for authentication (i.e. a password) and is not considered a secret per se. While a kennitala can be kept unencrypted in a database, publishing a kennitala or a list of them is generally not considered good practice and might cause liability.
|
12
|
+
|
13
|
+
A kennitala is assigned to every newborn person and foreign national residing in Iceland as well as organizations and companies operating there. It is statically assigned and can not be changed.
|
14
|
+
|
15
|
+
Article II, paragraph 10 of the 77/2000 Act on Data Protection (http://www.althingi.is/lagas/nuna/2000077.html) provides the legal framework regarding the use and processing of the kennitala in Iceland:
|
16
|
+
|
17
|
+
> The use of a kennitala is allowed if it has a an objective cause and is necessary to ensure reliable identification of persons. The Data Protection Authority may ban or order the use of kennitala.
|
18
|
+
|
19
|
+
## Technicalities
|
20
|
+
|
21
|
+
The kennitala (`DDMMYY-RRCM`) is a 10-digit numeric string consisting on a date (date of birth for persons, date of registration for companies) in the form of `DDMMYY`, two random digits (`RR`) a check digit (`C`) and a century identifier (`M`). A hyphen or space is often added between the year and random values (Example: `010130-2989`).
|
22
|
+
|
23
|
+
The number 40 is added to the registration day of companies and organizations. Hence, a kennitala for a company registered at January 1 1990 starts with `410190` as opposed to `010190` for a person born that day.
|
24
|
+
|
25
|
+
The century identifier has 3 legal values. `8` for the 19th century, `9` for the 20th century and `0` for the 21st century.
|
26
|
+
|
27
|
+
## Examples
|
28
|
+
|
29
|
+
### Working with Kennitala objects
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
# Initialize a Kennitala object.
|
33
|
+
# The string provided may include spaces, hyphens and alphabetical characters,
|
34
|
+
# which will then be erased from the resulting string.
|
35
|
+
k = Kennitala.new(' 010130-2989')
|
36
|
+
# => #<Kennitala:0x007fe35d041bc0 @value="0101302989">
|
37
|
+
|
38
|
+
# Invalid strings are rejected with an argument error
|
39
|
+
f = Kennitala.new('010130-2979')
|
40
|
+
# => ArgumentError: Kennitala is invalid
|
41
|
+
|
42
|
+
# If no kennitala string is specified, a random one will be generated
|
43
|
+
r = Kennitala.new
|
44
|
+
# => #<Kennitala:0x007fc589339f18 @value="2009155509">
|
45
|
+
|
46
|
+
# Retrieve the kennitala as a string.
|
47
|
+
# This is a sanitized string, without any non-numeric characters.
|
48
|
+
# Pretty useful when storing it in a database.
|
49
|
+
k.to_s
|
50
|
+
# => "0101302989"
|
51
|
+
|
52
|
+
# Pretty print the kennitala
|
53
|
+
# Adds a space between the 6th and the 7th digits for readability
|
54
|
+
k.pp
|
55
|
+
# => "010130 2989"
|
56
|
+
|
57
|
+
# You can also pass a string to .pp to use as a spacer
|
58
|
+
k.pp('–')
|
59
|
+
# => "010130-2989"
|
60
|
+
|
61
|
+
# You can also pass a cat to the .pp method
|
62
|
+
k.pp('🐈')
|
63
|
+
# => "010130🐈2989"
|
64
|
+
|
65
|
+
# Get the entity type (results in 'person' or 'company')
|
66
|
+
k.entity_type
|
67
|
+
# => "person"
|
68
|
+
|
69
|
+
# It's also possible to use .company? and .person? to achieve the same thing
|
70
|
+
k.company?
|
71
|
+
# => false
|
72
|
+
|
73
|
+
k.person?
|
74
|
+
# => true
|
75
|
+
|
76
|
+
# Cast the kennitala to a Date object
|
77
|
+
k.to_date
|
78
|
+
# => #<Date: 1930-01-01 ((2425978j,0s,0n),+0s,2299161j)>
|
79
|
+
|
80
|
+
# Get the current age of the entity. Useful for age restrictions.
|
81
|
+
k.age
|
82
|
+
# => 86
|
83
|
+
```
|
84
|
+
|
85
|
+
### Casting strings
|
86
|
+
|
87
|
+
```ruby
|
88
|
+
# Casting a string to a Kennitala object
|
89
|
+
'010130 2989'.to_kt
|
90
|
+
# => #<Kennitala:0x007fc5893286a0 @value="0101302989">
|
91
|
+
|
92
|
+
# Get the current age based on a String
|
93
|
+
'0101302989'.to_kt.age
|
94
|
+
# => 86
|
95
|
+
```
|
96
|
+
|
97
|
+
## Installation
|
98
|
+
|
99
|
+
Install the gem and add to the application's Gemfile by executing:
|
100
|
+
|
101
|
+
$ bundle kennitala
|
102
|
+
|
103
|
+
If bundler is not being used to manage dependencies:
|
104
|
+
|
105
|
+
$ gem install kennitala
|
106
|
+
|
107
|
+
## Development
|
108
|
+
|
109
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
110
|
+
|
111
|
+
## Contributing
|
112
|
+
|
113
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/aldavigdis/kennitala-gem. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/aldavigdis/kennitala-gem/blob/master/CODE_OF_CONDUCT.md).
|
114
|
+
|
115
|
+
Don't be afraid of using Rubocop for correcting your code and to make it conform to the standards defined in `.rubocop.yml`. You will want to run `bundle exec rubocop -A` to see if your code has any errors and correct them on the fly.
|
116
|
+
|
117
|
+
## License
|
118
|
+
|
119
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
120
|
+
|
121
|
+
## Code of Conduct
|
122
|
+
|
123
|
+
Everyone interacting in the Kennitala project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/aldavigdis/kennitala-gem/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/lib/kennitala.rb
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require_relative 'kennitala_string'
|
5
|
+
|
6
|
+
# The main Kennitala class
|
7
|
+
class Kennitala
|
8
|
+
# The current version of the gem
|
9
|
+
VERSION = '0.1.1'
|
10
|
+
|
11
|
+
# Initialize a Kennitala object
|
12
|
+
#
|
13
|
+
# @note The string provided may include spaces, hyphens and alphabetical
|
14
|
+
# characters, which will then be erased from the resulting string.
|
15
|
+
#
|
16
|
+
# @param [String, Boolean] kt_string Either Kennitala String or Boolean false.
|
17
|
+
# @param [Boolean] is_company Wether the randomly generated kennitala is
|
18
|
+
# for a company
|
19
|
+
# @return [Kennitala] description of returned object
|
20
|
+
#
|
21
|
+
# @example Initialize a Kennitala object based on an unsanitized String
|
22
|
+
# Kennitala.new(' 010130-2989')
|
23
|
+
# => #<Kennitala:0x007fe35d041bc0 @value="0101302989">
|
24
|
+
#
|
25
|
+
# @example Invalid strings are rejected with an argument error
|
26
|
+
# Kennitala.new('010130-2979')
|
27
|
+
# => ArgumentError: Kennitala is invalid
|
28
|
+
#
|
29
|
+
# @example If no kennitala string is specified, a random one will be generated
|
30
|
+
# Kennitala.new
|
31
|
+
# => #<Kennitala:0x007fc589339f18 @value="2009155509">
|
32
|
+
def initialize(kt_string = false, is_company = false)
|
33
|
+
kt_string = fake_kt_string(is_company) if kt_string == false
|
34
|
+
unless kt_string.instance_of?(String)
|
35
|
+
raise ArgumentError, 'Kennitala needs to be provided as a String or ' \
|
36
|
+
'Boolean (false)'
|
37
|
+
end
|
38
|
+
sanitised_kt = sanitize(kt_string)
|
39
|
+
raise ArgumentError, 'Kennitala is invalid' if sanitised_kt.nil?
|
40
|
+
|
41
|
+
@value = sanitised_kt
|
42
|
+
end
|
43
|
+
|
44
|
+
# Get the type of entity - If it is a person or an organization
|
45
|
+
#
|
46
|
+
# @return [String] Either 'person' or 'company'
|
47
|
+
#
|
48
|
+
# @example Get the entity type (results in 'person' or 'company')
|
49
|
+
# k = Kennitala.new('0101302989')
|
50
|
+
# => #<Kennitala:0x007fe35d041bc0 @value="0101302989">
|
51
|
+
# k.entity_type
|
52
|
+
# => "person"
|
53
|
+
def entity_type
|
54
|
+
date_integer = @value[0, 2].to_i
|
55
|
+
return 'person' if date_integer < 32
|
56
|
+
return 'company' if (date_integer > 40) && (date_integer < 72)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Check if the entity is a company
|
60
|
+
#
|
61
|
+
# @return [Boolean]
|
62
|
+
def company?
|
63
|
+
date_integer = @value[0, 2].to_i
|
64
|
+
return true if (date_integer > 40) && (date_integer < 72)
|
65
|
+
|
66
|
+
false
|
67
|
+
end
|
68
|
+
|
69
|
+
# Check if the entity is a person
|
70
|
+
#
|
71
|
+
# @return [Boolean]
|
72
|
+
def person?
|
73
|
+
date_integer = @value[0, 2].to_i
|
74
|
+
return true if date_integer < 32
|
75
|
+
|
76
|
+
false
|
77
|
+
end
|
78
|
+
|
79
|
+
# Get the year of birth or registration
|
80
|
+
#
|
81
|
+
# @return [Fixnum]
|
82
|
+
def year
|
83
|
+
century = (10 + @value[9].to_i) * 100
|
84
|
+
year = @value[4, 2].to_i
|
85
|
+
return century + year if (1800..1900).cover?(century)
|
86
|
+
return 2000 + year if century == 1000
|
87
|
+
end
|
88
|
+
|
89
|
+
# Get the day of the month of birth or registration
|
90
|
+
#
|
91
|
+
# @return [Fixnum]
|
92
|
+
def day
|
93
|
+
date_integer = @value[0, 2].to_i
|
94
|
+
return @value[0, 2].to_i if date_integer < 32
|
95
|
+
return @value[0, 2].to_i - 40 if (date_integer > 40) && (date_integer < 71)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get a numeric representation of the month of birth or registration
|
99
|
+
#
|
100
|
+
# @return [Fixnum]
|
101
|
+
def month
|
102
|
+
@value[2, 2].to_i
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the age of entity in years. Useful when dealing with age restrictions.
|
106
|
+
#
|
107
|
+
# @return [Fixnum]
|
108
|
+
#
|
109
|
+
# @example Get the current age of the entity
|
110
|
+
# k = Kennitala.new('0101302989')
|
111
|
+
# => #<Kennitala:0x007fe35d041bc0 @value="0101302989">
|
112
|
+
# k.age
|
113
|
+
# => 86
|
114
|
+
def age
|
115
|
+
year_diff = Date.today.year - to_date.year
|
116
|
+
month_diff = Date.today.month - to_date.month
|
117
|
+
day_diff = Date.today.month - to_date.month
|
118
|
+
|
119
|
+
return year_diff -= 1 if month_diff.negative? || (month_diff.zero? && day_diff.negative?)
|
120
|
+
|
121
|
+
year_diff
|
122
|
+
end
|
123
|
+
|
124
|
+
# Cast the kennitala to a Date object
|
125
|
+
#
|
126
|
+
# @return [Date]
|
127
|
+
#
|
128
|
+
# @example Cast the kennitala to a Date object
|
129
|
+
# k = Kennitala.new('0101302989')
|
130
|
+
# => #<Kennitala:0x007fe35d041bc0 @value="0101302989">
|
131
|
+
# k.to_date
|
132
|
+
# => #<Date: 1930-01-01 ((2425978j,0s,0n),+0s,2299161j)>
|
133
|
+
def to_date
|
134
|
+
Date.new(year, month, day)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Cast the kennitala to a String object
|
138
|
+
#
|
139
|
+
# @return [String]
|
140
|
+
def to_s
|
141
|
+
@value.to_s
|
142
|
+
end
|
143
|
+
|
144
|
+
# Pretty print a kennitala
|
145
|
+
#
|
146
|
+
# Adds a space between the 6th and the 7th digits for readability.
|
147
|
+
#
|
148
|
+
# @param [String] spacer A single space by default
|
149
|
+
# @return [String]
|
150
|
+
#
|
151
|
+
# @example Pretty print a Kennitala it its default form
|
152
|
+
# k = Kennitala.new('0101302989')
|
153
|
+
# => #<Kennitala:0x007fc589339f18 @value="2009155509">
|
154
|
+
# k.pp
|
155
|
+
# => "010130 2989"
|
156
|
+
#
|
157
|
+
# @example Use a hyphen as a spacer instead of the default single space
|
158
|
+
# k = Kennitala.new('0101302989')
|
159
|
+
# => #<Kennitala:0x007fc589339f18 @value="2009155509">
|
160
|
+
# k.pp('-')
|
161
|
+
# => "010130-2989"
|
162
|
+
def pp(spacer = ' ')
|
163
|
+
raise ArgumentError 'Spacer must be a string' unless spacer.instance_of?(String)
|
164
|
+
|
165
|
+
@value[0, 6] + spacer + @value[6, 9]
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
# Generate fake a birth number and check digit based on the first 6 digits
|
171
|
+
#
|
172
|
+
# @param [String] date_hash The first six digits is a kennitala
|
173
|
+
# @return [Hash, nil]
|
174
|
+
def fake_randoms(date_hash)
|
175
|
+
first_six = date_hash[:day] + date_hash[:month] + date_hash[:year]
|
176
|
+
loop do
|
177
|
+
birth_number = Random.rand(1..99).to_s.rjust(2, '0')
|
178
|
+
first_eight = "#{first_six}#{birth_number}"
|
179
|
+
check_digit = calculate_check_digit(first_eight)
|
180
|
+
return { check_digit: check_digit, birth_number: birth_number } if check_checksum(first_eight)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
# Generate a fake year and century Hash
|
185
|
+
#
|
186
|
+
# @return [Hash]
|
187
|
+
def fake_year
|
188
|
+
century = [9, 9, 9, 8, 0, 0].sample
|
189
|
+
current_year = Date.today.strftime('%y').to_i
|
190
|
+
return { year: Random.rand(0..current_year), century: century } if century.zero?
|
191
|
+
|
192
|
+
{ year: Random.rand(0..99), century: century }
|
193
|
+
end
|
194
|
+
|
195
|
+
# Generate a fake hash that includes randomly generated date elements
|
196
|
+
#
|
197
|
+
# @todo Take leap years into account and thus allow for 29 February
|
198
|
+
#
|
199
|
+
# @param [Boolean] is_company true if the day string is for a company
|
200
|
+
# @return [Hash]
|
201
|
+
def fake_date_hash(is_company = false)
|
202
|
+
year_hash = fake_year
|
203
|
+
|
204
|
+
month_days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
|
205
|
+
month = Random.rand(1..12)
|
206
|
+
|
207
|
+
day = Random.rand(1..month_days[month - 1])
|
208
|
+
day = (day.to_i + 40).to_s.rjust(2, '0') if is_company == true
|
209
|
+
|
210
|
+
{ century: year_hash[:century].to_s,
|
211
|
+
year: year_hash[:year].to_s.rjust(2, '0'),
|
212
|
+
month: month.to_s.rjust(2, '0'), day: day.to_s.rjust(2, '0') }
|
213
|
+
end
|
214
|
+
|
215
|
+
# Generate a fake, random kennitala string
|
216
|
+
#
|
217
|
+
# @param [Boolean] is_company If the kennitala is for a company, not a person
|
218
|
+
# @return [String] A 10-digit string representing a kennitala
|
219
|
+
def fake_kt_string(is_company = false)
|
220
|
+
date_hash = fake_date_hash(is_company)
|
221
|
+
randoms_hash = fake_randoms(date_hash)
|
222
|
+
|
223
|
+
first_six = date_hash[:day] + date_hash[:month] + date_hash[:year]
|
224
|
+
randoms = randoms_hash[:birth_number].to_s + randoms_hash[:check_digit].to_s
|
225
|
+
|
226
|
+
first_six + randoms + date_hash[:century]
|
227
|
+
end
|
228
|
+
|
229
|
+
# Get year from a kennitala string
|
230
|
+
#
|
231
|
+
# @param [String] kt_string Sanitized kennitala string
|
232
|
+
# @return [Fixnum] description of returned object
|
233
|
+
def get_year_from_string(kt_string)
|
234
|
+
century_code = kt_string[9, 1].to_i
|
235
|
+
case century_code
|
236
|
+
when 0
|
237
|
+
"20#{kt_string[4, 2]}".to_i
|
238
|
+
when 8..9
|
239
|
+
"1#{century_code}#{kt_string[4, 2]}".to_i
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
# Sanitize the kennitala
|
244
|
+
#
|
245
|
+
# @param [String] kt_string Unsanitised string representing a kennitala
|
246
|
+
# @return [String, nil] Sanitized kennitala, nil if invalid
|
247
|
+
def sanitize(kt_string)
|
248
|
+
sanitized_kt = kt_string.gsub(/[^0-9]/, '')
|
249
|
+
checks = check_checksum(sanitized_kt)
|
250
|
+
|
251
|
+
year = get_year_from_string(sanitized_kt)
|
252
|
+
day = sanitized_kt[0, 2].to_i
|
253
|
+
day -= 40 if day > 40
|
254
|
+
month = sanitized_kt[2, 2].to_i
|
255
|
+
date = Date.new(year, month, day)
|
256
|
+
|
257
|
+
return sanitized_kt if checks == true && date.instance_of?(Date)
|
258
|
+
rescue ArgumentError
|
259
|
+
nil
|
260
|
+
end
|
261
|
+
|
262
|
+
# Calculate the checksum
|
263
|
+
#
|
264
|
+
# @param [String] kt_string Sanitized kennitala
|
265
|
+
# @return [Fixnum] The checksum
|
266
|
+
def checksum(kt_string)
|
267
|
+
checksum = 0
|
268
|
+
multipliers = [3, 2, 7, 6, 5, 4, 3, 2]
|
269
|
+
multipliers.each_with_index do |multiplier, index|
|
270
|
+
checksum += multiplier * kt_string[index].to_i
|
271
|
+
end
|
272
|
+
checksum
|
273
|
+
end
|
274
|
+
|
275
|
+
# Calculate the check digit for a kennitala
|
276
|
+
#
|
277
|
+
# @param [String] kt_string Sanitized kennitala
|
278
|
+
# @return [Fixnum]
|
279
|
+
def calculate_check_digit(kt_string)
|
280
|
+
remainder = checksum(kt_string).modulo(11)
|
281
|
+
|
282
|
+
# A kennitala with a remainder of 10 is always considered to be invalid
|
283
|
+
return nil if remainder == 10
|
284
|
+
|
285
|
+
# The check digit should be 11 minus the remainder,
|
286
|
+
# unless the remainder is 0, then the theck digit becomes 0.
|
287
|
+
return 0 if remainder.zero?
|
288
|
+
|
289
|
+
11 - remainder
|
290
|
+
end
|
291
|
+
|
292
|
+
# Check validity of the check digit
|
293
|
+
#
|
294
|
+
# @param [String] kt_string Sanitized kennitala
|
295
|
+
# @return [Boolean, nil] true on success, false if the check digit is invalid
|
296
|
+
def check_checksum(kt_string)
|
297
|
+
expected_check_digit = calculate_check_digit(kt_string)
|
298
|
+
actual_check_digit = kt_string[8].to_i
|
299
|
+
return true if expected_check_digit == actual_check_digit
|
300
|
+
|
301
|
+
false
|
302
|
+
end
|
303
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Monkey patch for the String class
|
4
|
+
class String
|
5
|
+
# Converts a String to a Kennitala object
|
6
|
+
#
|
7
|
+
# @return [Kennitala]
|
8
|
+
def to_kt
|
9
|
+
Kennitala.new(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Checks if the String is valid for initializing a Kennitala object
|
13
|
+
#
|
14
|
+
# This is useful if you want to deal with conditionals before catching errors.
|
15
|
+
#
|
16
|
+
# @return Boolean
|
17
|
+
def kt?
|
18
|
+
true if Kennitala.new(self)
|
19
|
+
rescue ArgumentError, TypeError
|
20
|
+
false
|
21
|
+
end
|
22
|
+
|
23
|
+
alias_method 'to_kennitala', 'to_kt'
|
24
|
+
alias_method 'kennitala?', 'kt?'
|
25
|
+
end
|
data/sig/kennitala.rbs
ADDED
metadata
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: kennitala
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Alda Vigdís Skarphéðinsdóttir
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2023-02-08 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '13.0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '13.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '3.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '3.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.21'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.21'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: yard
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.9'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.9'
|
69
|
+
description:
|
70
|
+
email:
|
71
|
+
- aldavigdis@aldavigdis.is
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- ".editorconfig"
|
77
|
+
- ".rspec"
|
78
|
+
- ".rubocop.yml"
|
79
|
+
- ".ruby-version"
|
80
|
+
- CODE_OF_CONDUCT.md
|
81
|
+
- Gemfile
|
82
|
+
- Gemfile.lock
|
83
|
+
- LICENSE.txt
|
84
|
+
- README.md
|
85
|
+
- Rakefile
|
86
|
+
- lib/kennitala.rb
|
87
|
+
- lib/kennitala_string.rb
|
88
|
+
- sig/kennitala.rbs
|
89
|
+
homepage: https://github.com/aldavigdis/kennitala-gem/
|
90
|
+
licenses:
|
91
|
+
- MIT
|
92
|
+
metadata:
|
93
|
+
homepage_uri: https://github.com/aldavigdis/kennitala-gem/
|
94
|
+
source_code_uri: https://github.com/aldavigdis/kennitala-gem/
|
95
|
+
rubygems_mfa_required: 'true'
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 2.6.0
|
105
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
requirements: []
|
111
|
+
rubygems_version: 3.3.7
|
112
|
+
signing_key:
|
113
|
+
specification_version: 4
|
114
|
+
summary: Process Icelandic kenntiala identification numbers.
|
115
|
+
test_files: []
|