mini_phone 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: f62c6d5370c3066ee7a24d8fbb4b6297f3382708f2492ffde4a4637239134064
4
+ data.tar.gz: ed947cb56dc592a7aa2be638ca1d59f608ff2ac5b3fab52e573c0ad3bde99306
5
+ SHA512:
6
+ metadata.gz: d32d5ee76afd3d7ccd526f5854725ab718f45c5c9c0cb03be318e895b87c0fd33af8fefe2f6797320e15bd09e8134c900a783e0354e716e94e3b0d8228bab561
7
+ data.tar.gz: e8612f26f40ab9ad5fbbf50d36201f1b2d2411728ef31bf005608029af1cbc4ee427802c68e6a583b0f089ca5fc3306115c5ebae28776ddbc630c35069b7b8dc
@@ -0,0 +1,3 @@
1
+ ---
2
+ BasedOnStyle: LLVM
3
+ ColumnLimit: 120
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: CI
3
+
4
+ on: push
5
+
6
+ jobs:
7
+ build:
8
+ runs-on: ubuntu-16.04
9
+ strategy:
10
+ fail-fast: false
11
+ matrix:
12
+ ruby: ["2.5", "2.6", "2.7"]
13
+ os: [ubuntu-latest, macos-latest]
14
+ experimental: [false]
15
+ include:
16
+ - ruby: "truffleruby"
17
+ os: ubuntu-latest
18
+ experimental: true
19
+ - ruby: "truffleruby"
20
+ os: macos-latest
21
+ experimental: true
22
+ name: ${{ matrix.ruby }} on ${{ matrix.os }}
23
+ continue-on-error: ${{ matrix.experimental }}
24
+ env:
25
+ CI_EXPERIMENTAL: ${{ matrix.experimental }}
26
+ steps:
27
+ - uses: actions/checkout@v2
28
+
29
+ - uses: ruby/setup-ruby@v1
30
+ with:
31
+ ruby-version: ${{ matrix.ruby }}
32
+
33
+ - name: Install dependencies (system)
34
+ if: ${{ matrix.os == 'ubuntu-latest' }}
35
+ run: sudo apt-get -yqq install libphonenumber-dev
36
+
37
+ - name: Install dependencies (system)
38
+ if: ${{ matrix.os == 'macos-latest' }}
39
+ run: brew install --build-from-source libphonenumber && brew link libphonenumber
40
+
41
+ - name: Install dependencies (ruby)
42
+ run: gem install bundler && bundle install --jobs 4 --retry 3
43
+
44
+ - name: Compile
45
+ run: bundle exec rake compile
46
+
47
+ - name: RSpec
48
+ run: bundle exec rspec --format RSpec::Github::Formatter --format documentation
49
+
50
+ - name: Rubocop
51
+ run: bundle exec rubocop --format github
@@ -0,0 +1,19 @@
1
+ .bundle/
2
+ .yardoc
3
+ _yardoc/
4
+ coverage/
5
+ doc/
6
+ pkg/
7
+ spec/reports/
8
+ tmp/
9
+ *.bundle
10
+ *.so
11
+ *.o
12
+ *.a
13
+ mkmf.log
14
+
15
+ # rspec failure tracking
16
+ .rspec_status
17
+ Gemfile.lock
18
+ vendor/
19
+ *.gem
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,18 @@
1
+ # The behavior of RuboCop can be controlled via the .rubocop.yml
2
+ # configuration file. It makes it possible to enable/disable
3
+ # certain cops (checks) and to alter their behavior if they accept
4
+ # any parameters. The file can be placed either in your home
5
+ # directory or in some project directory.
6
+ #
7
+ # RuboCop will start looking for the configuration file in the directory
8
+ # where the inspected file is and continue its way up to the root directory.
9
+ #
10
+ # See https://docs.rubocop.org/rubocop/configuration
11
+ AllCops:
12
+ TargetRubyVersion: 2.5
13
+ NewCops: enable
14
+ SuggestExtensions: false
15
+
16
+ Metrics/BlockLength:
17
+ Exclude:
18
+ - spec/**/*
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.1
6
+ before_install: gem install bundler -v 2.1.4
File without changes
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at i.kerseymer@gmail.com. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in mini_phone.gemspec
6
+ gemspec
7
+
8
+ gem 'rake', '~> 12.0'
9
+ gem 'rake-compiler', github: 'larskanis/rake-compiler', branch: 'fix-native-version'
10
+ gem 'rspec', '~> 3.0'
11
+ gem 'rspec-github', require: false
12
+ gem 'rubocop'
13
+
14
+ group :bench do
15
+ gem 'benchmark-ips'
16
+ gem 'phonelib'
17
+ end
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 Ian Ker-Seymer
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,149 @@
1
+ # MiniPhone
2
+
3
+ A Ruby gem which plugs directly in the the Google's native C++
4
+ [libphonenumber](https://github.com/google/libphonenumber) for extemely
5
+ _fast_ and _robust_ phone number parsing, validation, and formatting. On
6
+ average, most methods are 40x t0 50x faster than other Ruby phone number
7
+ libraries.
8
+
9
+ ## Usage
10
+
11
+ ### Checking if a phone number is valid
12
+
13
+ ```ruby
14
+ MiniPhone.valid?('+14043841399') # true
15
+ MiniPhone.valid_for_country?('(404) 384-1399', 'US') # true
16
+ MiniPhone.valid_for_country?('(404) 384-1399', 'GB') # false
17
+ ```
18
+
19
+ ### Formatting a number
20
+
21
+ ```ruby
22
+ MiniPhone.default_country = 'US'
23
+
24
+ phone_number = MiniPhone.parse('404-384-1399')
25
+
26
+ phone_number.e164 # +14043841399
27
+ phone_number.national # (404) 384-1399
28
+ phone_number.international # +1 404-384-1399
29
+ phone_number.rfc3966 # tel:+1-404-384-1384
30
+ ```
31
+
32
+ ### Checking if a phone number is possible
33
+
34
+ ```ruby
35
+ phone_number = MiniPhone.parse('-12')
36
+
37
+ phone_number.possible? # false
38
+ ```
39
+
40
+ ### Getting the type of a phone number
41
+
42
+ ```ruby
43
+ MiniPhone.parse('+12423570000').type # :mobile
44
+ MiniPhone.parse['+12423651234').type # :fixed_line
45
+ ```
46
+
47
+ The possible types are directly mapped from [this
48
+ enum](https://github.com/google/libphonenumber/blob/4e9954edea7cf263532c5dd3861a801104c3f012/cpp/src/phonenumbers/phonenumberutil.h#L91):
49
+
50
+ ```ruby
51
+ :fixed_line
52
+ :mobile
53
+ :fixed_line_or_mobile
54
+ :toll_free
55
+ :premium_rate
56
+ :shared_cost
57
+ :voip
58
+ :personal_number
59
+ :pager
60
+ :uan
61
+ :voicemail
62
+ :unknown
63
+ ```
64
+
65
+ ## Compatibility with PhoneLib
66
+
67
+ MiniPhone aims to be compatible with
68
+ [Phonelib](https://github.com/daddyz/phonelib) so in many cases it can be a
69
+ drop in replacement. It has a smaller feature set, so if you need it it not a
70
+ drop in replacement for every use case. If there is a feature you need, open
71
+ an issue and we will try to support it.
72
+
73
+ ## Benchmarks
74
+
75
+ On average, most methods are 40x t0 50x faster than other libraries. To run
76
+ the benchmarks locally, execute: `bundle exec rake bench`
77
+
78
+ ```
79
+ Comparison:
80
+ MiniPhone: valid?: 23111.4 i/s
81
+ Phonelib: valid?: 482.3 i/s - 47.92x (± 0.00) slower
82
+ ```
83
+
84
+ ```
85
+ Comparison:
86
+ MiniPhone: e164: 31567.0 i/s
87
+ Phonelib: e164: 652.3 i/s - 48.39x (± 0.00) slower
88
+ ```
89
+
90
+ ## Installation
91
+
92
+ 1. Install libphonenumber
93
+
94
+ ```sh
95
+ brew install libphonenumber # mac
96
+ ```
97
+
98
+ ```sh
99
+ apt-get install -y libphonenumber # debian / ubuntu
100
+ ```
101
+
102
+ 2. Add this line to your application's Gemfile:
103
+
104
+ ```ruby
105
+ gem 'mini_phone'
106
+ ```
107
+
108
+ 3. And then execute:
109
+
110
+ ```sh
111
+ bundle install
112
+ ```
113
+
114
+ Or install it yourself as:
115
+
116
+ ```sh
117
+ gem install mini_phone
118
+ ```
119
+
120
+ ## Development
121
+
122
+ After checking out the repo, run `bin/setup` to install dependencies. Then,
123
+ run `rake spec` to run the tests. You can also run `bin/console` for an
124
+ interactive prompt that will allow you to experiment.
125
+
126
+ To install this gem onto your local machine, run `bundle exec rake install`.
127
+ To release a new version, update the version number in `version.rb`, and then
128
+ run `bundle exec rake release`, which will create a git tag for the version,
129
+ push git commits and tags, and push the `.gem` file to
130
+ [rubygems.org](https://rubygems.org).
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at
135
+ https://github.com/[USERNAME]/mini_phone. This project is intended to be a
136
+ safe, welcoming space for collaboration, and contributors are expected to
137
+ adhere to the [code of
138
+ conduct](https://github.com/[USERNAME]/mini_phone/blob/master/CODE_OF_CONDUCT.md).
139
+
140
+ ## License
141
+
142
+ The gem is available as open source under the terms of the [MIT
143
+ License](https://opensource.org/licenses/MIT).
144
+
145
+ ## Code of Conduct
146
+
147
+ Everyone interacting in the MiniPhone project's codebases, issue trackers,
148
+ chat rooms and mailing lists is expected to follow the [code of
149
+ conduct](https://github.com/[USERNAME]/mini_phone/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+ require 'rubygems/package_task'
6
+ require 'rake/extensiontask'
7
+
8
+
9
+ RSpec::Core::RakeTask.new(:spec)
10
+
11
+ task build: :compile
12
+
13
+ task default: %i[clobber compile spec]
14
+
15
+ spec = Gem::Specification::load(File.expand_path('../mini_phone.gemspec', __FILE__))
16
+
17
+ Gem::PackageTask.new(spec) do |pkg|
18
+ end
19
+
20
+ Rake::ExtensionTask.new('mini_phone', spec) do |ext|
21
+ ext.lib_dir = 'lib/mini_phone'
22
+ end
23
+
24
+ task bench: %i[clobber compile] do
25
+ Dir['bench/**/*'].each do |f|
26
+ require_relative f
27
+ end
28
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ # rubocop:disable Style/GlobalVars
4
+
5
+ require 'mkmf'
6
+
7
+ LIBDIR = RbConfig::CONFIG['libdir']
8
+
9
+ INCLUDEDIR = RbConfig::CONFIG['includedir']
10
+
11
+ header_dirs = [
12
+ # First search /opt/local for macports
13
+ '/opt/local/include',
14
+
15
+ # Then search /usr/local for people that installed from source
16
+ '/usr/local/include',
17
+
18
+ # Check the ruby install locations
19
+ INCLUDEDIR,
20
+
21
+ # Finally fall back to /usr
22
+ '/usr/include'
23
+ ]
24
+
25
+ lib_dirs = [
26
+ # First search /opt/local for macports
27
+ '/opt/local/lib',
28
+
29
+ # Then search /usr/local for people that installed from source
30
+ '/usr/local/lib',
31
+
32
+ # Check the ruby install locations
33
+ LIBDIR,
34
+
35
+ # Finally fall back to /usr
36
+ '/usr/lib'
37
+ ]
38
+
39
+ # Detect homebrew installs
40
+ if find_executable('brew')
41
+ brew_prefix = `brew --prefix`.strip
42
+ header_dirs.unshift "#{brew_prefix}/include"
43
+ lib_dirs.unshift "#{brew_prefix}/lib"
44
+ end
45
+
46
+ dir_config('mini_phone', header_dirs, lib_dirs)
47
+
48
+ unless have_library('phonenumber')
49
+ abort <<~MSG
50
+
51
+ ,----------------------------------------------------------------------,
52
+ | |
53
+ | Hey there, sorry for the inconvenience! |
54
+ | |
55
+ | It looks like libphonenumber is not installed on this system. Don't |
56
+ | worry, this should be easy to fix: |
57
+ | |
58
+ | 1. Install the lib |
59
+ | |
60
+ | On Mac: |
61
+ | brew install libphonenumber |
62
+ | |
63
+ | On Debian / Ubuntu: |
64
+ | apt-get install -y libphonenumber |
65
+ | |
66
+ | 2. Retry installing the gem (i.e `bundle install`) |
67
+ | |
68
+ '----------------------------------------------------------------------'
69
+
70
+ MSG
71
+ end
72
+
73
+ $CXXFLAGS += ' -std=c++11 '
74
+
75
+ create_makefile('mini_phone/mini_phone')
76
+
77
+ # rubocop:enable Style/GlobalVars
@@ -0,0 +1,377 @@
1
+ #include "mini_phone.h"
2
+ #include "phonenumbers/phonenumberutil.h"
3
+
4
+ using namespace ::i18n::phonenumbers;
5
+
6
+ static VALUE rb_mMiniPhone;
7
+
8
+ static VALUE rb_cPhoneNumber;
9
+
10
+ extern "C" struct PhoneNumberInfo {
11
+ PhoneNumber phone_number;
12
+ std::string raw_phone_number;
13
+ std::string raw_country_code;
14
+ };
15
+
16
+ static inline VALUE is_phone_number_valid(VALUE self, VALUE str, VALUE cc) {
17
+ PhoneNumber parsed_number;
18
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
19
+
20
+ std::string phone_number(RSTRING_PTR(str), RSTRING_LEN(str));
21
+ std::string country_code(RSTRING_PTR(cc), RSTRING_LEN(cc));
22
+
23
+ auto result = phone_util->ParseAndKeepRawInput(phone_number, country_code, &parsed_number);
24
+
25
+ if (result == PhoneNumberUtil::NO_PARSING_ERROR && phone_util->IsValidNumber(parsed_number)) {
26
+ return Qtrue;
27
+ } else {
28
+ return Qfalse;
29
+ }
30
+ }
31
+
32
+ extern "C" VALUE rb_is_phone_number_valid(VALUE self, VALUE str) {
33
+ VALUE def_cc = rb_iv_get(rb_mMiniPhone, "@default_country_code");
34
+
35
+ return is_phone_number_valid(self, str, def_cc);
36
+ }
37
+
38
+ extern "C" VALUE rb_is_phone_number_valid_for_country(VALUE self, VALUE str, VALUE cc) {
39
+ return is_phone_number_valid(self, str, cc);
40
+ }
41
+
42
+ extern "C" VALUE rb_is_phone_number_invalid(VALUE self, VALUE str) {
43
+ return rb_is_phone_number_valid(self, str) == Qtrue ? Qfalse : Qtrue;
44
+ }
45
+
46
+ extern "C" VALUE rb_is_phone_number_invalid_for_country(VALUE self, VALUE str, VALUE cc) {
47
+ return is_phone_number_valid(self, str, cc) == Qtrue ? Qfalse : Qtrue;
48
+ }
49
+
50
+ extern "C" VALUE rb_is_phone_number_possible(VALUE self, VALUE str) {
51
+ PhoneNumber parsed_number;
52
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
53
+
54
+ VALUE def_cc = rb_iv_get(rb_mMiniPhone, "@default_country_code");
55
+ std::string phone_number(RSTRING_PTR(str), RSTRING_LEN(str));
56
+ std::string country_code(RSTRING_PTR(def_cc), RSTRING_LEN(def_cc));
57
+
58
+ auto result = phone_util->Parse(phone_number, country_code, &parsed_number);
59
+
60
+ if (result == PhoneNumberUtil::NO_PARSING_ERROR && phone_util->IsPossibleNumber(parsed_number)) {
61
+ return Qtrue;
62
+ } else {
63
+ return Qfalse;
64
+ }
65
+ }
66
+
67
+ extern "C" VALUE rb_is_phone_number_impossible(VALUE self, VALUE str) {
68
+ return rb_is_phone_number_possible(self, str) == Qtrue ? Qfalse : Qtrue;
69
+ }
70
+
71
+ extern "C" VALUE rb_set_default_country_code(VALUE self, VALUE str_code) {
72
+ return rb_iv_set(self, "@default_country_code", str_code);
73
+ }
74
+
75
+ extern "C" VALUE rb_get_default_country_code(VALUE self) { return rb_iv_get(self, "@default_country_code"); }
76
+
77
+ extern "C" void rb_phone_number_dealloc(PhoneNumberInfo *phone_number_info) { delete phone_number_info; }
78
+
79
+ extern "C" VALUE rb_phone_number_parse(int argc, VALUE *argv, VALUE self) {
80
+ return rb_class_new_instance(argc, argv, rb_cPhoneNumber);
81
+ }
82
+
83
+ extern "C" VALUE rb_phone_number_alloc(VALUE self) {
84
+ PhoneNumberInfo *phone_number_info = new PhoneNumberInfo();
85
+
86
+ /* wrap */
87
+ return Data_Wrap_Struct(self, NULL, &rb_phone_number_dealloc, phone_number_info);
88
+ }
89
+
90
+ static inline VALUE rb_phone_number_nullify_ivars(VALUE self) {
91
+ rb_iv_set(self, "@national", Qnil);
92
+ rb_iv_set(self, "@international", Qnil);
93
+ rb_iv_set(self, "@e164", Qnil);
94
+ rb_iv_set(self, "@country_code", Qnil);
95
+ rb_iv_set(self, "@region_code", Qnil);
96
+ rb_iv_set(self, "@rfc3966", Qnil);
97
+ rb_iv_set(self, "@type", Qnil);
98
+ rb_iv_set(self, "@valid", Qfalse);
99
+ rb_iv_set(self, "@possible", Qfalse);
100
+
101
+ return Qtrue;
102
+ }
103
+
104
+ extern "C" VALUE rb_phone_number_initialize(int argc, VALUE *argv, VALUE self) {
105
+ VALUE str;
106
+ VALUE def_cc;
107
+
108
+ rb_scan_args(argc, argv, "11", &str, &def_cc);
109
+
110
+ if (NIL_P(def_cc)) {
111
+ def_cc = rb_iv_get(rb_mMiniPhone, "@default_country_code");
112
+ }
113
+
114
+ rb_iv_set(self, "@input", str);
115
+
116
+ if (FIXNUM_P(str)) {
117
+ str = rb_fix2str(str, 10);
118
+ } else if (!RB_TYPE_P(str, T_STRING)) {
119
+ return rb_phone_number_nullify_ivars(self);
120
+ }
121
+
122
+ PhoneNumberInfo *phone_number_info;
123
+ PhoneNumber parsed_number;
124
+
125
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
126
+
127
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
128
+
129
+ std::string phone_number(RSTRING_PTR(str), RSTRING_LEN(str));
130
+ std::string country_code(RSTRING_PTR(def_cc), RSTRING_LEN(def_cc));
131
+
132
+ auto result = phone_util->Parse(phone_number, country_code, &parsed_number);
133
+
134
+ if (result != PhoneNumberUtil::NO_PARSING_ERROR) {
135
+ rb_phone_number_nullify_ivars(self);
136
+ } else {
137
+ phone_number_info->phone_number = parsed_number;
138
+ }
139
+
140
+ return self;
141
+ }
142
+
143
+ static inline VALUE rb_phone_number_format(VALUE self, PhoneNumberUtil::PhoneNumberFormat fmt) {
144
+ std::string formatted_number;
145
+ PhoneNumberInfo *phone_number_info;
146
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
147
+
148
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
149
+ PhoneNumber parsed_number = phone_number_info->phone_number;
150
+ phone_util->Format(parsed_number, fmt, &formatted_number);
151
+
152
+ return rb_str_new(formatted_number.c_str(), formatted_number.size());
153
+ }
154
+
155
+ extern "C" VALUE rb_phone_number_e164(VALUE self) {
156
+ if (rb_ivar_defined(self, rb_intern("@e164"))) {
157
+ return rb_iv_get(self, "@e164");
158
+ }
159
+
160
+ return rb_iv_set(self, "@e164", rb_phone_number_format(self, PhoneNumberUtil::PhoneNumberFormat::E164));
161
+ }
162
+
163
+ extern "C" VALUE rb_phone_number_national(VALUE self) {
164
+ if (rb_ivar_defined(self, rb_intern("@national"))) {
165
+ return rb_iv_get(self, "@national");
166
+ }
167
+
168
+ return rb_iv_set(self, "@national", rb_phone_number_format(self, PhoneNumberUtil::PhoneNumberFormat::NATIONAL));
169
+ }
170
+
171
+ extern "C" VALUE rb_phone_number_international(VALUE self) {
172
+ if (rb_ivar_defined(self, rb_intern("@international"))) {
173
+ return rb_iv_get(self, "@international");
174
+ }
175
+
176
+ return rb_iv_set(self, "@international",
177
+ rb_phone_number_format(self, PhoneNumberUtil::PhoneNumberFormat::INTERNATIONAL));
178
+ }
179
+
180
+ extern "C" VALUE rb_phone_number_rfc3966(VALUE self) {
181
+ if (rb_ivar_defined(self, rb_intern("@rfc3966"))) {
182
+ return rb_iv_get(self, "@rfc3966");
183
+ }
184
+
185
+ return rb_iv_set(self, "@rfc3966", rb_phone_number_format(self, PhoneNumberUtil::PhoneNumberFormat::RFC3966));
186
+ }
187
+
188
+ extern "C" VALUE rb_phone_number_valid_eh(VALUE self) {
189
+ if (rb_ivar_defined(self, rb_intern("@valid"))) {
190
+ return rb_iv_get(self, "@valid");
191
+ }
192
+
193
+ std::string formatted_number;
194
+ PhoneNumberInfo *phone_number_info;
195
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
196
+
197
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
198
+
199
+ if (phone_util->IsValidNumber(phone_number_info->phone_number)) {
200
+ return rb_iv_set(self, "@valid", Qtrue);
201
+ } else {
202
+ return rb_iv_set(self, "@valid", Qfalse);
203
+ }
204
+ }
205
+
206
+ extern "C" VALUE rb_phone_number_invalid_eh(VALUE self) {
207
+ return rb_phone_number_valid_eh(self) == Qtrue ? Qfalse : Qtrue;
208
+ }
209
+
210
+ extern "C" VALUE rb_phone_number_possible_eh(VALUE self) {
211
+ if (rb_ivar_defined(self, rb_intern("@possible"))) {
212
+ return rb_iv_get(self, "@possible");
213
+ }
214
+
215
+ std::string formatted_number;
216
+ PhoneNumberInfo *phone_number_info;
217
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
218
+
219
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
220
+
221
+ if (phone_util->IsPossibleNumber(phone_number_info->phone_number)) {
222
+ return rb_iv_set(self, "@possible", Qtrue);
223
+ } else {
224
+ return rb_iv_set(self, "@possible", Qfalse);
225
+ }
226
+ }
227
+
228
+ extern "C" VALUE rb_phone_number_impossible_eh(VALUE self) {
229
+ return rb_phone_number_possible_eh(self) == Qtrue ? Qfalse : Qtrue;
230
+ }
231
+
232
+ extern "C" VALUE rb_phone_number_region_code(VALUE self) {
233
+ if (rb_ivar_defined(self, rb_intern("@region_code"))) {
234
+ return rb_iv_get(self, "@region_code");
235
+ }
236
+
237
+ PhoneNumberInfo *phone_number_info;
238
+ std::string code;
239
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
240
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
241
+
242
+ phone_util->GetRegionCodeForCountryCode(phone_number_info->phone_number.country_code(), &code);
243
+
244
+ VALUE result = rb_str_new(code.c_str(), code.size());
245
+
246
+ return rb_iv_set(self, "@region_code", result);
247
+ }
248
+
249
+ extern "C" VALUE rb_phone_number_country_code(VALUE self) {
250
+ if (rb_ivar_defined(self, rb_intern("@country_code"))) {
251
+ return rb_iv_get(self, "@country_code");
252
+ }
253
+
254
+ PhoneNumberInfo *phone_number_info;
255
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
256
+
257
+ int code = phone_number_info->phone_number.country_code();
258
+
259
+ VALUE result = INT2NUM(code);
260
+
261
+ return rb_iv_set(self, "@country_code", result);
262
+ }
263
+
264
+ extern "C" VALUE rb_phone_number_eql_eh(VALUE self, VALUE other) {
265
+ if (!rb_obj_is_instance_of(other, rb_cPhoneNumber)) {
266
+ return Qfalse;
267
+ }
268
+
269
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
270
+
271
+ PhoneNumberInfo *self_phone_number_info;
272
+ Data_Get_Struct(self, PhoneNumberInfo, self_phone_number_info);
273
+
274
+ PhoneNumberInfo *other_phone_number_info;
275
+ Data_Get_Struct(other, PhoneNumberInfo, other_phone_number_info);
276
+ if (phone_util->IsNumberMatch(other_phone_number_info->phone_number, self_phone_number_info->phone_number)) {
277
+ return Qtrue;
278
+ } else {
279
+ return Qfalse;
280
+ }
281
+ }
282
+
283
+ extern "C" VALUE rb_phone_number_type(VALUE self) {
284
+ if (rb_ivar_defined(self, rb_intern("@type"))) {
285
+ return rb_iv_get(self, "@type");
286
+ }
287
+
288
+ PhoneNumberInfo *phone_number_info;
289
+ Data_Get_Struct(self, PhoneNumberInfo, phone_number_info);
290
+ PhoneNumberUtil *phone_util = PhoneNumberUtil::GetInstance();
291
+
292
+ VALUE result;
293
+
294
+ // @see
295
+ // https://github.com/google/libphonenumber/blob/4e9954edea7cf263532c5dd3861a801104c3f012/cpp/src/phonenumbers/phonenumberutil.h#L91
296
+ switch (phone_util->GetNumberType(phone_number_info->phone_number)) {
297
+ case PhoneNumberUtil::PREMIUM_RATE:
298
+ result = rb_intern("premium_rate");
299
+ break;
300
+ case PhoneNumberUtil::TOLL_FREE:
301
+ result = rb_intern("toll_free");
302
+ break;
303
+ case PhoneNumberUtil::MOBILE:
304
+ result = rb_intern("mobile");
305
+ break;
306
+ case PhoneNumberUtil::FIXED_LINE:
307
+ result = rb_intern("fixed_line");
308
+ break;
309
+ case PhoneNumberUtil::FIXED_LINE_OR_MOBILE:
310
+ result = rb_intern("fixed_line_or_mobile");
311
+ break;
312
+ case PhoneNumberUtil::SHARED_COST:
313
+ result = rb_intern("shared_cost");
314
+ break;
315
+ case PhoneNumberUtil::VOIP:
316
+ result = rb_intern("voip");
317
+ break;
318
+ case PhoneNumberUtil::PERSONAL_NUMBER:
319
+ result = rb_intern("personal_number");
320
+ break;
321
+ case PhoneNumberUtil::PAGER:
322
+ result = rb_intern("pager");
323
+ break;
324
+ case PhoneNumberUtil::UAN:
325
+ result = rb_intern("uan");
326
+ break;
327
+ case PhoneNumberUtil::VOICEMAIL:
328
+ result = rb_intern("voicemail");
329
+ break;
330
+ default:
331
+ result = rb_intern("unknown");
332
+ break;
333
+ }
334
+
335
+ return rb_iv_set(self, "@type", ID2SYM(result));
336
+ }
337
+
338
+ extern "C" void Init_mini_phone(void) {
339
+ rb_mMiniPhone = rb_define_module("MiniPhone");
340
+
341
+ // Unknown
342
+ rb_iv_set(rb_mMiniPhone, "@default_country_code", rb_str_new("ZZ", 2));
343
+
344
+ rb_define_module_function(rb_mMiniPhone, "valid?", reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_valid), 1);
345
+ rb_define_module_function(rb_mMiniPhone, "valid_for_country?",
346
+ reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_valid_for_country), 2);
347
+ rb_define_module_function(rb_mMiniPhone, "invalid?", reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_invalid), 1);
348
+ rb_define_module_function(rb_mMiniPhone, "invalid_for_country?",
349
+ reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_invalid_for_country), 2);
350
+ rb_define_module_function(rb_mMiniPhone, "possible?", reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_valid), 1);
351
+ rb_define_module_function(rb_mMiniPhone, "impossible?", reinterpret_cast<VALUE (*)(...)>(rb_is_phone_number_invalid),
352
+ 1);
353
+ rb_define_module_function(rb_mMiniPhone,
354
+ "default_country_code=", reinterpret_cast<VALUE (*)(...)>(rb_set_default_country_code), 1);
355
+ rb_define_module_function(rb_mMiniPhone, "default_country_code",
356
+ reinterpret_cast<VALUE (*)(...)>(rb_get_default_country_code), 0);
357
+ rb_define_module_function(rb_mMiniPhone, "parse", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_parse), -1);
358
+
359
+ rb_cPhoneNumber = rb_define_class_under(rb_mMiniPhone, "PhoneNumber", rb_cObject);
360
+
361
+ rb_define_singleton_method(rb_cPhoneNumber, "parse", reinterpret_cast<VALUE (*)(...)>(rb_class_new_instance), -1);
362
+ rb_define_alloc_func(rb_cPhoneNumber, rb_phone_number_alloc);
363
+ rb_define_method(rb_cPhoneNumber, "initialize", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_initialize), -1);
364
+ rb_define_method(rb_cPhoneNumber, "valid?", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_valid_eh), 0);
365
+ rb_define_method(rb_cPhoneNumber, "invalid?", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_invalid_eh), 0);
366
+ rb_define_method(rb_cPhoneNumber, "possible?", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_possible_eh), 0);
367
+ rb_define_method(rb_cPhoneNumber, "impossible?", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_impossible_eh), 0);
368
+ rb_define_method(rb_cPhoneNumber, "e164", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_e164), 0);
369
+ rb_define_method(rb_cPhoneNumber, "national", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_national), 0);
370
+ rb_define_method(rb_cPhoneNumber, "international", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_international),
371
+ 0);
372
+ rb_define_method(rb_cPhoneNumber, "rfc3966", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_rfc3966), 0);
373
+ rb_define_method(rb_cPhoneNumber, "region_code", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_region_code), 0);
374
+ rb_define_method(rb_cPhoneNumber, "country_code", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_country_code), 0);
375
+ rb_define_method(rb_cPhoneNumber, "type", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_type), 0);
376
+ rb_define_method(rb_cPhoneNumber, "eql?", reinterpret_cast<VALUE (*)(...)>(rb_phone_number_eql_eh), 1);
377
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef MINI_PHONE_H
2
+ #define MINI_PHONE_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ #endif /* MINI_PHONE_H */
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mini_phone/version'
4
+ require 'mini_phone/mini_phone'
5
+
6
+ module MiniPhone
7
+ class Error < StandardError; end
8
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MiniPhone
4
+ VERSION = '0.1.0'
5
+ end
@@ -0,0 +1,12 @@
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": "."
5
+ }
6
+ ],
7
+ "settings": {
8
+ "editor.formatOnSave": true,
9
+ "ruby.format": "rubocop",
10
+ "ruby.useBundler": true
11
+ }
12
+ }
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'lib/mini_phone/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'mini_phone'
7
+ spec.version = MiniPhone::VERSION
8
+ spec.authors = ['Ian Ker-Seymer']
9
+ spec.email = ['i.kerseymer@gmail.com']
10
+
11
+ spec.summary = 'Uses the Google libphonenumber C lib to parse, validate, and format phone numbers'
12
+ spec.description = <<~MSG.strip
13
+ Plugs directly in the the Google's native C++ [libphonenumber](https://github.com/google/libphonenumber) for extemely _fast_ and _robust_ phone number parsing, validation, and formatting. On average, most methods are 40x t0 50x faster than other Ruby phone number libraries.
14
+ MSG
15
+ spec.homepage = 'https://github.com/ianks/mini_phone'
16
+ spec.license = 'MIT'
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.5.0')
18
+
19
+ spec.platform = Gem::Platform::RUBY
20
+
21
+ # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
22
+
23
+ spec.metadata['homepage_uri'] = spec.homepage
24
+ spec.metadata['source_code_uri'] = 'https://github.com/ianks/mini_phone'
25
+ spec.metadata['changelog_uri'] = 'https://github.com/ianks/mini_phone/blob/master/CHANGELOG.md'
26
+
27
+ # Specify which files should be added to the gem when it is released.
28
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
29
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
30
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(bin|spec|bench)/}) }
31
+ end
32
+ spec.bindir = 'exe'
33
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
34
+ spec.require_paths = ['lib']
35
+ spec.extensions = ['ext/mini_phone/extconf.rb']
36
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mini_phone
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ian Ker-Seymer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-01-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Plugs directly in the the Google's native C++ [libphonenumber](https://github.com/google/libphonenumber)
14
+ for extemely _fast_ and _robust_ phone number parsing, validation, and formatting.
15
+ On average, most methods are 40x t0 50x faster than other Ruby phone number libraries.
16
+ email:
17
+ - i.kerseymer@gmail.com
18
+ executables: []
19
+ extensions:
20
+ - ext/mini_phone/extconf.rb
21
+ extra_rdoc_files: []
22
+ files:
23
+ - ".clang-format"
24
+ - ".github/workflows/ci.yml"
25
+ - ".gitignore"
26
+ - ".rspec"
27
+ - ".rubocop.yml"
28
+ - ".travis.yml"
29
+ - CHANGELOG.md
30
+ - CODE_OF_CONDUCT.md
31
+ - Gemfile
32
+ - LICENSE.txt
33
+ - README.md
34
+ - Rakefile
35
+ - ext/mini_phone/extconf.rb
36
+ - ext/mini_phone/mini_phone.cc
37
+ - ext/mini_phone/mini_phone.h
38
+ - lib/mini_phone.rb
39
+ - lib/mini_phone/version.rb
40
+ - mini_phone.code-workspace
41
+ - mini_phone.gemspec
42
+ homepage: https://github.com/ianks/mini_phone
43
+ licenses:
44
+ - MIT
45
+ metadata:
46
+ homepage_uri: https://github.com/ianks/mini_phone
47
+ source_code_uri: https://github.com/ianks/mini_phone
48
+ changelog_uri: https://github.com/ianks/mini_phone/blob/master/CHANGELOG.md
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 2.5.0
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubygems_version: 3.0.3
65
+ signing_key:
66
+ specification_version: 4
67
+ summary: Uses the Google libphonenumber C lib to parse, validate, and format phone
68
+ numbers
69
+ test_files: []