amka 1.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 39f2441f545f0d191f6d676cf36159edd5f73e84
4
- data.tar.gz: 5d169c49e5b564d2418883e458da46c9e6fdafee
2
+ SHA256:
3
+ metadata.gz: de5ab67f3f55def8127fdcc62f80227e3a1eac67310f5505fb0d2bd885235b5e
4
+ data.tar.gz: 8898093154bfa16391dbf6721204350f4f2052088db0db2be4dc0ee8cda51e38
5
5
  SHA512:
6
- metadata.gz: 42afd4b4c20dfc561ac5583e8684ad4a078f1b7ff5532d7e7fc113d46812d4899e31783e4c4e6216d81401a2acfef1dedf09781b709e9a99dc6ec2d24f90a2bf
7
- data.tar.gz: 5b2e20904e8ea39b2b960aeb61364d3964cc72446ed46ce20e22a496cfb3a1bc6f08fa0befcbd6a03ffa8dde3bd6925e83262be13837317d986c9b965c6466c0
6
+ metadata.gz: 1e46d541a313fd37f7d58f98d244459110d77d22630e65d2b8941fa7c5a4a69f1d0eb8e50962acd6d7e83bec5f6b4fa227e8c1d8e3c9ef8a01c9bbd2b2589b15
7
+ data.tar.gz: d2b9da885d006f520a93b4a08f07561f5518b1369f16fd9c40da0a78560cee23edef7a49359e9addf8c0ec1cbeed401ebcf0159870d37325fb87ebf07a483faa
data/CHANGELOG.md ADDED
@@ -0,0 +1,47 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.1.0] - 2023-05-14
9
+
10
+ ### Added
11
+
12
+ - New `validate` method that returns detailed validation errors
13
+ - New `validate!` method that raises exceptions with descriptive messages
14
+ - Added `safe_valid?` method to Luhn class for exception-free validation
15
+ - Improved error handling with specific `ValidationError` class
16
+
17
+ ### Changed
18
+
19
+ - Improved `valid?` method to never raise exceptions
20
+ - Refactored validation logic for better maintainability
21
+ - Better error messages for validation failures
22
+
23
+ ### Fixed
24
+
25
+ - Rubocop compliance for method length
26
+
27
+ ## [2.0.0] - 2025-04-16
28
+
29
+ ### Changed
30
+
31
+ - Updated minimum Ruby version to 2.7.0
32
+ - Migrated from Minitest to RSpec for testing
33
+ - Modernized gem infrastructure
34
+ - Added GitHub Actions for CI
35
+ - Added SimpleCov for test coverage
36
+ - Improved documentation
37
+
38
+ ### Security
39
+
40
+ - Added MFA requirement for gem publishing
41
+ - Updated development dependencies
42
+
43
+ ## [1.0.1] - 2015-10-15
44
+
45
+ ### Added
46
+
47
+ - Initial release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,84 @@
1
+ # Contributing to AMKA
2
+
3
+ Thank you for considering contributing to the AMKA gem! This document outlines the process for contributing to this project.
4
+
5
+ ## How Can I Contribute?
6
+
7
+ ### Reporting Bugs
8
+
9
+ This section guides you through submitting a bug report. Following these guidelines helps maintainers and the community understand your report, reproduce the behavior, and find related reports.
10
+
11
+ - **Use the GitHub issue tracker** — Check if the bug has already been reported by searching on GitHub under [Issues](https://github.com/ioagel/amka/issues).
12
+ - **Use the bug report template** — If you're reporting a bug, be sure to include all the information requested in the bug report template.
13
+ - **Provide specific examples** — Include specific steps to reproduce the problem, with example code if possible.
14
+ - **Describe the behavior you observed** — What happened? How did it differ from what you expected?
15
+ - **Include relevant details** — OS, Ruby version, AMKA gem version, etc.
16
+
17
+ ### Suggesting Enhancements
18
+
19
+ This section guides you through submitting an enhancement suggestion, including completely new features and minor improvements to existing functionality.
20
+
21
+ - **Use the GitHub issue tracker** — Check if the enhancement has already been suggested by searching on GitHub under [Issues](https://github.com/ioagel/amka/issues).
22
+ - **Use the feature request template** — Include as much detail as possible in the template.
23
+ - **Provide a clear use case** — Explain why this enhancement would be useful to most AMKA users.
24
+
25
+ ### Pull Requests
26
+
27
+ - **Fill in the required template**
28
+ - **Follow the Ruby style guide**
29
+ - **Include tests** — Your patch won't be accepted if it doesn't have tests.
30
+ - **Document your changes** — Update any relevant documentation.
31
+ - **Include updates to the CHANGELOG.md** file for any user-facing changes.
32
+ - **Create feature branches** — Don't ask us to pull from your main branch.
33
+
34
+ ## Development Process
35
+
36
+ ### Setting Up the Development Environment
37
+
38
+ 1. Fork and clone the repository
39
+ 2. Run `bin/setup` to install dependencies
40
+ 3. Run `bundle exec rake spec` to run the tests
41
+
42
+ ### Testing
43
+
44
+ - All tests must pass before a pull request will be accepted
45
+ - Write tests for all new functionality
46
+ - Run tests using `bundle exec rake spec`
47
+ - Check code coverage (we aim for >90%) using `open coverage/index.html` after running tests
48
+
49
+ ### Style Guidelines
50
+
51
+ - Follow the Ruby style guide:
52
+ - Use 2 spaces for indentation
53
+ - Use snake_case for methods and variables
54
+ - Use CamelCase for classes and modules
55
+ - Add descriptive comments for public methods and classes
56
+ - Run `bundle exec rubocop` before submitting to ensure code quality
57
+ - Add YARD documentation for new public methods
58
+
59
+ ### Git Commit Messages
60
+
61
+ - Use the present tense ("Add feature" not "Added feature")
62
+ - Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
63
+ - Limit the first line to 72 characters or less
64
+ - Reference issues and pull requests after the first line
65
+
66
+ ## Releasing a New Version
67
+
68
+ For maintainers only:
69
+
70
+ 1. Update the version number in `lib/amka/version.rb`
71
+ 2. Update the CHANGELOG.md
72
+ 3. Run the test suite to make sure everything works
73
+ 4. Commit changes with message "Bump version to x.y.z"
74
+ 5. Tag the commit with "vx.y.z"
75
+ 6. Push to GitHub
76
+ 7. Run `bundle exec rake release`
77
+
78
+ ## Additional Resources
79
+
80
+ - [General GitHub documentation](https://help.github.com)
81
+ - [GitHub pull request documentation](https://help.github.com/articles/about-pull-requests/)
82
+ - [Ruby style guide](https://github.com/rubocop/ruby-style-guide)
83
+
84
+ Thank you for contributing!
data/README.md CHANGED
@@ -1,23 +1,37 @@
1
1
  # AMKA and Luhn IDs Validator / Generator
2
- ##### Α.Μ.Κ.Α (Αριθμός Μητρώου Κοινωνικής Ασφάλισης)
2
+
3
+ [![Ruby CI](https://github.com/ioagel/amka/actions/workflows/ci.yml/badge.svg)](https://github.com/ioagel/amka/actions/workflows/ci.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/amka.svg)](https://badge.fury.io/rb/amka)
5
+ [![Yard Docs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/gems/amka)
6
+
7
+ ## Α.Μ.Κ.Α (Αριθμός Μητρώου Κοινωνικής Ασφάλισης)
8
+
3
9
  This gem validates **A.M.K.A** Greek social security IDs by
4
10
  using the Luhn algorithm, as described in this [Wikipedia article](https://en.wikipedia.org/wiki/Luhn_algorithm).
5
11
  The only additional validation needed that applies to Greek A.M.K.A is that the
6
12
  first 6 digits must be a valid date (date of birth).
7
13
 
8
- The gem also *validates* and *generates* **Luhn** ids, as well as *generating*
9
- **AMKA** (like) ids. The *like* here means that the AMKA generated are not
14
+ The gem also _validates_ and _generates_ **Luhn** ids, as well as _generating_
15
+ **AMKA** (like) ids. The _like_ here means that the AMKA generated are not
10
16
  **real** as those provided by the Official Authorities in Greece.
11
17
 
12
18
  There is an online calculator in [planetcalc](http://planetcalc.com/2464/), you
13
19
  can validate the results of the gem, until you are confident that is working
14
20
  as it should. Try you own and your family AMKA.
15
21
 
16
- **DISCLAIMER**: The gem *does not* validate the real existence of the A.M.K.A or that it
22
+ **DISCLAIMER**: The gem _does not_ validate the real existence of the A.M.K.A or that it
17
23
  belongs to a given person. The same applies to the generator.
18
24
 
25
+ ## Documentation
26
+
27
+ - [Usage Guide](USAGE.md) - Comprehensive examples and use cases
28
+ - [API Documentation](http://rubydoc.info/gems/amka) - Full method documentation
29
+ - [Contributing Guide](CONTRIBUTING.md) - How to contribute to this gem
30
+ - [Changelog](CHANGELOG.md) - Version history and changes
31
+
19
32
  ## Installation
20
- Requires ruby >= 2.0.0
33
+
34
+ Requires ruby >= 2.7.0
21
35
 
22
36
  Add this line to your application's Gemfile:
23
37
 
@@ -27,23 +41,46 @@ gem 'amka'
27
41
 
28
42
  And then execute:
29
43
 
30
- $ bundle
44
+ ```bash
45
+ bundle install
46
+ ```
31
47
 
32
48
  Or install it yourself as:
33
49
 
34
- $ gem install amka
50
+ ```bash
51
+ gem install amka
52
+ ```
35
53
 
36
- ## Usage
54
+ ## Quick Start
37
55
 
38
56
  ```ruby
39
57
  require 'amka'
40
58
 
41
- # To validate (takes the AMKA as a string and returns true or false)
59
+ # Simple Validation (returns true or false)
42
60
  Amka.valid?('01011441432')
43
- # returns false (i did not use a valid one in this example just in case
44
- # it belonged to a real person!!!!)
45
-
46
- # To generate (returns the AMKA as a string)
61
+ # returns false
62
+
63
+ # You can also pass the 4 digit year of birth as a second string argument
64
+ # This will increase the accuracy of the date part of validation to 100%.
65
+ Amka.valid?('111098xxxxx', '1998')
66
+
67
+ # Detailed Validation with Error Messages
68
+ errors = Amka.validate('01AB9012345')
69
+ if errors.empty?
70
+ puts "Valid AMKA!"
71
+ else
72
+ puts "Invalid AMKA: #{errors.join(', ')}"
73
+ # => "Invalid AMKA: AMKA must contain only digits"
74
+ end
75
+
76
+ # Exception-based Validation
77
+ begin
78
+ Amka.validate!('01019012345') # Returns true if valid
79
+ rescue Amka::ValidationError => e
80
+ puts "Error: #{e.message}"
81
+ end
82
+
83
+ # To generate a random AMKA (returns the AMKA as a string)
47
84
  Amka.generate
48
85
 
49
86
  # To generate with a specific date (date format: [d]d/[m]m/yyyy)
@@ -58,24 +95,28 @@ Amka::Luhn.valid?('1142376755673') # returns true
58
95
  # To generate a Luhn id
59
96
  # takes up to 2 arguments: total, id_start(optional)
60
97
  # total: how many digits you want in the Luhn generated
61
- # id_start: string of numbers to include from the start of the Luhn
62
- # - total must be greater than id_start by at least one, to account for the
63
- # rightmost check digit.
98
+ # id_start: string of numbers to include at the start of the Luhn
99
+ # - total must be greater than the length of id_start by at least one, to
100
+ # account for the rightmost check digit.
64
101
  Amka::Luhn.generate(13) # returns '5361281695669'
65
102
  Amka::Luhn.generate(10, '123456789') # returns '1234567897', 7 is the check digit.
66
103
  Amka::Luhn.generate(15, '12345') # returns something like: '123450789968798'
67
104
  ```
68
105
 
69
- ## TODO
106
+ See the [Usage Guide](USAGE.md) for more detailed examples and advanced usage.
70
107
 
71
- Add tests for the Luhn class.
108
+ ## Development
109
+
110
+ 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.
111
+
112
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
72
113
 
73
114
  ## Contributing
74
115
 
75
- Bug reports and pull requests are welcome on GitHub at https://github.com/ioagel/amka.
116
+ Bug reports and pull requests are welcome on GitHub at <https://github.com/ioagel/amka>.
76
117
 
118
+ Please see the [Contributing Guide](CONTRIBUTING.md) for more details on how to contribute.
77
119
 
78
120
  ## License
79
121
 
80
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
81
-
122
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/USAGE.md ADDED
@@ -0,0 +1,161 @@
1
+ # AMKA Gem Usage Guide
2
+
3
+ This document provides comprehensive examples and guidelines for using the AMKA gem in your Ruby projects.
4
+
5
+ ## Table of Contents
6
+
7
+ - [AMKA Gem Usage Guide](#amka-gem-usage-guide)
8
+ - [Table of Contents](#table-of-contents)
9
+ - [Understanding AMKA](#understanding-amka)
10
+ - [Basic Usage](#basic-usage)
11
+ - [Installation](#installation)
12
+ - [Validation](#validation)
13
+ - [Generation](#generation)
14
+ - [Advanced Usage](#advanced-usage)
15
+ - [Working with Birth Dates](#working-with-birth-dates)
16
+ - [Using the Luhn Algorithm Directly](#using-the-luhn-algorithm-directly)
17
+ - [Error Handling](#error-handling)
18
+ - [Performance Considerations](#performance-considerations)
19
+ - [Use Cases](#use-cases)
20
+
21
+ ## Understanding AMKA
22
+
23
+ An AMKA (Αριθμός Μητρώου Κοινωνικής Ασφάλισης) is a unique 11-digit identification number used in Greece. It's similar to a Social Security Number in the United States and follows these rules:
24
+
25
+ 1. It's always exactly 11 digits long
26
+ 2. The first 6 digits represent the person's date of birth in format DDMMYY
27
+ 3. The number (as a whole) must pass validation by the Luhn algorithm
28
+ 4. The last digit is a check digit computed using the Luhn algorithm
29
+
30
+ ## Basic Usage
31
+
32
+ ### Installation
33
+
34
+ Add the gem to your Gemfile:
35
+
36
+ ```ruby
37
+ gem 'amka'
38
+ ```
39
+
40
+ Or install it directly:
41
+
42
+ ```bash
43
+ gem install amka
44
+ ```
45
+
46
+ Then require it in your code:
47
+
48
+ ```ruby
49
+ require 'amka'
50
+ ```
51
+
52
+ ### Validation
53
+
54
+ You can validate an AMKA number with a simple call:
55
+
56
+ ```ruby
57
+ # Basic validation
58
+ Amka.valid?('01019012345') # => true or false
59
+
60
+ # The validation checks:
61
+ # 1. If it's exactly 11 digits
62
+ # 2. If the first 6 digits form a valid date (ddmmyy)
63
+ # 3. If the number passes the Luhn algorithm check
64
+ ```
65
+
66
+ When working with dates from different centuries, you can provide the 4-digit year for more accurate validation:
67
+
68
+ ```ruby
69
+ # Explicitly specify century for validation
70
+ Amka.valid?('01019012345', '1990') # => true
71
+ Amka.valid?('01019012345', '2090') # => false (if the year doesn't match)
72
+ ```
73
+
74
+ ### Generation
75
+
76
+ You can generate random valid AMKA numbers:
77
+
78
+ ```ruby
79
+ # Generate a random valid AMKA
80
+ amka = Amka.generate # => "01019012345"
81
+ ```
82
+
83
+ Or generate an AMKA with a specific birth date:
84
+
85
+ ```ruby
86
+ # Generate an AMKA for someone born January 1, 1990
87
+ amka = Amka.generate('1/1/1990') # => "01019012345"
88
+
89
+ # Days and months can be 1 or 2 digits
90
+ amka = Amka.generate('15/12/1985') # => "15128512345"
91
+ ```
92
+
93
+ ## Advanced Usage
94
+
95
+ ### Working with Birth Dates
96
+
97
+ The first 6 digits of an AMKA represent a date of birth in DDMMYY format:
98
+
99
+ ```ruby
100
+ amka = '01019012345'
101
+ birth_date = Date.strptime(amka[0..5], '%d%m%y') # => #<Date: 1990-01-01>
102
+
103
+ # For ambiguous 2-digit years, you might need to adjust the century
104
+ if birth_date > Date.today
105
+ birth_date = Date.strptime(amka[0..3] + '19' + amka[4..5], '%d%m%Y')
106
+ end
107
+ ```
108
+
109
+ ### Using the Luhn Algorithm Directly
110
+
111
+ The gem includes a standalone implementation of the Luhn algorithm that can be used for other purposes:
112
+
113
+ ```ruby
114
+ # Validate any number against the Luhn algorithm
115
+ Amka::Luhn.valid?('4532015112830366') # => true (valid credit card number)
116
+ Amka::Luhn.valid?('1234567890') # => false
117
+
118
+ # Generate a valid Luhn number of specific length
119
+ luhn_id = Amka::Luhn.generate(16) # => "4532015112830366"
120
+
121
+ # Generate a valid Luhn number with a specific prefix
122
+ luhn_id = Amka::Luhn.generate(16, '4532') # => "4532015112830366"
123
+ ```
124
+
125
+ ## Error Handling
126
+
127
+ The gem will raise descriptive `ArgumentError` exceptions for invalid inputs:
128
+
129
+ ```ruby
130
+ begin
131
+ Amka.valid?(12345) # Not a string
132
+ rescue ArgumentError => e
133
+ puts e.message # => "'12345': must be a string of digits only!"
134
+ end
135
+
136
+ begin
137
+ Amka.generate('invalid-date')
138
+ rescue ArgumentError => e
139
+ puts e.message # => "date of birth must be in this format: [d]d/[m]m/yyyy"
140
+ end
141
+
142
+ begin
143
+ Amka.generate('31/2/1990') # February 31st doesn't exist
144
+ rescue ArgumentError => e
145
+ puts e.message # => "The date of birth is invalid!"
146
+ end
147
+ ```
148
+
149
+ ## Performance Considerations
150
+
151
+ - The validation and generation methods are lightweight and suitable for high-volume use
152
+ - No external dependencies are required
153
+ - All operations are performed in memory without any I/O
154
+
155
+ ## Use Cases
156
+
157
+ - Government systems that need to validate AMKA numbers
158
+ - Healthcare applications that need to work with patient identification
159
+ - Testing systems that need to generate sample data
160
+ - Any application that needs to implement the Luhn algorithm validation
161
+ - Form validation for Greek websites
data/lib/amka/luhn.rb CHANGED
@@ -1,40 +1,132 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Amka
2
4
  # Implements the Luhn algorithm as described in the wikipedia article
3
5
  # https://en.wikipedia.org/wiki/Luhn_algorithm
6
+ #
7
+ # The Luhn algorithm (also known as the "modulus 10" or "mod 10" algorithm)
8
+ # is a simple checksum formula used to validate various identification numbers,
9
+ # such as credit card numbers, IMEI numbers, and national identification numbers
10
+ # (like AMKA in Greece).
11
+ #
12
+ # ## Algorithm Steps
13
+ # 1. Starting from the rightmost digit, double the value of every second digit
14
+ # 2. If doubling a number results in a two-digit number (>9), subtract 9 from the result
15
+ # 3. Sum all digits (both doubled and non-doubled)
16
+ # 4. If the total modulo 10 is 0, then the number is valid
17
+ #
18
+ # ## Uses
19
+ # This class provides both validation of existing IDs and generation of
20
+ # new valid IDs that follow the Luhn algorithm.
21
+ #
22
+ # @example Validate a Luhn ID
23
+ # Amka::Luhn.valid?('79927398713') #=> true
24
+ #
25
+ # @example Generate a new Luhn ID of specified length
26
+ # Amka::Luhn.generate(10) #=> "7992739871"
27
+ #
28
+ # @example Generate a Luhn ID with a specific prefix
29
+ # Amka::Luhn.generate(16, '4532') #=> "4532015112830366"
4
30
  class Luhn
5
-
31
+ # Validates if a given ID follows the Luhn algorithm
32
+ #
33
+ # Applies the standard Luhn algorithm to check if a number is valid:
34
+ # 1. From the rightmost digit, double every second digit
35
+ # 2. Sum the digits (if any doubled digit > 9, subtract 9)
36
+ # 3. If the sum is divisible by 10, the number is valid
37
+ #
38
+ # @param luhn_id [String] the ID to validate, must be a string containing only digits
39
+ # @return [Boolean] true if valid according to Luhn algorithm, false otherwise
40
+ # @raise [ArgumentError] if the ID is not a string of digits
41
+ # @example Validating a credit card number
42
+ # Amka::Luhn.valid?('4532015112830366') #=> true
43
+ # @example Validating an invalid number
44
+ # Amka::Luhn.valid?('1234567890') #=> false
6
45
  def self.valid?(luhn_id)
7
- Utils.string_with_digits_or_fail luhn_id
46
+ Utils.string_with_digits_or_fail(luhn_id)
8
47
 
9
48
  digits_sum = calculate_digits_sum(luhn_id)
10
49
 
11
- (digits_sum % 10) == 0
50
+ (digits_sum % 10).zero?
12
51
  end
13
52
 
53
+ # Validates if a given ID follows the Luhn algorithm, without raising exceptions
54
+ #
55
+ # This is a safe version of valid? that returns false for any invalid input
56
+ # instead of raising exceptions. It's designed for use in validation pipelines
57
+ # where exceptions would need to be caught.
58
+ #
59
+ # @param luhn_id [Object] the ID to validate
60
+ # @return [Boolean] true if valid according to Luhn algorithm, false otherwise
61
+ # @example Safe validation with clean inputs
62
+ # Amka::Luhn.safe_valid?('4532015112830366') #=> true
63
+ # @example Safe validation with problematic inputs
64
+ # Amka::Luhn.safe_valid?(nil) #=> false
65
+ # Amka::Luhn.safe_valid?(12345) #=> false
66
+ # Amka::Luhn.safe_valid?('abc-123') #=> false
67
+ def self.safe_valid?(luhn_id)
68
+ # Return false for non-string input
69
+ return false unless luhn_id.is_a?(String)
70
+ # Return false for strings with non-digits
71
+ return false unless luhn_id.match(/\A\d+\Z/)
72
+
73
+ digits_sum = calculate_digits_sum(luhn_id)
74
+ (digits_sum % 10).zero?
75
+ rescue StandardError
76
+ # Catch any unexpected errors and return false
77
+ false
78
+ end
79
+
80
+ # Generates a valid Luhn ID
81
+ #
82
+ # Creates a number that passes the Luhn check by:
83
+ # 1. Taking the provided prefix (or creating a random one)
84
+ # 2. Calculating what the check digit should be for the number to be valid
85
+ # 3. Appending the check digit to create a valid Luhn number
86
+ #
87
+ # @param total [Integer] the total length of the ID to generate
88
+ # @param id_start [String] optional digits to use at the start of the ID
89
+ # @return [String] a valid Luhn ID of exactly 'total' length
90
+ # @raise [ArgumentError] if arguments are invalid
91
+ # @example Generate a 16-digit number (like a credit card)
92
+ # Amka::Luhn.generate(16) #=> "4532015112830366"
93
+ # @example Generate a number with a specific prefix
94
+ # Amka::Luhn.generate(10, '123456') #=> "1234567897"
95
+ # @example Special case for length 1
96
+ # Amka::Luhn.generate(1) #=> "0"
14
97
  def self.generate(total, id_start = '')
15
98
  validate_generate_args_or_fail(total, id_start)
16
- id_start == '' && total == 1 and return '0'
99
+ return '0' if id_start.empty? && total == 1
17
100
 
18
101
  last_digits_length = total - id_start.length
19
- last_digits_except_check = ''
102
+ # Use String.new to create an unfrozen string
103
+ last_digits_except_check = String.new
20
104
  # subtract by one to account for the check digit
21
105
  (last_digits_length - 1).times { last_digits_except_check << rand(0..9).to_s }
22
- luhn_id_except_check_digit = id_start << last_digits_except_check
106
+ # Using + instead of << for string concatenation to prevent frozen string issues
107
+ luhn_id_except_check_digit = id_start + last_digits_except_check
23
108
 
24
- digits_sum = calculate_digits_sum(luhn_id_except_check_digit, true)
109
+ digits_sum = calculate_digits_sum(luhn_id_except_check_digit, generate: true)
25
110
 
26
111
  check_digit = (digits_sum * 9) % 10
27
112
 
28
- luhn_id_except_check_digit << check_digit.to_s
113
+ luhn_id_except_check_digit + check_digit.to_s
29
114
  end
30
115
 
31
- def self.calculate_digits_sum(luhn_id, generate = false)
116
+ # Calculates the sum of digits according to the Luhn algorithm
117
+ #
118
+ # This implementation handles both validation of existing numbers and
119
+ # generation of new numbers with the 'generate' parameter determining the pattern
120
+ # of which digits to double.
121
+ #
122
+ # @param luhn_id [String] the ID to calculate the sum for
123
+ # @param generate [Boolean] whether we're generating (affects digit doubling pattern)
124
+ # @return [Integer] the sum of digits according to Luhn algorithm rules
125
+ def self.calculate_digits_sum(luhn_id, generate: false)
32
126
  luhn_id_double = luhn_id.chars.reverse.map(&:to_i).map.with_index do |digit, i|
33
- if (!generate && i.odd?) || (generate && i.even?)
34
- if (digit *= 2) > 9
35
- # digit = digit.to_s.chars.map(&:to_i).reduce(:+)
36
- digit -= 9
37
- end
127
+ if ((!generate && i.odd?) || (generate && i.even?)) && ((digit *= 2) > 9)
128
+ # Same as: digit = digit.to_s.chars.map(&:to_i).reduce(:+)
129
+ digit -= 9
38
130
  end
39
131
  digit
40
132
  end
@@ -42,14 +134,25 @@ module Amka
42
134
  end
43
135
  private_class_method :calculate_digits_sum
44
136
 
137
+ # Validates the arguments for the generate method
138
+ #
139
+ # Performs several checks:
140
+ # 1. Ensures id_start is a string containing only digits (or empty)
141
+ # 2. Ensures total is a positive integer
142
+ # 3. Ensures total > id_start.length to allow room for the check digit
143
+ #
144
+ # @param total [Integer] the total length of the ID to generate
145
+ # @param id_start [String] digits to use at the start of the ID
146
+ # @raise [ArgumentError] if arguments are invalid
45
147
  def self.validate_generate_args_or_fail(total, id_start)
46
- Utils.string_with_digits_or_empty_or_fail id_start
47
- Utils.positive_integer_or_fail total
48
- total > id_start.length or
49
- fail ArgumentError, "'#{total}': must be greater at least by one from string length: "\
50
- "#{id_start.length}, to account for the check digit!"
148
+ Utils.string_with_digits_or_empty_or_fail(id_start)
149
+ Utils.positive_integer_or_fail(total)
150
+
151
+ return if total > id_start.length
152
+
153
+ raise ArgumentError, "'#{total}': must be greater at least by one from string length: " \
154
+ "#{id_start.length}, to account for the check digit!"
51
155
  end
52
156
  private_class_method :validate_generate_args_or_fail
53
-
54
157
  end
55
158
  end
data/lib/amka/utils.rb CHANGED
@@ -1,57 +1,175 @@
1
- module Amka
1
+ # frozen_string_literal: true
2
2
 
3
+ module Amka
4
+ # Utility methods for the Amka module
5
+ #
6
+ # This class provides helper methods for parameter validation and date handling
7
+ # that are used throughout the AMKA implementation. These utilities encapsulate
8
+ # common validation logic and error handling to keep the main code cleaner.
9
+ #
10
+ # The methods primarily fall into these categories:
11
+ # - String validation (digits only, format checking)
12
+ # - Number validation
13
+ # - Date validation and conversion
14
+ #
15
+ # All validation methods follow a consistent pattern: they either succeed silently
16
+ # or raise descriptive ArgumentError exceptions.
3
17
  class Utils
4
18
  class << self
19
+ # Validates that the input is a string with only digits
20
+ #
21
+ # Used when a value must be a non-empty string of digits, such as
22
+ # an ID or number that needs to be processed digit by digit.
23
+ #
24
+ # @param id [String] the string to validate
25
+ # @raise [ArgumentError] if the string is not all digits
26
+ # @example
27
+ # Utils.string_with_digits_or_fail('12345') # => nil (success)
28
+ # Utils.string_with_digits_or_fail('123abc') # => ArgumentError
29
+ # Utils.string_with_digits_or_fail('') # => ArgumentError
5
30
  def string_with_digits_or_fail(id)
6
- id.is_a?(String) && id.match(/\A\d+\Z/) or
7
- fail ArgumentError, "'#{id}': must be a string of digits only!"
31
+ return if id.is_a?(String) && id.match(/\A\d+\Z/)
32
+
33
+ raise ArgumentError, "'#{id}': must be a string of digits only!"
8
34
  end
9
35
 
36
+ # Validates that the input is a string with only digits or empty
37
+ #
38
+ # Similar to string_with_digits_or_fail but allows empty strings.
39
+ # Used when an optional string of digits is expected.
40
+ #
41
+ # @param id [String] the string to validate
42
+ # @raise [ArgumentError] if the string contains non-digits
43
+ # @example
44
+ # Utils.string_with_digits_or_empty_or_fail('12345') # => nil (success)
45
+ # Utils.string_with_digits_or_empty_or_fail('') # => nil (success)
46
+ # Utils.string_with_digits_or_empty_or_fail('123abc') # => ArgumentError
10
47
  def string_with_digits_or_empty_or_fail(id)
11
- id.is_a?(String) && id.match(/\A\d*\Z/) or
12
- fail ArgumentError, "'#{id}': must be a string of digits or even an empty one!"
48
+ return if id.is_a?(String) && id.match(/\A\d*\Z/)
49
+
50
+ raise ArgumentError, "'#{id}': must be a string of digits or even an empty one!"
13
51
  end
14
52
 
53
+ # Validates that the input is a string with date in format dd/mm/yyyy
54
+ #
55
+ # Checks that the string follows the Greek date format where:
56
+ # - Day can be 1 or 2 digits (1-31)
57
+ # - Month can be 1 or 2 digits (1-12)
58
+ # - Year must be 4 digits
59
+ #
60
+ # Note: This only checks the format, not if the date is valid
61
+ # (e.g., 31/02/2022 would pass this check but is not a valid date)
62
+ #
63
+ # @param date [String] the date string to validate
64
+ # @raise [ArgumentError] if the date format is invalid
65
+ # @example
66
+ # Utils.string_with_date_or_fail('1/1/2020') # => nil (success)
67
+ # Utils.string_with_date_or_fail('31/12/2020') # => nil (success)
68
+ # Utils.string_with_date_or_fail('2020-12-31') # => ArgumentError
15
69
  def string_with_date_or_fail(date)
16
- date.is_a?(String) && date.match(%r{\A\d?\d{1}/\d?\d{1}/\d{4}\Z}) or
17
- fail ArgumentError, 'date of birth must be in this format: [d]d/[m]m/yyyy'
70
+ return if date.is_a?(String) && date.match(%r{\A\d?\d{1}/\d?\d{1}/\d{4}\Z})
71
+
72
+ raise ArgumentError, 'date of birth must be in this format: [d]d/[m]m/yyyy'
18
73
  end
19
74
 
75
+ # Validates that the input is a positive integer
76
+ #
77
+ # Used primarily for length/count parameters where negative or
78
+ # zero values would be invalid.
79
+ #
80
+ # @param num [Integer] the number to validate
81
+ # @raise [ArgumentError] if the number is not a positive integer
82
+ # @example
83
+ # Utils.positive_integer_or_fail(10) # => nil (success)
84
+ # Utils.positive_integer_or_fail(0) # => ArgumentError
85
+ # Utils.positive_integer_or_fail(-5) # => ArgumentError
86
+ # Utils.positive_integer_or_fail('5') # => ArgumentError
20
87
  def positive_integer_or_fail(num)
21
- num.is_a?(Integer) && num > 0 or
22
- fail ArgumentError, "'#{num}': must be a non-zero positive integer!"
88
+ return if num.is_a?(Integer) && num.positive?
89
+
90
+ raise ArgumentError, "'#{num}': must be a non-zero positive integer!"
23
91
  end
24
92
 
93
+ # Validates that the given date string is a valid date
94
+ #
95
+ # This is a more comprehensive date validation that:
96
+ # 1. Checks the basic format
97
+ # 2. Confirms the date actually exists in the calendar
98
+ # 3. Optionally verifies it matches a specific 4-digit year
99
+ #
100
+ # This is particularly important for AMKA validation since the
101
+ # first 6 digits must represent a valid date of birth.
102
+ #
103
+ # @param date [String] the date string to validate in format ddmmyy
104
+ # @param year [String, nil] optional 4-digit year to check against
105
+ # @return [Boolean] true if date is valid, false otherwise
106
+ # @raise [ArgumentError] if year format or range is invalid
107
+ # @example Simple validation (auto-guesses century)
108
+ # Utils.valid_date?('010190') # => true (January 1, 1990)
109
+ # @example With explicit year
110
+ # Utils.valid_date?('010190', '1990') # => true
111
+ # Utils.valid_date?('010190', '2090') # => ArgumentError
25
112
  def valid_date?(date, year = nil)
26
113
  return false unless date.match(/\A\d{6,}\Z/)
27
114
 
28
- unless year.nil?
29
- year.is_a?(String) && year.match(/\A\d{4}\Z/) or
30
- fail ArgumentError, 'Year must be a 4 digit string'
31
- year.to_i >= 1800 && year.to_i <= Date.today.year or
32
- fail ArgumentError, 'Year must be between 1800 and current!'
33
- year[2..3] == date[4..5] or
34
- fail ArgumentError, 'The last 2 digits of year parameter and the 2 digits'\
35
- ' that correspond to the year in date must be equal!'
36
- begin
37
- dob = Date.strptime(date[0..3] << year, '%d%m%Y')
38
- return dob < Date.today
39
- rescue ArgumentError
40
- return false
41
- end
115
+ if year
116
+ validate_year_format(year, date)
117
+ return validate_full_date(date, year)
118
+ end
119
+
120
+ validate_short_date(date)
121
+ end
122
+
123
+ private
124
+
125
+ # Validates the year format and range
126
+ #
127
+ # @param year [String] the 4-digit year to validate
128
+ # @param date [String] the date string (for year digit comparison)
129
+ # @raise [ArgumentError] if year format or range is invalid
130
+ def validate_year_format(year, date)
131
+ unless year.is_a?(String) && year.match(/\A\d{4}\Z/)
132
+ raise ArgumentError, 'Year must be a 4 digit string'
42
133
  end
43
134
 
44
- begin
45
- date_2_digit_year = Date.strptime(date[0..5], '%d%m%y')
46
- if date_2_digit_year > Date.today
47
- Date.strptime(date[0..3] << '19' << date[4..5], '%d%m%Y')
48
- end
49
- return true
50
- rescue ArgumentError
51
- return false
135
+ unless year.to_i.between?(1800, Date.today.year)
136
+ raise ArgumentError, 'Year must be between 1800 and current!'
52
137
  end
138
+
139
+ return if year[2..3] == date[4..5]
140
+
141
+ raise ArgumentError, 'The last 2 digits of year parameter and the 2 digits ' \
142
+ 'that correspond to the year in date must be equal!'
143
+ end
144
+
145
+ # Validates a date with a full 4-digit year
146
+ #
147
+ # Checks if the given date in DDMMYYYY format is valid and in the past.
148
+ #
149
+ # @param date [String] the date string (first 6 digits, format ddmmyy)
150
+ # @param year [String] the 4-digit year
151
+ # @return [Boolean] true if date is valid and in the past
152
+ def validate_full_date(date, year)
153
+ dob = Date.strptime(date[0..3] << year, '%d%m%Y')
154
+ dob < Date.today
155
+ rescue ArgumentError
156
+ false
157
+ end
158
+
159
+ # Validates a date with just 2-digit year
160
+ #
161
+ # Handles the ambiguity of 2-digit years by trying to interpret
162
+ # them as 20xx or 19xx years based on what makes sense.
163
+ #
164
+ # @param date [String] the date string (first 6 digits, format ddmmyy)
165
+ # @return [Boolean] true if date is valid
166
+ def validate_short_date(date)
167
+ date_2_digit_year = Date.strptime(date[0..5], '%d%m%y')
168
+ Date.strptime(date[0..3] << '19' << date[4..5], '%d%m%Y') if date_2_digit_year > Date.today
169
+ true
170
+ rescue ArgumentError
171
+ false
53
172
  end
54
173
  end
55
174
  end
56
-
57
175
  end
data/lib/amka/version.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Amka
2
- VERSION = '1.0.1'
4
+ VERSION = '2.1.0'
3
5
  end
data/lib/amka.rb CHANGED
@@ -1,31 +1,208 @@
1
- require 'amka/version'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'amka/version'
2
4
  require 'date'
3
5
 
6
+ # The Amka module provides functionality for validating and generating
7
+ # Greek A.M.K.A (social security) IDs as well as generic Luhn algorithm IDs.
8
+ # A.M.K.A IDs follow the Luhn algorithm and have their first 6 digits
9
+ # representing the date of birth in format DDMMYY.
10
+ #
11
+ # An AMKA (Αριθμός Μητρώου Κοινωνικής Ασφάλισης) is a unique 11-digit
12
+ # number assigned to each Greek citizen or resident who works, pays social
13
+ # security contributions, or is entitled to healthcare. It follows these rules:
14
+ #
15
+ # 1. The first 6 digits represent the date of birth in format DDMMYY
16
+ # 2. The remaining 5 digits include a check digit based on the Luhn algorithm
17
+ # 3. The total length is always 11 digits
18
+ #
19
+ # @example Validating an AMKA
20
+ # Amka.valid?('01018012345') #=> true or false
21
+ #
22
+ # @example Generating a random AMKA
23
+ # Amka.generate #=> "21129012345"
24
+ #
25
+ # @example Generating an AMKA with specific birth date
26
+ # Amka.generate('21/12/1990') #=> "21129012345"
27
+ #
28
+ # @author Ioannis Angelakopoulos
29
+ # @since 1.0.0
4
30
  module Amka
31
+ # Standard error class for the Amka gem
32
+ class Error < StandardError; end
5
33
 
6
- def self.valid?(amka, year = nil)
7
- Utils.string_with_digits_or_fail amka
34
+ # Error raised when AMKA format or content is invalid
35
+ class ValidationError < Error; end
8
36
 
37
+ # Validates whether a given string is a valid AMKA
38
+ #
39
+ # The validation checks three conditions:
40
+ # 1. The AMKA must be exactly 11 digits long
41
+ # 2. The first 6 digits must form a valid date (DDMMYY format)
42
+ # 3. The entire number must satisfy the Luhn algorithm check
43
+ #
44
+ # This method will always return a boolean and never raise exceptions,
45
+ # returning false for any input that doesn't meet the AMKA criteria.
46
+ #
47
+ # @param amka [Object] the AMKA to validate, should be a string containing only digits
48
+ # @param year [String, nil] optional four-digit year to match against
49
+ # When provided, improves the validation by ensuring the birth year
50
+ # matches exactly (resolves century ambiguity in 2-digit years)
51
+ # @return [Boolean] true if all validation criteria are met, false otherwise
52
+ # @example Validating with default century assumption
53
+ # Amka.valid?('17019012345') #=> true
54
+ # @example Validating with explicit year
55
+ # Amka.valid?('17019012345', '1990') #=> true
56
+ # @example Validating invalid input
57
+ # Amka.valid?(nil) #=> false
58
+ # Amka.valid?(12345) #=> false
59
+ # Amka.valid?('abc123') #=> false
60
+ def self.valid?(amka, year = nil)
61
+ # Return false for non-string input
62
+ return false unless amka.is_a?(String)
63
+ # Return false for strings with non-digits
64
+ return false unless amka.match(/\A\d+\Z/)
65
+ # Check length requirement
9
66
  return false unless length_is_11?(amka)
10
67
 
11
- return Luhn.valid?(amka) if Utils.valid_date?(amka, year)
68
+ # Check if date and Luhn algorithm are valid
69
+ begin
70
+ return false unless Utils.valid_date?(amka, year)
71
+
72
+ Luhn.safe_valid?(amka)
73
+ rescue ArgumentError
74
+ # If any validation raises an exception, the AMKA is invalid
75
+ false
76
+ end
77
+ end
78
+
79
+ # Validates an AMKA and returns an array of validation errors
80
+ #
81
+ # This method provides detailed feedback about why validation failed,
82
+ # returning an empty array for valid AMKAs or an array of error messages
83
+ # for invalid ones.
84
+ #
85
+ # @param amka [Object] the AMKA to validate
86
+ # @param year [String, nil] optional four-digit year to match against
87
+ # @return [Array<String>] empty array if valid, otherwise contains error messages
88
+ # @example Get validation errors
89
+ # errors = Amka.validate(input)
90
+ # if errors.empty?
91
+ # puts "Valid AMKA!"
92
+ # else
93
+ # puts "Invalid AMKA: #{errors.join(', ')}"
94
+ # end
95
+ def self.validate(amka, year = nil)
96
+ errors = []
97
+
98
+ # Check for basic format issues and return early if found
99
+ basic_format_errors = check_basic_format(amka)
100
+ return basic_format_errors unless basic_format_errors.empty?
101
+
102
+ # Check length
103
+ errors << 'AMKA must be exactly 11 digits long' unless length_is_11?(amka)
104
+
105
+ # Check date format
106
+ check_date(errors, amka, year)
107
+
108
+ # Check Luhn algorithm
109
+ check_luhn(errors, amka)
110
+
111
+ errors
112
+ end
113
+
114
+ # Helper method to validate basic format requirements
115
+ # @return [Array<String>] empty if valid, otherwise contains error message
116
+ def self.check_basic_format(amka)
117
+ errors = []
118
+
119
+ unless amka.is_a?(String)
120
+ errors << 'AMKA must be a string'
121
+ return errors
122
+ end
123
+
124
+ errors << 'AMKA must contain only digits' unless amka.match(/\A\d+\Z/)
125
+
126
+ errors
127
+ end
128
+ private_class_method :check_basic_format
12
129
 
13
- false
130
+ # Helper method to validate date format
131
+ def self.check_date(errors, amka, year)
132
+ unless Utils.valid_date?(amka, year)
133
+ errors << 'First 6 digits of AMKA must form a valid date (DDMMYY)'
134
+ end
135
+ rescue ArgumentError
136
+ errors << 'Invalid date format in AMKA'
137
+ end
138
+ private_class_method :check_date
139
+
140
+ # Helper method to validate Luhn algorithm
141
+ def self.check_luhn(errors, amka)
142
+ errors << 'AMKA must satisfy the Luhn algorithm check' unless Luhn.safe_valid?(amka)
143
+ rescue StandardError
144
+ errors << 'Error checking Luhn algorithm'
14
145
  end
146
+ private_class_method :check_luhn
147
+
148
+ # Strictly validates an AMKA and raises exceptions for invalid input
149
+ #
150
+ # Similar to valid? but raises specific exceptions when validation fails,
151
+ # which is useful for cases where you need detailed error information
152
+ # or when you prefer exceptions for control flow.
153
+ #
154
+ # @param amka [Object] the AMKA to validate
155
+ # @param year [String, nil] optional four-digit year to match against
156
+ # @return [true] if the AMKA is valid
157
+ # @raise [ValidationError] if the AMKA is invalid, with details about why
158
+ # @example Strict validation with exception handling
159
+ # begin
160
+ # Amka.validate!('17019012345') #=> true (if valid)
161
+ # rescue Amka::ValidationError => e
162
+ # puts e.message # Contains detailed error info
163
+ # end
164
+ def self.validate!(amka, year = nil)
165
+ errors = validate(amka, year)
15
166
 
167
+ raise ValidationError, errors.first unless errors.empty?
168
+
169
+ true
170
+ end
171
+
172
+ # Generates a random valid AMKA
173
+ #
174
+ # This method can create an AMKA with either:
175
+ # - A random valid birth date (when called without parameters)
176
+ # - A specific birth date (when called with a date parameter)
177
+ #
178
+ # The generated AMKA will always satisfy all validation rules:
179
+ # - 11 digits in length
180
+ # - First 6 digits form a valid date
181
+ # - Passes the Luhn algorithm check
182
+ #
183
+ # @param date_of_birth [String, nil] optional date in format 'dd/mm/yyyy'
184
+ # @return [String] a valid AMKA with the first 6 digits representing the
185
+ # birth date, and the remainder satisfying the Luhn algorithm
186
+ # @example Generate a random AMKA
187
+ # Amka.generate #=> "01011912345"
188
+ # @example Generate an AMKA with specific birth date
189
+ # Amka.generate('1/1/1990') #=> "01019012345"
16
190
  def self.generate(date_of_birth = nil)
17
191
  return generate_with_date_of_birth(date_of_birth) unless date_of_birth.nil?
18
192
 
19
- date_6_digits = ''
193
+ date_6_digits = String.new
20
194
  loop do
21
- day = rand(0..3).to_s << rand(0..9).to_s
195
+ day = "#{rand(0..3)}#{rand(0..9)}"
22
196
  next if day == '00' || day.to_i > 31
23
- month = rand(0..1).to_s << rand(0..2).to_s
197
+
198
+ month = "#{rand(0..1)}#{rand(0..2)}"
24
199
  next if month == '00' || month.to_i > 12
25
- year = rand(19..20).to_s << rand(0..9).to_s << rand(0..9).to_s
200
+
201
+ year = "#{rand(19..20)}#{rand(0..9)}#{rand(0..9)}"
26
202
  next if year.to_i > Date.today.year
27
203
 
28
- if Utils.valid_date?(date = day << month << year[2..3], year)
204
+ date = day + month + year[2..3]
205
+ if Utils.valid_date?(date, year)
29
206
  date_6_digits = date
30
207
  break
31
208
  end
@@ -34,23 +211,43 @@ module Amka
34
211
  Luhn.generate(11, date_6_digits)
35
212
  end
36
213
 
214
+ # Generates an AMKA with a specific date of birth
215
+ #
216
+ # @param date_of_birth [String] date in format 'dd/mm/yyyy'
217
+ # Day and month can be either 1 or 2 digits
218
+ # Year must be 4 digits
219
+ # @return [String] a valid AMKA with the given date of birth
220
+ # @raise [ArgumentError] if date format is invalid or date is invalid
221
+ # @example
222
+ # Amka.generate_with_date_of_birth('1/12/1980') #=> "01128012345"
223
+ # @example Invalid date raises an error
224
+ # Amka.generate_with_date_of_birth('31/2/1990')
225
+ # #=> ArgumentError: The date of birth is invalid!
37
226
  def self.generate_with_date_of_birth(date_of_birth)
38
- Utils.string_with_date_or_fail date_of_birth
227
+ Utils.string_with_date_or_fail(date_of_birth)
39
228
 
40
- day, month, year = date_of_birth.split('/').map { |i| i.length == 1 && ('0' << i) or i }
41
- date_6_digit = day << month << year[2..3]
42
- fail ArgumentError, 'The date of birth is invalid!' unless Utils.valid_date?(date_6_digit, year)
229
+ day, month, year = date_of_birth.split('/').map { |i| i.length == 1 ? "0#{i}" : i }
230
+ date_6_digit = day + month + year[2..3]
231
+ unless Utils.valid_date?(date_6_digit, year)
232
+ raise ArgumentError, 'The date of birth is invalid!'
233
+ end
43
234
 
44
235
  Luhn.generate(11, date_6_digit)
45
236
  end
46
237
  private_class_method :generate_with_date_of_birth
47
238
 
239
+ # Checks if the ID has exactly 11 digits
240
+ #
241
+ # AMKA numbers are required to be exactly 11 digits in length.
242
+ # This is a validation helper method.
243
+ #
244
+ # @param id [String] the ID to check
245
+ # @return [Boolean] true if length is 11, false otherwise
48
246
  def self.length_is_11?(id)
49
247
  id.length == 11
50
248
  end
51
249
  private_class_method :length_is_11?
52
-
53
250
  end
54
251
 
55
- require 'amka/luhn'
56
- require 'amka/utils'
252
+ require_relative 'amka/luhn'
253
+ require_relative 'amka/utils'
metadata CHANGED
@@ -1,103 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: amka
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ioannis Angelakopoulos
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-12-16 00:00:00.000000000 Z
12
- dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: minitest
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0'
20
- type: :development
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: minitest-reporters
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
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: mocha
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: bundler
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - "~>"
60
- - !ruby/object:Gem::Version
61
- version: '1.10'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - "~>"
67
- - !ruby/object:Gem::Version
68
- version: '1.10'
69
- - !ruby/object:Gem::Dependency
70
- name: rake
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '10.0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '10.0'
83
- description: 'Generates and Validates Greek A.M.K.A (social security number) and Luhn
84
- IDs, using the Luhn algorithm. DISCLAIMER: It does not validate the real existence
85
- of the A.M.K.A or that it belongs to a given person. The same applies to the generator.'
11
+ date: 2025-04-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: 'Generates and Validates Greek A.M.K.A and Luhn IDs, using the Luhn algorithm.
14
+ DISCLAIMER: It does not validate the real existence of the A.M.K.A or that it belongs
15
+ to a given person. The same applies to the generator.'
86
16
  email:
87
17
  - ioagel@gmail.com
88
18
  executables: []
89
19
  extensions: []
90
20
  extra_rdoc_files: []
91
21
  files:
92
- - ".gitignore"
93
- - ".ruby-version"
94
- - Gemfile
22
+ - CHANGELOG.md
23
+ - CONTRIBUTING.md
95
24
  - LICENSE.txt
96
25
  - README.md
97
- - Rakefile
98
- - amka.gemspec
99
- - bin/console
100
- - bin/setup
26
+ - USAGE.md
101
27
  - lib/amka.rb
102
28
  - lib/amka/luhn.rb
103
29
  - lib/amka/utils.rb
@@ -105,8 +31,13 @@ files:
105
31
  homepage: https://github.com/ioagel/amka
106
32
  licenses:
107
33
  - MIT
108
- metadata: {}
109
- post_install_message:
34
+ metadata:
35
+ homepage_uri: https://github.com/ioagel/amka
36
+ source_code_uri: https://github.com/ioagel/amka
37
+ changelog_uri: https://github.com/ioagel/amka/blob/master/CHANGELOG.md
38
+ documentation_uri: http://rubydoc.info/gems/amka
39
+ rubygems_mfa_required: 'true'
40
+ post_install_message:
110
41
  rdoc_options: []
111
42
  require_paths:
112
43
  - lib
@@ -114,16 +45,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
45
  requirements:
115
46
  - - ">="
116
47
  - !ruby/object:Gem::Version
117
- version: 2.0.0
48
+ version: 2.7.0
118
49
  required_rubygems_version: !ruby/object:Gem::Requirement
119
50
  requirements:
120
51
  - - ">="
121
52
  - !ruby/object:Gem::Version
122
53
  version: '0'
123
54
  requirements: []
124
- rubyforge_project:
125
- rubygems_version: 2.5.0
126
- signing_key:
55
+ rubygems_version: 3.4.19
56
+ signing_key:
127
57
  specification_version: 4
128
58
  summary: AMKA/Luhn IDs Generator and Validator
129
59
  test_files: []
data/.gitignore DELETED
@@ -1,9 +0,0 @@
1
- /.bundle/
2
- /.yardoc
3
- /Gemfile.lock
4
- /_yardoc/
5
- /coverage/
6
- /doc/
7
- /pkg/
8
- /spec/reports/
9
- /tmp/
data/.ruby-version DELETED
@@ -1 +0,0 @@
1
- 2.2.3@amka
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in amka.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,8 +0,0 @@
1
- require 'bundler/gem_tasks'
2
-
3
- require 'rake/testtask'
4
-
5
- Rake::TestTask.new do |t|
6
- t.libs << 'test'
7
- t.pattern = 'test/*_test.rb'
8
- end
data/amka.gemspec DELETED
@@ -1,28 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'amka/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "amka"
8
- spec.version = Amka::VERSION
9
- spec.authors = ["Ioannis Angelakopoulos"]
10
- spec.email = ["ioagel@gmail.com"]
11
-
12
- spec.summary = %q{AMKA/Luhn IDs Generator and Validator}
13
- spec.description = %q{Generates and Validates Greek A.M.K.A (social security number) and Luhn IDs, using the Luhn algorithm. DISCLAIMER: It does not validate the real existence of the A.M.K.A or that it belongs to a given person. The same applies to the generator.}
14
- spec.homepage = "https://github.com/ioagel/amka"
15
- spec.license = "MIT"
16
-
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- spec.bindir = "exe"
19
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
- spec.require_paths = ["lib"]
21
- spec.required_ruby_version = '>= 2.0.0'
22
-
23
- spec.add_development_dependency "minitest"
24
- spec.add_development_dependency "minitest-reporters"
25
- spec.add_development_dependency "mocha"
26
- spec.add_development_dependency "bundler", "~> 1.10"
27
- spec.add_development_dependency "rake", "~> 10.0"
28
- end
data/bin/console DELETED
@@ -1,14 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "amka"
5
-
6
- # You can add fixtures and/or initialization code here to make experimenting
7
- # with your gem easier. You can also use a different console, if you like.
8
-
9
- # (If you use this, don't forget to add pry to your Gemfile!)
10
- # require "pry"
11
- # Pry.start
12
-
13
- require "irb"
14
- IRB.start
data/bin/setup DELETED
@@ -1,7 +0,0 @@
1
- #!/bin/bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
-
5
- bundle install
6
-
7
- # Do any other automated setup that you need to do here