diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..953f5c6 --- /dev/null +++ b/Gemfile @@ -0,0 +1,4 @@ + +source "https://rubygems.org" + +gemspec diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..b4d83f6 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,42 @@ +PATH + remote: . + specs: + people (0.3.0) + +GEM + remote: https://rubygems.org/ + specs: + awesome_print (1.8.0) + coderay (1.1.2) + diff-lcs (1.3) + method_source (0.9.0) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + rake (12.3.1) + rspec (3.8.0) + rspec-core (~> 3.8.0) + rspec-expectations (~> 3.8.0) + rspec-mocks (~> 3.8.0) + rspec-core (3.8.0) + rspec-support (~> 3.8.0) + rspec-expectations (3.8.1) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-mocks (3.8.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.8.0) + rspec-support (3.8.0) + +PLATFORMS + ruby + +DEPENDENCIES + awesome_print + people! + pry + rake + rspec + +BUNDLED WITH + 1.16.3 diff --git a/Rakefile b/Rakefile index 3898919..1cf231f 100644 --- a/Rakefile +++ b/Rakefile @@ -1,56 +1,19 @@ +#!/usr/bin/env rake +require "bundler/gem_tasks" + require 'rubygems' require 'rake' -begin - require 'jeweler' - Jeweler::Tasks.new do |gem| - gem.name = "people" - gem.summary = %Q{Matts Name Parser} - gem.email = "mericson@ericson.net" - gem.homepage = "http://github.com/mericson/people" - gem.authors = ["Matthew Ericson"] - - # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings - end -rescue LoadError - puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" -end - -require 'rake/testtask' -Rake::TestTask.new(:test) do |test| - test.libs << 'lib' << 'test' - test.pattern = 'test/**/*_test.rb' - test.verbose = true -end +require 'rspec/core/rake_task' -begin - require 'rcov/rcovtask' - Rcov::RcovTask.new do |test| - test.libs << 'test' - test.pattern = 'test/**/*_test.rb' - test.verbose = true - end -rescue LoadError - task :rcov do - abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov" - end +desc "Run RSpec" +RSpec::Core::RakeTask.new do |t| + t.verbose = false end - -task :default => :test - -require 'rake/rdoctask' -Rake::RDocTask.new do |rdoc| - if File.exist?('VERSION.yml') - config = YAML.load(File.read('VERSION.yml')) - version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}" - else - version = "" - end - - rdoc.rdoc_dir = 'rdoc' - rdoc.title = "people #{version}" - rdoc.rdoc_files.include('README*') - rdoc.rdoc_files.include('lib/**/*.rb') +desc "Run specs for all test cases" +task :spec_all do + system "rake spec" end +task :default => :spec diff --git a/VERSION.yml b/VERSION.yml deleted file mode 100644 index f9fa7f6..0000000 --- a/VERSION.yml +++ /dev/null @@ -1,4 +0,0 @@ ---- -:major: 0 -:minor: 2 -:patch: 0 diff --git a/lib/people.rb b/lib/people.rb index f11783e..d716e98 100644 --- a/lib/people.rb +++ b/lib/people.rb @@ -1,6 +1,11 @@ +require 'people/version' unless defined?(People::VERSION) + module People # Class to parse names into their components like first, middle, last, etc. + + # How it works: https://xkcd.com/208/ + class NameParser attr_reader :seen, :parsed @@ -8,8 +13,7 @@ class NameParser # Creates a name parsing object def initialize( opts={} ) - @name_chars = "A-Za-z0-9\\-\\'" - @nc = @name_chars + @nc = @name_chars = 'A-Za-z\\-' @opts = { :strip_mr => true, @@ -18,8 +22,6 @@ def initialize( opts={} ) :couples => false }.merge! opts - ## constants - @titles = [ 'Mr\.? and Mrs\.? ', 'Mrs\.? ', 'M/s\.? ', @@ -103,7 +105,6 @@ def initialize( opts={} ) 'Ald(\.|erman)? ' ]; - @suffixes = [ 'Jn?r\.?,? Esq\.?', 'Sn?r\.?,? Esq\.?', @@ -124,6 +125,8 @@ def initialize( opts={} ) 'Ph\.?d\.?', 'C\.?P\.?A\.?', + '1st', '2nd', '3rd', '\d+th', # numeric instead of roman + 'XI{1,3}', # 11th, 12th, 13th 'X', # 10th 'IV', # 4th @@ -135,25 +138,16 @@ def initialize( opts={} ) 'D.?M\.?D\.?' # M.D. ]; - @last_name_p = "((;.+)|(((Mc|Mac|Des|Dell[ae]|Del|De La|De Los|Da|Di|Du|La|Le|Lo|St\.|Den|Von|Van|Von Der|Van De[nr]) )?([#{@nc}]+)))"; - @mult_name_p = "((;.+)|(((Mc|Mac|Des|Dell[ae]|Del|De La|De Los|Da|Di|Du|La|Le|Lo|St\.|Den|Von|Van|Von Der|Van De[nr]) )?([#{@nc} ]+)))"; - @seen = 0 @parsed = 0; - end def parse( name ) - @seen += 1 - clean = '' - out = Hash.new( "" ) - + out = Hash.new('') out[:orig] = name.dup - name = name.dup - name = clean( name ) # strip trailing suffices @@ -163,27 +157,21 @@ def parse( name ) name.gsub!( sfx_p, "\\1 \\2" ) end - name.gsub!( /Mr\.? \& Mrs\.?/i, "Mr. and Mrs." ) + name.gsub!( /Mr\.? \& Mrs\.?/i, 'Mr. and Mrs.' ) # Flip last and first if contain comma - name.gsub!( /;/, "" ) - name.gsub!( /(.+),(.+)/, "\\2 ;\\1" ) - - - name.gsub!( /,/, "" ) + name.gsub!( /;/, '' ) + name.gsub!( /(.+),(.+)/, "\\2 \\1" ) + name.gsub!( /,/, '' ) name.strip! if @opts[:couples] - name.gsub!( / +and +/i, " \& " ) + name.gsub!( /\s+and\s+/i, ' & ' ) end - - if @opts[:couples] && name.match( /\&/ ) - names = name.split( / *& */ ) - a = names[0] - b = names[1] + a, b, _rest = name.split( / *& */ ) out[:title2] = get_title( b ); out[:suffix2] = get_suffix( b ); @@ -198,26 +186,26 @@ def parse( name ) out[:middle2] = parts[3] out[:last] = parts[4] + out[:title] = get_title( a ); out[:suffix] = get_suffix( a ); a.strip! - a += " " + a += ' ' parts = get_name_parts( a, true ) out[:parsed] = parts[0] out[:parse_type] = parts[1] out[:first] = parts[2] - out[:middle] = parts[3] + out[:middle] = parts[4] if out[:parsed] && out[:parsed2] out[:multiple] = true else - out = Hash.new( "" ) + out = Hash.new( '' ) end - else out[:title] = get_title( name ); @@ -230,7 +218,6 @@ def parse( name ) out[:first] = parts[2] out[:middle] = parts[3] out[:last] = parts[4] - end @@ -249,55 +236,43 @@ def parse( name ) end - if out[:parsed] - @parsed += 1 - end + @parsed += 1 if out[:parsed] out[:clean] = name - - - - return { - :title => "", - :first => "", - :middle => "", - :last => "", - :suffix => "", + :title => '', + :first => '', + :middle => '', + :last => '', + :suffix => '', - :title2 => "", - :first2 => "", - :middle2 => "", - :suffix2 => "", + :title2 => '', + :first2 => '', + :middle2 => '', + :suffix2 => '', - :clean => "", + :clean => '', :parsed => false, - :parse_type => "", - - :parsed => false, - :parse_type => "", + :parse_type => '', :parsed2 => false, - :parse_type2 => "", + :parse_type2 => '', :multiple => false }.merge( out ) - end + private def clean( s ) + # IMPORTANT: we DO NOT want to remove "illegal" characters here, because it would be stripping off valid UTF-8 characters - # remove illegal characters - s.gsub!( /[^A-Za-z0-9\-\'\.&\/ \,]/, "" ) - # remove repeating spaces - s.gsub!( / +/, " " ) - s.gsub!( /\s+/, " " ) + # squish repeating spaces + s.gsub!( /\s+/, ' ' ) s.strip! s - end def get_title( name ) @@ -305,15 +280,12 @@ def get_title( name ) @titles.each do |title| title_p = Regexp.new( "^(#{title})(.+)", true ) if m = name.match( title_p ) - title = m[1] name.replace( m[-1].strip ) - return title + return title.strip end - end - - return "" + return '' end def get_suffix( name ) @@ -322,135 +294,71 @@ def get_suffix( name ) sfx_p = Regexp.new( "(.+) (#{sfx})$", true ) if name.match( sfx_p ) name.replace $1.strip - suffix = $2 - return $2 + return $2.strip # return the suffix end - end - - return "" + return '' end def get_name_parts( name, no_last_name = false ) - - first = "" - middle = "" - last = "" - - if no_last_name - last_name_p = '' - mult_name_p = '' - else - last_name_p = @last_name_p - mult_name_p = @mult_name_p - end + first = '' + middle = '' + last = '' parsed = false - # M ERICSON - if name.match( /^([A-Za-z])\.? (#{last_name_p})$/i ) - first = $1; - middle = ''; - last = $2; + # FIRST strip-off the last name + if /(?((;.+)|(((Mc|Mac|Des|Dell[ae]|Del|De La|De Los|Da|Di|Du|La|Le|Lo|St\.|Den|Von|Van|V[ao]n De[nr]) )?([\p{Word}\-\.\']+))))$/i =~ name + last = ln + name.slice!(ln) + name.strip! parsed = true - parse_type = 1; + parse_type = 0 - # M E ERICSON - elsif name.match( /^([A-Za-z])\.? ([A-Za-z])\.? (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; - parsed = true - parse_type = 2; + # THEN analyze the remaining names + if /^(?[\p{Word}\-\.]+)[\s+\.](?([\p{Word}\-\.]+\s*)+)\s*$/ =~ name + first = fn + middle = mn - # M.E. ERICSON - elsif name.match( /^([A-Za-z])\.([A-Za-z])\. (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; - parsed = true - parse_type = 3; + elsif /^(?[\p{Word}\-\.]+)$/ =~ name + first = fn - # M E E ERICSON - elsif name.match( /^([A-Za-z])\.? ([A-Za-z])\.? ([A-Za-z])\.? (#{last_name_p})$/i ) - first = $1; - middle = $2 + ' ' + $3; - last = $4; - parsed = true - parse_type = 4; - - # M EDWARD ERICSON - elsif name.match( /^([A-Za-z])\.? ([#{@nc}]+) (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; - parsed = true - parse_type = 5; - - # MATTHEW E ERICSON - elsif name.match( /^([#{@nc}]+) ([A-Za-z])\.? (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; - parsed = true - parse_type = 6; - - # MATTHEW E E ERICSON - elsif name.match( /^([#{@nc}]+) ([A-Za-z])\.? ([A-Za-z])\.? (#{last_name_p})$/i ) - first = $1; - middle = $2 + ' ' + $3; - last = $4; - parsed = true - parse_type = 7; - - # MATTHEW E.E. ERICSON - elsif name.match( /^([#{@nc}]+) ([A-Za-z]\.[A-Za-z]\.) (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; - parsed = true - parse_type = 8; + else +# binding.pry + end - # MATTHEW ERICSON - elsif name.match( /^([#{@nc}]+) (#{last_name_p})$/i ) - first = $1; - middle = ''; - last = $2; + elsif /^(?[\p{Word}\-\.]+)[\s+\.](?([\p{Word}\-\.]+\s*)+)\s+(?[\p{Word}\-\.]+)$/ =~ name + first = fn + middle = mn + last = ln parsed = true - parse_type = 9; + parse_type = 97 - # MATTHEW EDWARD ERICSON - elsif name.match( /^([#{@nc}]+) ([#{@nc}]+) (#{last_name_p})$/i ) - first = $1; - middle = $2; - last = $3; + # as a fall-back -- try first last + elsif /^(?[\p{Word}\-\.]+)[\s+\.](?[\p{Word}\-\.]+)\s*$/ =~ name + first = fn + last = ln parsed = true - parse_type = 10; + parse_type = 98 - # MATTHEW E. SHEIE ERICSON - elsif name.match( /^([#{@nc}]+) ([A-Za-z])\.? ($multNamePat)$/i ) - first = $1; - middle = $2; - last = $3; + elsif /^(?[\p{Word}\-\.]+)\s*$/ =~ name # used for Jack and Jill; parsing the first name only + first = fn parsed = true - parse_type = 11; + parse_type = 99 end - last.gsub!( /;/, "" ) + last.gsub!( /;/, '' ) return [ parsed, parse_type, first, middle, last ]; - end - - def proper ( name ) fixed = name.downcase # Now uppercase first letter of every word. By checking on word boundaries, # we will account for apostrophes (D'Angelo) and hyphenated names - fixed.gsub!( /\b(\w+)/ ) { |m| m.match( /^[ixv]$+/i ) ? m.upcase : m.capitalize } + fixed.gsub!( /\b(\p{Word}+)/ ) { |m| m.match( /^[ixv]+$/i ) ? m.upcase : m.capitalize } # Name case Macs and Mcs # Exclude names with 1-2 letters after prefix like Mack, Macky, Mace @@ -482,18 +390,15 @@ def proper ( name ) fixed.gsub!( /\b(Mc)([a-z]+)/i ) do |m| $1 + $2.capitalize end - end # Exceptions (only 'Mac' name ending in 'o' ?) fixed.gsub!( /Macmurdo/i, 'MacMurdo' ) return fixed - end end - end diff --git a/lib/people/version.rb b/lib/people/version.rb new file mode 100644 index 0000000..733527c --- /dev/null +++ b/lib/people/version.rb @@ -0,0 +1,3 @@ +module People + VERSION = '0.3.0' +end \ No newline at end of file diff --git a/people.gemspec b/people.gemspec index ac834c1..a0e0ef5 100644 --- a/people.gemspec +++ b/people.gemspec @@ -2,48 +2,31 @@ # DO NOT EDIT THIS FILE # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec` # -*- encoding: utf-8 -*- +require File.expand_path('../lib/people/version', __FILE__) Gem::Specification.new do |s| - s.name = %q{people} - s.version = "0.2.0" + s.name ='people' + s.version = People::VERSION - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = ["Matthew Ericson"] - s.date = %q{2010-10-29} - s.email = %q{mericson@ericson.net} + s.authors = ['Matthew Ericson'] + s.date = '2010-10-29' + s.email = 'mericson@ericson.net' s.extra_rdoc_files = [ "LICENSE", "README.rdoc" ] - s.files = [ - ".document", - ".gitignore", - "LICENSE", - "README.rdoc", - "Rakefile", - "VERSION.yml", - "lib/people.rb", - "people.gemspec", - "test/people_test.rb", - "test/test_helper.rb" - ] - s.homepage = %q{http://github.com/mericson/people} + s.files = `git ls-files`.split($\) + s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) } + s.test_files = s.files.grep(%r{^(test|spec|features)/}) + + s.homepage = 'http://github.com/mericson/people' s.rdoc_options = ["--charset=UTF-8"] s.require_paths = ["lib"] s.rubygems_version = %q{1.3.7} - s.summary = %q{Matts Name Parser} - s.test_files = [ - "test/people_test.rb", - "test/test_helper.rb" - ] - - if s.respond_to? :specification_version then - current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION - s.specification_version = 3 + s.summary = 'Matts Name Parser' - if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - else - end - else - end + s.add_development_dependency 'pry' + s.add_development_dependency 'rake' + s.add_development_dependency 'rspec' + s.add_development_dependency 'awesome_print' end diff --git a/spec/people_spec.rb b/spec/people_spec.rb new file mode 100644 index 0000000..41463d9 --- /dev/null +++ b/spec/people_spec.rb @@ -0,0 +1,457 @@ +require 'spec_helper' + +module People + describe 'Parse standard name variations' do + before( :each ) do + @np = People::NameParser.new + end + + it 'should parse just first initial' do + name = @np.parse( 'M' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq '' + expect(name[:last]).to eq 'M' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first and last initial' do + name = @np.parse( 'M E' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:last]).to eq 'E' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first and last initial with periods' do + name = @np.parse( 'M. E.' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M.' + expect(name[:last]).to eq 'E.' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial, last name' do + name = @np.parse( 'M ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial, middle initial, last name' do + name = @np.parse( 'M E ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:middle]).to eq 'E' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial with period, middle initial with period, last name' do + name = @np.parse( "M.E. ERICSON" ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:middle]).to eq 'E.' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial, two middle initials, last name' do + name = @np.parse( 'M E E ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:middle]).to eq 'E E' + expect(name[:last]).to eq "Ericson" + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial, two middle initials with periods, last name' do + name = @np.parse( 'M. E. E. ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M.' + expect(name[:middle]).to eq 'E. E.' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first initial, middle name, last name' do + name = @np.parse( 'M EDWARD ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'M' + expect(name[:middle]).to eq 'Edward' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, middle initial, last name' do + name = @np.parse( 'MATTHEW E ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'E' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, two middle initials, last name' do + name = @np.parse( 'MATTHEW E E ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'E E' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, two middle initials with periods without space, last name' do + name = @np.parse( 'MATTHEW E.E. ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'E.E.' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, three middle initials with periods, last name' do + name = @np.parse( 'MATTHEW A. B. C. ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'A. B. C.' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, last name' do + name = @np.parse( 'MATTHEW ERICSON' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse reverse order last name, first name' do + name = @np.parse( 'ERICSON, MATTHEW' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse reverse order last name, first name middle initials' do + name = @np.parse( 'DUCK, DONALD R. S' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Donald' + expect(name[:last]).to eq 'Duck' + expect(name[:middle]).to eq 'R. S' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse compound first name, last name' do + name = @np.parse( 'MATTHEW-JOSEPH ERICSON-MILLER' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew-Joseph' + expect(name[:last]).to eq 'Ericson-Miller' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse first name, middle name, last name' do + name = @np.parse( "MATTHEW EDWARD ERICSON" ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'Edward' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 0 + end + + it 'parses first name, middle initial, middle name, last name' do + name = @np.parse( 'MATTHEW E. SHEIE ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'E. Sheie' + expect( name[:last]).to eq 'Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses first name, middle name, middle initial, last name' do + name = @np.parse( 'MATTHEW ERWIN S. ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'Erwin S.' + expect( name[:last]).to eq 'Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses two middle names' do + name = @np.parse( 'MATTHEW EDWARD RICHARD ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'Edward Richard' + expect( name[:last]).to eq 'Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses three middle names' do + name = @np.parse( "MATTHEW EDWARD RICHARD MARIA ERICSON" ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'Edward Richard Maria' + expect( name[:last]).to eq 'Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses two middle names and handles compound names' do + name = @np.parse( 'MATTHEW EDWARD RICHARD MARIA-ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'Edward Richard' + expect( name[:last]).to eq 'Maria-Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses one middle names and handles compound names' do + name = @np.parse( 'MATTHEW HANS-WURST MARIA-ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Matthew' + expect( name[:middle]).to eq 'Hans-Wurst' + expect( name[:last]).to eq 'Maria-Ericson' + expect( name[:parse_type]).to eq 0 + end + + it "should parse compound first name, last name" do + name = @np.parse( 'MATTHEW-JOSEPH HANS-WURST ERICSON-MILLER' ) + expect(name[:parsed]).to be true + expect(name[:first]).to eq 'Matthew-Joseph' + expect(name[:middle]).to eq 'Hans-Wurst' + expect(name[:last]).to eq 'Ericson-Miller' + expect(name[:parse_type]).to eq 0 + end + + it 'parses one middle names and handles compound names' do + name = @np.parse( 'HANS-WURST MATTHEW MARIA-ERICSON' ) + expect( name[:parsed]).to be true + expect( name[:first]).to eq 'Hans-Wurst' + expect( name[:middle]).to eq 'Matthew' + expect( name[:last]).to eq 'Maria-Ericson' + expect( name[:parse_type]).to eq 0 + end + + it 'parses one middle names and handles compound names' do + name = @np.parse( 'HANS WURST MATTHEW MARIA DE LA CULPA-GARCÍA' ) + expect( name[:parsed]).to be true + expect( name[:last]).to eq 'De La Culpa-García' + expect( name[:middle]).to eq 'Wurst Matthew Maria' + expect( name[:first]).to eq 'Hans' + expect( name[:parse_type]).to eq 0 + end + end + + describe 'Parse multiple names' do + before( :each ) do + @np = People::NameParser.new( :couples => true ) + end + + it 'should parse multiple first names and last name' do + name = @np.parse( 'Joe and Jill Hill' ) + expect(name[:parsed]).to eq true + expect(name[:multiple]).to be true + expect(name[:parsed2]).to be true + expect(name[:first2]).to eq 'Jill' + expect(name[:parse_type]).to eq 99 + end + + it "should parse multiple first names and last name with UTF-8 characters" do + name = @np.parse('MARÍA AND JOSÉ GARCÍA-D\'ANGELO' ) + expect(name[:parsed]).to eq true + expect(name[:multiple]).to be true + expect(name[:parsed2]).to be true + expect(name[:first]).to eq 'María' + expect(name[:first2]).to eq 'José' + expect(name[:last]).to eq 'García-D\'Angelo' + expect(name[:parse_type]).to eq 99 + end + + it 'should parse multiple first names, middle initial, last name' do + name = @np.parse( 'Joe and Jill S Hill' ) + expect(name[:parsed]).to eq true + expect(name[:multiple]).to be true + expect(name[:parsed2]).to be true + expect(name[:middle2]).to eq 'S' + expect(name[:first2]).to eq 'Jill' + expect(name[:first]).to eq 'Joe' + expect(name[:last]).to eq 'Hill' + expect(name[:parse_type]).to eq 99 + end + + it 'should parse multiple first names, middle initial, last name' do + name = @np.parse( 'Joe S and Jill X Hill' ) + expect(name[:parsed]).to eq true + expect(name[:multiple]).to be true + expect(name[:parsed2]).to be true + expect(name[:first2]).to eq 'Jill' + expect(name[:middle2]).to eq 'X' + expect(name[:first]).to eq 'Joe' + expect(name[:middle]).to eq 'S' + expect(name[:last]).to eq 'Hill' + expect(name[:parse_type]).to eq 98 + end + + it 'should parse reverse order with two first names and initials' do + name = @np.parse( 'Ericson, Matthew R. and Ben Q.' ) + expect(name[:parsed]).to eq true + expect(name[:multiple]).to be true + expect(name[:parsed2]).to be true + expect(name[:first2]).to eq 'Ben' + expect(name[:middle2]).to eq 'Q.' + expect(name[:first]).to eq 'Matthew' + expect(name[:middle]).to eq 'R.' + expect(name[:last]).to eq 'Ericson' + expect(name[:parse_type]).to eq 98 + end + end + + describe 'Parse unusual names' do + before( :each ) do + @np = People::NameParser.new + end + + it 'should parse multiple-word last name' do + name = @np.parse( 'Rev Matthew De La Hoya Jr.' ) + expect(name[:parsed]).to be true + expect(name[:last]).to eq 'De La Hoya' + expect(name[:suffix]).to eq 'Jr.' + expect(name[:title]).to eq 'Rev' + expect(name[:parse_type]).to eq 0 + end + + # BUG: suffixes do not work in this format + it 'should parse reverse order name' do + name = @np.parse( 'Van Der Graf, Dr. Matthew' ) + expect(name[:parsed]).to be true + expect(name[:last]).to eq 'Van Der Graf' + expect(name[:first]).to eq 'Matthew' + expect(name[:title]).to eq 'Dr.' + expect(name[:middle]).to eq '' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse long name with title and suffix' do + name = @np.parse( 'Dr. Matthew Q Ericson IV' ) + expect(name[:parsed]).to be true + expect(name[:last]).to eq 'Ericson' + expect(name[:first]).to eq 'Matthew' + expect(name[:title]).to eq 'Dr.' + expect(name[:middle]).to eq 'Q' + expect(name[:suffix]).to eq 'IV' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse last name with cammel case' do + name = @np.parse( 'Matthew McIntosh' ) + expect(name[:parsed]).to be true + expect(name[:last]).to eq 'McIntosh' + expect(name[:parse_type]).to eq 0 + end + end + + describe 'Parse names with decorations' do + before( :each ) do + @np = People::NameParser.new + end + + it "should parse name with the suffix 'Jr'" do + name = @np.parse( 'Matthew E Ericson Jr' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq 'Jr' + end + + it 'should parse name with a roman numeral suffix' do + name = @np.parse( 'Matthew E Ericson III' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq 'III' + end + + it 'should parse name with a numerical suffix 1' do + name = @np.parse( 'Matthew E Ericson 1st' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq '1st' + end + + it 'should parse name with a numerical suffix 2' do + name = @np.parse( 'Matthew E Ericson 2nd' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq '2nd' + end + + it 'should parse name with a numerical suffix 3' do + name = @np.parse( 'Matthew E Ericson 3rd' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq '3rd' + end + + it 'should parse name with a numerical suffix 4' do + name = @np.parse( 'Matthew E Ericson 4th' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq '4th' + end + + it 'should parse name with a numerical suffix 13' do + name = @np.parse( 'Matthew E Ericson 13th' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq '13th' + end + + it 'should parse name with a suffix with periods' do + name = @np.parse( 'Matthew E Ericson M.D.' ) + expect(name[:parsed]).to be true + expect(name[:suffix]).to eq 'M.D.' + end + + it 'should parse name with a title' do + name = @np.parse( 'Mr Matthew E Ericson' ) + expect(name[:parsed]).to be true + expect(name[:title]).to eq 'Mr' + end + + it 'should parse name with a title with a period' do + name = @np.parse( 'Mr. Matthew E Ericson' ) + expect(name[:parsed]).to be true + expect(name[:title]).to eq 'Mr.' + end + + it 'should parse name with a title, first initial' do + name = @np.parse( 'Rabbi M Edward Ericson' ) + expect(name[:parsed]).to be true + expect(name[:title]).to eq 'Rabbi' + expect(name[:first]).to eq 'M' + expect(name[:parse_type]).to eq 0 + end + + it 'should parse 1950s married couple name' do + name = @np.parse( 'Mr. and Mrs. Matthew E Ericson' ) + expect(name[:parsed]).to be true + expect(name[:title]).to eq 'Mr. And Mrs.' + expect(name[:first]).to eq 'Matthew' + end + end + + describe 'Name case options' do + it 'should change upper case to proper case' do + proper_np = People::NameParser.new( :case_mode => 'proper' ) + name = proper_np.parse( 'MATTHEW ERICSON' ) + expect(name[:first]).to eq 'Matthew' + expect(name[:last]).to eq 'Ericson' + end + + it 'should change proper case to upper case' do + proper_np = People::NameParser.new( :case_mode => 'upper' ) + name = proper_np.parse( 'Matthew Ericson' ) + expect(name[:first]).to eq 'MATTHEW' + expect(name[:last]).to eq 'ERICSON' + end + + it 'should leave case as is' do + proper_np = People::NameParser.new( :case_mode => 'leave' ) + name = proper_np.parse( 'mATTHEW eRicSon' ) + expect(name[:first]).to eq 'mATTHEW' + expect(name[:last]).to eq 'eRicSon' + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ae34ca0 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,3 @@ +require 'people' +require 'pry' +require 'awesome_print' diff --git a/test/people_test.rb b/test/people_test.rb deleted file mode 100644 index 616f036..0000000 --- a/test/people_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class PeopleTest < Test::Unit::TestCase - should "probably rename this file and start testing for real" do - flunk "hey buddy, you should probably rename this file and start testing for real" - end -end diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index f11cc62..0000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'rubygems' -require 'test/unit' -require 'shoulda' - -$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) -$LOAD_PATH.unshift(File.dirname(__FILE__)) -require 'people' - -class Test::Unit::TestCase -end