nameable 1.1.3 → 1.1.4
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.
- 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
|