nameable 1.1.3 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.rubocop.yml +17 -0
- data/.travis.yml +7 -2
- data/Gemfile +1 -1
- data/Gemfile.lock +10 -9
- data/README.md +12 -10
- data/Rakefile +2 -3
- data/bin/nameable_web_service +5 -5
- data/checksums/nameable-1.1.1.gem.sha512 +1 -1
- data/checksums/nameable-1.1.3.gem.sha512 +1 -0
- data/data/yob2016.txt +32868 -0
- data/lib/nameable.rb +5 -5
- data/lib/nameable/assets.rb +5 -3
- data/lib/nameable/error.rb +0 -1
- data/lib/nameable/latin.rb +52 -50
- data/lib/nameable/latin/patterns.rb +22 -17
- data/lib/nameable/version.rb +1 -1
- data/nameable.gemspec +9 -7
- data/spec/nameable/extensions_spec.rb +11 -0
- data/spec/nameable/latin_spec.rb +101 -241
- data/spec/nameable_spec.rb +2 -8
- data/spec/spec_helper.rb +4 -5
- metadata +39 -24
- metadata.gz.sig +0 -0
- data/Guardfile +0 -57
- data/data/yob2014.txt +0 -33044
- data/spec/spec.opts +0 -5
data/lib/nameable.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'nameable/version'
|
2
|
+
require 'nameable/error'
|
3
|
+
require 'nameable/assets'
|
4
|
+
require 'nameable/latin'
|
5
|
+
require 'nameable/extensions'
|
6
6
|
|
7
7
|
module Nameable
|
8
8
|
def self.parse(name)
|
data/lib/nameable/assets.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
module Nameable
|
2
|
-
|
3
|
-
|
1
|
+
module Nameable
|
2
|
+
module Assets
|
3
|
+
GENDER_TABLE = File.expand_path(File.join('..', '..', '..', 'data', 'yob2016.txt'), __FILE__)
|
4
|
+
ETHNICITY_TABLE = File.expand_path(File.join('..', '..', '..', 'data', 'app_c.csv'), __FILE__)
|
5
|
+
end
|
4
6
|
end
|
data/lib/nameable/error.rb
CHANGED
data/lib/nameable/latin.rb
CHANGED
@@ -9,7 +9,6 @@ module Nameable
|
|
9
9
|
|
10
10
|
attr_accessor :prefix, :first, :middle, :last, :suffix
|
11
11
|
|
12
|
-
##
|
13
12
|
def initialize(*args)
|
14
13
|
if args.size == 1 && args.first.class == Hash
|
15
14
|
parts = args.first
|
@@ -19,29 +18,28 @@ module Nameable
|
|
19
18
|
@last = parts[:last] ? parts[:last] : nil
|
20
19
|
@suffix = parts[:suffix] ? parts[:suffix] : nil
|
21
20
|
else
|
22
|
-
@first = args.shift
|
21
|
+
@first = args.shift unless args.empty?
|
23
22
|
@middle = args.shift if args.size >= 2 # Only grab a middle name if we've got a last name left
|
24
|
-
@last = args.shift
|
23
|
+
@last = args.shift unless args.empty?
|
25
24
|
end
|
26
25
|
end
|
27
26
|
|
28
27
|
##
|
29
28
|
# name is an Array
|
30
29
|
def extract_prefix(name)
|
31
|
-
return unless name
|
32
|
-
Nameable::Latin::Patterns::PREFIX.
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
30
|
+
return unless name && name.size > 1 && @prefix.nil? && @first.nil?
|
31
|
+
Nameable::Latin::Patterns::PREFIX.each do |pretty, regex|
|
32
|
+
next unless name.first =~ regex
|
33
|
+
@prefix = pretty
|
34
|
+
name.delete(name.first)
|
35
|
+
break
|
38
36
|
end
|
39
37
|
end
|
40
38
|
|
41
39
|
##
|
42
40
|
# name is an Array
|
43
41
|
def extract_suffix(name)
|
44
|
-
return unless name
|
42
|
+
return unless name && name.size >= 3
|
45
43
|
|
46
44
|
(name.size - 1).downto(2) do |n|
|
47
45
|
suff = nil
|
@@ -50,48 +48,52 @@ module Nameable
|
|
50
48
|
suff = pretty if name[n] =~ regex
|
51
49
|
end
|
52
50
|
|
53
|
-
if name[n] =~ Nameable::Latin::Patterns::SUFFIX_ACADEMIC
|
54
|
-
|
51
|
+
if name[n] =~ Nameable::Latin::Patterns::SUFFIX_ACADEMIC ||
|
52
|
+
name[n] =~ Nameable::Latin::Patterns::SUFFIX_PROFESSIONAL ||
|
53
|
+
name[n] =~ Nameable::Latin::Patterns::SUFFIX_GENERATIONAL_ROMAN
|
54
|
+
suff = name[n].upcase.delete('.')
|
55
55
|
end
|
56
56
|
|
57
|
-
if !suff &&
|
58
|
-
|
57
|
+
if !suff &&
|
58
|
+
name.join != name.join.upcase &&
|
59
|
+
name[n].length > 1 &&
|
60
|
+
name[n] =~ Nameable::Latin::Patterns::SUFFIX_ABBREVIATION
|
61
|
+
suff = name[n].upcase.delete('.')
|
59
62
|
end
|
60
63
|
|
61
64
|
if suff
|
62
65
|
@suffix = @suffix ? "#{suff}, #{@suffix}" : suff
|
63
66
|
name.delete_at(n)
|
64
67
|
end
|
65
|
-
|
66
68
|
end
|
67
69
|
end
|
68
70
|
|
69
71
|
##
|
70
72
|
# name is an Array
|
71
73
|
def extract_first(name)
|
72
|
-
return unless name
|
74
|
+
return unless name && name.size >= 1
|
73
75
|
|
74
76
|
@first = name.first
|
75
77
|
name.delete_at(0)
|
76
78
|
|
77
|
-
@first.capitalize! unless @first =~ /[a-z]/
|
79
|
+
@first.capitalize! unless @first =~ /[a-z]/ && @first =~ /[A-Z]/
|
78
80
|
end
|
79
81
|
|
80
82
|
##
|
81
83
|
# name is an Array
|
82
84
|
def extract_last(name)
|
83
|
-
return unless name
|
85
|
+
return unless name && name.size >= 1
|
84
86
|
|
85
87
|
@last = name.last.gsub(/['`"]+/, "'").gsub(/-+/, '-')
|
86
88
|
name.delete_at(name.size - 1)
|
87
89
|
|
88
|
-
@last.capitalize! unless @last =~ /[a-z]/
|
90
|
+
@last.capitalize! unless @last =~ /[a-z]/ && @last =~ /[A-Z]/
|
89
91
|
end
|
90
92
|
|
91
93
|
##
|
92
94
|
# name is an Array
|
93
95
|
def extract_middle(name)
|
94
|
-
return unless name
|
96
|
+
return unless name && name.size >= 1
|
95
97
|
|
96
98
|
(name.size - 1).downto(0) do |n|
|
97
99
|
next unless name[n]
|
@@ -100,9 +102,9 @@ module Nameable
|
|
100
102
|
@last = "#{name[n].downcase.capitalize} #{@last}"
|
101
103
|
elsif name[n] =~ Nameable::Latin::Patterns::O_LAST_NAME_PRE_CONCATS
|
102
104
|
@last = "O'#{@last}"
|
103
|
-
elsif name[n] =~ /\-/
|
104
|
-
@last = "#{name[n-1].
|
105
|
-
name[n-1] = nil
|
105
|
+
elsif name[n] =~ /\-/ && n > 0 && name[n - 1]
|
106
|
+
@last = "#{name[n - 1].delete('-')}-#{@last}"
|
107
|
+
name[n - 1] = nil
|
106
108
|
else
|
107
109
|
@middle = @middle ? "#{name[n]} #{@middle}" : name[n]
|
108
110
|
end
|
@@ -110,21 +112,21 @@ module Nameable
|
|
110
112
|
name.delete_at(n)
|
111
113
|
end
|
112
114
|
|
113
|
-
@middle.capitalize! if @middle
|
114
|
-
@middle = "#{@middle}." if @middle
|
115
|
+
@middle.capitalize! if @middle && !(@middle =~ /[a-z]/ && @middle =~ /[A-Z]/)
|
116
|
+
@middle = "#{@middle}." if @middle && @middle.size == 1
|
115
117
|
end
|
116
118
|
|
117
119
|
def parse(name)
|
118
120
|
raise InvalidNameError unless name
|
119
121
|
if name.class == String
|
120
122
|
if name.index(',')
|
121
|
-
name = "#{
|
123
|
+
name = "#{Regexp.last_match(2)} #{Regexp.last_match(1)}" if name =~ /^([a-z]+)\s*,\s*,*([^,]*)/i
|
122
124
|
end
|
123
125
|
|
124
126
|
name = name.strip.split(/\s+/)
|
125
127
|
end
|
126
128
|
|
127
|
-
name = name.first.split(/[^[:alnum:]]+/) if name.size == 1
|
129
|
+
name = name.first.split(/[^[:alnum:]]+/) if name.size == 1 && name.first.split(/[^[:alnum:]]+/)
|
128
130
|
|
129
131
|
extract_prefix(name)
|
130
132
|
extract_suffix(name)
|
@@ -138,7 +140,7 @@ module Nameable
|
|
138
140
|
end
|
139
141
|
|
140
142
|
# http://www.ssa.gov/oact/babynames/limits.html
|
141
|
-
def load_huge_gender_table(gender_table=Nameable::Assets::GENDER_TABLE)
|
143
|
+
def load_huge_gender_table(gender_table = Nameable::Assets::GENDER_TABLE)
|
142
144
|
ranked = {}
|
143
145
|
|
144
146
|
CSV.read(gender_table).each do |first, gender, rank|
|
@@ -161,45 +163,45 @@ module Nameable
|
|
161
163
|
|
162
164
|
# http://www.census.gov/genealogy/www/data/2000surnames/index.html
|
163
165
|
def load_huge_ethnicity_table
|
164
|
-
CSV.read(File.expand_path(File.join('..', '..', '..', 'data', 'app_c.csv'), __FILE__)).each do |name, rank, count,
|
166
|
+
CSV.read(File.expand_path(File.join('..', '..', '..', 'data', 'app_c.csv'), __FILE__)).each do |name, rank, count, _prop100k, _cum_prop100k, pctwhite, pctblack, pctapi, pctaian, pct2prace, pcthispanic|
|
165
167
|
next if name == 'name'
|
166
168
|
@@last_names[name.downcase] = {
|
167
|
-
rank:rank.to_i,
|
168
|
-
count:count.to_i,
|
169
|
-
percent_white:pctwhite.to_f,
|
170
|
-
percent_black:pctblack.to_f,
|
171
|
-
percent_asian_pacific_islander:pctapi.to_f,
|
172
|
-
percent_american_indian_alaska_native:pctaian.to_f,
|
173
|
-
percent_two_or_more_races:pct2prace.to_f,
|
174
|
-
percent_hispanic:pcthispanic.to_f
|
169
|
+
rank: rank.to_i,
|
170
|
+
count: count.to_i,
|
171
|
+
percent_white: pctwhite.to_f,
|
172
|
+
percent_black: pctblack.to_f,
|
173
|
+
percent_asian_pacific_islander: pctapi.to_f,
|
174
|
+
percent_american_indian_alaska_native: pctaian.to_f,
|
175
|
+
percent_two_or_more_races: pct2prace.to_f,
|
176
|
+
percent_hispanic: pcthispanic.to_f
|
175
177
|
}
|
176
178
|
end
|
177
179
|
end
|
178
180
|
|
179
181
|
def gender
|
180
182
|
return @gender if @gender
|
181
|
-
load_huge_gender_table unless @@first_names &&
|
183
|
+
load_huge_gender_table unless @@first_names && !@@first_names.empty?
|
182
184
|
@gender = @@first_names[@first.to_s.downcase] ? @@first_names[@first.to_s.downcase] : :unknown
|
183
185
|
@gender
|
184
186
|
end
|
185
187
|
|
186
188
|
def ethnicity
|
187
189
|
return @ethnicity if @ethnicity
|
188
|
-
load_huge_ethnicity_table unless @@last_names &&
|
189
|
-
@ethnicity =
|
190
|
+
load_huge_ethnicity_table unless @@last_names && !@@last_names.empty?
|
191
|
+
@ethnicity = @last && @@last_names[@last.downcase] ? @@last_names[@last.downcase] : :unknown
|
190
192
|
@ethnicity
|
191
193
|
end
|
192
194
|
|
193
195
|
def male?
|
194
|
-
|
196
|
+
gender == :male
|
195
197
|
end
|
196
198
|
|
197
199
|
def female?
|
198
|
-
|
200
|
+
gender == :female
|
199
201
|
end
|
200
202
|
|
201
203
|
def to_s
|
202
|
-
[@prefix, @first, @middle, @last].compact.join(' ') + (@suffix ? ", #{@suffix}" :
|
204
|
+
[@prefix, @first, @middle, @last].compact.join(' ') + (@suffix ? ", #{@suffix}" : '')
|
203
205
|
end
|
204
206
|
|
205
207
|
def to_name
|
@@ -235,12 +237,12 @@ module Nameable
|
|
235
237
|
end
|
236
238
|
|
237
239
|
def to_hash
|
238
|
-
|
239
|
-
:
|
240
|
-
:
|
241
|
-
:
|
242
|
-
:
|
243
|
-
:
|
240
|
+
{
|
241
|
+
prefix: @prefix,
|
242
|
+
first: @first,
|
243
|
+
middle: @middle,
|
244
|
+
last: @last,
|
245
|
+
suffix: @suffix
|
244
246
|
}
|
245
247
|
end
|
246
248
|
end
|
@@ -1,30 +1,35 @@
|
|
1
1
|
module Nameable
|
2
2
|
class Latin
|
3
|
-
##
|
4
3
|
# Regex's to match the detritus that people add to their names
|
5
4
|
module Patterns
|
6
5
|
PREFIX = {
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
6
|
+
'Capt.' => /^\(*(capt\.*|captain)\)*$/i,
|
7
|
+
'Dame' => /^\(*(dame)\)*$/i,
|
8
|
+
'Dr.' => /^\(*(dr\.*|doctor)\)*$/i,
|
9
|
+
'Fr.' => /^\(*(fr\.*|friar|father)\)*$/i,
|
10
|
+
'Hon.' => /^\(*(hon\.*|honorable)\)*$/i,
|
11
|
+
'Imam' => /^\(*(imam)\)*$/i,
|
12
|
+
'Ofc.' => /^\(*(ofc\.*|officer)\)*$/i,
|
13
|
+
'Mr.' => /^\(*(mr\.*|mister)\)*$/i,
|
14
|
+
'Mrs.' => /^\(*(mrs\.*|misses)\)*$/i,
|
15
|
+
'Ms.' => /^\(*(ms\.*|miss)\)*$/i,
|
16
|
+
'Rev.' => /^\(*(rev\.*|reverend)\)*$/i,
|
17
|
+
'Master' => /^\(*(master)\)*$/i,
|
18
|
+
'Rabbi' => /^\(*(rabbi)\)*$/i,
|
19
|
+
'Sir' => /^\(*(sir)\)*$/i
|
20
|
+
}.freeze
|
16
21
|
|
17
22
|
SUFFIX = {
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
}
|
23
|
+
'Sr.' => /^\(*(sr\.?|senior)\)*$/i,
|
24
|
+
'Jr.' => /^\(*(jr\.?|junior)\)*$/i,
|
25
|
+
'Esq.' => /^\(*(esq\.?|esquire)\)*$/i,
|
26
|
+
'Ph.D.' => /^\(*(p\.?h\.?d\.?)\)*$/i
|
27
|
+
}.freeze
|
23
28
|
|
24
29
|
SUFFIX_GENERATIONAL_ROMAN = /^\(*[IVX.]+\)*$/i
|
25
30
|
SUFFIX_ACADEMIC = /^(APR|RPh|MD|MA|DMD|DDS|PharmD|EngD|DPhil|JD|DD|DO|BA|BS|BSc|BE|BFA|MA|MS|MSc|MFA|MLA|MBA)$/i
|
26
|
-
SUFFIX_PROFESSIONAL = /^(PE|CSA|CPA|CPL|CME|CEng|OFM|CSV
|
27
|
-
SUFFIX_ABBREVIATION = /^[A-Z
|
31
|
+
SUFFIX_PROFESSIONAL = /^(PE|CSA|CPA|CPL|CME|CEng|OFM|CSV)$/i
|
32
|
+
SUFFIX_ABBREVIATION = /^[A-Z]\.?[A-Z]\.?[A-Z]?\.?$/ # 2-3 characters, possibly separated with '.'
|
28
33
|
|
29
34
|
# http://www.onlineaspect.com/2009/08/17/splitting-names/
|
30
35
|
LAST_NAME_PRE_DANGLERS = /^(mc|vere|von|van|da|de|del|della|di|da|pietro|vanden|du|st|la|ter|ten)$/i
|
data/lib/nameable/version.rb
CHANGED
data/nameable.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'nameable/version'
|
@@ -12,20 +13,21 @@ Gem::Specification.new do |spec|
|
|
12
13
|
spec.description = 'A library that provides parsing and output of person names, as well as Gender & Ethnicity matching.'
|
13
14
|
spec.homepage = 'https://github.com/chorn/nameable'
|
14
15
|
spec.license = 'MIT'
|
15
|
-
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
spec.required_ruby_version = '>= 1.9'
|
20
21
|
|
21
|
-
signing_key = File.expand_path
|
22
|
+
signing_key = File.expand_path '~/.certs/chorn@chorn.com-rubygems.key'
|
22
23
|
if File.file?(signing_key)
|
23
24
|
spec.signing_key = signing_key
|
24
25
|
spec.cert_chain = ['certs/chorn.pem']
|
25
26
|
end
|
26
27
|
|
27
|
-
spec.add_development_dependency 'bundler'
|
28
|
-
spec.add_development_dependency '
|
29
|
-
spec.add_development_dependency '
|
30
|
-
spec.add_development_dependency '
|
28
|
+
spec.add_development_dependency 'bundler'
|
29
|
+
spec.add_development_dependency 'rake'
|
30
|
+
spec.add_development_dependency 'rspec', '~> 3.6'
|
31
|
+
spec.add_development_dependency 'simplecov'
|
32
|
+
spec.add_development_dependency 'codeclimate-test-reporter'
|
31
33
|
end
|
data/spec/nameable/latin_spec.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
shared_examples :generalized_parsing do |input, outputs|
|
4
|
+
let(:nameable) { Nameable::Latin.new.parse(input) }
|
5
|
+
|
6
|
+
it "#parse ``#{input}''" do
|
7
|
+
expect(nameable.prefix).to eq outputs[0]
|
8
|
+
expect(nameable.first).to eq outputs[1]
|
9
|
+
expect(nameable.middle).to eq outputs[2]
|
10
|
+
expect(nameable.last).to eq outputs[3]
|
11
|
+
expect(nameable.suffix).to eq outputs[4]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
3
15
|
describe Nameable::Latin do
|
4
16
|
describe '.new' do
|
5
17
|
it "doesn't raise" do
|
@@ -7,318 +19,166 @@ describe Nameable::Latin do
|
|
7
19
|
end
|
8
20
|
end
|
9
21
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
expect(n.prefix).to be_nil
|
14
|
-
expect(n.first).to eq('Chris')
|
15
|
-
expect(n.middle).to be_nil
|
16
|
-
expect(n.last).to be_nil
|
17
|
-
expect(n.suffix).to be_nil
|
22
|
+
describe '#parse' do
|
23
|
+
context 'with a single word name' do
|
24
|
+
it_behaves_like :generalized_parsing, 'Chris', [nil, 'Chris', nil, nil, nil]
|
18
25
|
end
|
19
|
-
end
|
20
26
|
|
21
|
-
|
22
|
-
|
23
|
-
it '.prefix' do
|
24
|
-
expect(n.prefix).to be_nil
|
27
|
+
context 'with a simple first and last name' do
|
28
|
+
it_behaves_like :generalized_parsing, 'Chris Horn', [nil, 'Chris', nil, 'Horn', nil]
|
25
29
|
end
|
26
|
-
it '.first' do
|
27
|
-
expect(n.first).to eq('Chris')
|
28
|
-
end
|
29
|
-
it '.middle' do
|
30
|
-
expect(n.middle).to be_nil
|
31
|
-
end
|
32
|
-
it '.last' do
|
33
|
-
expect(n.last).to eq('Horn')
|
34
|
-
end
|
35
|
-
it '.suffix' do
|
36
|
-
expect(n.suffix).to be_nil
|
37
|
-
end
|
38
|
-
end
|
39
30
|
|
40
|
-
|
41
|
-
|
42
|
-
it '.first' do
|
43
|
-
expect(n.first).to eq('Chris')
|
31
|
+
context 'with an uppercase first and last name' do
|
32
|
+
it_behaves_like :generalized_parsing, 'CHRIS HORN', [nil, 'Chris', nil, 'Horn', nil]
|
44
33
|
end
|
45
|
-
it '.last' do
|
46
|
-
expect(n.last).to eq('Horn')
|
47
|
-
end
|
48
|
-
end
|
49
34
|
|
50
|
-
|
51
|
-
|
52
|
-
it '.first' do
|
53
|
-
expect(n.first).to eq('Chris')
|
54
|
-
end
|
55
|
-
it '.last' do
|
56
|
-
expect(n.last).to eq('Horn')
|
35
|
+
context 'with a lowercase first and last name' do
|
36
|
+
it_behaves_like :generalized_parsing, 'chris horn', [nil, 'Chris', nil, 'Horn', nil]
|
57
37
|
end
|
58
|
-
end
|
59
38
|
|
60
|
-
|
61
|
-
|
62
|
-
it '.first' do
|
63
|
-
expect(n.first).to eq('DeChris')
|
39
|
+
context 'with a mixed case first and last name' do
|
40
|
+
it_behaves_like :generalized_parsing, 'DeChris Horn', [nil, 'DeChris', nil, 'Horn', nil]
|
64
41
|
end
|
65
|
-
end
|
66
42
|
|
67
|
-
|
68
|
-
|
69
|
-
it '.prefix' do
|
70
|
-
expect(n.prefix).to be_nil
|
43
|
+
context 'with last name, first name' do
|
44
|
+
it_behaves_like :generalized_parsing, 'Horn, Chris', [nil, 'Chris', nil, 'Horn', nil]
|
71
45
|
end
|
72
|
-
it '.first' do
|
73
|
-
expect(n.first).to eq('Chris')
|
74
|
-
end
|
75
|
-
it '.middle' do
|
76
|
-
expect(n.middle).to be_nil
|
77
|
-
end
|
78
|
-
it '.last' do
|
79
|
-
expect(n.last).to eq('Horn')
|
80
|
-
end
|
81
|
-
it '.suffix' do
|
82
|
-
expect(n.suffix).to be_nil
|
83
|
-
end
|
84
|
-
end
|
85
46
|
|
86
|
-
|
87
|
-
|
88
|
-
it '.prefix' do
|
89
|
-
expect(n.prefix).to be_nil
|
47
|
+
context 'with last name, first name,' do
|
48
|
+
it_behaves_like :generalized_parsing, 'Horn, Chris,', [nil, 'Chris', nil, 'Horn', nil]
|
90
49
|
end
|
91
|
-
it '.first' do
|
92
|
-
expect(n.first).to eq('Chris')
|
93
|
-
end
|
94
|
-
it '.middle' do
|
95
|
-
expect(n.middle).to be_nil
|
96
|
-
end
|
97
|
-
it '.last' do
|
98
|
-
expect(n.last).to eq('Horn')
|
99
|
-
end
|
100
|
-
it '.suffix' do
|
101
|
-
expect(n.suffix).to be_nil
|
102
|
-
end
|
103
|
-
end
|
104
50
|
|
105
|
-
|
106
|
-
|
107
|
-
it '.prefix' do
|
108
|
-
expect(n.prefix).to be_nil
|
51
|
+
context 'with first middle last name' do
|
52
|
+
it_behaves_like :generalized_parsing, 'Chris Derp Horn', [nil, 'Chris', 'Derp', 'Horn', nil]
|
109
53
|
end
|
110
|
-
it '.first' do
|
111
|
-
expect(n.first).to eq('Chris')
|
112
|
-
end
|
113
|
-
it '.middle' do
|
114
|
-
expect(n.middle).to eq('Derp')
|
115
|
-
end
|
116
|
-
it '.last' do
|
117
|
-
expect(n.last).to eq('Horn')
|
118
|
-
end
|
119
|
-
it '.suffix' do
|
120
|
-
expect(n.suffix).to be_nil
|
121
|
-
end
|
122
|
-
end
|
123
54
|
|
124
|
-
|
125
|
-
|
126
|
-
it '.prefix' do
|
127
|
-
expect(n.prefix).to eq('Sir')
|
55
|
+
context 'with all uppercase first middle last name' do
|
56
|
+
it_behaves_like :generalized_parsing, 'CHRIS DERP HORN', [nil, 'Chris', 'Derp', 'Horn', nil]
|
128
57
|
end
|
129
|
-
it '.first' do
|
130
|
-
expect(n.first).to eq('Chris')
|
131
|
-
end
|
132
|
-
it '.middle' do
|
133
|
-
expect(n.middle).to be_nil
|
134
|
-
end
|
135
|
-
it '.last' do
|
136
|
-
expect(n.last).to eq('Horn')
|
137
|
-
end
|
138
|
-
it '.suffix' do
|
139
|
-
expect(n.suffix).to be_nil
|
140
|
-
end
|
141
|
-
end
|
142
58
|
|
143
|
-
|
144
|
-
|
145
|
-
it '.prefix' do
|
146
|
-
expect(n.prefix).to be_nil
|
147
|
-
end
|
148
|
-
it '.first' do
|
149
|
-
expect(n.first).to eq('Chris')
|
150
|
-
end
|
151
|
-
it '.middle' do
|
152
|
-
expect(n.middle).to be_nil
|
153
|
-
end
|
154
|
-
it '.last' do
|
155
|
-
expect(n.last).to eq('Horn')
|
156
|
-
end
|
157
|
-
it '.suffix' do
|
158
|
-
expect(n.suffix).to eq('Ph.D.')
|
59
|
+
context 'with all lowercase first middle last name' do
|
60
|
+
it_behaves_like :generalized_parsing, 'chris derp horn', [nil, 'Chris', 'Derp', 'Horn', nil]
|
159
61
|
end
|
160
|
-
end
|
161
62
|
|
162
|
-
|
163
|
-
|
164
|
-
it prefix do
|
165
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq('Mr.')
|
166
|
-
end
|
63
|
+
context 'with all mixed case first middle last name' do
|
64
|
+
it_behaves_like :generalized_parsing, 'DeChris LeDerp ZeHorn', [nil, 'DeChris', 'LeDerp', 'ZeHorn', nil]
|
167
65
|
end
|
168
66
|
|
169
|
-
|
170
|
-
|
171
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq('Mrs.')
|
172
|
-
end
|
67
|
+
context 'with first last suffix' do
|
68
|
+
it_behaves_like :generalized_parsing, 'Chris Horn DRP', [nil, 'Chris', nil, 'Horn', 'DRP']
|
173
69
|
end
|
174
70
|
|
175
|
-
|
176
|
-
|
177
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq('Ms.')
|
178
|
-
end
|
71
|
+
context 'with prefix first last suffix' do
|
72
|
+
it_behaves_like :generalized_parsing, 'Mr. Chris Horn DRP', ['Mr.', 'Chris', nil, 'Horn', 'DRP']
|
179
73
|
end
|
180
74
|
|
181
|
-
%w
|
182
|
-
|
183
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq('Dr.')
|
184
|
-
end
|
75
|
+
%w[Dame Rabbi Imam Master Sir].each do |prefix|
|
76
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", [prefix, 'Chris', nil, 'Horn', nil]
|
185
77
|
end
|
186
78
|
|
187
|
-
|
188
|
-
|
189
|
-
|
79
|
+
context 'with a normalizing prefix' do
|
80
|
+
%w[Mr Mr. Mister].each do |prefix|
|
81
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Mr.', 'Chris', nil, 'Horn', nil]
|
190
82
|
end
|
191
|
-
end
|
192
83
|
|
193
|
-
|
194
|
-
|
195
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq('Fr.')
|
84
|
+
%w[Mrs Mrs. Misses].each do |prefix|
|
85
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Mrs.', 'Chris', nil, 'Horn', nil]
|
196
86
|
end
|
197
|
-
end
|
198
87
|
|
199
|
-
|
200
|
-
|
201
|
-
expect(Nameable::Latin.new.parse("#{prefix} Chris Horn").prefix).to eq(prefix)
|
88
|
+
%w[Ms Ms. Miss].each do |prefix|
|
89
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Ms.', 'Chris', nil, 'Horn', nil]
|
202
90
|
end
|
203
|
-
end
|
204
|
-
|
205
|
-
end
|
206
91
|
|
207
|
-
|
208
|
-
|
209
|
-
it suffix do
|
210
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq('Sr.')
|
92
|
+
%w[Dr Dr. Doctor].each do |prefix|
|
93
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Dr.', 'Chris', nil, 'Horn', nil]
|
211
94
|
end
|
212
|
-
end
|
213
95
|
|
214
|
-
|
215
|
-
|
216
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq('Jr.')
|
96
|
+
%w[Rev Rev. Reverend].each do |prefix|
|
97
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Rev.', 'Chris', nil, 'Horn', nil]
|
217
98
|
end
|
218
|
-
end
|
219
99
|
|
220
|
-
|
221
|
-
|
222
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq('Esq.')
|
100
|
+
%w[Fr Fr. Friar Father].each do |prefix|
|
101
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Fr.', 'Chris', nil, 'Horn', nil]
|
223
102
|
end
|
224
|
-
end
|
225
103
|
|
226
|
-
|
227
|
-
|
228
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq('Ph.D.')
|
104
|
+
%w[Hon Hon. Honorable].each do |prefix|
|
105
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Hon.', 'Chris', nil, 'Horn', nil]
|
229
106
|
end
|
230
|
-
end
|
231
107
|
|
232
|
-
|
233
|
-
|
234
|
-
context('suffix patterns (generational)') do
|
235
|
-
%w{ii III iv V VI ix Xiii}.each do |suffix|
|
236
|
-
it suffix do
|
237
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq(suffix.upcase)
|
108
|
+
%w[Capt Capt. Captain].each do |prefix|
|
109
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Capt.', 'Chris', nil, 'Horn', nil]
|
238
110
|
end
|
239
|
-
end
|
240
|
-
end
|
241
111
|
|
242
|
-
|
243
|
-
|
244
|
-
it suffix do
|
245
|
-
expect(Nameable::Latin.new.parse("Chris Horn #{suffix}").suffix).to eq(suffix.upcase)
|
112
|
+
%w[Ofc Ofc. Officer].each do |prefix|
|
113
|
+
it_behaves_like :generalized_parsing, "#{prefix} Chris Horn", ['Ofc.', 'Chris', nil, 'Horn', nil]
|
246
114
|
end
|
247
115
|
end
|
248
|
-
end
|
249
|
-
|
250
|
-
context('suffix patterns (generic uppercase)') do
|
251
|
-
it 'generic DERP suffix' do
|
252
|
-
expect(Nameable::Latin.new.parse("Chris Horn DERP").suffix).to eq('DERP')
|
253
|
-
end
|
254
|
-
|
255
|
-
it 'no generic DERP suffix when all uppercase name' do
|
256
|
-
expect(Nameable::Latin.new.parse("CHRIS HORN DERP").suffix).to be_nil
|
257
|
-
end
|
258
|
-
end
|
259
116
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
expect(Nameable::Latin.new.parse("Chris #{prefix} Last").last).to eq("#{prefix.downcase.capitalize} Last")
|
117
|
+
context 'with a normalizing suffix' do
|
118
|
+
%w[Sr Sr. Senior].each do |suffix|
|
119
|
+
it_behaves_like :generalized_parsing, "Chris Horn #{suffix}", [nil, 'Chris', nil, 'Horn', 'Sr.']
|
264
120
|
end
|
265
|
-
end
|
266
|
-
end
|
267
121
|
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
end
|
122
|
+
%w[Jr Jr. Junior].each do |suffix|
|
123
|
+
it_behaves_like :generalized_parsing, "Chris Horn #{suffix}", [nil, 'Chris', nil, 'Horn', 'Jr.']
|
124
|
+
end
|
272
125
|
|
273
|
-
|
274
|
-
|
275
|
-
|
126
|
+
%w[Esq Esq. Esquire].each do |suffix|
|
127
|
+
it_behaves_like :generalized_parsing, "Chris Horn #{suffix}", [nil, 'Chris', nil, 'Horn', 'Esq.']
|
128
|
+
end
|
276
129
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
end
|
130
|
+
%w[PHD PhD Ph.D Ph.D. P.H.D.].each do |suffix|
|
131
|
+
it_behaves_like :generalized_parsing, "Chris Horn #{suffix}", [nil, 'Chris', nil, 'Horn', 'Ph.D.']
|
132
|
+
end
|
281
133
|
|
282
|
-
|
283
|
-
|
284
|
-
|
134
|
+
%w[
|
135
|
+
ii III iv V VI ix Xiii APR RPh MD MA DMD DDS PharmD EngD DPhil
|
136
|
+
JD DD DO BA BS BSc BE BFA MA MS MSc MFA MLA MBA PE CSA CPA CPL CME CEng OFM CSV
|
137
|
+
].each do |suffix|
|
138
|
+
it_behaves_like :generalized_parsing, "Chris Horn #{suffix}", [nil, 'Chris', nil, 'Horn', suffix.upcase]
|
139
|
+
end
|
285
140
|
end
|
286
141
|
|
287
|
-
|
288
|
-
|
142
|
+
context 'with a multi word last name' do
|
143
|
+
%w[mc vere von van da de del della di da pietro vanden du st la ter ten].each do |prefix|
|
144
|
+
it_behaves_like :generalized_parsing, "Chris #{prefix} Horn", [nil, 'Chris', nil, "#{prefix.downcase.capitalize} Horn", nil]
|
145
|
+
end
|
289
146
|
end
|
290
147
|
|
291
|
-
|
292
|
-
|
148
|
+
context "with an o'last-name" do
|
149
|
+
["O'Horn", 'O`Horn', "O' Horn"].each do |last|
|
150
|
+
it_behaves_like :generalized_parsing, "Chris #{last}", [nil, 'Chris', nil, "O'Horn", nil]
|
151
|
+
end
|
293
152
|
end
|
294
153
|
|
295
|
-
|
296
|
-
|
154
|
+
context 'with a hyphenated last name' do
|
155
|
+
['Horn - Derp', 'Horn-Derp', 'Horn--Derp', 'Horn -- Derp'].each do |last|
|
156
|
+
it_behaves_like :generalized_parsing, "Chris #{last}", [nil, 'Chris', nil, 'Horn-Derp', nil]
|
157
|
+
end
|
297
158
|
end
|
298
159
|
end
|
299
160
|
|
300
|
-
context
|
301
|
-
it
|
302
|
-
expect(Nameable::Latin.new.parse(
|
161
|
+
context 'gender' do
|
162
|
+
it 'Chris is more likely to be male' do
|
163
|
+
expect(Nameable::Latin.new.parse('Chris Horn').male?).to be true
|
303
164
|
end
|
304
165
|
|
305
|
-
it
|
306
|
-
expect(Nameable::Latin.new.parse(
|
166
|
+
it 'Janine is more likely to be female' do
|
167
|
+
expect(Nameable::Latin.new.parse('Janine Horn').female?).to be true
|
307
168
|
end
|
308
169
|
|
309
|
-
it
|
310
|
-
expect(Nameable::Latin.new.parse(
|
170
|
+
it 'Derp has :unknown gender' do
|
171
|
+
expect(Nameable::Latin.new.parse('Derp Horn').gender).to eq(:unknown)
|
311
172
|
end
|
312
173
|
end
|
313
174
|
|
314
|
-
context
|
315
|
-
it
|
316
|
-
expect(Nameable::Latin.new.parse(
|
175
|
+
context 'ethnicity' do
|
176
|
+
it 'Horn has a hash of ethnicity results' do
|
177
|
+
expect(Nameable::Latin.new.parse('Chris Horn').ethnicity).to be_a Hash
|
317
178
|
end
|
318
179
|
|
319
180
|
it "Horn's census :percent_white > 80% " do
|
320
|
-
expect(Nameable::Latin.new.parse(
|
181
|
+
expect(Nameable::Latin.new.parse('Chris Horn').ethnicity[:percent_white]).to be >= 80
|
321
182
|
end
|
322
183
|
end
|
323
|
-
|
324
184
|
end
|