Browse files

initial commit

  • Loading branch information...
0 parents commit 0fe2a45af7a9dcdbc97798bd61843b8c5406ca87 @chorn committed Jun 28, 2010
Showing with 368 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +20 −0 LICENSE
  3. +13 −0 README
  4. +3 −0 README.rdoc
  5. +57 −0 Rakefile
  6. +3 −0 TODO
  7. +185 −0 lib/nameable.rb
  8. +25 −0 nameable.gemspec
  9. +14 −0 script/destroy
  10. +14 −0 script/generate
  11. +7 −0 spec/nameable_spec.rb
  12. +2 −0 spec/spec_helper.rb
  13. +23 −0 test.rb
2 .gitignore
@@ -0,0 +1,2 @@
+.DS_Store
+*.gem
20 LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2008 Chris Horn http://chorn.com/
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13 README
@@ -0,0 +1,13 @@
+
+A person's name is trivial when you validate the input at data entry
+time. That is not always possible, and for my enterprise it is almost
+never possible.
+
+References: http://www.onlineaspect.com/2009/08/17/splitting-names/
+
+require "nameable"
+
+
+
+
+-chorn
3 README.rdoc
@@ -0,0 +1,3 @@
+== nameable
+
+A gem that provides parsing and output of people's names.
57 Rakefile
@@ -0,0 +1,57 @@
+require 'rubygems'
+require 'rake/gempackagetask'
+require 'rubygems/specification'
+require 'date'
+require 'spec/rake/spectask'
+
+GEM = "dialable"
+GEM_VERSION = "0.0.1"
+AUTHOR = "Chris Horn"
+EMAIL = "chorn@chorn.com"
+HOMEPAGE = "http://github.com/chorn/dialable"
+SUMMARY = "A gem that provides parsing and output of phone numbers according to NANPA standards."
+
+spec = Gem::Specification.new do |s|
+ s.name = GEM
+ s.version = GEM_VERSION
+ s.platform = Gem::Platform::RUBY
+ s.has_rdoc = true
+ s.extra_rdoc_files = ["README", "LICENSE", 'TODO']
+ s.summary = SUMMARY
+ s.description = s.summary
+ s.author = AUTHOR
+ s.email = EMAIL
+ s.homepage = HOMEPAGE
+
+ # Uncomment this to add a dependency
+ # s.add_dependency "foo"
+
+ s.require_path = 'lib'
+ s.autorequire = GEM
+ s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,spec}/**/*")
+end
+
+task :default => :spec
+
+desc "Run specs"
+Spec::Rake::SpecTask.new do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts = %w(-fs --color)
+end
+
+
+Rake::GemPackageTask.new(spec) do |pkg|
+ pkg.gem_spec = spec
+end
+
+desc "install the gem locally"
+task :install => [:package] do
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
+end
+
+desc "create a gemspec file"
+task :make_spec do
+ File.open("#{GEM}.gemspec", "w") do |file|
+ file.puts spec.to_ruby
+ end
+end
3 TODO
@@ -0,0 +1,3 @@
+TODO:
+Make rspec actually test some stuff.
+
185 lib/nameable.rb
@@ -0,0 +1,185 @@
+# Copyright (c) 2010 Chris Horn http://chorn.com/
+# See MIT-LICENSE.txt
+
+# TODO: Make this less sucky.
+
+module Nameable
+ class Latin
+
+ ##
+ # Raised if something other than a valid Name is supplied
+ class InvalidNameError < StandardError
+ end
+
+ ##
+ # Regex's to match the detritus that people add to their names
+ module Patterns
+ PREFIX = {
+ "Mr." => /^\(*(mr\.*|mister)\)*$/i,
+ "Mrs." => /^\(*(mrs\.*|misses)\)*$/i,
+ "Ms." => /^\(*(ms\.*|miss)\)*$/i,
+ "Dr." => /^\(*(dr\.*|doctor)\)*$/i,
+ "Rev." => /^\(*(rev\.*|reverand)\)*$/i,
+ "Fr." => /^\(*(fr\.*|friar)\)*$/i,
+ "Master" => /^\(*(master)\)*$/i,
+ "Sir" => /^\(*(sir)\)*$/i
+ }
+
+ SUFFIX = {
+ "Sr." => /^\(*(sr\.|senior)\)*$/i,
+ "Jr." => /^\(*(jr\.|junior)\)*$/i,
+ "Esq." => /^\(*(esq\.|esquire)\)*$/i,
+ "PhD." => /^\(*(phd\.)\)*$/i
+ }
+
+ SUFFIX_GENERATIONAL_ROMAN = /^\(*[IVX\.]+\)*$/i
+ 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
+ SUFFIX_PROFESSIONAL = /^(PE|CSA|CPA|CPL|CME|CEng|OFM|CSV|Douchebag)$/i
+ SUFFIX_ABBREVIATION = /^[A-Z\.]+$/
+
+ LAST_NAME_PRE_DANGLERS = /^(vere|von|van|de|del|della|di|da|pietro|vanden|du|st|la|ter|ten)$/i
+ LAST_NAME_PRE_CONCATS = /^(o'|o`|mc)$/i
+ end
+
+ attr_accessor :prefix, :first, :middle, :last, :suffix
+
+ ##
+ #
+ def initialize(parts={})
+ self.prefix = parts[:prefix] ? parts[:prefix] : nil
+ self.first = parts[:first] ? parts[:first] : nil
+ self.middle = parts[:middle] ? parts[:middle] : nil
+ self.last = parts[:last] ? parts[:last] : nil
+ self.suffix = parts[:suffix] ? parts[:suffix] : nil
+ end
+
+ ##
+ # name is an Array
+ def extract_prefix(name)
+ return unless name and name.size > 1 and @prefix.nil? and @first.nil?
+ Patterns::PREFIX.each_pair do |pretty, regex|
+ if name.first =~ regex
+ @prefix = pretty
+ name.delete(name.first)
+ return
+ end
+ end
+ end
+
+ ##
+ # name is an Array
+ def extract_suffix(name)
+ return unless name and name.size >= 3
+
+ (name.size - 1).downto(2) do |n|
+ suff = nil
+
+ Patterns::SUFFIX.each_pair do |pretty, regex|
+ suff = pretty if name[n] =~ regex
+ end
+
+ if name[n] =~ Patterns::SUFFIX_ACADEMIC or name[n] =~ Patterns::SUFFIX_PROFESSIONAL or name[n] =~ Patterns::SUFFIX_GENERATIONAL_ROMAN or name[n] =~ Patterns::SUFFIX_ABBREVIATION
+ suff = name[n].upcase.gsub(/\./,'')
+ end
+
+ if suff
+ @suffix = @suffix ? "#{suff}, #{@suffix}" : suff
+ name.delete_at(n)
+ end
+
+ end
+ end
+
+ ##
+ # name is an Array
+ def extract_first(name)
+ return unless name and name.size >= 1
+
+ @first = name.first
+ name.delete_at(0)
+
+ @first.capitalize! unless @first =~ /[a-z]/ and @first =~ /[A-Z]/
+ end
+
+ ##
+ # name is an Array
+ def extract_last(name)
+ return unless name and name.size >= 1
+
+ @last = name.last
+ name.delete_at(name.size - 1)
+
+ @last.capitalize! unless @last =~ /[a-z]/ and @last =~ /[A-Z]/
+ end
+
+ ##
+ # name is an Array
+ def extract_middle(name)
+ return unless name and name.size >= 1
+
+ (name.size - 1).downto(0) do |n|
+ next unless name[n]
+
+ if name[n] =~ Patterns::LAST_NAME_PRE_DANGLERS
+ @last = "#{name[n]} #{@last}"
+ elsif name[n] =~ Patterns::LAST_NAME_PRE_CONCATS
+ @last = "O'#{@last}"
+ elsif name[n] =~ /-+/ and n > 0 and name[n-1]
+ @last = "#{name[n-1]}-#{@last}"
+ name[n-1] = nil
+ else
+ @middle = name[n]
+ end
+
+ name.delete_at(n)
+ end
+
+ @middle.capitalize! if @middle and !(@middle =~ /[a-z]/ and @middle =~ /[A-Z]/)
+ @middle = "#{@middle}." if @middle and @middle.size == 1
+ end
+
+ def parse(name)
+ if name.class == String
+ name = name.split(/\s+/)
+ end
+
+ name = name.first.split(/[^[:alnum:]]+/) if name.size == 1 and name.first.split(/[^[:alnum:]]+/)
+
+ extract_prefix(name)
+ extract_suffix(name)
+ extract_first(name)
+ extract_last(name)
+ extract_middle(name)
+
+ raise InvalidNameError, "A parseable name was not found. #{name.inspect}" unless @first
+
+ self
+ end
+
+ def to_s
+ [@prefix, @first, @middle, @last, @suffix].compact.join(' ')
+ end
+
+ def to_name
+ to_nameable
+ end
+
+ def to_fullname
+ [@prefix, @first, @middle, @last, @suffix].compact.join(' ')
+ end
+
+ def to_nameable
+ [@first, @last].compact.join(' ')
+ end
+
+ def to_hash
+ return {
+ :prefix => @prefix,
+ :first => @first,
+ :middle => @middle,
+ :last => @last,
+ :suffix => @suffix
+ }
+ end
+ end
+end
25 nameable.gemspec
@@ -0,0 +1,25 @@
+Gem::Specification.new do |s|
+ s.name = "nameable"
+ s.version = "0.1"
+ s.date = "2010-06-23"
+ s.summary = "Provides parsing and output of person names."
+ s.email = "chorn@chorn.com"
+ s.homepage = "http://github.com/chorn/nameable"
+ s.description = "A gem that provides parsing and output of person names."
+ s.has_rdoc = true
+ s.authors = ["Chris Horn"]
+ s.files = [ "LICENSE",
+ "README",
+ "README.rdoc",
+ "Rakefile",
+ "TODO",
+ "nameable.gemspec",
+ "lib/nameable.rb",
+ "script/destroy",
+ "script/generate",
+ "spec/nameable_spec.rb",
+ "spec/spec_helper.rb"
+ ]
+ s.test_files = []
+ s.rdoc_options = []
+end
14 script/destroy
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/destroy'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
+RubiGen::Scripts::Destroy.new.run(ARGV)
14 script/generate
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
+
+begin
+ require 'rubigen'
+rescue LoadError
+ require 'rubygems'
+ require 'rubigen'
+end
+require 'rubigen/scripts/generate'
+
+ARGV.shift if ['--help', '-h'].include?(ARGV[0])
+RubiGen::Base.use_component_sources! [:newgem_simple, :test_unit]
+RubiGen::Scripts::Generate.new.run(ARGV)
7 spec/nameable_spec.rb
@@ -0,0 +1,7 @@
+require File.dirname(__FILE__) + '/spec_helper'
+
+describe "nameable" do
+ it "should do nothing" do
+ true.should == true
+ end
+end
2 spec/spec_helper.rb
@@ -0,0 +1,2 @@
+$TESTING=true
+$:.push File.join(File.dirname(__FILE__), '..', 'lib')
23 test.rb
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+
+require "rubygems"
+require "lib/nameable"
+
+DATA.each do |testcase|
+ n = Nameable::Latin.new.parse(testcase)
+ puts "``#{testcase.chomp}'' -> #{n.to_fullname}"
+end
+
+__END__
+Mr. Chris Horn PhD
+Chris Horn T.I.T.L.E.
+Chris Horn II
+Chris Horn II Esquire
+Chris O'Horn
+Chris McHorn
+Chris Von Horn
+Chris O' Horn
+Chris K Horn - Horn
+CHRIS HORN
+CHRIS-HORN
+CHRIS;HORN

0 comments on commit 0fe2a45

Please sign in to comment.