piplapis-ruby 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +11 -0
- data/README.md +38 -0
- data/lib/pipl.rb +35 -0
- data/lib/pipl/client.rb +165 -0
- data/lib/pipl/configurable.rb +55 -0
- data/lib/pipl/consts.rb +74 -0
- data/lib/pipl/containers.rb +289 -0
- data/lib/pipl/default.rb +55 -0
- data/lib/pipl/errors.rb +33 -0
- data/lib/pipl/fields.rb +714 -0
- data/lib/pipl/response.rb +126 -0
- data/lib/pipl/utils.rb +51 -0
- data/lib/pipl/version.rb +3 -0
- data/pipl.gemspec +19 -0
- metadata +77 -0
data/LICENSE
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
2
|
+
you may not use this file except in compliance with the License.
|
3
|
+
You may obtain a copy of the License at
|
4
|
+
|
5
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
6
|
+
|
7
|
+
Unless required by applicable law or agreed to in writing, software
|
8
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
9
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
10
|
+
See the License for the specific language governing permissions and
|
11
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
piplapis Ruby Library
|
2
|
+
===========================
|
3
|
+
|
4
|
+
This is a Ruby client library for easily integrating Pipl's APIs into your application.
|
5
|
+
|
6
|
+
* Full details about Pipl's APIs - [http://dev.pipl.com](http://dev.pipl.com)
|
7
|
+
* This library is available in other languages - [http://dev.pipl.com/docs/libraries](http://dev.pipl.com/docs/libraries)
|
8
|
+
|
9
|
+
Library Requirements
|
10
|
+
--------------------
|
11
|
+
|
12
|
+
* Ruby 1.9 and above.
|
13
|
+
|
14
|
+
Library Notes
|
15
|
+
-------------
|
16
|
+
|
17
|
+
* According to the [documentation](http://dev.pipl.com/docs/read/search_api/data#fields-objects) some field objects should have a `.display` attribute,
|
18
|
+
since this is reserved in Ruby this attribute was renamed to `.show`.
|
19
|
+
|
20
|
+
Getting Started & Code Snippets
|
21
|
+
-------------------------------
|
22
|
+
|
23
|
+
**Pipl's Search API**
|
24
|
+
* Getting started tutorial - [http://dev.pipl.com/docs/search_api/getstarted](http://dev.pipl.com/docs/search_api/getstarted)
|
25
|
+
* Code snippets - [http://dev.pipl.com/docs/search_api/code](http://dev.pipl.com/docs/search_api/code)
|
26
|
+
|
27
|
+
**Pipl's Name API**
|
28
|
+
* Getting started tutorial - [http://dev.pipl.com/docs/name_api/getstarted](http://dev.pipl.com/docs/name_api/getstarted)
|
29
|
+
* Code snippets - [http://dev.pipl.com/docs/name_api/code](http://dev.pipl.com/docs/name_api/code)
|
30
|
+
|
31
|
+
**Pipl's Thumbnail API**
|
32
|
+
* Getting started tutorial - [http://dev.pipl.com/docs/thumbnail_api/getstarted](http://dev.pipl.com/docs/thumbnail_api/getstarted)
|
33
|
+
* Code snippets - [http://dev.pipl.com/docs/thumbnail_api/code](http://dev.pipl.com/docs/thumbnail_api/code)
|
34
|
+
|
35
|
+
Credits
|
36
|
+
-------
|
37
|
+
|
38
|
+
Thanks to [srochy](https://github.com/srochy) for writing this library!
|
data/lib/pipl.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative 'pipl/client'
|
2
|
+
require_relative 'pipl/default'
|
3
|
+
|
4
|
+
module Pipl
|
5
|
+
|
6
|
+
class << self
|
7
|
+
include Pipl::Configurable
|
8
|
+
|
9
|
+
attr_accessor :logger
|
10
|
+
|
11
|
+
def client
|
12
|
+
@client = Client.new(options) unless defined?(@client) && @client.same_options?(options)
|
13
|
+
@client
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def respond_to_missing?(method_name, include_private=false)
|
18
|
+
; client.respond_to?(method_name, include_private);
|
19
|
+
end if RUBY_VERSION >= '1.9'
|
20
|
+
|
21
|
+
def respond_to?(method_name, include_private=false)
|
22
|
+
; client.respond_to?(method_name, include_private) || super;
|
23
|
+
end if RUBY_VERSION < '1.9'
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def method_missing(method_name, *args, &block)
|
28
|
+
return super unless client.respond_to?(method_name)
|
29
|
+
client.send(method_name, *args, &block)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
Pipl.setup
|
data/lib/pipl/client.rb
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
require_relative 'configurable'
|
5
|
+
require_relative 'containers'
|
6
|
+
require_relative 'errors'
|
7
|
+
require_relative 'fields'
|
8
|
+
require_relative 'response'
|
9
|
+
|
10
|
+
|
11
|
+
module Pipl
|
12
|
+
|
13
|
+
class Client
|
14
|
+
|
15
|
+
include Pipl::Configurable
|
16
|
+
|
17
|
+
def initialize(options = {})
|
18
|
+
Pipl::Configurable.keys.each do |key|
|
19
|
+
instance_variable_set(:"@#{key}", options[key] || Pipl.instance_variable_get(:"@#{key}"))
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def same_options?(opts)
|
25
|
+
opts.hash == options.hash
|
26
|
+
end
|
27
|
+
|
28
|
+
def search(params={})
|
29
|
+
opts = options.merge params
|
30
|
+
create_search_person(opts)
|
31
|
+
validate_search_params(opts)
|
32
|
+
http, req = create_http_request(opts)
|
33
|
+
if opts.key? :callback
|
34
|
+
do_send_async http, req, opts[:callback]
|
35
|
+
elsif opts[:async] and block_given?
|
36
|
+
do_send_async http, req, Proc.new
|
37
|
+
else
|
38
|
+
do_send http, req
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def create_search_person(opts)
|
45
|
+
return if opts.key? :search_pointer
|
46
|
+
|
47
|
+
person = opts[:person] || Pipl::Person.new
|
48
|
+
person.add_field Pipl::Name.new(raw: opts[:raw_name]) if opts[:raw_name]
|
49
|
+
person.add_field Pipl::Email.new(address: opts[:email]) if opts[:email]
|
50
|
+
person.add_field Pipl::Username.new(content: opts[:username]) if opts[:username]
|
51
|
+
person.add_field Pipl::Address.new(raw: opts[:raw_address]) if opts[:raw_address]
|
52
|
+
|
53
|
+
if opts[:first_name] || opts[:middle_name] || opts[:last_name]
|
54
|
+
person.add_field Pipl::Name.new(first: opts[:first_name], middle: opts[:middle_name], last: opts[:last_name])
|
55
|
+
end
|
56
|
+
|
57
|
+
if opts[:country] || opts[:state] || opts[:city]
|
58
|
+
person.add_field Pipl::Address.new(country: opts[:country], state: opts[:state], city: opts[:city])
|
59
|
+
end
|
60
|
+
|
61
|
+
if opts[:phone]
|
62
|
+
if opts[:phone].is_a? String
|
63
|
+
person.add_field Pipl::Phone.new(raw: opts[:phone])
|
64
|
+
else
|
65
|
+
person.add_field Pipl::Phone.new(number: opts[:phone])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if opts[:from_age] || opts[:to_age]
|
70
|
+
person.add_field Pipl::DOB.from_age_range((opts[:from_age] || 0).to_i, (opts[:to_age].to_i || 1000).to_i)
|
71
|
+
end
|
72
|
+
|
73
|
+
opts[:person] = person
|
74
|
+
end
|
75
|
+
|
76
|
+
def validate_search_params(opts)
|
77
|
+
unless opts[:api_key] and not opts[:api_key].empty?
|
78
|
+
raise ArgumentError.new('API key is missing')
|
79
|
+
end
|
80
|
+
|
81
|
+
if opts[:search_pointer] and opts[:search_pointer].empty?
|
82
|
+
raise ArgumentError.new('Given search pointer is empty')
|
83
|
+
end
|
84
|
+
|
85
|
+
unless opts.key? :search_pointer
|
86
|
+
unless opts[:person] and opts[:person].is_searchable?
|
87
|
+
raise ArgumentError.new('At least one valid name/username/phone/email is required for search')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
if opts[:strict_validation]
|
92
|
+
if opts[:minimum_probability] and not (0..1).include? opts[:minimum_probability]
|
93
|
+
raise ArgumentError.new('minimum_probability must be a float in 0..1')
|
94
|
+
end
|
95
|
+
|
96
|
+
if opts[:minimum_match] and not (0..1).include? opts[:minimum_match]
|
97
|
+
raise ArgumentError.new('minimum_match must be a float in 0..1')
|
98
|
+
end
|
99
|
+
|
100
|
+
unless [Pipl::Configurable::SHOW_SOURCES_ALL,
|
101
|
+
Pipl::Configurable::SHOW_SOURCES_MATCHING,
|
102
|
+
Pipl::Configurable::SHOW_SOURCES_NONE, nil].include? opts[:show_sources]
|
103
|
+
raise ArgumentError.new('show_sources must be one of all, matching or false')
|
104
|
+
end
|
105
|
+
|
106
|
+
unless opts.key? :search_pointer
|
107
|
+
unsearchable = opts[:person].unsearchable_fields
|
108
|
+
if unsearchable and not unsearchable.empty?
|
109
|
+
raise ArgumentError.new("Some fields are unsearchable: #{unsearchable}")
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def create_http_request(opts)
|
116
|
+
uri = URI(opts[:api_endpoint])
|
117
|
+
keys = %w(minimum_probability minimum_match hide_sponsored live_feeds show_sources)
|
118
|
+
query_params = ["key=#{opts[:api_key]}"] + keys.map { |k| "#{k}=#{opts[k.to_sym]}" unless opts[k.to_sym].nil? }
|
119
|
+
uri.query = query_params.compact.join('&')
|
120
|
+
|
121
|
+
req = Net::HTTP::Post.new(uri.request_uri)
|
122
|
+
req['User-Agent'] = opts[:user_agent]
|
123
|
+
if opts.key? :search_pointer
|
124
|
+
req.set_form_data search_pointer: opts[:search_pointer]
|
125
|
+
else
|
126
|
+
h = opts[:person].to_hash
|
127
|
+
req.set_form_data person: h.reject { |_, value| value.nil? }.to_json
|
128
|
+
end
|
129
|
+
|
130
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
131
|
+
http.use_ssl = uri.scheme == 'https'
|
132
|
+
http.open_timeout = opts[:http_timeout]
|
133
|
+
|
134
|
+
[http, req]
|
135
|
+
end
|
136
|
+
|
137
|
+
def do_send(http, req)
|
138
|
+
response = http.request(req)
|
139
|
+
if response.is_a? Net::HTTPSuccess
|
140
|
+
SearchResponse.from_json(response.body)
|
141
|
+
else
|
142
|
+
begin
|
143
|
+
err = Pipl::Client::APIError.from_json(response.body)
|
144
|
+
rescue
|
145
|
+
err = Pipl::Client::APIError.new response.message, response.code
|
146
|
+
end
|
147
|
+
raise err
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def do_send_async(http, req, callback)
|
152
|
+
Thread.new do
|
153
|
+
begin
|
154
|
+
response = do_send http, req
|
155
|
+
callback.call response: response
|
156
|
+
rescue Pipl::Client::APIError => err
|
157
|
+
callback.call error: err
|
158
|
+
rescue Exception => msg
|
159
|
+
callback.call error: msg
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Pipl
|
2
|
+
|
3
|
+
module Configurable
|
4
|
+
|
5
|
+
SHOW_SOURCES_ALL = 'all'
|
6
|
+
SHOW_SOURCES_MATCHING = 'matching'
|
7
|
+
SHOW_SOURCES_NONE = 'false'
|
8
|
+
|
9
|
+
attr_accessor :api_key, :minimum_probability, :minimum_match, :hide_sponsored, :live_feeds, :show_sources
|
10
|
+
attr_accessor :strict_validation, :user_agent
|
11
|
+
attr_writer :api_endpoint
|
12
|
+
|
13
|
+
class << self
|
14
|
+
|
15
|
+
def keys
|
16
|
+
@keys ||= [
|
17
|
+
:api_key,
|
18
|
+
:minimum_probability,
|
19
|
+
:minimum_match,
|
20
|
+
:hide_sponsored,
|
21
|
+
:live_feeds,
|
22
|
+
:show_sources,
|
23
|
+
:strict_validation,
|
24
|
+
:api_endpoint,
|
25
|
+
:user_agent
|
26
|
+
]
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
def configure
|
32
|
+
yield self
|
33
|
+
end
|
34
|
+
|
35
|
+
def reset!
|
36
|
+
Pipl::Configurable.keys.each do |key|
|
37
|
+
instance_variable_set(:"@#{key}", Pipl::Default.options[key])
|
38
|
+
end
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
42
|
+
alias setup reset!
|
43
|
+
|
44
|
+
def api_endpoint
|
45
|
+
File.join(@api_endpoint, '')
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def options
|
51
|
+
Hash[Pipl::Configurable.keys.map { |key| [key, instance_variable_get(:"@#{key}")] }]
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
end
|
data/lib/pipl/consts.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module Pipl
|
4
|
+
|
5
|
+
DATE_FORMAT = '%Y-%m-%d'
|
6
|
+
|
7
|
+
STATES = {
|
8
|
+
US: {WA: 'Washington', VA: 'Virginia', DE: 'Delaware', DC: 'District Of Columbia', WI: 'Wisconsin',
|
9
|
+
WV: 'West Virginia', HI: 'Hawaii', FL: 'Florida', YT: 'Yukon', WY: 'Wyoming', PR: 'Puerto Rico',
|
10
|
+
NJ: 'New Jersey', NM: 'New Mexico', TX: 'Texas', LA: 'Louisiana', NC: 'North Carolina', ND: 'North Dakota',
|
11
|
+
NE: 'Nebraska', FM: 'Federated States Of Micronesia', TN: 'Tennessee', NY: 'New York', PA: 'Pennsylvania',
|
12
|
+
CT: 'Connecticut', RI: 'Rhode Island', NV: 'Nevada', NH: 'New Hampshire', GU: 'Guam', CO: 'Colorado',
|
13
|
+
VI: 'Virgin Islands', AK: 'Alaska', AL: 'Alabama', AS: 'American Samoa', AR: 'Arkansas', VT: 'Vermont',
|
14
|
+
IL: 'Illinois', GA: 'Georgia', IN: 'Indiana', IA: 'Iowa', MA: 'Massachusetts', AZ: 'Arizona',
|
15
|
+
CA: 'California', ID: 'Idaho', PW: 'Pala', ME: 'Maine', MD: 'Maryland', OK: 'Oklahoma', OH: 'Ohio',
|
16
|
+
UT: 'Utah', MO: 'Missouri', MN: 'Minnesota', MI: 'Michigan', MH: 'Marshall Islands', KS: 'Kansas',
|
17
|
+
MT: 'Montana', MP: 'Northern Mariana Islands', MS: 'Mississippi', SC: 'South Carolina', KY: 'Kentucky',
|
18
|
+
OR: 'Oregon', SD: 'South Dakota'},
|
19
|
+
CA: {AB: 'Alberta', BC: 'British Columbia', MB: 'Manitoba', NB: 'New Brunswick', NT: 'Northwest Territories',
|
20
|
+
NS: 'Nova Scotia', NU: 'Nunavut', ON: 'Ontario', PE: 'Prince Edward Island', QC: 'Quebec',
|
21
|
+
SK: 'Saskatchewan', YU: 'Yukon', NL: 'Newfoundland and Labrador'},
|
22
|
+
AU: {WA: 'State of Western Australia', SA: 'State of South Australia', NT: 'Northern Territory',
|
23
|
+
VIC: 'State of Victoria', TAS: 'State of Tasmania', QLD: 'State of Queensland',
|
24
|
+
NSW: 'State of New South Wales', ACT: 'Australian Capital Territory'},
|
25
|
+
GB: {WLS: 'Wales', SCT: 'Scotland', NIR: 'Northern Ireland', ENG: 'England'}
|
26
|
+
}
|
27
|
+
|
28
|
+
COUNTRIES = {BD: 'Bangladesh', WF: 'Wallis And Futuna Islands', BF: 'Burkina Faso', PY: 'Paraguay',
|
29
|
+
BA: 'Bosnia And Herzegovina', BB: 'Barbados', BE: 'Belgium', BM: 'Bermuda', BN: 'Brunei Darussalam',
|
30
|
+
BO: 'Bolivia', BH: 'Bahrain', BI: 'Burundi', BJ: 'Benin', BT: 'Bhutan', JM: 'Jamaica',
|
31
|
+
BV: 'Bouvet Island', BW: 'Botswana', WS: 'Samoa', BR: 'Brazil', BS: 'Bahamas', JE: 'Jersey',
|
32
|
+
BY: 'Belarus', BZ: 'Belize', RU: 'Russian Federation', RW: 'Rwanda', LT: 'Lithuania', RE: 'Reunion',
|
33
|
+
TM: 'Turkmenistan', TJ: 'Tajikistan', RO: 'Romania', LS: 'Lesotho', GW: 'Guinea-bissa', GU: 'Guam',
|
34
|
+
GT: 'Guatemala', GS: 'South Georgia And South Sandwich Islands', GR: 'Greece', GQ: 'Equatorial Guinea',
|
35
|
+
GP: 'Guadeloupe', JP: 'Japan', GY: 'Guyana', GG: 'Guernsey', GF: 'French Guiana', GE: 'Georgia',
|
36
|
+
GD: 'Grenada', GB: 'Great Britain', GA: 'Gabon', GN: 'Guinea', GM: 'Gambia', GL: 'Greenland',
|
37
|
+
GI: 'Gibraltar', GH: 'Ghana', OM: 'Oman', TN: 'Tunisia', JO: 'Jordan', HR: 'Croatia', HT: 'Haiti',
|
38
|
+
SV: 'El Salvador', HK: 'Hong Kong', HN: 'Honduras', HM: 'Heard And Mcdonald Islands', AD: 'Andorra',
|
39
|
+
PR: 'Puerto Rico', PS: 'Palestine', PW: 'Pala', PT: 'Portugal', SJ: 'Svalbard And Jan Mayen Islands',
|
40
|
+
VG: 'Virgin Islands, British', AI: 'Anguilla', KP: 'North Korea', PF: 'French Polynesia',
|
41
|
+
PG: 'Papua New Guinea', PE: 'Per', PK: 'Pakistan', PH: 'Philippines', PN: 'Pitcairn', PL: 'Poland',
|
42
|
+
PM: 'Saint Pierre And Miquelon', ZM: 'Zambia', EH: 'Western Sahara', EE: 'Estonia', EG: 'Egypt',
|
43
|
+
ZA: 'South Africa', EC: 'Ecuador', IT: 'Italy', AO: 'Angola', KZ: 'Kazakhstan', ET: 'Ethiopia',
|
44
|
+
ZW: 'Zimbabwe', SA: 'Saudi Arabia', ES: 'Spain', ER: 'Eritrea', ME: 'Montenegro', MD: 'Moldova',
|
45
|
+
MG: 'Madagascar', MA: 'Morocco', MC: 'Monaco', UZ: 'Uzbekistan', MM: 'Myanmar', ML: 'Mali', MO: 'Maca',
|
46
|
+
MN: 'Mongolia', MH: 'Marshall Islands', US: 'United States', UM: 'United States Minor Outlying Islands',
|
47
|
+
MT: 'Malta', MW: 'Malawi', MV: 'Maldives', MQ: 'Martinique', MP: 'Northern Mariana Islands',
|
48
|
+
MS: 'Montserrat', NA: 'Namibia', IM: 'Isle Of Man', UG: 'Uganda', MY: 'Malaysia', MX: 'Mexico',
|
49
|
+
IL: 'Israel', BG: 'Bulgaria', FR: 'France', AW: 'Aruba', AX: 'Åland', FI: 'Finland',
|
50
|
+
FJ: 'Fiji', FK: 'Falkland Islands', FM: 'Micronesia', FO: 'Faroe Islands', NI: 'Nicaragua',
|
51
|
+
NL: 'Netherlands', NO: 'Norway', SO: 'Somalia', NC: 'New Caledonia', NE: 'Niger', NF: 'Norfolk Island',
|
52
|
+
NG: 'Nigeria', NZ: 'New Zealand', NP: 'Nepal', NR: 'Naur', NU: 'Niue', MR: 'Mauritania',
|
53
|
+
CK: 'Cook Islands', CI: "Côte D'ivoire", CH: 'Switzerland', CO: 'Colombia', CN: 'China',
|
54
|
+
CM: 'Cameroon', CL: 'Chile', CC: 'Cocos (keeling) Islands', CA: 'Canada', CG: 'Congo (brazzaville)',
|
55
|
+
CF: 'Central African Republic', CD: 'Congo (kinshasa)', CZ: 'Czech Republic', CY: 'Cyprus',
|
56
|
+
CX: 'Christmas Island', CS: 'Serbia', CR: 'Costa Rica', HU: 'Hungary', CV: 'Cape Verde', CU: 'Cuba',
|
57
|
+
SZ: 'Swaziland', SY: 'Syria', KG: 'Kyrgyzstan', KE: 'Kenya', SR: 'Suriname', KI: 'Kiribati',
|
58
|
+
KH: 'Cambodia', KN: 'Saint Kitts And Nevis', KM: 'Comoros', ST: 'Sao Tome And Principe', SK: 'Slovakia',
|
59
|
+
KR: 'South Korea', SI: 'Slovenia', SH: 'Saint Helena', KW: 'Kuwait', SN: 'Senegal', SM: 'San Marino',
|
60
|
+
SL: 'Sierra Leone', SC: 'Seychelles', SB: 'Solomon Islands', KY: 'Cayman Islands', SG: 'Singapore',
|
61
|
+
SE: 'Sweden', SD: 'Sudan', DO: 'Dominican Republic', DM: 'Dominica', DJ: 'Djibouti', DK: 'Denmark',
|
62
|
+
DE: 'Germany', YE: 'Yemen', AT: 'Austria', DZ: 'Algeria', MK: 'Macedonia', UY: 'Uruguay', YT: 'Mayotte',
|
63
|
+
MU: 'Mauritius', TZ: 'Tanzania', LC: 'Saint Lucia', LA: 'Laos', TV: 'Tuval', TW: 'Taiwan',
|
64
|
+
TT: 'Trinidad And Tobago', TR: 'Turkey', LK: 'Sri Lanka', LI: 'Liechtenstein', LV: 'Latvia',
|
65
|
+
TO: 'Tonga', TL: 'Timor-leste', LU: 'Luxembourg', LR: 'Liberia', TK: 'Tokela', TH: 'Thailand',
|
66
|
+
TF: 'French Southern Lands', TG: 'Togo', TD: 'Chad', TC: 'Turks And Caicos Islands', LY: 'Libya',
|
67
|
+
VA: 'Vatican City', AC: 'Ascension Island', VC: 'Saint Vincent And The Grenadines',
|
68
|
+
AE: 'United Arab Emirates', VE: 'Venezuela', AG: 'Antigua And Barbuda', AF: 'Afghanistan', IQ: 'Iraq',
|
69
|
+
VI: 'Virgin Islands, U.s.', IS: 'Iceland', IR: 'Iran', AM: 'Armenia', AL: 'Albania', VN: 'Vietnam',
|
70
|
+
AN: 'Netherlands Antilles', AQ: 'Antarctica', AS: 'American Samoa', AR: 'Argentina', AU: 'Australia',
|
71
|
+
VU: 'Vanuat', IO: 'British Indian Ocean Territory', IN: 'India', LB: 'Lebanon', AZ: 'Azerbaijan',
|
72
|
+
IE: 'Ireland', ID: 'Indonesia', PA: 'Panama', UA: 'Ukraine', QA: 'Qatar', MZ: 'Mozambique'}
|
73
|
+
|
74
|
+
end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
require_relative 'fields'
|
2
|
+
require_relative 'utils'
|
3
|
+
|
4
|
+
|
5
|
+
module Pipl
|
6
|
+
|
7
|
+
class FieldsContainer
|
8
|
+
|
9
|
+
CLASS_CONTAINER = {
|
10
|
+
Name: 'names',
|
11
|
+
Address: 'addresses',
|
12
|
+
Phone: 'phones',
|
13
|
+
Email: 'emails',
|
14
|
+
Job: 'jobs',
|
15
|
+
Education: 'educations',
|
16
|
+
Image: 'images',
|
17
|
+
Username: 'usernames',
|
18
|
+
UserID: 'user_ids',
|
19
|
+
Url: 'urls',
|
20
|
+
Ethnicity: 'ethnicities',
|
21
|
+
Language: 'languages',
|
22
|
+
OriginCountry: 'origin_countries',
|
23
|
+
Relationship: 'relationships',
|
24
|
+
Tag: 'tags',
|
25
|
+
}
|
26
|
+
|
27
|
+
attr_reader :names, :addresses, :phones, :emails, :jobs, :educations, :images, :usernames, :user_ids, :urls
|
28
|
+
attr_reader :relationships, :tags, :ethnicities, :languages, :origin_countries, :dob, :gender
|
29
|
+
|
30
|
+
def initialize(params={})
|
31
|
+
@names = []
|
32
|
+
@addresses = []
|
33
|
+
@phones = []
|
34
|
+
@emails = []
|
35
|
+
@jobs = []
|
36
|
+
@educations = []
|
37
|
+
@images = []
|
38
|
+
@usernames = []
|
39
|
+
@user_ids = []
|
40
|
+
@urls = []
|
41
|
+
@ethnicities = []
|
42
|
+
@languages = []
|
43
|
+
@origin_countries = []
|
44
|
+
@relationships = []
|
45
|
+
@tags = []
|
46
|
+
@dob = nil
|
47
|
+
@gender = nil
|
48
|
+
|
49
|
+
add_fields params[:fields] if params.key? :fields
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.from_hash(h)
|
53
|
+
raise AbstractMethodInvoked.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.fields_from_hash(h)
|
57
|
+
fields = self::CLASS_CONTAINER.map do |cls_name, container|
|
58
|
+
cls = Pipl.const_get cls_name
|
59
|
+
h[container.to_sym].map { |x| cls.from_hash(x) } if h.key? container.to_sym
|
60
|
+
end
|
61
|
+
.flatten.compact
|
62
|
+
fields << DOB.from_hash(h[:dob]) if h.key? :dob
|
63
|
+
fields << Gender.from_hash(h[:gender]) if h.key? :gender
|
64
|
+
fields
|
65
|
+
end
|
66
|
+
|
67
|
+
def fields_to_hash
|
68
|
+
h = {}
|
69
|
+
h[:dob] = @dob.to_hash if @dob
|
70
|
+
h[:gender] = @gender.to_hash if @gender
|
71
|
+
self.class::CLASS_CONTAINER.values.each do |container|
|
72
|
+
fields = instance_variable_get("@#{container}")
|
73
|
+
h[container.to_sym] = fields.map { |field| field.to_hash }.compact unless fields.empty?
|
74
|
+
end
|
75
|
+
h.reject { |_, value| value.nil? or (value.kind_of?(Array) and value.empty?) }
|
76
|
+
end
|
77
|
+
|
78
|
+
def add_fields(fields)
|
79
|
+
fields.each { |f| add_field f }
|
80
|
+
end
|
81
|
+
|
82
|
+
def add_field(field)
|
83
|
+
cls_sym = field.class.name.split('::').last.to_sym
|
84
|
+
container = self.class::CLASS_CONTAINER[cls_sym]
|
85
|
+
if container
|
86
|
+
instance_variable_get("@#{container}") << field
|
87
|
+
elsif cls_sym == :DOB
|
88
|
+
@dob = field
|
89
|
+
elsif cls_sym == :Gender
|
90
|
+
@gender = field
|
91
|
+
else
|
92
|
+
raise ArgumentError.new("Object of type #{field.class} is an invalid field")
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def all_fields
|
97
|
+
fields = self.class::CLASS_CONTAINER.values.map { |container| instance_variable_get("@#{container}") }
|
98
|
+
.flatten.compact
|
99
|
+
fields << @dob if @dob
|
100
|
+
fields << @gender if @gender
|
101
|
+
fields
|
102
|
+
end
|
103
|
+
|
104
|
+
def job
|
105
|
+
@jobs.first unless @jobs.empty?
|
106
|
+
end
|
107
|
+
|
108
|
+
def address
|
109
|
+
@addresses.first unless @addresses.empty?
|
110
|
+
end
|
111
|
+
|
112
|
+
def education
|
113
|
+
@educations.first unless @educations.empty?
|
114
|
+
end
|
115
|
+
|
116
|
+
def language
|
117
|
+
@languages.first unless @languages.empty?
|
118
|
+
end
|
119
|
+
|
120
|
+
def ethnicity
|
121
|
+
@ethnicities.first unless @ethnicities.empty?
|
122
|
+
end
|
123
|
+
|
124
|
+
def origin_country
|
125
|
+
@origin_countries.first unless @origin_countries.empty?
|
126
|
+
end
|
127
|
+
|
128
|
+
def phone
|
129
|
+
@phones.first unless @phones.empty?
|
130
|
+
end
|
131
|
+
|
132
|
+
def email
|
133
|
+
@emails.first unless @emails.empty?
|
134
|
+
end
|
135
|
+
|
136
|
+
def name
|
137
|
+
@names.first unless @names.empty?
|
138
|
+
end
|
139
|
+
|
140
|
+
def image
|
141
|
+
@images.first unless @images.empty?
|
142
|
+
end
|
143
|
+
|
144
|
+
def url
|
145
|
+
@urls.first unless @urls.empty?
|
146
|
+
end
|
147
|
+
|
148
|
+
def username
|
149
|
+
@usernames.first unless @usernames.empty?
|
150
|
+
end
|
151
|
+
|
152
|
+
def user_id
|
153
|
+
@user_ids.first unless @user_ids.empty?
|
154
|
+
end
|
155
|
+
|
156
|
+
def relationship
|
157
|
+
@relationships.first unless @relationships.empty?
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
class Relationship < FieldsContainer
|
164
|
+
|
165
|
+
CLASS_CONTAINER = FieldsContainer::CLASS_CONTAINER.clone
|
166
|
+
CLASS_CONTAINER.delete :Relationship
|
167
|
+
|
168
|
+
# @!attribute valid_since
|
169
|
+
# @see Field
|
170
|
+
# @!attribute inferred
|
171
|
+
# @see Field
|
172
|
+
# @!attribute type
|
173
|
+
# @return [String] Type of association of this relationship to a person.
|
174
|
+
# Possible values are:
|
175
|
+
# friend
|
176
|
+
# family
|
177
|
+
# work
|
178
|
+
# other
|
179
|
+
# @!attribute subtype
|
180
|
+
# @return [String] Subtype of association of this relationship to a person. Free text.
|
181
|
+
|
182
|
+
attr_accessor :valid_since, :inferred, :type, :subtype
|
183
|
+
|
184
|
+
def initialize(params={})
|
185
|
+
super params
|
186
|
+
@valid_since = params[:valid_since]
|
187
|
+
@inferred = params[:inferred]
|
188
|
+
@type = params[:type]
|
189
|
+
@subtype = params[:subtype]
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.from_hash(h)
|
193
|
+
params = Pipl::Field.base_params_from_hash h
|
194
|
+
params[:subtype] = h[:@subtype]
|
195
|
+
params[:fields] = self.fields_from_hash(h)
|
196
|
+
self.new(params)
|
197
|
+
end
|
198
|
+
|
199
|
+
def to_hash
|
200
|
+
fields_to_hash
|
201
|
+
end
|
202
|
+
|
203
|
+
def to_s
|
204
|
+
@names.first.to_s unless @names.empty?
|
205
|
+
end
|
206
|
+
|
207
|
+
end
|
208
|
+
|
209
|
+
|
210
|
+
class Source < FieldsContainer
|
211
|
+
|
212
|
+
attr_reader :match, :name, :category, :origin_url, :sponsored, :domain, :source_id, :person_id, :premium, :valid_since
|
213
|
+
|
214
|
+
def initialize(params={})
|
215
|
+
super params
|
216
|
+
@name = params[:name]
|
217
|
+
@category = params[:category]
|
218
|
+
@origin_url = params[:origin_url]
|
219
|
+
@domain = params[:domain]
|
220
|
+
@source_id = params[:source_id]
|
221
|
+
@person_id = params[:person_id]
|
222
|
+
@sponsored = params[:sponsored]
|
223
|
+
@premium = params[:premium]
|
224
|
+
@match = params[:match]
|
225
|
+
@valid_since = params[:valid_since]
|
226
|
+
end
|
227
|
+
|
228
|
+
def self.from_hash(h)
|
229
|
+
params = {
|
230
|
+
name: h[:@name],
|
231
|
+
category: h[:@category],
|
232
|
+
origin_url: h[:@origin_url],
|
233
|
+
domain: h[:@domain],
|
234
|
+
source_id: h[:@source_id],
|
235
|
+
person_id: h[:@person_id],
|
236
|
+
match: h[:@match],
|
237
|
+
sponsored: h[:@sponsored],
|
238
|
+
premium: h[:@premium],
|
239
|
+
}
|
240
|
+
params[:valid_since] = Pipl::Utils.str_to_date(h[:@valid_since]) if h.key? :@valid_since
|
241
|
+
params[:fields] = self.fields_from_hash(h)
|
242
|
+
self.new(params)
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
class Person < FieldsContainer
|
248
|
+
|
249
|
+
attr_reader :id, :match, :search_pointer
|
250
|
+
|
251
|
+
def initialize(params={})
|
252
|
+
super params
|
253
|
+
@id = params[:id]
|
254
|
+
@match = params[:match]
|
255
|
+
@search_pointer = params[:search_pointer]
|
256
|
+
end
|
257
|
+
|
258
|
+
def self.from_hash(h)
|
259
|
+
params = {
|
260
|
+
id: h[:@id],
|
261
|
+
match: h[:@match],
|
262
|
+
search_pointer: h[:@search_pointer],
|
263
|
+
}
|
264
|
+
params[:fields] = fields_from_hash(h)
|
265
|
+
self.new(params)
|
266
|
+
end
|
267
|
+
|
268
|
+
def to_hash
|
269
|
+
h = {}
|
270
|
+
h[:search_pointer] = @search_pointer if @search_pointer and not @search_pointer.empty?
|
271
|
+
h.update(fields_to_hash)
|
272
|
+
h
|
273
|
+
end
|
274
|
+
|
275
|
+
def is_searchable?
|
276
|
+
not @search_pointer.nil? or
|
277
|
+
@names.any? { |f| f.is_searchable? } or
|
278
|
+
@emails.any? { |f| f.is_searchable? } or
|
279
|
+
@phones.any? { |f| f.is_searchable? } or
|
280
|
+
@usernames.any? { |f| f.is_searchable? }
|
281
|
+
end
|
282
|
+
|
283
|
+
def unsearchable_fields
|
284
|
+
all_fields.reject { |f| f.is_searchable? }
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
end
|