Compare commits

...

49 Commits

Author SHA1 Message Date
Nemo cbc329f738
Merge pull request #4 from captn3m0/next 2023-07-10 21:52:04 +05:30
Nemo d986c22c5a v2 browser testing and build 2023-07-10 21:48:45 +05:30
Nemo fddfe4113c [npm] Fix ignored files 2023-07-02 15:11:57 +05:30
Nemo 2c02026a8f [docs] Add supported language note (skip-ci) 2023-07-02 15:07:27 +05:30
Nemo 9bb0367232 Fix README links 2023-07-02 15:00:28 +05:30
Nemo 86e276b81c [php] Fix 7.2 tests 2023-07-02 14:59:31 +05:30
Nemo 794f82a4e2 Version bump 2023-07-02 14:50:25 +05:30
Nemo a5adba4a5c Docs updates 2023-07-02 14:48:11 +05:30
Nemo 2461fc5ace [php] Add some old PHP versions for fun 2023-07-02 14:40:15 +05:30
Nemo f1eb4a8c80 [ci] Migrate from Travis 2023-07-02 14:33:09 +05:30
Nemo e1cb3c9054 [php] Ignore things for composer 2023-07-02 14:27:38 +05:30
Nemo 79b3302227 [php] Get PHP functional 2023-07-02 14:21:35 +05:30
Nemo fc6bfdb113 Lots of changes for v2
Mainly, add a search method to find all valid pincodes
See #3

Create a single unified regex.
Update some dependencies

However, this breaks on PHP,so I need to fix something for that.
2023-07-01 20:00:02 +05:30
Nemo 9105240bcb
Create FUNDING.yml 2022-05-30 14:50:01 +05:30
Nemo 5d5e939a6b [python] WIP switch to pdm 2021-12-19 19:19:10 +05:30
Nemo dc6531ee98 Minor updates 2021-12-19 19:18:41 +05:30
Nemo 062df76ec9 Adds language list 2020-05-11 17:58:31 +05:30
Nemo 108543a4b4 Updates regex 2020-04-27 12:07:11 +05:30
Nemo eb853b5f4c Fix .gitattributes 2020-04-27 11:57:42 +05:30
Nemo 2885606b4a Adds .gitattributes to fix GitHub language detection
See https://github.com/github/linguist#overrides
2020-04-26 20:57:16 +05:30
Nemo bf66a2f6b3 Fix contributing 2020-04-26 10:03:11 +05:30
Nemo 41b8940751 [docs] Updates docs with a WHY note 2020-04-26 09:34:42 +05:30
Nemo 511cd51730 [docs] Adds ruby in docs 2020-04-26 08:58:11 +05:30
Nemo b3f3441d2f [ruby] Fix missing version 2020-04-26 08:58:11 +05:30
Nemo c26443624f [ruby] Least supported ruby release is 2.5.0 2020-04-26 08:58:11 +05:30
Nemo dfa13e6d10 Don't commit Gemfile.lock
Current best practice is to commit this
https://bundler.io/man/bundle-install.1.html#THE-GEMFILE-LOCK
but we have zero deps, so it doesn't make sense
2020-04-26 08:58:11 +05:30
Nemo 8b3df3fb17 [ci/ruby] Adds travis builds for Ruby 2020-04-26 08:58:11 +05:30
Nemo 89d922062a [ruby] Adds test and code 2020-04-26 08:58:11 +05:30
Nemo 8ec8ab839e Initial commit for rubygem 2020-04-26 08:58:11 +05:30
Nemo e625caee29 Version Bump (1.0.4) 2020-04-21 21:11:41 +05:30
Nemo e8403d43e3 Adds support for browser-usage using browserify
- Browserify with the brfs plugin generates the stub JS file
  that can be included directly in the browser
- Jasmine is supported for an integration test-suite in the browser
2020-04-21 21:09:50 +05:30
Nemo 6562dd1cf3 Improve all ignore files 2020-03-17 01:21:31 +05:30
Nemo 986a0bfbc9 [node] Ignore dist directory for npm package 2020-03-17 01:19:33 +05:30
Nemo 6be76ef8d8 Improve pyproject 2020-03-13 18:08:45 +05:30
Nemo c2af505e2a Fixes bug on regex matching not stopping after 6 chars 2020-03-13 17:49:02 +05:30
Nemo af8d7d995b [ci] Travis/py 2020-03-13 17:35:49 +05:30
Nemo 85df2cd6f3 Version bump 2020-03-13 16:44:52 +05:30
Nemo d58c04274b Updates regex.txt definition 2020-03-13 16:44:29 +05:30
Nemo f24d67d914 Python package and other updates
- Break down regex into one-per area code
- Improves tests
- Adds python package
- Adds editorconfig
- Travis Updates
2020-03-13 16:42:58 +05:30
Nemo f42f70d156 Adds LICENSE and badges 2020-02-25 14:45:40 +05:30
Nemo 83f5afc398 Changes name to pincode-validator 2020-02-25 14:40:14 +05:30
Nemo 03e864b165 Adds npm ignore 2020-02-25 14:34:52 +05:30
Nemo ac0db7b024 node14 isn't there yet 2020-02-25 14:33:57 +05:30
Nemo 5bba7b9c87 Run more builds 2020-02-25 14:31:32 +05:30
Nemo 783c0dc956 Initial NPM package 2020-02-25 14:20:53 +05:30
Nemo b0fcf5a284 Adds HACKING.md 2020-02-25 00:20:42 +05:30
Nemo 84dadeb1ff Support phpunit 8.0 for php7.2 for now 2020-02-24 23:54:30 +05:30
Nemo cc55f45269 Fix composer install on travis 2020-02-24 23:47:34 +05:30
Nemo f14f923c26 Adds Travis badge 2020-02-24 23:45:55 +05:30
43 changed files with 10135 additions and 81 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[regex.txt]
insert_final_newline = true

7
.gitattributes vendored Normal file
View File

@ -0,0 +1,7 @@
# Generated file for browsers
pincode-regex.js linguist-generated
# jasmine is vendored for browser tests
tests/jasmine-3.5.0/* linguist-vendored
# regex.txt is generated (see src/generate.js)
regex.txt linguist-generated
regexes.txt linguist-generated

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
ko_fi: captn3m0
liberapay: captn3m0
github: captn3m0

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

26
.gitignore vendored
View File

@ -1,3 +1,25 @@
/vendor/
/.bundle/
/.yardoc
/_yardoc/
/coverage/
/dist/
/doc/
/node_modules/
composer.lock
/pkg/
/spec/reports/
/tmp/
/vendor/
__pycache__/
/composer.lock
/package-lock.json
# Current best practice is to commit this
# https://bundler.io/man/bundle-install.1.html#THE-GEMFILE-LOCK
# but we have zero deps, so it doesn't make sense
Gemfile.lock
*.gem
*.csv
.pdm.toml
__pypackages__/
src/pincode.egg-info/
.phpunit.*cache
*.bak

33
.npmignore Normal file
View File

@ -0,0 +1,33 @@
composer.*
vendor/
tests/
phpunit.xml
node_modules/
src/*.php
src/*.rb
.travis.yml
src/generate.js
HACKING.md
dist/
# This holds the python source
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,5 +0,0 @@
language: php
php:
- '7.2'
- '7.3'
- '7.4'

11
CHANGELOG.md Normal file
View File

@ -0,0 +1,11 @@
# Changelog
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [UNRELEASED][unreleased]
## [1.0.5][1.0.5]
##
[1.0.5]: https://github.com/captn3m0/india-pincode-regex/releases/tag/1.0.5

74
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,74 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, gender identity and expression, level of experience,
nationality, personal appearance, race, religion, or sexual identity and
orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
* Using welcoming and inclusive language
* Being respectful of differing viewpoints and experiences
* Gracefully accepting constructive criticism
* Focusing on what is best for the community
* Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
* The use of sexualized language or imagery and unwelcome sexual attention or
advances
* Trolling, insulting/derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or electronic
address, without explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers 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, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
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 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.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at [http://contributor-covenant.org/version/1/4][version]
[homepage]: http://contributor-covenant.org
[version]: http://contributor-covenant.org/version/1/4/

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.

4
Gemfile Normal file
View File

@ -0,0 +1,4 @@
source "https://rubygems.org"
# Specify your gem's dependencies in pincode-validator.gemspec
gemspec

23
HACKING.md Normal file
View File

@ -0,0 +1,23 @@
# 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`
# 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 1.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).

7
LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright 2020 Abhay Rana
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,14 +1,22 @@
# india-pincode-regex ![Packagist Version](https://img.shields.io/packagist/v/captn3m0/pincode?style=plastic)
# india-pincode-regex ![Packagist Version](https://img.shields.io/packagist/v/captn3m0/pincode?style=plastic) [![Build Status](https://travis-ci.org/captn3m0/india-pincode-regex.svg?branch=master)](https://travis-ci.org/captn3m0/india-pincode-regex) ![npm](https://img.shields.io/npm/v/pincode-validator?style=plastic) ![GitHub package.json version](https://img.shields.io/github/package-json/v/captn3m0/india-pincode-regex?style=plastic) ![GitHub](https://img.shields.io/github/license/captn3m0/india-pincode-regex?style=plastic)
Validate a [Postal Index Number][wiki] for India with a regex. The regexes are available in `regex.txt`. There are 2 regexes. The first validates PINs starting from 1-4, and the second validates the ones starting from 5-8. The reason for the split is to keep the regex size small. Currently they are both at 16K each.
Validate a [Postal Index Number][wiki] for India with a few regexes and zero false-positives. The regexes are available in `regex.txt`. There is one regex per area code (the first digit of the PIN, which goes from 1-8). Available as a package for Ruby, Python, Node.js, and browsers.
## Why?
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
The source for the data is the ["All India Pincode Directory"](https://data.gov.in/resources/all-india-pincode-directory) dataset on data.gov.in.
The source for the data is the ["All India Pincode Directory"](https://data.gov.in/resources/all-india-pincode-directory) dataset on data.gov.in. The last updated date for the dataset is currently 30th May 2019.
## Usage
The `regex.txt` file is 32KB in size, so you can easily use it wherever you want, including browsers.
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
@ -18,11 +26,84 @@ 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
The package is available on [`npm`](https://www.npmjs.com/package/pincode-validator).
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:
```ruby
gem 'pincode_validator'
```
And then execute:
$ bundle
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.
```html
<script src="../pincode-regex.js"></script>
<script>
Pincode.validate("560029"); // returns true
</script>
```
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 [`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
Licensed under the [MIT License](https://nemo.mit-license.org/). See LICENSE file for details.
[wiki]: https://en.wikipedia.org/wiki/Postal_Index_Number
[wiki]: https://en.wikipedia.org/wiki/Postal_Index_Number

8
Rakefile Normal file
View File

@ -0,0 +1,8 @@
require "bundler/gem_tasks"
require 'rspec/core/rake_task'
RSpec::Core::RakeTask.new(:spec) do |t|
t.pattern = 'tests/*_spec.rb'
end
task :default => :spec

14
bin/console Executable file
View File

@ -0,0 +1,14 @@
#!/usr/bin/env ruby
require "bundler/setup"
require "pincode_validator"
# You can add fixtures and/or initialization code here to make experimenting
# with your gem easier. You can also use a different console, if you like.
# (If you use this, don't forget to add pry to your Gemfile!)
# require "pry"
# Pry.start
require "irb"
IRB.start(__FILE__)

8
bin/setup Executable file
View File

@ -0,0 +1,8 @@
#!/usr/bin/env bash
set -euo pipefail
IFS=$'\n\t'
set -vx
bundle install
# Do any other automated setup that you need to do here

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"
"phpunit/phpunit": "^9.0 || ^8.0 || ^10.0"
}
}

30
package-lock.json generated
View File

@ -1,30 +0,0 @@
{
"name": "india-pincode-regex",
"version": "1.0.0",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
"jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true
},
"regenerate": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.0.tgz",
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg==",
"dev": true
},
"regexgen": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/regexgen/-/regexgen-1.3.0.tgz",
"integrity": "sha1-sCLzjlEgu0b9zTf6y5EWjxXm/no=",
"dev": true,
"requires": {
"jsesc": "^2.3.0",
"regenerate": "^1.3.2"
}
}
}
}

View File

@ -1,13 +1,15 @@
{
"name": "india-pincode-regex",
"version": "1.0.0",
"name": "pincode-validator",
"version": "2.0.0",
"description": "A simple regex based validator for PIN codes in India",
"main": "index.js",
"main": "src/index.js",
"directories": {
"test": "tests"
},
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
"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": {
"type": "git",
@ -27,6 +29,10 @@
},
"homepage": "https://github.com/captn3m0/india-pincode-regex#readme",
"devDependencies": {
"regexgen": "^1.3.0"
}
"brfs": "^2.0.2",
"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 Normal file

File diff suppressed because one or more lines are too long

29
pincode-validator.gemspec Normal file
View File

@ -0,0 +1,29 @@
require_relative 'src/pincode_validator'
Gem::Specification.new do |spec|
spec.name = "pincode_validator"
spec.version = PincodeValidator::VERSION
spec.authors = ["Nemo"]
spec.email = ["rubygems@captnemo.in"]
spec.summary = %q{A simple regex based offline validator for PIN codes in India}
spec.homepage = "https://github.com/captn3m0/india-pincode-regex"
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = "https://github.com/captn3m0/india-pincode-regex"
spec.files = [
'Gemfile',
'LICENSE',
'pincode-validator.gemspec',
'README.md',
'regex.txt',
'src/pincode_validator.rb',
]
spec.license = 'MIT'
spec.require_paths = ["src"]
spec.add_development_dependency 'rake', '~> 13.0'
spec.add_development_dependency 'rspec', '~> 3.12'
end

44
pyproject.toml Normal file
View File

@ -0,0 +1,44 @@
[project]
name = "pincode"
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.8"
license = {text = "MIT"}
keywords = ["pincode", "regex", "offline", "pin", "validator"]
classifiers = [
"Development Status :: 4 - Beta",
"Intended Audience :: Developers",
"Topic :: Software Development :: Libraries"
]
[project.urls]
homepage = "https://github.com/captn3m0/india-pincode-regex"
Repository = "https://github.com/captn3m0/india-pincode-regex"
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",
"tests/*.js",
"tests/*.rb",
"tests/*.html",
"tests/jasmine*",
"vendor/",
"composer.",
"package.json",
"package-lock.json",
"node_modules/"
]
[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"

3
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;
public static function validate(string $pin) {
if(!self::$regexes) {
self::$regexes = array_filter(file('regex.txt'));
}
static $regexes;
foreach (self::$regexes as $regex) {
if (preg_match($regex, $pin) === 1) {
return true;
}
}
public static function init() {
if(!self::$regexes) {
self::$regexes = array_map(function($line) {
return '/' . trim($line) . '/';
}, array_filter(file('regexes.txt')));
}
}
return false;
}
}
/**
* Validate a PIN code
* @param string $pin
* @return bool
*/
public static function validate(string $pin) : bool{
self::init();
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

@ -9,23 +9,33 @@ if (!process.argv[2]) {
const readInterface = readline.createInterface({
input: fs.createReadStream(process.argv[2]),
console: false
console: false,
});
let t1 = new Trie(),
t2 = new Trie();
regexes = [];
readInterface.on("line", function(line) {
// There are 3 Pincodes that start with 9, but we
// ignore those as test offices.
for (var i = 0; i < 8; i++) {
regexes.push(new Trie());
}
readInterface.on("line", function (line) {
if (line.length === 6) {
if (['1', '2', '3', '4'].includes(line.charAt(0))) {
t1.add(line);
} else {
t2.add(line)
// First character of the PIN
let areaCode = parseInt(line.charAt(0), 10);
if (areaCode < 9 && areaCode > 0) {
let areaCodeIndex = areaCode - 1;
regexes[areaCodeIndex].add(line.trim());
}
}
});
readInterface.on("close", function() {
console.log(t1.toRegExp("u"));
console.log(t2.toRegExp("u"));
});
readInterface.on("close", function () {
// 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"));
});

22
src/index.js Normal file
View File

@ -0,0 +1,22 @@
const readline = require("readline");
const fs = require("fs");
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) {
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
};

24
src/pincode/__init__.py Normal file
View File

@ -0,0 +1,24 @@
import os
import re
from pathlib import Path
def get_project_root() -> Path:
return Path(__file__).parent.parent.parent
regex = list()
dir_path = os.path.dirname(os.path.realpath(__file__))
f = open(get_project_root() / 'regex.txt')
lines = f.readlines()
for line in lines:
if (len(line) > 10):
# Remove the \n at the end
regex.append(re.compile('^' + line[1:-2] + '$'))
class Pincode:
@staticmethod
def validate(code):
for r in regex:
if r.match(code) != None:
return True
return False

25
src/pincode_validator.rb Normal file
View File

@ -0,0 +1,25 @@
module PincodeValidator
VERSION = "2.0.0"
FILENAME='regex.txt'
class Error < StandardError; end
def self.root
File.dirname __dir__
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)
return true if @@exactRegex.match? pincode
false
end
def self.search?(address)
address.scan(@@regex).map(&:first).map(&:strip)
end
end

View File

View File

@ -4,9 +4,9 @@ use PIN\Validator as P;
use PHPUnit\Framework\TestCase;
class SimpleTest extends TestCase {
const PINS = ['244713', '560029', '560030'];
const PINS = ['244713', '560029', '560030', '110011'];
const INVALID_PINS = ['999999'];
const INVALID_PINS = ['999999', '99999', '9999', '999', '99', '9', '111111', '2447131'];
public function testSamplePins() {
foreach(self::PINS as $pin) {
@ -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"]);
}
}

48
tests/browser-spec.js Normal file
View File

@ -0,0 +1,48 @@
describe("Pincode", function() {
it("should validate correct pincodes", function() {
expect(Pincode.validate("110011")).toEqual(true);
expect(Pincode.validate("244713")).toEqual(true);
expect(Pincode.validate("560029")).toEqual(true);
expect(Pincode.validate("560030")).toEqual(true);
});
it("should validate incorrect pincodes", function() {
// Incorrect
expect(Pincode.validate("1100111")).toEqual(false);
expect(Pincode.validate("111111")).toEqual(false);
expect(Pincode.validate("999999")).toEqual(false);
expect(Pincode.validate("99999")).toEqual(false);
expect(Pincode.validate("9999")).toEqual(false);
expect(Pincode.validate("999")).toEqual(false);
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)
})
});

18
tests/index.html Normal file
View File

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<head>
<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="../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>

136
tests/jasmine-3.5.0/boot.js vendored Normal file
View File

@ -0,0 +1,136 @@
/**
Starting with version 2.0, this file "boots" Jasmine, performing all of the necessary initialization before executing the loaded environment and all of a project's specs. This file should be loaded after `jasmine.js` and `jasmine_html.js`, but before any project source files or spec files are loaded. Thus this file can also be used to customize Jasmine for a project.
If a project is using Jasmine via the standalone distribution, this file can be customized directly. If a project is using Jasmine via the [Ruby gem][jasmine-gem], this file can be copied into the support directory via `jasmine copy_boot_js`. Other environments (e.g., Python) will have different mechanisms.
The location of `boot.js` can be specified and/or overridden in `jasmine.yml`.
[jasmine-gem]: http://github.com/pivotal/jasmine-gem
*/
(function() {
/**
* ## Require &amp; Instantiate
*
* Require Jasmine's core files. Specifically, this requires and attaches all of Jasmine's code to the `jasmine` reference.
*/
window.jasmine = jasmineRequire.core(jasmineRequire);
/**
* Since this is being run in a browser and the results should populate to an HTML page, require the HTML-specific Jasmine code, injecting the same reference.
*/
jasmineRequire.html(jasmine);
/**
* Create the Jasmine environment. This is used to run all specs in a project.
*/
var env = jasmine.getEnv();
/**
* ## The Global Interface
*
* Build up the functions that will be exposed as the Jasmine public interface. A project can customize, rename or alias any of these functions as desired, provided the implementation remains unchanged.
*/
var jasmineInterface = jasmineRequire.interface(jasmine, env);
/**
* Add all of the Jasmine global/public interface to the global scope, so a project can use the public interface directly. For example, calling `describe` in specs instead of `jasmine.getEnv().describe`.
*/
extend(window, jasmineInterface);
/**
* ## Runner Parameters
*
* More browser specific code - wrap the query string in an object and to allow for getting/setting parameters from the runner user interface.
*/
var queryString = new jasmine.QueryString({
getWindowLocation: function() { return window.location; }
});
var filterSpecs = !!queryString.getParam("spec");
var config = {
failFast: queryString.getParam("failFast"),
oneFailurePerSpec: queryString.getParam("oneFailurePerSpec"),
hideDisabled: queryString.getParam("hideDisabled")
};
var random = queryString.getParam("random");
if (random !== undefined && random !== "") {
config.random = random;
}
var seed = queryString.getParam("seed");
if (seed) {
config.seed = seed;
}
/**
* ## Reporters
* The `HtmlReporter` builds all of the HTML UI for the runner page. This reporter paints the dots, stars, and x's for specs, as well as all spec names and all failures (if any).
*/
var htmlReporter = new jasmine.HtmlReporter({
env: env,
navigateWithNewParam: function(key, value) { return queryString.navigateWithNewParam(key, value); },
addToExistingQueryString: function(key, value) { return queryString.fullStringWithNewParam(key, value); },
getContainer: function() { return document.body; },
createElement: function() { return document.createElement.apply(document, arguments); },
createTextNode: function() { return document.createTextNode.apply(document, arguments); },
timer: new jasmine.Timer(),
filterSpecs: filterSpecs
});
/**
* The `jsApiReporter` also receives spec results, and is used by any environment that needs to extract the results from JavaScript.
*/
env.addReporter(jasmineInterface.jsApiReporter);
env.addReporter(htmlReporter);
/**
* Filter which specs will be run by matching the start of the full name against the `spec` query param.
*/
var specFilter = new jasmine.HtmlSpecFilter({
filterString: function() { return queryString.getParam("spec"); }
});
config.specFilter = function(spec) {
return specFilter.matches(spec.getFullName());
};
env.configure(config);
/**
* Setting up timing functions to be able to be overridden. Certain browsers (Safari, IE 8, phantomjs) require this hack.
*/
window.setTimeout = window.setTimeout;
window.setInterval = window.setInterval;
window.clearTimeout = window.clearTimeout;
window.clearInterval = window.clearInterval;
/**
* ## Execution
*
* Replace the browser window's `onload`, ensure it's called, and then run all of the loaded specs. This includes initializing the `HtmlReporter` instance and then executing the loaded Jasmine environment. All of this will happen after all of the specs are loaded.
*/
var currentWindowOnload = window.onload;
window.onload = function() {
if (currentWindowOnload) {
currentWindowOnload();
}
htmlReporter.initialize();
env.execute();
};
/**
* Helper function for readability above.
*/
function extend(destination, source) {
for (var property in source) destination[property] = source[property];
return destination;
}
}());

817
tests/jasmine-3.5.0/jasmine-html.js vendored Normal file
View File

@ -0,0 +1,817 @@
/*
Copyright (c) 2008-2019 Pivotal Labs
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
jasmineRequire.html = function(j$) {
j$.ResultsNode = jasmineRequire.ResultsNode();
j$.HtmlReporter = jasmineRequire.HtmlReporter(j$);
j$.QueryString = jasmineRequire.QueryString();
j$.HtmlSpecFilter = jasmineRequire.HtmlSpecFilter();
};
jasmineRequire.HtmlReporter = function(j$) {
function ResultsStateBuilder() {
this.topResults = new j$.ResultsNode({}, '', null);
this.currentParent = this.topResults;
this.specsExecuted = 0;
this.failureCount = 0;
this.pendingSpecCount = 0;
}
ResultsStateBuilder.prototype.suiteStarted = function(result) {
this.currentParent.addChild(result, 'suite');
this.currentParent = this.currentParent.last();
};
ResultsStateBuilder.prototype.suiteDone = function(result) {
this.currentParent.updateResult(result);
if (this.currentParent !== this.topResults) {
this.currentParent = this.currentParent.parent;
}
if (result.status === 'failed') {
this.failureCount++;
}
};
ResultsStateBuilder.prototype.specStarted = function(result) {};
ResultsStateBuilder.prototype.specDone = function(result) {
this.currentParent.addChild(result, 'spec');
if (result.status !== 'excluded') {
this.specsExecuted++;
}
if (result.status === 'failed') {
this.failureCount++;
}
if (result.status == 'pending') {
this.pendingSpecCount++;
}
};
function HtmlReporter(options) {
var config = function() {
return (options.env && options.env.configuration()) || {};
},
getContainer = options.getContainer,
createElement = options.createElement,
createTextNode = options.createTextNode,
navigateWithNewParam = options.navigateWithNewParam || function() {},
addToExistingQueryString =
options.addToExistingQueryString || defaultQueryString,
filterSpecs = options.filterSpecs,
htmlReporterMain,
symbols,
deprecationWarnings = [];
this.initialize = function() {
clearPrior();
htmlReporterMain = createDom(
'div',
{ className: 'jasmine_html-reporter' },
createDom(
'div',
{ className: 'jasmine-banner' },
createDom('a', {
className: 'jasmine-title',
href: 'http://jasmine.github.io/',
target: '_blank'
}),
createDom('span', { className: 'jasmine-version' }, j$.version)
),
createDom('ul', { className: 'jasmine-symbol-summary' }),
createDom('div', { className: 'jasmine-alert' }),
createDom(
'div',
{ className: 'jasmine-results' },
createDom('div', { className: 'jasmine-failures' })
)
);
getContainer().appendChild(htmlReporterMain);
};
var totalSpecsDefined;
this.jasmineStarted = function(options) {
totalSpecsDefined = options.totalSpecsDefined || 0;
};
var summary = createDom('div', { className: 'jasmine-summary' });
var stateBuilder = new ResultsStateBuilder();
this.suiteStarted = function(result) {
stateBuilder.suiteStarted(result);
};
this.suiteDone = function(result) {
stateBuilder.suiteDone(result);
if (result.status === 'failed') {
failures.push(failureDom(result));
}
addDeprecationWarnings(result);
};
this.specStarted = function(result) {
stateBuilder.specStarted(result);
};
var failures = [];
this.specDone = function(result) {
stateBuilder.specDone(result);
if (noExpectations(result)) {
var noSpecMsg = "Spec '" + result.fullName + "' has no expectations.";
if (result.status === 'failed') {
console.error(noSpecMsg);
} else {
console.warn(noSpecMsg);
}
}
if (!symbols) {
symbols = find('.jasmine-symbol-summary');
}
symbols.appendChild(
createDom('li', {
className: this.displaySpecInCorrectFormat(result),
id: 'spec_' + result.id,
title: result.fullName
})
);
if (result.status === 'failed') {
failures.push(failureDom(result));
}
addDeprecationWarnings(result);
};
this.displaySpecInCorrectFormat = function(result) {
return noExpectations(result) && result.status === 'passed'
? 'jasmine-empty'
: this.resultStatus(result.status);
};
this.resultStatus = function(status) {
if (status === 'excluded') {
return config().hideDisabled
? 'jasmine-excluded-no-display'
: 'jasmine-excluded';
}
return 'jasmine-' + status;
};
this.jasmineDone = function(doneResult) {
var banner = find('.jasmine-banner');
var alert = find('.jasmine-alert');
var order = doneResult && doneResult.order;
var i;
alert.appendChild(
createDom(
'span',
{ className: 'jasmine-duration' },
'finished in ' + doneResult.totalTime / 1000 + 's'
)
);
banner.appendChild(optionsMenu(config()));
if (stateBuilder.specsExecuted < totalSpecsDefined) {
var skippedMessage =
'Ran ' +
stateBuilder.specsExecuted +
' of ' +
totalSpecsDefined +
' specs - run all';
var skippedLink = addToExistingQueryString('spec', '');
alert.appendChild(
createDom(
'span',
{ className: 'jasmine-bar jasmine-skipped' },
createDom(
'a',
{ href: skippedLink, title: 'Run all specs' },
skippedMessage
)
)
);
}
var statusBarMessage = '';
var statusBarClassName = 'jasmine-overall-result jasmine-bar ';
var globalFailures = (doneResult && doneResult.failedExpectations) || [];
var failed = stateBuilder.failureCount + globalFailures.length > 0;
if (totalSpecsDefined > 0 || failed) {
statusBarMessage +=
pluralize('spec', stateBuilder.specsExecuted) +
', ' +
pluralize('failure', stateBuilder.failureCount);
if (stateBuilder.pendingSpecCount) {
statusBarMessage +=
', ' + pluralize('pending spec', stateBuilder.pendingSpecCount);
}
}
if (doneResult.overallStatus === 'passed') {
statusBarClassName += ' jasmine-passed ';
} else if (doneResult.overallStatus === 'incomplete') {
statusBarClassName += ' jasmine-incomplete ';
statusBarMessage =
'Incomplete: ' +
doneResult.incompleteReason +
', ' +
statusBarMessage;
} else {
statusBarClassName += ' jasmine-failed ';
}
var seedBar;
if (order && order.random) {
seedBar = createDom(
'span',
{ className: 'jasmine-seed-bar' },
', randomized with seed ',
createDom(
'a',
{
title: 'randomized with seed ' + order.seed,
href: seedHref(order.seed)
},
order.seed
)
);
}
alert.appendChild(
createDom(
'span',
{ className: statusBarClassName },
statusBarMessage,
seedBar
)
);
var errorBarClassName = 'jasmine-bar jasmine-errored';
var afterAllMessagePrefix = 'AfterAll ';
for (i = 0; i < globalFailures.length; i++) {
alert.appendChild(
createDom(
'span',
{ className: errorBarClassName },
globalFailureMessage(globalFailures[i])
)
);
}
function globalFailureMessage(failure) {
if (failure.globalErrorType === 'load') {
var prefix = 'Error during loading: ' + failure.message;
if (failure.filename) {
return (
prefix + ' in ' + failure.filename + ' line ' + failure.lineno
);
} else {
return prefix;
}
} else {
return afterAllMessagePrefix + failure.message;
}
}
addDeprecationWarnings(doneResult);
var warningBarClassName = 'jasmine-bar jasmine-warning';
for (i = 0; i < deprecationWarnings.length; i++) {
var warning = deprecationWarnings[i];
alert.appendChild(
createDom(
'span',
{ className: warningBarClassName },
'DEPRECATION: ' + warning
)
);
}
var results = find('.jasmine-results');
results.appendChild(summary);
summaryList(stateBuilder.topResults, summary);
if (failures.length) {
alert.appendChild(
createDom(
'span',
{ className: 'jasmine-menu jasmine-bar jasmine-spec-list' },
createDom('span', {}, 'Spec List | '),
createDom(
'a',
{ className: 'jasmine-failures-menu', href: '#' },
'Failures'
)
)
);
alert.appendChild(
createDom(
'span',
{ className: 'jasmine-menu jasmine-bar jasmine-failure-list' },
createDom(
'a',
{ className: 'jasmine-spec-list-menu', href: '#' },
'Spec List'
),
createDom('span', {}, ' | Failures ')
)
);
find('.jasmine-failures-menu').onclick = function() {
setMenuModeTo('jasmine-failure-list');
};
find('.jasmine-spec-list-menu').onclick = function() {
setMenuModeTo('jasmine-spec-list');
};
setMenuModeTo('jasmine-failure-list');
var failureNode = find('.jasmine-failures');
for (i = 0; i < failures.length; i++) {
failureNode.appendChild(failures[i]);
}
}
};
return this;
function failureDom(result) {
var failure = createDom(
'div',
{ className: 'jasmine-spec-detail jasmine-failed' },
failureDescription(result, stateBuilder.currentParent),
createDom('div', { className: 'jasmine-messages' })
);
var messages = failure.childNodes[1];
for (var i = 0; i < result.failedExpectations.length; i++) {
var expectation = result.failedExpectations[i];
messages.appendChild(
createDom(
'div',
{ className: 'jasmine-result-message' },
expectation.message
)
);
messages.appendChild(
createDom(
'div',
{ className: 'jasmine-stack-trace' },
expectation.stack
)
);
}
if (result.failedExpectations.length === 0) {
messages.appendChild(
createDom(
'div',
{ className: 'jasmine-result-message' },
'Spec has no expectations'
)
);
}
return failure;
}
function summaryList(resultsTree, domParent) {
var specListNode;
for (var i = 0; i < resultsTree.children.length; i++) {
var resultNode = resultsTree.children[i];
if (filterSpecs && !hasActiveSpec(resultNode)) {
continue;
}
if (resultNode.type === 'suite') {
var suiteListNode = createDom(
'ul',
{ className: 'jasmine-suite', id: 'suite-' + resultNode.result.id },
createDom(
'li',
{
className:
'jasmine-suite-detail jasmine-' + resultNode.result.status
},
createDom(
'a',
{ href: specHref(resultNode.result) },
resultNode.result.description
)
)
);
summaryList(resultNode, suiteListNode);
domParent.appendChild(suiteListNode);
}
if (resultNode.type === 'spec') {
if (domParent.getAttribute('class') !== 'jasmine-specs') {
specListNode = createDom('ul', { className: 'jasmine-specs' });
domParent.appendChild(specListNode);
}
var specDescription = resultNode.result.description;
if (noExpectations(resultNode.result)) {
specDescription = 'SPEC HAS NO EXPECTATIONS ' + specDescription;
}
if (
resultNode.result.status === 'pending' &&
resultNode.result.pendingReason !== ''
) {
specDescription =
specDescription +
' PENDING WITH MESSAGE: ' +
resultNode.result.pendingReason;
}
specListNode.appendChild(
createDom(
'li',
{
className: 'jasmine-' + resultNode.result.status,
id: 'spec-' + resultNode.result.id
},
createDom(
'a',
{ href: specHref(resultNode.result) },
specDescription
)
)
);
}
}
}
function optionsMenu(config) {
var optionsMenuDom = createDom(
'div',
{ className: 'jasmine-run-options' },
createDom('span', { className: 'jasmine-trigger' }, 'Options'),
createDom(
'div',
{ className: 'jasmine-payload' },
createDom(
'div',
{ className: 'jasmine-stop-on-failure' },
createDom('input', {
className: 'jasmine-fail-fast',
id: 'jasmine-fail-fast',
type: 'checkbox'
}),
createDom(
'label',
{ className: 'jasmine-label', for: 'jasmine-fail-fast' },
'stop execution on spec failure'
)
),
createDom(
'div',
{ className: 'jasmine-throw-failures' },
createDom('input', {
className: 'jasmine-throw',
id: 'jasmine-throw-failures',
type: 'checkbox'
}),
createDom(
'label',
{ className: 'jasmine-label', for: 'jasmine-throw-failures' },
'stop spec on expectation failure'
)
),
createDom(
'div',
{ className: 'jasmine-random-order' },
createDom('input', {
className: 'jasmine-random',
id: 'jasmine-random-order',
type: 'checkbox'
}),
createDom(
'label',
{ className: 'jasmine-label', for: 'jasmine-random-order' },
'run tests in random order'
)
),
createDom(
'div',
{ className: 'jasmine-hide-disabled' },
createDom('input', {
className: 'jasmine-disabled',
id: 'jasmine-hide-disabled',
type: 'checkbox'
}),
createDom(
'label',
{ className: 'jasmine-label', for: 'jasmine-hide-disabled' },
'hide disabled tests'
)
)
)
);
var failFastCheckbox = optionsMenuDom.querySelector('#jasmine-fail-fast');
failFastCheckbox.checked = config.failFast;
failFastCheckbox.onclick = function() {
navigateWithNewParam('failFast', !config.failFast);
};
var throwCheckbox = optionsMenuDom.querySelector(
'#jasmine-throw-failures'
);
throwCheckbox.checked = config.oneFailurePerSpec;
throwCheckbox.onclick = function() {
navigateWithNewParam('throwFailures', !config.oneFailurePerSpec);
};
var randomCheckbox = optionsMenuDom.querySelector(
'#jasmine-random-order'
);
randomCheckbox.checked = config.random;
randomCheckbox.onclick = function() {
navigateWithNewParam('random', !config.random);
};
var hideDisabled = optionsMenuDom.querySelector('#jasmine-hide-disabled');
hideDisabled.checked = config.hideDisabled;
hideDisabled.onclick = function() {
navigateWithNewParam('hideDisabled', !config.hideDisabled);
};
var optionsTrigger = optionsMenuDom.querySelector('.jasmine-trigger'),
optionsPayload = optionsMenuDom.querySelector('.jasmine-payload'),
isOpen = /\bjasmine-open\b/;
optionsTrigger.onclick = function() {
if (isOpen.test(optionsPayload.className)) {
optionsPayload.className = optionsPayload.className.replace(
isOpen,
''
);
} else {
optionsPayload.className += ' jasmine-open';
}
};
return optionsMenuDom;
}
function failureDescription(result, suite) {
var wrapper = createDom(
'div',
{ className: 'jasmine-description' },
createDom(
'a',
{ title: result.description, href: specHref(result) },
result.description
)
);
var suiteLink;
while (suite && suite.parent) {
wrapper.insertBefore(createTextNode(' > '), wrapper.firstChild);
suiteLink = createDom(
'a',
{ href: suiteHref(suite) },
suite.result.description
);
wrapper.insertBefore(suiteLink, wrapper.firstChild);
suite = suite.parent;
}
return wrapper;
}
function suiteHref(suite) {
var els = [];
while (suite && suite.parent) {
els.unshift(suite.result.description);
suite = suite.parent;
}
return addToExistingQueryString('spec', els.join(' '));
}
function addDeprecationWarnings(result) {
if (result && result.deprecationWarnings) {
for (var i = 0; i < result.deprecationWarnings.length; i++) {
var warning = result.deprecationWarnings[i].message;
if (!j$.util.arrayContains(warning)) {
deprecationWarnings.push(warning);
}
}
}
}
function find(selector) {
return getContainer().querySelector('.jasmine_html-reporter ' + selector);
}
function clearPrior() {
// return the reporter
var oldReporter = find('');
if (oldReporter) {
getContainer().removeChild(oldReporter);
}
}
function createDom(type, attrs, childrenVarArgs) {
var el = createElement(type);
for (var i = 2; i < arguments.length; i++) {
var child = arguments[i];
if (typeof child === 'string') {
el.appendChild(createTextNode(child));
} else {
if (child) {
el.appendChild(child);
}
}
}
for (var attr in attrs) {
if (attr == 'className') {
el[attr] = attrs[attr];
} else {
el.setAttribute(attr, attrs[attr]);
}
}
return el;
}
function pluralize(singular, count) {
var word = count == 1 ? singular : singular + 's';
return '' + count + ' ' + word;
}
function specHref(result) {
return addToExistingQueryString('spec', result.fullName);
}
function seedHref(seed) {
return addToExistingQueryString('seed', seed);
}
function defaultQueryString(key, value) {
return '?' + key + '=' + value;
}
function setMenuModeTo(mode) {
htmlReporterMain.setAttribute('class', 'jasmine_html-reporter ' + mode);
}
function noExpectations(result) {
var allExpectations =
result.failedExpectations.length + result.passedExpectations.length;
return (
allExpectations === 0 &&
(result.status === 'passed' || result.status === 'failed')
);
}
function hasActiveSpec(resultNode) {
if (resultNode.type == 'spec' && resultNode.result.status != 'excluded') {
return true;
}
if (resultNode.type == 'suite') {
for (var i = 0, j = resultNode.children.length; i < j; i++) {
if (hasActiveSpec(resultNode.children[i])) {
return true;
}
}
}
}
}
return HtmlReporter;
};
jasmineRequire.HtmlSpecFilter = function() {
function HtmlSpecFilter(options) {
var filterString =
options &&
options.filterString() &&
options.filterString().replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&');
var filterPattern = new RegExp(filterString);
this.matches = function(specName) {
return filterPattern.test(specName);
};
}
return HtmlSpecFilter;
};
jasmineRequire.ResultsNode = function() {
function ResultsNode(result, type, parent) {
this.result = result;
this.type = type;
this.parent = parent;
this.children = [];
this.addChild = function(result, type) {
this.children.push(new ResultsNode(result, type, this));
};
this.last = function() {
return this.children[this.children.length - 1];
};
this.updateResult = function(result) {
this.result = result;
};
}
return ResultsNode;
};
jasmineRequire.QueryString = function() {
function QueryString(options) {
this.navigateWithNewParam = function(key, value) {
options.getWindowLocation().search = this.fullStringWithNewParam(
key,
value
);
};
this.fullStringWithNewParam = function(key, value) {
var paramMap = queryStringToParamMap();
paramMap[key] = value;
return toQueryString(paramMap);
};
this.getParam = function(key) {
return queryStringToParamMap()[key];
};
return this;
function toQueryString(paramMap) {
var qStrPairs = [];
for (var prop in paramMap) {
qStrPairs.push(
encodeURIComponent(prop) + '=' + encodeURIComponent(paramMap[prop])
);
}
return '?' + qStrPairs.join('&');
}
function queryStringToParamMap() {
var paramStr = options.getWindowLocation().search.substring(1),
params = [],
paramMap = {};
if (paramStr.length > 0) {
params = paramStr.split('&');
for (var i = 0; i < params.length; i++) {
var p = params[i].split('=');
var value = decodeURIComponent(p[1]);
if (value === 'true' || value === 'false') {
value = JSON.parse(value);
}
paramMap[decodeURIComponent(p[0])] = value;
}
}
return paramMap;
}
}
return QueryString;
};

128
tests/jasmine-3.5.0/jasmine.css vendored Normal file

File diff suppressed because one or more lines are too long

8218
tests/jasmine-3.5.0/jasmine.js vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
tests/jasmine-3.5.0/jasmine_favicon.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

12
tests/test.py Normal file
View File

@ -0,0 +1,12 @@
from pincode import Pincode
from nose.tools import assert_equals
def test_valid_pincodes():
VALID_PINS = ['244713', '560029', '560030', '110011']
for pin in VALID_PINS:
assert_equals(True, Pincode.validate(pin), pin + " should be valid")
def test_invalid_pincodes():
INVALID_PINS = ['999999', '99999', '9999', '999', '99', '9', '111111', '2447131'];
for pin in INVALID_PINS:
assert_equals(False, Pincode.validate(pin), pin + " should be invalid")

43
tests/validate.js Normal file
View File

@ -0,0 +1,43 @@
const P = require('../src/index');
const assert = require('assert');
// A few correct ones
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(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)

23
tests/validate_spec.rb Normal file
View File

@ -0,0 +1,23 @@
require 'pincode_validator'
describe PincodeValidator do
it 'should validate pincodes' do
['244713', '560029', '560030', '110011'].each do |pin|
expect(described_class::valid? pin).to eq(true)
end
end
it 'should validate incorrect pincodes' do
['999999', '99999', '9999', '999', '99', '9', '111111', '2447131'].each do |pin|
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