nameable_record 0.2.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -21
- data/.rspec +4 -0
- data/.rvmrc +2 -0
- data/Gemfile +15 -0
- data/Gemfile.lock +134 -0
- data/Guardfile +7 -0
- data/README.rdoc +0 -25
- data/Rakefile +1 -45
- data/lib/nameable_record/active_record_extensions.rb +16 -24
- data/lib/nameable_record/data/given-names.txt +5678 -0
- data/lib/nameable_record/data/surnames.txt +9527 -0
- data/lib/nameable_record/name.rb +56 -32
- data/lib/nameable_record/name_recognition.rb +119 -0
- data/lib/nameable_record/name_recognizer.rb +10 -0
- data/lib/nameable_record/railtie.rb +14 -0
- data/lib/nameable_record/version.rb +4 -0
- data/lib/nameable_record.rb +8 -6
- data/nameable_record.gemspec +23 -48
- data/script/environment.rb +17 -0
- data/spec/database.yml +4 -0
- data/spec/lib/nameable_record/active_record_extensions_spec.rb +43 -0
- data/spec/lib/nameable_record/name_recognition_sharedspec.rb +182 -0
- data/spec/lib/nameable_record/name_recognition_spec.rb +17 -0
- data/spec/lib/nameable_record/name_recognizer_spec.rb +17 -0
- data/spec/lib/nameable_record/name_spec.rb +239 -0
- data/spec/lib/nameable_record_spec.rb +8 -0
- data/spec/rails_spec_helper.rb +3 -0
- data/spec/spec_helper.rb +26 -6
- metadata +76 -27
- data/LICENSE +0 -20
- data/VERSION +0 -1
- data/spec/nameable_record_spec.rb +0 -7
- data/spec/spec.opts +0 -1
data/lib/nameable_record/name.rb
CHANGED
@@ -1,31 +1,28 @@
|
|
1
1
|
module NameableRecord
|
2
|
+
|
2
3
|
class Name
|
4
|
+
|
3
5
|
attr_reader :first, :middle, :last, :prefix, :suffix
|
4
6
|
|
5
|
-
def initialize(
|
6
|
-
@
|
7
|
-
|
8
|
-
@name_pattern_map = {
|
9
|
-
/%l/ => last || "",
|
10
|
-
/%f/ => first || "",
|
11
|
-
/%m/ => middle || "",
|
12
|
-
/%p/ => prefix || "",
|
13
|
-
/%s/ => suffix || "",
|
14
|
-
}
|
15
|
-
|
16
|
-
@@predefined_patterns = {
|
17
|
-
:full => " %l, %f %s",
|
18
|
-
:full_with_middle => " %l, %f %m %s",
|
19
|
-
:full_with_prefix => " %l, %p %f %s",
|
20
|
-
:full_sentence => "%p %f %l %s",
|
21
|
-
:full_sentence_with_middle => "%p %f %m %l %s"
|
22
|
-
}
|
7
|
+
def initialize( *args )
|
8
|
+
@last, @first, @prefix, @middle, @suffix = args
|
23
9
|
|
24
10
|
self.freeze
|
25
11
|
end
|
26
12
|
|
27
|
-
|
28
|
-
|
13
|
+
def ==( another_name )
|
14
|
+
return false unless another_name.is_a?( self.class )
|
15
|
+
|
16
|
+
%w(last first middle suffix prefix).map do |attr|
|
17
|
+
another_name.send( attr ) == send( attr )
|
18
|
+
end.all? { |r| r == true }
|
19
|
+
end
|
20
|
+
|
21
|
+
def eql?( another_name )
|
22
|
+
self == another_name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Creates a name based on pattern provided. Defaults to last, first.
|
29
26
|
#
|
30
27
|
# Symbols:
|
31
28
|
# %l - last name
|
@@ -34,19 +31,46 @@ module NameableRecord
|
|
34
31
|
# %p - prefix
|
35
32
|
# %s - suffix
|
36
33
|
#
|
37
|
-
def to_s( pattern=
|
38
|
-
if pattern.is_a?( Symbol )
|
39
|
-
to_return = @@predefined_patterns[pattern]
|
40
|
-
else
|
41
|
-
to_return = pattern
|
42
|
-
end
|
43
|
-
to_return = @@predefined_patterns[:full] if to_return.empty?
|
34
|
+
def to_s( pattern='%l, %f' )
|
35
|
+
pattern = PREDEFINED_PATTERNS[pattern] if pattern.is_a?( Symbol )
|
44
36
|
|
45
|
-
|
46
|
-
|
37
|
+
PATTERN_MAP.inject( pattern ) do |name, mapping|
|
38
|
+
name = name.gsub( mapping.first,
|
39
|
+
(send( mapping.last ) || '') )
|
47
40
|
end
|
48
|
-
|
49
|
-
to_return.strip
|
50
41
|
end
|
42
|
+
|
43
|
+
PREDEFINED_PATTERNS = {
|
44
|
+
:full => "%l, %f %s",
|
45
|
+
:full_with_middle => "%l, %f %m %s",
|
46
|
+
:full_with_prefix => "%l, %p %f %s",
|
47
|
+
:full_sentence => "%p %f %l %s",
|
48
|
+
:full_sentence_with_middle => "%p %f %m %l %s"
|
49
|
+
}.freeze
|
50
|
+
|
51
|
+
PATTERN_MAP = {
|
52
|
+
/%l/ => :last,
|
53
|
+
/%f/ => :first,
|
54
|
+
/%m/ => :middle,
|
55
|
+
/%p/ => :prefix,
|
56
|
+
/%s/ => :suffix
|
57
|
+
}.freeze
|
58
|
+
|
59
|
+
PREFIX_BASE = %w(Mr Mrs Miss Dr General) # The order of this matters because of PREFIXES_CORRECTIONS
|
60
|
+
SUFFIX_BASE = %w(Jr III V IV Esq) # The order of this matters because of SUFFIXES_CORRECTIONS
|
61
|
+
|
62
|
+
PREFIXES = PREFIX_BASE.map { |p| [p, "#{p}.", p.upcase, "#{p.upcase}.", p.downcase, "#{p.downcase}."] }.flatten.sort
|
63
|
+
SUFFIXES = SUFFIX_BASE.map { |p| [p, "#{p}.", p.upcase, "#{p.upcase}.", p.downcase, "#{p.downcase}."] }.flatten.sort
|
64
|
+
|
65
|
+
PREFIXES_CORRECTIONS = Hash[*PREFIX_BASE.map do |base|
|
66
|
+
PREFIXES.grep( /#{base}/i ).map { |p| [p,base] }
|
67
|
+
end.flatten]
|
68
|
+
|
69
|
+
SUFFIXES_CORRECTIONS = Hash[*SUFFIX_BASE.map do |base|
|
70
|
+
SUFFIXES.grep( /#{base}/i ).map { |p| [p,base] }
|
71
|
+
end.flatten]
|
72
|
+
|
51
73
|
end
|
52
|
-
|
74
|
+
|
75
|
+
end
|
76
|
+
|
@@ -0,0 +1,119 @@
|
|
1
|
+
module NameableRecord::NameRecognition
|
2
|
+
|
3
|
+
def surnames
|
4
|
+
@surnames ||= surnames_from_file
|
5
|
+
end
|
6
|
+
|
7
|
+
def given_names
|
8
|
+
@given_names ||= given_names_from_file
|
9
|
+
end
|
10
|
+
|
11
|
+
%w(downcase upcase).each do |desired_case|
|
12
|
+
define_method "surnames_all_#{desired_case}" do
|
13
|
+
surnames.map( &desired_case.to_sym )
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method "given_names_all_#{desired_case}" do
|
17
|
+
given_names.map( &desired_case.to_sym )
|
18
|
+
end
|
19
|
+
|
20
|
+
define_method "all_name_words_#{desired_case}" do
|
21
|
+
(surnames + given_names + prefixes + suffixes + initials_downcase).map { |n| n.send( desired_case ) }.uniq
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def human_name?( name, lowest_affirmative_probabilty=100, disqualifying_words=[] )
|
26
|
+
probability_is_human_name( name, disqualifying_words ) >= lowest_affirmative_probabilty
|
27
|
+
end
|
28
|
+
|
29
|
+
def probability_is_human_name( name, disqualifying_words=[] )
|
30
|
+
if name.match( /^[a-zA-Z]*\s*[a-zA-Z]{2}$/ )
|
31
|
+
return probability_from_last_and_intials( name, :last_name_first => true )
|
32
|
+
elsif name.match( /^[a-zA-Z]{2}\s*[a-zA-Z]*$/ )
|
33
|
+
return probability_from_last_and_intials( name, :last_name_first => false )
|
34
|
+
end
|
35
|
+
|
36
|
+
name_parts = split_and_clean_name( name )
|
37
|
+
|
38
|
+
return 0 if ((default_disqualifying_words + disqualifying_words) & name_parts).size > 0
|
39
|
+
|
40
|
+
score = (100 - ((name_parts.size - (name_parts & all_name_words_downcase).size) * points_per_additional_word))
|
41
|
+
score < 0 ? 0 : score
|
42
|
+
end
|
43
|
+
|
44
|
+
def default_disqualifying_words
|
45
|
+
%w(
|
46
|
+
corporation
|
47
|
+
corp
|
48
|
+
co
|
49
|
+
llc
|
50
|
+
ltd
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def points_per_additional_word
|
57
|
+
20
|
58
|
+
end
|
59
|
+
|
60
|
+
def probability_from_last_and_intials( name, options )
|
61
|
+
name_parts = split_and_clean_name( name )
|
62
|
+
|
63
|
+
last_name = options[:last_name_first] ? name_parts.first : name_parts.last
|
64
|
+
|
65
|
+
return ([last_name] & surnames_all_downcase).size == 1 ?
|
66
|
+
100 :
|
67
|
+
50
|
68
|
+
end
|
69
|
+
|
70
|
+
def split_and_clean_name( name )
|
71
|
+
name.split( ' ' ).compact.map { |p| p.strip.downcase }
|
72
|
+
end
|
73
|
+
|
74
|
+
def prefixes
|
75
|
+
NameableRecord::Name::PREFIXES
|
76
|
+
end
|
77
|
+
|
78
|
+
def suffixes
|
79
|
+
NameableRecord::Name::SUFFIXES
|
80
|
+
end
|
81
|
+
|
82
|
+
def initials_downcase
|
83
|
+
alphabet_downcase + alphabet_downcase.map { |l| "#{l}." }
|
84
|
+
end
|
85
|
+
|
86
|
+
def alphabet_downcase
|
87
|
+
('a'..'z').to_a
|
88
|
+
end
|
89
|
+
|
90
|
+
def surnames_from_file
|
91
|
+
File.open( surnames_file_path, 'r' ) { |f| f.readlines }.map { |n| n.strip }
|
92
|
+
end
|
93
|
+
|
94
|
+
def surnames_file_path
|
95
|
+
File.join File.dirname(__FILE__),
|
96
|
+
'data',
|
97
|
+
surnames_file_name
|
98
|
+
end
|
99
|
+
|
100
|
+
def surnames_file_name
|
101
|
+
'surnames.txt'
|
102
|
+
end
|
103
|
+
|
104
|
+
def given_names_from_file
|
105
|
+
File.open( given_names_file_path, 'r' ) { |f| f.readlines }.map { |n| n.strip }
|
106
|
+
end
|
107
|
+
|
108
|
+
def given_names_file_path
|
109
|
+
File.join File.dirname(__FILE__),
|
110
|
+
'data',
|
111
|
+
given_names_file_name
|
112
|
+
end
|
113
|
+
|
114
|
+
def given_names_file_name
|
115
|
+
'given-names.txt'
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rails'
|
2
|
+
|
3
|
+
module NameableRecord
|
4
|
+
|
5
|
+
class Railtie < ::Rails::Railtie
|
6
|
+
|
7
|
+
initializer "nameable_record.initialize" do
|
8
|
+
ActiveRecord::Base.send( :include, NameableRecord::ActiveRecordExtensions ) if defined?( ActiveRecord::Base )
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
data/lib/nameable_record.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require 'nameable_record/active_record_extensions'
|
4
|
-
require 'nameable_record/name'
|
1
|
+
require "nameable_record/railtie" if defined?( ::Rails )
|
2
|
+
require "nameable_record/version"
|
5
3
|
|
6
4
|
module NameableRecord
|
7
|
-
|
5
|
+
|
6
|
+
autoload :ActiveRecordExtensions, 'nameable_record/active_record_extensions'
|
7
|
+
autoload :Name, 'nameable_record/name'
|
8
|
+
autoload :NameRecognition, 'nameable_record/name_recognition'
|
9
|
+
autoload :NameRecognizer, 'nameable_record/name_recognizer'
|
10
|
+
|
8
11
|
end
|
9
12
|
|
10
|
-
ActiveRecord::Base.send( :include, NameableRecord::ActiveRecordExtensions ) if defined?( ActiveRecord::Base )
|
data/nameable_record.gemspec
CHANGED
@@ -1,57 +1,32 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "nameable_record/version"
|
5
4
|
|
6
5
|
Gem::Specification.new do |s|
|
7
|
-
s.name
|
8
|
-
s.version
|
6
|
+
s.name = "nameable_record"
|
7
|
+
s.version = NameableRecord::VERSION
|
8
|
+
s.authors = ["Jason Harrelson"]
|
9
|
+
s.email = ["jason@lookforwardenterprises.com"]
|
10
|
+
s.homepage = "https://github.com/midas/nameable_record"
|
11
|
+
s.summary = %q{Abstracts the ActiveRecord composed_of pattern for names.}
|
12
|
+
s.description = %q{Abstracts the ActiveRecord composed_of pattern for names. Also provides other convenience utilieis for working with human names.}
|
9
13
|
|
10
|
-
s.
|
11
|
-
s.authors = ["C. Jason Harrelson"]
|
12
|
-
s.date = %q{2010-10-02}
|
13
|
-
s.description = %q{Encapsulates the composed of pattern for names into any easy to use library.}
|
14
|
-
s.email = %q{jason@lookforwardenterprises.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
".gitignore",
|
22
|
-
"LICENSE",
|
23
|
-
"README.rdoc",
|
24
|
-
"Rakefile",
|
25
|
-
"VERSION",
|
26
|
-
"lib/nameable_record.rb",
|
27
|
-
"lib/nameable_record/active_record_extensions.rb",
|
28
|
-
"lib/nameable_record/name.rb",
|
29
|
-
"nameable_record.gemspec",
|
30
|
-
"spec/nameable_record_spec.rb",
|
31
|
-
"spec/spec.opts",
|
32
|
-
"spec/spec_helper.rb"
|
33
|
-
]
|
34
|
-
s.homepage = %q{http://github.com/midas/nameable_record}
|
35
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
36
|
-
s.require_paths = ["lib"]
|
37
|
-
s.rubygems_version = %q{1.3.7}
|
38
|
-
s.summary = %q{Encapsulates the composed of pattern for names into any easy to use library.}
|
39
|
-
s.test_files = [
|
40
|
-
"spec/nameable_record_spec.rb",
|
41
|
-
"spec/spec_helper.rb"
|
42
|
-
]
|
14
|
+
s.rubyforge_project = "nameable_record"
|
43
15
|
|
44
|
-
|
45
|
-
|
46
|
-
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
47
20
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
s.
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
%w(
|
23
|
+
gem-dandy
|
24
|
+
rspec
|
25
|
+
rails
|
26
|
+
).each do |development_dependency|
|
27
|
+
s.add_development_dependency development_dependency
|
55
28
|
end
|
29
|
+
|
30
|
+
# s.add_runtime_dependency "rest-client"
|
56
31
|
end
|
57
32
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
ActiveRecord::Base.configurations = YAML::load( IO.read( File.dirname(__FILE__) + '/../spec/database.yml' ) )
|
2
|
+
ActiveRecord::Base.establish_connection( 'test' )
|
3
|
+
|
4
|
+
silence_stream STDOUT do
|
5
|
+
|
6
|
+
ActiveRecord::Schema.define :version => 1 do
|
7
|
+
create_table :users, :force => true do |t|
|
8
|
+
t.string :name_first, :anem_last, :name_middle, :name_prefix, :name_suffix
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
class User < ActiveRecord::Base
|
15
|
+
has_name :name
|
16
|
+
end
|
17
|
+
|
data/spec/database.yml
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rails_spec_helper'
|
3
|
+
|
4
|
+
describe NameableRecord::ActiveRecordExtensions do
|
5
|
+
|
6
|
+
let :user do
|
7
|
+
User.new
|
8
|
+
end
|
9
|
+
|
10
|
+
let :name do
|
11
|
+
NameableRecord::Name.new *name_parts
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'setting the composed of fields from a NameableRecord::Name instance' do
|
15
|
+
|
16
|
+
before :each do
|
17
|
+
user.name = name
|
18
|
+
end
|
19
|
+
|
20
|
+
it '#name_last' do
|
21
|
+
user.name_last.should == 'Smith'
|
22
|
+
end
|
23
|
+
|
24
|
+
it '#name_first' do
|
25
|
+
user.name_first.should == 'John'
|
26
|
+
end
|
27
|
+
|
28
|
+
it '#name_middle' do
|
29
|
+
user.name_middle.should == 'Jacob'
|
30
|
+
end
|
31
|
+
|
32
|
+
it '#name_prefix' do
|
33
|
+
user.name_prefix.should == 'Mr.'
|
34
|
+
end
|
35
|
+
|
36
|
+
it '#name_suffix' do
|
37
|
+
user.name_suffix.should == 'III'
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
shared_examples_for 'any name recognition' do
|
4
|
+
|
5
|
+
context '#surnames' do
|
6
|
+
|
7
|
+
subject { recognition_instance.surnames.size }
|
8
|
+
|
9
|
+
it { should == 9527 }
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
context '#surnames_all_downcase' do
|
14
|
+
|
15
|
+
subject { recognition_instance.surnames_all_downcase.first }
|
16
|
+
|
17
|
+
it { should == 'aaron' }
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
context '#surnames_all_upcase' do
|
22
|
+
|
23
|
+
subject { recognition_instance.surnames_all_upcase.first }
|
24
|
+
|
25
|
+
it { should == 'AARON' }
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
context '#human_name?' do
|
30
|
+
|
31
|
+
context 'when the name matches the pattern {last name} {first initial}{middle initial}' do
|
32
|
+
|
33
|
+
subject { recognition_instance.human_name?( 'HOMER SA' ) }
|
34
|
+
|
35
|
+
it { should be_true }
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when the name matches the pattern {first initial}{middle initial} {last name}' do
|
40
|
+
|
41
|
+
subject { recognition_instance.human_name?( 'SA HOMER' ) }
|
42
|
+
|
43
|
+
it { should be_true }
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
context 'when the middle initial has no .' do
|
48
|
+
|
49
|
+
subject { recognition_instance.human_name?( 'HOMER STEPHEN A' ) }
|
50
|
+
|
51
|
+
it { should be_true }
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'when the middle initial has a .' do
|
56
|
+
|
57
|
+
subject { recognition_instance.human_name?( 'HOMER STEPHEN A.' ) }
|
58
|
+
|
59
|
+
it { should be_true }
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when the name is an 80% match' do
|
64
|
+
|
65
|
+
subject { recognition_instance.human_name?( 'CUP STEPHEN A' ) }
|
66
|
+
|
67
|
+
it { should be_false }
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
context 'when the name is less an 80% match with a 70% threshold' do
|
72
|
+
|
73
|
+
subject { recognition_instance.human_name?( 'CUP STEPHEN A', 80 ) }
|
74
|
+
|
75
|
+
it { should be_true }
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
context '#given_names' do
|
82
|
+
|
83
|
+
subject { recognition_instance.given_names.size }
|
84
|
+
|
85
|
+
it { should == 5678 }
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
context '#given_names_all_downcase' do
|
90
|
+
|
91
|
+
subject { recognition_instance.given_names_all_downcase.first }
|
92
|
+
|
93
|
+
it { should == 'aj' }
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
context '#given_names_all_upcase' do
|
98
|
+
|
99
|
+
subject { recognition_instance.given_names_all_upcase.first }
|
100
|
+
|
101
|
+
it { should == 'AJ' }
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
context '#all_name_words_downcase' do
|
106
|
+
|
107
|
+
subject { recognition_instance.all_name_words_downcase.size }
|
108
|
+
|
109
|
+
it { should == 14297 }
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
context '#all_name_words_upcase' do
|
114
|
+
|
115
|
+
subject { recognition_instance.all_name_words_upcase.size }
|
116
|
+
|
117
|
+
it { should == 14297 }
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
context '#prefixes' do
|
122
|
+
|
123
|
+
subject { recognition_instance.send( :prefixes ).sort }
|
124
|
+
|
125
|
+
it { should == ["DR", "DR.", "Dr", "Dr.", "GENERAL", "GENERAL.", "General", "General.", "MISS", "MISS.", "MR", "MR.", "MRS", "MRS.", "Miss", "Miss.", "Mr", "Mr.", "Mrs", "Mrs.", "dr", "dr.", "general", "general.", "miss", "miss.", "mr", "mr.", "mrs", "mrs."] }
|
126
|
+
|
127
|
+
end
|
128
|
+
|
129
|
+
context '#suffixes' do
|
130
|
+
|
131
|
+
subject { recognition_instance.send( :suffixes ).sort }
|
132
|
+
|
133
|
+
it { should == ["ESQ", "ESQ.", "Esq", "Esq.", "III", "III", "III.", "III.", "IV", "IV", "IV.", "IV.", "JR", "JR.", "Jr", "Jr.", "V", "V", "V.", "V.", "esq", "esq.", "iii", "iii.", "iv", "iv.", "jr", "jr.", "v", "v."] }
|
134
|
+
|
135
|
+
end
|
136
|
+
|
137
|
+
context '#probability_is_human_name' do
|
138
|
+
|
139
|
+
context 'when all words are a name part' do
|
140
|
+
|
141
|
+
subject { recognition_instance.probability_is_human_name( 'Mr. Charles Langford III' ) }
|
142
|
+
|
143
|
+
it { should == 100 }
|
144
|
+
|
145
|
+
end
|
146
|
+
|
147
|
+
context 'when there is one word that is not a name part' do
|
148
|
+
|
149
|
+
subject { recognition_instance.probability_is_human_name( 'Introducing Mr. Charles Langford III' ) }
|
150
|
+
|
151
|
+
it { should == 80 }
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
context 'when there are two words that are not a name part' do
|
156
|
+
|
157
|
+
subject { recognition_instance.probability_is_human_name( 'Home of Mr. Charles Langford III' ) }
|
158
|
+
|
159
|
+
it { should == 60 }
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'when there are five words that are not a name part' do
|
164
|
+
|
165
|
+
subject { recognition_instance.probability_is_human_name( 'This is the home of Mr. Charles Langford III' ) }
|
166
|
+
|
167
|
+
it { should == 0 }
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
context 'when there are six words that are not a name part' do
|
172
|
+
|
173
|
+
subject { recognition_instance.probability_is_human_name( 'It is at the home of Mr. Charles Langford III' ) }
|
174
|
+
|
175
|
+
it { should == 0 }
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path( "#{File.dirname __FILE__}/name_recognition_sharedspec" )
|
3
|
+
|
4
|
+
describe NameableRecord::NameRecognition do
|
5
|
+
|
6
|
+
class SomeClass
|
7
|
+
include NameableRecord::NameRecognition
|
8
|
+
end
|
9
|
+
|
10
|
+
let :recognition_instance do
|
11
|
+
SomeClass.new
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like 'any name recognition'
|
15
|
+
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require File.expand_path( "#{File.dirname __FILE__}/name_recognition_sharedspec" )
|
3
|
+
|
4
|
+
describe NameableRecord::NameRecognizer do
|
5
|
+
|
6
|
+
let :recognition_instance do
|
7
|
+
described_class.new
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'should include the NameRecognition module' do
|
11
|
+
described_class.should include NameableRecord::NameRecognition
|
12
|
+
end
|
13
|
+
|
14
|
+
it_should_behave_like 'any name recognition'
|
15
|
+
|
16
|
+
end
|
17
|
+
|