dburkes-people_places_things 1.1.0 → 1.1.2

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.
@@ -0,0 +1,157 @@
1
+ class PersonName
2
+ attr_accessor :parts
3
+ attr_accessor :raw
4
+
5
+ def initialize(options={})
6
+ @parts = options.inject({}) do |parts, (k, v)|
7
+ sym_key = (k.to_sym rescue key) || key
8
+ parts[sym_key] = v if SUPPORTED_PARTS.include?(sym_key)
9
+ parts
10
+ end
11
+ end
12
+
13
+ def self.parse(string, fmt = :auto_detect)
14
+ raise "Unsupported Format" if !PARSE_FORMATS.include?(fmt)
15
+
16
+ if fmt == :auto_detect
17
+ fmt = string.include?(',') ? :last_first_middle : :first_middle_last
18
+ end
19
+
20
+ args = {}
21
+
22
+ parts = string.split(/\s|,/).collect {|p| p.strip}.reject {|p| PersonName.blank?(p) || p == ',' }
23
+
24
+ if parts.size == 1
25
+ args[:last] = parts.first
26
+ else
27
+ case fmt
28
+ when :first_middle_last
29
+ if parts.size > 2 and SUPPORTED_SUFFIXES.detect {|s| s.casecmp(parts.last) == 0}
30
+ args[:suffix] = normalize_suffix(parts.last)
31
+ parts.delete_at(parts.size - 1)
32
+ end
33
+
34
+ args[:first] = parts.first if parts.size > 0
35
+ args[:last] = parts.last if parts.size > 1
36
+
37
+ if parts.size > 2 && ODD_LAST_NAME_PREFIXES.detect {|s| s.casecmp(parts[-2]) == 0}
38
+ args[:last] = "#{parts[-2]}#{args[:last]}"
39
+ parts.delete_at(parts.size - 2)
40
+ end
41
+
42
+ args[:middle] = parts[1..(parts.size - 2)].join(' ') if parts.size > 2
43
+
44
+ when :last_first_middle
45
+ args[:last] = parts.first if parts.size > 0
46
+
47
+ if parts.size > 1 && ODD_LAST_NAME_PREFIXES.detect {|s| s.casecmp(args[:last]) == 0}
48
+ args[:last] << parts[1]
49
+ parts.delete_at(1)
50
+ end
51
+
52
+ if parts.size > 2 and SUPPORTED_SUFFIXES.detect {|s| s.casecmp(parts[1]) == 0}
53
+ args[:suffix] = normalize_suffix(parts[1])
54
+ parts.delete_at(1)
55
+ end
56
+
57
+ args[:first] = parts[1] if parts.size > 1
58
+ args[:middle] = parts[2..(parts.size - 1)].join(' ') if parts.size > 2
59
+ end
60
+ end
61
+
62
+ n = PersonName.new args
63
+ n.raw = string
64
+ n
65
+ end
66
+
67
+ def self.format(options, fmt=:full)
68
+ PersonName.new(options).to_s(fmt)
69
+ end
70
+
71
+ def [](which)
72
+ @parts[which]
73
+ end
74
+
75
+ def to_s(fmt = :full)
76
+ raise "Unsupported Format" if !OUTPUT_FORMATS.include?(fmt)
77
+
78
+ case fmt
79
+ when :first, :middle, :last
80
+ @parts[fmt]
81
+
82
+ when :full
83
+ [@parts[:first], @parts[:middle], @parts[:last], @parts[:suffix]].compact.join(' ')
84
+
85
+ when :full_reverse
86
+ [@parts[:last], @parts[:first], @parts[:middle], @parts[:suffix]].compact.join(' ')
87
+
88
+ when :first_space_last
89
+ [@parts[:first], @parts[:last]].compact.join(' ')
90
+
91
+ when :last_space_first
92
+ [@parts[:last], @parts[:first]].compact.join(' ')
93
+
94
+ when :last_comma_first
95
+ [@parts[:last], @parts[:first]].compact.join(',')
96
+
97
+ when :last_comma_space_first
98
+ [(@parts[:first] ? "#{@parts[:last]}," : @parts[:last]), @parts[:first]].compact.join(' ')
99
+ end
100
+ end
101
+
102
+ def first
103
+ @parts[:first]
104
+ end
105
+
106
+ def first_i
107
+ first[0,1] rescue nil
108
+ end
109
+
110
+ def middle
111
+ @parts[:middle]
112
+ end
113
+
114
+ def middle_i
115
+ middle[0,1] rescue nil
116
+ end
117
+
118
+ def last
119
+ @parts[:last]
120
+ end
121
+
122
+ def last_i
123
+ last[0,1] rescue nil
124
+ end
125
+
126
+ def suffix
127
+ @parts[:suffix]
128
+ end
129
+
130
+ def eql?(other, initials_only=false)
131
+ if other.is_a?(PersonName)
132
+ (self.parts.keys & other.parts.keys).all? do |k|
133
+ msg = (k != :last && initials_only) ? "#{k}_i" : k
134
+ self.send(msg).casecmp(other.send(msg)) == 0
135
+ end
136
+ else
137
+ false
138
+ end
139
+ end
140
+
141
+ PARSE_FORMATS = [:first_middle_last, :last_first_middle, :auto_detect]
142
+ OUTPUT_FORMATS = [:first, :middle, :last, :full, :full_reverse, :first_space_last, :last_space_first, :last_comma_first, :last_comma_space_first]
143
+
144
+ private
145
+
146
+ def self.blank?(string_or_nil)
147
+ string_or_nil.nil? || string_or_nil !~ /\S/
148
+ end
149
+
150
+ def self.normalize_suffix(suffix)
151
+ suffix.match(/\w+/)[0] rescue suffix
152
+ end
153
+
154
+ SUPPORTED_PARTS = [:first,:middle,:last, :suffix]
155
+ SUPPORTED_SUFFIXES = %w(II III IV V JR JR. SR SR.)
156
+ ODD_LAST_NAME_PREFIXES = %w(MC ST ST.)
157
+ end
@@ -0,0 +1,38 @@
1
+ class PhoneNumber
2
+ @@SUPPORTED_PARTS = [:country_code, :area_code, :number, :exchange, :suffix]
3
+ @@SUPPORTED_PARTS.each {|attr| attr_accessor attr}
4
+ attr_accessor :raw
5
+
6
+ def initialize(parts={})
7
+ parts.keys.each do |k|
8
+ send("#{k}=", parts[k]) if @@SUPPORTED_PARTS.include?(k)
9
+ end
10
+ end
11
+
12
+ def self.parse(string)
13
+ extract = string.match(/.*?([-()\d ]+)/)[1].gsub(/[^\d]/, '') rescue nil
14
+ raise "Unsupported Format" if !extract || extract.length < 10 || extract.length > 11
15
+
16
+ parts = {}
17
+
18
+ if extract.length == 11
19
+ parts[:country_code] = extract.slice!(0..0)
20
+ else
21
+ parts[:country_code] = '1'
22
+ end
23
+
24
+ raise "Unsupported Format" if parts[:country_code] != '1'
25
+
26
+ parts[:area_code] = extract.slice!(0..2)
27
+
28
+ parts[:number] = extract.dup
29
+
30
+ parts[:exchange] = extract.slice!(0..2)
31
+
32
+ parts[:suffix] = extract
33
+
34
+ ret = PhoneNumber.new parts
35
+ ret.raw = string
36
+ ret
37
+ end
38
+ end
@@ -0,0 +1,195 @@
1
+ class StreetAddress
2
+ @@SUPPORTED_PARTS = [:number, :pre_direction, :name, :suffix, :post_direction, :unit_type, :unit]
3
+ @@SUPPORTED_PARTS.each {|attr| attr_accessor attr}
4
+ attr_accessor :raw
5
+
6
+ def initialize(parts={})
7
+ parts.keys.each do |k|
8
+ send("#{k}=", parts[k]) if @@SUPPORTED_PARTS.include?(k)
9
+ end
10
+
11
+ validate_parts
12
+ end
13
+
14
+ def self.parse(string)
15
+ tokens = string.split(/[\s,]/).select {|s| !s.empty?}
16
+ parts = {}
17
+
18
+ # Check the first token for leading numericality. If so, set number to the first token, and delete it
19
+ #
20
+ if tokens.first =~ /(^\d+.*)/
21
+ parts[:number] = $1
22
+ tokens.shift
23
+ end
24
+
25
+ # If at least two tokens remain, check next-to-last token as unit type. If so, set unit_type and unit, and delete the tokens
26
+ #
27
+ if tokens.size > 1
28
+ parts[:unit_type] = find_token(tokens[-2], UNIT_TYPES)
29
+ if parts[:unit_type]
30
+ parts[:unit] = tokens[-1]
31
+ tokens.slice!(tokens.size - 2, 2)
32
+ end
33
+ end
34
+
35
+ # If at least one token remains, check last token for directionality. If so, set post_direction and delete the token
36
+ #
37
+ if tokens.size > 0
38
+ parts[:post_direction] = find_token(tokens[-1], DIRECTIONS)
39
+ if parts[:post_direction]
40
+ post_direction_token = tokens[-1]
41
+ tokens.slice!(tokens.size - 1)
42
+ end
43
+ end
44
+
45
+ # If at least one token remains, check last token for suffix. If so, self set.suffix and delete the token
46
+ #
47
+ if tokens.size > 0
48
+ parts[:suffix] = find_token(tokens[-1], SUFFIXES)
49
+ tokens.slice!(tokens.size - 1) if parts[:suffix]
50
+ end
51
+
52
+ # If at least two tokens remain, check first for directionality. If so, set pre_direction and delete token
53
+ #
54
+ if tokens.size > 1
55
+ parts[:pre_direction] = find_token(tokens.first, DIRECTIONS)
56
+ tokens.shift if parts[:pre_direction]
57
+ end
58
+
59
+ # if any tokens remain, set joined remaining tokens as name, otherwise, set name to post_direction, if set, and set post_direction to nil
60
+ #
61
+ if tokens.size > 0
62
+ parts[:name] = tokens.join(' ')
63
+ else
64
+ parts[:name] = post_direction_token
65
+ parts[:post_direction] = nil
66
+ end
67
+
68
+ ret = StreetAddress.new parts
69
+ ret.raw = string
70
+ ret
71
+ end
72
+
73
+ def self.format(parts)
74
+ StreetAddress.new(parts).to_s
75
+ end
76
+
77
+ def to_s
78
+ parts = []
79
+ parts << self.number if self.number
80
+ parts << DIRECTIONS[self.pre_direction].first if self.pre_direction
81
+ parts << self.name if self.name
82
+ parts << SUFFIXES[self.suffix].first if self.suffix
83
+ parts << DIRECTIONS[self.post_direction].first if self.post_direction
84
+ parts << UNIT_TYPES[self.unit_type].first if self.unit_type
85
+ parts << self.unit if self.unit
86
+ parts.join(' ')
87
+ end
88
+
89
+ def self.string_for(symbol, form)
90
+ raise "Requested unknown form \"#{type}\" for :#{symbol}" if !SUPPORTED_FORMS.include?(form)
91
+
92
+ val = DIRECTIONS[symbol] || SUFFIXES[symbol] || UNIT_TYPES[symbol]
93
+
94
+ if val
95
+ val = ((val[SUPPORTED_FORMS.index(form)] rescue nil) || (val.first rescue val))
96
+ end
97
+
98
+ val
99
+ end
100
+
101
+ private
102
+
103
+ def validate_parts
104
+ [:pre_direction, :suffix, :post_direction, :unit_type].each do |p|
105
+ if self.send(p)
106
+ legal_values = p == :suffix ? SUFFIXES : p == :unit_type ? UNIT_TYPES : DIRECTIONS
107
+ raise "Invalid #{p.to_s} \"#{self.send(p)}\"" if !legal_values.include?(self.send(p))
108
+ end
109
+ end
110
+ end
111
+
112
+ def self.find_token(token, values)
113
+ values.keys.each do |k|
114
+ return k if values[k].detect {|v| v.casecmp(token) == 0}
115
+ end
116
+
117
+ nil
118
+ end
119
+
120
+ DIRECTIONS = {
121
+ :north => %w(north n n.),
122
+ :northeast => %w(northeast ne ne. n.e.),
123
+ :east => %w(east e e.),
124
+ :southeast => %w(southeast se se. s.e.),
125
+ :south => %w(south s s.),
126
+ :southwest => %w(southwest sw sw. s.w.),
127
+ :west => %w(west w w.),
128
+ :northwest => %w(northwest nw nw. n.w.)
129
+ }
130
+
131
+ SUFFIXES = {
132
+ :alley => %w(alley al al.),
133
+ :avenue => %w(avenue ave ave. av av.),
134
+ :beach => %w(beach bch bch.),
135
+ :bend => %w(bend),
136
+ :boulevard => %w(boulevard blvd blvd. blv blv.),
137
+ :center => %w(center ctr ctr.),
138
+ :circle => %w(circle cir cir.),
139
+ :cliff => %w(cliff clf clf.),
140
+ :club => %w(club),
141
+ :condo => %w(condo con con.),
142
+ :court => %w(court ct ct. cor cor.),
143
+ :cove => %w(cove),
144
+ :creek => %w(creek crk crk.),
145
+ :crossing => %w(crossing xing xing. crs crs.),
146
+ :drive => %w(drive dr dr.),
147
+ :extension => %w(extension ext ext.),
148
+ :freeway => %w(freeway fwy fwy.),
149
+ :gardens => %w(gardens gdns gdns.),
150
+ :glen => %w(glen gl gl.),
151
+ :green => %w(green grn grn.),
152
+ :heights => %w(heights hts hts.),
153
+ :highway => %w(highway hwy hwy. hgwy hgwy.),
154
+ :hill => %w(hill),
155
+ :knoll => %w(knoll knl knl.),
156
+ :lake => %w(lake),
157
+ :lane => %w(lane ln ln.),
158
+ :landing => %w(landing lndg lndg.),
159
+ :loop => %w(loop),
160
+ :meadows => %w(meadows mdws mdws.),
161
+ :manor => %w(manor mnr mnr.),
162
+ :mountain => %w(mountain mtn mtn. mnt mnt.),
163
+ :oaks => %w(oaks),
164
+ :oval => %w(oval),
165
+ :park => %w(park pk pk. prk prk.),
166
+ :parkway => %w(parkway pkwy pkwy. pky pky.),
167
+ :pier => %w(pier),
168
+ :place => %w(place pl pl.),
169
+ :plaza => %w(plaza plz plz.),
170
+ :point => %w(point pt pt. pnt pnt.),
171
+ :ridge => %w(ridge ri ri.),
172
+ :road => %w(road rd rd.),
173
+ :row => %w(row),
174
+ :run => %w(run),
175
+ :springs => %w(springs spgs spgs.),
176
+ :square => %w(square sq sq.),
177
+ :street => %w(street st st.),
178
+ :station => %w(station sta sta.),
179
+ :terrace => %w(terrace ter ter. te te.),
180
+ :turnpike => %w(turnpike tpke tpke.),
181
+ :trace => %w(trace trc trc.),
182
+ :trail => %w(trail trl trl. tl tl.),
183
+ :valley => %w(valley vly vly.),
184
+ :walk => %w(walk),
185
+ :way => %w(way)
186
+ }
187
+
188
+ UNIT_TYPES = {
189
+ :suite => %w(suite ste ste.),
190
+ :number => %w(number # nbr nbr.),
191
+ :apartment => %w(apartment apt apt.)
192
+ }
193
+
194
+ SUPPORTED_FORMS = [:long, :short]
195
+ end
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), 'people_places_things', 'street_address', 'street_address')
2
+ require File.join(File.dirname(__FILE__), 'people_places_things', 'person_name', 'person_name')
3
+ require File.join(File.dirname(__FILE__), 'people_places_things', 'ansi_counties', 'ansi_counties')
4
+ require File.join(File.dirname(__FILE__), 'people_places_things', 'phone_number', 'phone_number')
5
+
6
+ module PeoplePlacesThings
7
+ VERSION = '1.1.2'
8
+ end
@@ -0,0 +1,61 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{people_places_things}
8
+ s.version = "1.1.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Danny Burkes"]
12
+ s.date = %q{2009-08-28}
13
+ s.description = %q{Parsers and formatters for person names, street addresses, city/state/zip, phone numbers, etc.}
14
+ s.email = %q{dburkes@netable.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "lib/people_places_things.rb",
24
+ "lib/people_places_things/ansi_counties/ansi_counties.rb",
25
+ "lib/people_places_things/ansi_counties/data/data.yml",
26
+ "lib/people_places_things/ansi_counties/data/process_data.rb",
27
+ "lib/people_places_things/ansi_counties/data/raw.txt",
28
+ "lib/people_places_things/person_name/person_name.rb",
29
+ "lib/people_places_things/phone_number/phone_number.rb",
30
+ "lib/people_places_things/street_address/street_address.rb",
31
+ "people_places_things.gemspec",
32
+ "spec/ansi_counties_spec.rb",
33
+ "spec/helper.rb",
34
+ "spec/person_name_spec.rb",
35
+ "spec/phone_number_spec.rb",
36
+ "spec/street_address_spec.rb"
37
+ ]
38
+ s.has_rdoc = true
39
+ s.homepage = %q{http://github.com/dburkes/people_places_things}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.1}
43
+ s.summary = %q{Parsers and formatters for person names, street addresses, city/state/zip, phone numbers, etc.}
44
+ s.test_files = [
45
+ "spec/ansi_counties_spec.rb",
46
+ "spec/helper.rb",
47
+ "spec/person_name_spec.rb",
48
+ "spec/phone_number_spec.rb",
49
+ "spec/street_address_spec.rb"
50
+ ]
51
+
52
+ if s.respond_to? :specification_version then
53
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
54
+ s.specification_version = 2
55
+
56
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
57
+ else
58
+ end
59
+ else
60
+ end
61
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec/helper'
2
+
3
+ describe PhoneNumber do
4
+ it "should parse ten digits" do
5
+ phone = PhoneNumber.parse '4045551212'
6
+ phone.area_code.should == '404'
7
+ phone.number.should == '5551212'
8
+ phone.exchange.should == '555'
9
+ phone.suffix.should == '1212'
10
+ end
11
+
12
+ it "should parse eleven digits" do
13
+ phone = PhoneNumber.parse '14045551212'
14
+ phone.area_code.should == '404'
15
+ phone.number.should == '5551212'
16
+ phone.exchange.should == '555'
17
+ phone.suffix.should == '1212'
18
+ end
19
+
20
+ it "should ignore certain characters" do
21
+ phone = PhoneNumber.parse '1 (404) 555-1212'
22
+ phone.area_code.should == '404'
23
+ phone.number.should == '5551212'
24
+ phone.exchange.should == '555'
25
+ phone.suffix.should == '1212'
26
+ end
27
+
28
+ it "should support international format, at least for US numbers, for now" do
29
+ phone = PhoneNumber.parse '+1 404 555-1212'
30
+ phone.area_code.should == '404'
31
+ phone.number.should == '5551212'
32
+ phone.exchange.should == '555'
33
+ phone.suffix.should == '1212'
34
+ end
35
+
36
+ it "should throw exception on unsupported format" do
37
+ lambda { PhoneNumber.parse('40455512') }.should raise_error
38
+ end
39
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dburkes-people_places_things
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Danny Burkes
@@ -22,9 +22,27 @@ extensions: []
22
22
  extra_rdoc_files:
23
23
  - README.rdoc
24
24
  files:
25
+ - .gitignore
25
26
  - README.rdoc
27
+ - Rakefile
28
+ - VERSION
29
+ - lib/people_places_things.rb
30
+ - lib/people_places_things/ansi_counties/ansi_counties.rb
31
+ - lib/people_places_things/ansi_counties/data/data.yml
32
+ - lib/people_places_things/ansi_counties/data/process_data.rb
33
+ - lib/people_places_things/ansi_counties/data/raw.txt
34
+ - lib/people_places_things/person_name/person_name.rb
35
+ - lib/people_places_things/phone_number/phone_number.rb
36
+ - lib/people_places_things/street_address/street_address.rb
37
+ - people_places_things.gemspec
38
+ - spec/ansi_counties_spec.rb
39
+ - spec/helper.rb
40
+ - spec/person_name_spec.rb
41
+ - spec/phone_number_spec.rb
42
+ - spec/street_address_spec.rb
26
43
  has_rdoc: true
27
44
  homepage: http://github.com/dburkes/people_places_things
45
+ licenses:
28
46
  post_install_message:
29
47
  rdoc_options:
30
48
  - --charset=UTF-8
@@ -45,12 +63,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
45
63
  requirements: []
46
64
 
47
65
  rubyforge_project:
48
- rubygems_version: 1.2.0
66
+ rubygems_version: 1.3.5
49
67
  signing_key:
50
68
  specification_version: 2
51
69
  summary: Parsers and formatters for person names, street addresses, city/state/zip, phone numbers, etc.
52
70
  test_files:
53
- - spec/person_name_spec.rb
54
- - spec/street_address_spec.rb
55
71
  - spec/ansi_counties_spec.rb
56
72
  - spec/helper.rb
73
+ - spec/person_name_spec.rb
74
+ - spec/phone_number_spec.rb
75
+ - spec/street_address_spec.rb