libphonenumberについて
電話番号のバリデーション関連ライブラリをGoogleが公開したもの。Googleの2段階認証プロセスなどで導入されているらしい。
番号のパターンからフリーダイヤルか携帯かなど種別も判定することができる。 実態はPhoneNumberMetadata.xmlを始めとする正規表現のメタデータを元に判定している様子。
libphonenumber関連記事
ruby on railsでlibphonenumberを使うにあたって
今のところ公式にサポートされている言語はJava, C++, JavaScriptのみだが、libphonenumberベースのruby gemは非公式ながらも存在する。
どちらのGemもPhoneNumberMetadata.xmlをMarshal.dumpしたdatファイルをgem内に有している。
で、どっちを使えばいいの?
libphonenumberの基本的な機能はどちらのGemでも大体サポートしている
ロケーションや番号種別ごとのバリデーション
irb(main):020:0> TelephoneNumber.valid?('03-1234-5678', :jp) => true # Phonelib.parse('03-1234-5678', :jp).valid? でもいける irb(main):018:0> Phonelib.valid_for_country?('03-1234-5678', :jp) => true
任意の番号ルールの拡張
標準の番号ルールを拡張して、独自のブラックリストなりを作ることも可能そう。
TelephoneNumber.override_file = '/path/to/override_phone_data.dat' Phonelib.override_phone_data = '/path/to/override_phone_data.dat'
固定電話番号からのジオコーディング
irb(main):008:0> TelephoneNumber.parse('03-1234-5678', :jp).location => "Tokyo" irb(main):009:0> Phonelib.parse('03-1234-5678', :jp).geo_name => "Tokyo"
電話番号からの種別判定
irb(main):051:0> TelephoneNumber.parse('0120123456', :jp).valid_types => [:toll_free] irb(main):029:0> Phonelib.parse('0120123456', :jp).types => [:toll_free]
電話番号のE164フォーマット化
irb(main):054:0> TelephoneNumber.parse('0120123456', :jp).e164_number => "+81120123456" irb(main):055:0> Phonelib.parse('0120123456', :jp).e164 => "+81120123456"
Phonelibでのみサポートされている機能
ActiveRecord Integration
modelのvalidatesにもこんな感じでそのまま書ける。validatorクラスとか作りたくない人用かな。
validates :attribute, phone: { possible: true, allow_blank: true, types: [:voip, :mobile], country_specifier: -> phone { phone.country.try(:upcase) } }
Phonelibを使うことにした
- telephone_numberと比較して提供されてる機能が豊富
- libphonenumberの最新のメタデータに追従できている
- commit頻度が高くcontributorも多い安心感
railsのmodelにバリデーションを適用する
やりたいこと
- 電話番号のフォーマット違反の検知
- これは
Phonelib.parse(value, :jp).valid?
などでいける
- これは
- 受け入れたくない電話番号種別の検知
Phonelib.parse(value, :jp).types
でvalidした電話番号種別が取れるので、良しなに弾く
受け入れる番号種別はホワイトリストではなくブラックリストで運用した方が良さそう
電話番号の種別はこれからも増えることが予想され、そういった場合ホワイトリストだと意図しないものまで弾いてしまいかねない。
よって確実に通したくないものを追記する運用で考える。
今回は明らかに個人ではないと思われる電話番号種別を弾いてみる。
- フリーダイヤル(toll_free: 0120, 0800, 0037, 0066, 00777, 00882)
- ナビダイヤル(uan: 0570)
- ダイヤルQ2(premium_rate: 0990)
- M2M(pager: 020)
実装
# frozen_string_literal: true class TelephoneValidator < ActiveModel::EachValidator REJECTABLE_TYPES = %i(toll_free uan premium_rate pager).freeze def validate_each(record, attribute, value) parsed_tel = Phonelib.parse(value, :jp) return unless parsed_tel.invalid? || parsed_tel.types.any? { |type| REJECTABLE_TYPES.include?(type) } record.errors[attribute] << '無効な電話番号です' end end
あとはmodelに
# frozen_string_literal: true class User < ApplicationRecord validates :tel, telephone: true end
みたいな感じで適用して終わり!
[49] pry(main)> User.new(tel: '08012345678').valid? => true # 080は11桁であるべきなのでinvalid [50] pry(main)> User.new(tel: '080123456789').valid? => false # 0120は許可してない種別(フリーダイヤル)なのでinvalid [51] pry(main)> User.new(tel: '0120123456').valid? => false