Merge pull request #4 from captn3m0/next

This commit is contained in:
Nemo 2023-07-10 21:52:04 +05:30 committed by GitHub
commit cbc329f738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 356 additions and 136 deletions

1
.gitattributes vendored
View File

@ -4,3 +4,4 @@ pincode-regex.js linguist-generated
tests/jasmine-3.5.0/* linguist-vendored
# regex.txt is generated (see src/generate.js)
regex.txt linguist-generated
regexes.txt linguist-generated

45
.github/workflows/tests.yml vendored Normal file
View File

@ -0,0 +1,45 @@
on: push
name: Tests
jobs:
node:
strategy:
matrix:
node: ["12", "14", "16", "18", "20"]
name: Node.js
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v2
with:
node-version: ${{matrix.node}}
- run: npm install
- run: npm test
php:
strategy:
matrix:
# https://www.php.net/supported-versions.php
php: ["7.2", "7.3", "7.4", "8.0", "8.1", "8.2"]
name: PHP
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: shivammathur/setup-php@v2
with:
php-version: ${{matrix.php}}
tools: phpunit, composer
- run: composer install --no-interaction
- run: ./vendor/bin/phpunit
ruby:
strategy:
matrix:
ruby: ["2.6", "2.7", "3.0", "3.1"]
name: Ruby
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true
- run: bundle install
- run: bundle exec rake

2
.gitignore vendored
View File

@ -21,3 +21,5 @@ Gemfile.lock
.pdm.toml
__pypackages__/
src/pincode.egg-info/
.phpunit.*cache
*.bak

View File

@ -4,6 +4,7 @@ tests/
phpunit.xml
node_modules/
src/*.php
src/*.rb
.travis.yml
src/generate.js
HACKING.md
@ -13,3 +14,20 @@ src/pincode
*.toml
Pipfile
.editorconfig
regexes.txt
__pypackages__
.github
.gitattributes
.phpunit*
CHANGELOG.md
CONTRIBUTING.md
CODE_OF_CONDUCT.md
Gemfile*
Rakefile
bin/
phpunit.*
*.gem
*.csv
pincode-regex.js
pincode-validator.gemspec
src/pincode.egg-info/

View File

@ -1,35 +0,0 @@
jobs:
include:
- language: php
php: '7.2'
install: composer install
- language: php
php: '7.3'
install: composer install
- language: php
php: '7.4'
install: composer install
- language: node_js
node_js: '10'
- language: node_js
node_js: '12'
- language: node_js
node_js: '13'
- language: python
python: '3.5'
script: nosetests tests/test.py
- language: python
python: '3.6'
script: nosetests tests/test.py
- language: python
python: '3.7'
script: nosetests tests/test.py
- language: python
python: '3.8'
script: nosetests tests/test.py
- language: ruby
ruby: '2.5'
- language: ruby
ruby: '2.6'
- language: ruby
ruby: '2.7'

View File

@ -55,7 +55,7 @@ further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at me@captnemo.in. All
reported by contacting the project team at coc@captnemo.in. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.

38
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,38 @@
# Contribution Guidelines
## Generating the regex
1. Download the latest CSV file from <https://data.gov.in/resources/all-india-pincode-directory-till-last-month>.
2. Copy all the pincodes to a pincodes.csv file
3. Generate all unique pincodes by running `npm run build`
## Regex Notes
Note that the regexes are all PCRE compatible, but do not include `/` at the beginning or end. This is for cross-compatibility and ease of use.
The two files are:
- `regex.txt` - This is the regex for validating a pincode. It is a single regex.
- `regexes.txt` - It is a list of regexes, one per line. One regex is available per area code (the first digit of the PIN, which goes from 1-8).
## Generating the browser-version
1. Make sure development dependencies are installed.
2. `npm run browserify`.
3. Test it by opening `tests/index.html` in your browser.
# PHP Release
1. `git tag v1.2.3`
2. `git push --tags`
## Ruby Development
After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
Run `gem build pincode-validator.gemspec` to build the gem, and `tar --to-stdout -xf *.gem data.tar.gz | tar -zt` to validate the list of files inside the gem before publishing it.
## Code of Conduct
Please see [CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md) for the code of conduct.

View File

@ -1,10 +1,8 @@
# Generating the regex
1. Download the latest CSV file from <https://data.gov.in/resources/all-india-pincode-directory-till-last-month>.
2. Copy all the pincodes to a pincodes.txt file
3. Generate all unique pincodes by running `sort -u pincodes.txt > /tmp/pin.txt`
4. `npm install`
5. `node src/generate.js /tmp/pin.txt > regex.txt`
2. Copy all the pincodes to a pincodes.csv file
3. Generate all unique pincodes by running `npm run build`
# Generating the browser-version

View File

@ -4,7 +4,7 @@ Validate a [Postal Index Number][wiki] for India with a few regexes and zero fal
## Why?
A simple `\d{6}` approach marks a lot of invalid pincodes as valid. Out of the 900000 possible combinations, only approximately `155600` are valid pincodes in India. A simple example is `111111` which is an invalid pincode, but any simple 6 digit-check will pass it as a valid one. File a PR to support any other language.
A simple `\d{6}` approach marks a lot of invalid pincodes as valid. Out of the 900000 possible combinations, only approximately `19000` are valid pincodes in India. A simple example is `111111` which is an invalid pincode, but any simple 6 digit-check will pass it as a valid one.
## Source
@ -12,7 +12,11 @@ The source for the data is the ["All India Pincode Directory"](https://data.gov.
## Usage
The `regex.txt` file is 32KB in size, so you can easily use it wherever you want, including browsers. If you are using any of the packages below, this is already delivered compressed.
The `regex.txt` file is 32KB in size, so you can easily use it wherever you want, including browsers. If you are using any of the packages below, this is already delivered compressed. You can use the regex directly, or via a few helper methods.
## Supported Language Versions
This project only supports [supported versions](https://endoflife.date) of various languages.
### PHP
@ -22,7 +26,13 @@ To use the PHP package:
```php
use PIN\Validator as P;
// validates a given pincode
// returns boolean
P::validate('110011'); // returns true;
// Searches for all valid pincodes in a given string.
// returns array(string)
P::search('bangalore 560029'); // returns ["560029"]
```
### Node.js
@ -34,8 +44,15 @@ To use the package:
```js
const P = require('pincode-validator');
P.validate('110011'); // returns true
P.search('my pincode is 560029'); // returns ['560029']
// or directly use the regex in your code
P.exactRegex.match('560029')
"address with pincode (560029)".matchAll(P.regex)
````
Please see `tests/validate.js` for more examples.
## Ruby
Add this line to your application's Gemfile:
@ -52,6 +69,16 @@ Or install it yourself as:
$ gem install pincode_validator
```ruby
require 'pincode_validator'
Pin::valid?('560029') # returns true
Pin::valid?('111111') # returns false
Pin::search('my pincode is 244713') # returns ['244713']
Pin::search('my pincode is 244713 or 560029') # returns ['244713', '560029']
```
### Browser
To use it in the browser, download the `pincode-regex.js` file and include it in your browser. `Pincode` is available as a Global variable.
@ -63,17 +90,16 @@ Pincode.validate("560029"); // returns true
</script>
```
Since there is no hotline/CDN for this yet, please watch to repo to get notified of new releases.
You can use githack for directly using this in your code: <https://rawcdn.githack.com/captn3m0/india-pincode-regex/v2.0.0/pincode-regex.js> (Make sure you use the latest version). Please watch the repo to get notified of new releases.
## Code of Conduct
Everyone interacting in the this projects codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/captn3m0/outliner/blob/master/CODE_OF_CONDUCT.md).
## Contributing
- See [`HACKING.md`](HACKING.md) for some development details.
- Pull requests are welcome for adding libraries in other languages (in the same repo).
- See [`CONTRIBUTING.md`](CONTRIBUTING.md) for some development details and contribution guidelines
- Pull requests are welcome for adding libraries in other languages (in the same repo). Python support is WIP, and I'd love to have support for other languages as well.
- This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
## License

View File

@ -14,8 +14,28 @@
"PIN\\": "src/"
}
},
"archive": {
"exclude": [
"/tests",
"/regex.txt",
"/.editorconfig",
"/phpunit.xml",
"/.npmignore",
"/package.json",
"/pincode-regex.js",
"/src/generate.js",
"/src/index.js",
"/pyproject.toml",
"/src/pincode/__init__.py",
"/pincode-validator.gemspec",
"/bin",
"/src/pincode_validator.rb"
]
},
"require": {},
"require-dev": {
"phpunit/phpunit": "^9.0 || ^8.0"
"phpunit/phpunit": "^9.0 || ^8.0 || ^10.0"
}
}

View File

@ -1,6 +1,6 @@
{
"name": "pincode-validator",
"version": "1.0.4",
"version": "2.0.0",
"description": "A simple regex based validator for PIN codes in India",
"main": "src/index.js",
"directories": {
@ -8,6 +8,7 @@
},
"scripts": {
"test": "node tests/validate.js",
"build": "node src/generate.js <(sort -u pincodes.csv) > regex.txt",
"browserify": "browserify --transform brfs --outfile pincode-regex.js -s Pincode src/index.js"
},
"repository": {
@ -29,8 +30,9 @@
"homepage": "https://github.com/captn3m0/india-pincode-regex#readme",
"devDependencies": {
"brfs": "^2.0.2",
"browserify": "^16.5.1",
"regexgen": "^1.3.0"
"browserify": "^17.0.0",
"regexgen": "^1.3.0",
"jasmine-core": "^5.0.0"
},
"dependencies": {}
}

View File

@ -22,4 +22,4 @@
</whitelist>
</filter>
</phpunit>
</phpunit>

28
pincode-regex.js generated

File diff suppressed because one or more lines are too long

View File

@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
spec.license = 'MIT'
spec.require_paths = ["src"]
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.8'
spec.add_development_dependency 'rspec', '~> 3.12'
end

View File

@ -1,13 +1,13 @@
[project]
name = "pincode"
version = "1.0.4"
version = "2.0.0"
description = "A simple offline pincode validator for India"
authors = [
{name = "Nemo", email = "python@captnemo.in"},
]
dependencies = ["requests"]
readme = "README.md"
requires-python = ">=3.7"
requires-python = ">=3.8"
license = {text = "MIT"}
keywords = ["pincode", "regex", "offline", "pin", "validator"]
classifiers = [
@ -24,6 +24,7 @@ Documentation = "https://github.com/captn3m0/india-pincode-regex/wiki"
[tool.pdm]
includes = ["CHANGELOG.md", "README.md", "regex.txt", "src/pincode"]
excludes = [
".github/",
"src/*.js",
"src/*.php",
"tests/*.php",

9
regex.txt generated

File diff suppressed because one or more lines are too long

8
regexes.txt generated Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,19 +3,53 @@
namespace PIN;
class Validator {
static $regexes;
static $regexes;
public static function validate(string $pin) {
if(!self::$regexes) {
self::$regexes = array_filter(file('regex.txt'));
}
public static function init() {
if(!self::$regexes) {
self::$regexes = array_map(function($line) {
return '/' . trim($line) . '/';
}, array_filter(file('regexes.txt')));
}
}
foreach (self::$regexes as $regex) {
if (strlen($pin) === 6 and preg_match($regex, $pin) === 1) {
return true;
}
}
/**
* Validate a PIN code
* @param string $pin
* @return bool
*/
public static function validate(string $pin) : bool{
self::init();
return false;
}
foreach (self::$regexes as $regex) {
if (strlen($pin) === 6 and preg_match($regex, $pin) === 1) {
return true;
}
}
return false;
}
/**
* Search for PIN codes in a string
* @param string $address
* @return array
*/
public static function search(string $address){
self::init();
$results = [];
foreach (self::$regexes as $regex) {
preg_match_all($regex, $address, $matches);
$results = array_reduce($matches, function($res, $match) {
if (isset($match[0]) and in_array($match[0], $res, true) === false){
$res[] = $match[0];
}
return $res;
}, $results);
}
return array_values($results);
}
}

View File

@ -26,13 +26,16 @@ readInterface.on("line", function (line) {
let areaCode = parseInt(line.charAt(0), 10);
if (areaCode < 9 && areaCode > 0) {
let areaCodeIndex = areaCode - 1;
regexes[areaCodeIndex].add(line);
regexes[areaCodeIndex].add(line.trim());
}
}
});
readInterface.on("close", function () {
for (i in regexes) {
console.log(regexes[i].toRegExp());
}
// We maintain two files, one with a single regex using (|) for each segment
fs.writeFileSync("regex.txt", "(" + regexes.join('|') + ")");
// And another with 8 regexes, one for each area code.
// The latter is required for some languages, which have a regex character limit
// (Currently PHP)
fs.writeFileSync("regexes.txt", regexes.join("\n"));
});

View File

@ -1,25 +1,22 @@
const readline = require("readline");
const fs = require("fs");
const regexes = fs
.readFileSync(__dirname + "/../regex.txt", "utf8")
.split("\n")
// Remove empty lines
.filter(function(r) {
return r.length > 1;
})
// Remove the opening and closing slashes
.map(function(r) {
return new RegExp("^" + r.slice(1, -1) + "$");
});
let contents = fs.readFileSync(__dirname + "/../regex.txt", "utf8").trim()
const regex = new RegExp(contents, "gm");
const exactRegex = new RegExp("^" + contents + "$");
module.exports = {
// Validates an exact 6 digit string as a valid pincode
validate: function(pin) {
for (let i in regexes) {
if (regexes[i].test(pin)) {
return true;
}
if (exactRegex.test(pin)) {
return true;
}
return false;
}
},
// Returns all valid PIN codes for a given address
search: function(address) {
return Array.from(address.matchAll(regex), (x) => x[0])
},
regex: regex,
exactRegex: exactRegex
};

View File

@ -1,5 +1,5 @@
module PincodeValidator
VERSION = "1.0.3"
VERSION = "2.0.0"
FILENAME='regex.txt'
class Error < StandardError; end
@ -8,14 +8,18 @@ module PincodeValidator
File.dirname __dir__
end
@@regexes ||= IO.readlines(File.join root, FILENAME).map do |line|
Regexp.new("^#{line.strip[1...-1]}$")
end
@@regex ||=
Regexp.new(File.read(File.join root, FILENAME).strip)
@@exactRegex ||=
Regexp.new("^#{File.read(File.join root, FILENAME).strip}$")
def self.valid?(pincode)
@@regexes.each do |r|
return true if r.match? pincode
end
return true if @@exactRegex.match? pincode
false
end
def self.search?(address)
address.scan(@@regex).map(&:first).map(&:strip)
end
end

View File

@ -17,4 +17,11 @@ class SimpleTest extends TestCase {
$this->assertFalse(P::validate($pin), "$pin should be invalid");
}
}
public function testSearch() {
$this->assertSame(P::search("560029"), ["560029"]);
$this->assertSame(P::search("my pincode is 560029"), ["560029"]);
$this->assertSame(P::search("560029 244713"), ["244713", "560029"]);
$this->assertSame(P::search("address 560038 bangalore"), ["560038"]);
}
}

View File

@ -17,4 +17,32 @@ describe("Pincode", function() {
expect(Pincode.validate("99")).toEqual(false);
expect(Pincode.validate("9")).toEqual(false);
});
it("should support search method", function() {
expect(Pincode.search('bangalore 560038 244713')).toEqual(['560038', '244713']);
expect(Pincode.search('bangalore 560038')).toEqual(['560038']);
expect(Pincode.search('560038 BENGALURU')).toEqual(['560038']);
expect(Pincode.search('560038')).toEqual(['560038']);
expect(Pincode.search('my pincode is 244713')).toEqual(['244713']);
expect(Pincode.search('560029 pin')).toEqual(['560029']);
})
it("should export direct regexes", function() {
expect(Pincode.regex instanceof RegExp).toBe(true);
expect(Pincode.exactRegex instanceof RegExp).toBe(true);
})
it("should support exact regex matches", function() {
expect(Pincode.exactRegex.test('560029')).toBe(true);
expect('111111').not.toMatch(Pincode.exactRegex);
expect('address is 560029').not.toMatch(Pincode.exactRegex);
})
it("should support inexact regex matches", function() {
expect('560029').toMatch(Pincode.regex);
expect('address is 560029').toMatch(Pincode.regex);
expect('address is 111111').not.toMatch(Pincode.regex)
expect('111111').not.toMatch(Pincode.regex)
})
});

View File

@ -1,18 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<link rel="shortcut icon" type="image/png" href="jasmine-3.5.0/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="jasmine-3.5.0/jasmine.css">
<link rel="shortcut icon" type="image/png" href="../node_modules/jasmine-core/images/jasmine_favicon.png">
<link rel="stylesheet" type="text/css" href="../node_modules/jasmine-core/lib/jasmine-core/jasmine.css">
<script type="text/javascript" src="jasmine-3.5.0/jasmine.js"></script>
<script type="text/javascript" src="jasmine-3.5.0/jasmine-html.js"></script>
<script type="text/javascript" src="jasmine-3.5.0/boot.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/jasmine.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/jasmine-html.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/boot0.js"></script>
<script type="text/javascript" src="../node_modules/jasmine-core/lib/jasmine-core/boot1.js"></script>
<title>Jasmine tests for Pincode Validator</title>
<script src="../pincode-regex.js"></script>
<script src="browser-spec.js"></script>
</head>
<body>
</body>
</html>

View File

@ -1,19 +1,43 @@
const pincode = require('../src/index');
const P = require('../src/index');
const assert = require('assert');
// A few correct ones
assert.strictEqual(pincode.validate('110011'), true, '110011');
assert.strictEqual(pincode.validate('244713'), true, '244713');
assert.strictEqual(pincode.validate('560029'), true, '560029');
assert.strictEqual(pincode.validate('560030'), true, '560030');
assert.strictEqual(P.validate('110011'), true, '110011');
assert.strictEqual(P.validate('244713'), true, '244713');
assert.strictEqual(P.validate('560029'), true, '560029');
assert.strictEqual(P.validate('560030'), true, '560030');
// Incorrect
assert.strictEqual(pincode.validate('1100111'), false, '1100111');
assert.strictEqual(pincode.validate('111111'), false, '111111');
assert.strictEqual(pincode.validate('999999'), false, '999999');
assert.strictEqual(pincode.validate('99999'), false, '99999');
assert.strictEqual(pincode.validate('9999'), false, '9999');
assert.strictEqual(pincode.validate('999'), false, '999');
assert.strictEqual(pincode.validate('99'), false, '99');
assert.strictEqual(pincode.validate('9'), false, '9');
assert.strictEqual(P.validate('address 560030'), false, 'address 560030 should fail');
assert.strictEqual(P.validate('1100111'), false, '1100111');
assert.strictEqual(P.validate('111111'), false, '111111');
assert.strictEqual(P.validate('999999'), false, '999999');
assert.strictEqual(P.validate('99999'), false, '99999');
assert.strictEqual(P.validate('9999'), false, '9999');
assert.strictEqual(P.validate('999'), false, '999');
assert.strictEqual(P.validate('99'), false, '99');
assert.strictEqual(P.validate('9'), false, '9');
// Validate search method
assert.deepEqual(P.search('bangalore 560038 244713'), ['560038', '244713'])
assert.deepEqual(P.search('bangalore 560038'), ['560038'])
assert.deepEqual(P.search('560038 BENGALURU'), ['560038'])
assert.deepEqual(P.search('560038'), ['560038'])
assert.deepEqual(P.search('my pincode is 244713'), ['244713'])
assert.deepEqual(P.search('560029 pin'), ['560029'])
// Validate direct regex exports
assert(P.regex instanceof RegExp)
assert(P.exactRegex instanceof RegExp)
// exact regex only works with 6 digit strings
assert(P.exactRegex.test('560029'))
assert.match('560029', P.exactRegex)
assert.doesNotMatch('111111', P.exactRegex)
assert.doesNotMatch('address is 560029', P.exactRegex)
// Normal regex works with long addresses
assert.match('560029', P.regex)
assert.match('address is 560029', P.regex)
assert.doesNotMatch('address is 111111', P.regex)
assert.doesNotMatch('111111', P.regex)

View File

@ -12,4 +12,12 @@ describe PincodeValidator do
expect(described_class::valid? pin).to eq(false), "#{pin} should be invalid"
end
end
it 'should search pincodes' do
expect(described_class::search? '560029').to eq(['560029'])
expect(described_class::search? '560029, 560030').to eq(['560029', '560030'])
expect(described_class::search? '560029, 560030, 110011').to eq(['560029', '560030', '110011'])
expect(described_class::search? 'bangalore 560029').to eq(['560029'])
expect(described_class::search? 'bangalore 1').to eq([])
end
end