Skip to content

Instantly share code, notes, and snippets.

@patrick99e99
Created April 29, 2010 06:32
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save patrick99e99/383233 to your computer and use it in GitHub Desktop.
def import
parse_card
if found_contact
update_contact
else
create_contact
end
process_associations
end
def parse_card
card = Vpim::Vcard.decode(self.data).first
@card = {}
@card[:first_name] = card.name.given.capitalize unless card.name.given.blank?
@card[:last_name] = card.name.family.capitalize unless card.name.family.blank?
# if there is no first or last name, try getting it from the full name attribute
if !@card[:first_name] && !@card[:last_name]
@card[:first_name], @card[:last_name] = card.name.fullname.split.map(&:capitalize) unless card.name.fullname.blank?
end
@card[:birthday] = card.birthday unless card.birthday.blank?
@card[:url] = card.url.uri unless card.url.blank?
@card[:notes] = card.note unless card.note.blank?
@card[:photo] = card.photos.first unless card.photos.size.zero?
# phone is something like: #<Vpim::Vcard::Telephone: "123-456-7890", pref, work
# doing phone.to_s will extract just the number
@card[:phones] = card.telephones.map {|p| {:label => get_label(p), :number => p.to_s.downcase}}
# email is something like: #<Vpim::Vcard::Email: "test@yahoo.com", pref, home
# doing email.to_s will extract just the email address
@card[:emails] = card.emails.map {|e| {:label => get_label(e), :address => e.to_s.downcase}}
# set default country
@usa_id = Country.find_by_name("United States")
@card[:addresses] = card.addresses.map do |a|
country_id = get_country(a)
{:label => get_label(a),
:country_id => country_id,
:region_id => get_region(a, country_id),
:street_1 => a.street,
:street_2 => a.pobox,
:city => a.locality,
:postal_code => a.postalcode
}
end
filter_card_data(@card)
end
def filter_card_data(card_data)
# remove newlines and backslashes from text.
# {x=>"foo\n", y=>[{z=>"bar\\"}]} => {x=>"foo", y=>[{z=>"bar"}]}
card_data.each do |k, v|
if card_data[k].is_a?(Array)
card_data[k].each do |arr|
filter_card_data(arr)
end
else
next if skip_list.include?(k)
card_data[k] = remove_newlines(v).gsub("\\", "")
end
end
end
def skip_list
[:photo, :birthday]
end
def remove_newlines(text)
text.to_s.gsub("\n", "")
end
def get_country(card_address)
return @usa_id if card_address.country.blank?
# u.s.a. -> usa
card_country = card_address.country.gsub(".", "")
@countries ||= Country.all
country_attributes = [:name, :official_name, :alpha_2_code, :alpha_3_code]
find_in_list(@countries, country_attributes, card_country) || @usa_id
end
def get_region(card_address, country_id = @usa_id)
return if card_address.region.blank?
# nev. --> nev
card_region = card_address.region.split(/^(.*)?\.+$/).last
regions_for_country = Region.find_all_by_country_id(country_id)
region_attributes = [:name, :abbreviation, :abbrev2, :abbrev3]
find_in_list(regions_for_country, region_attributes, card_region)
end
def find_in_list(records, attributes, to_find)
# a better way of doing Country.all.find {|c| [c.name, c.official_name].compact.map(&:downcase).include?(to_find.downcase) }
records.find {|r| attributes.map{|a| r.send(a)}.compact.map(&:downcase).include?(to_find.downcase) } if records
end
def update_contact
@contact.update_attributes(contact_attributes)
end
def contact_attributes
{:url => @card[:url],
:notes => @card[:notes],
:birthday => @card[:birthday]}
end
def found_contact
[find_by_emails, find_by_phones, find_by_addresses].each do |method|
if contact_found = method
return @contact = contact_found
end
end
# not found
nil
end
def find_by_emails
emails = @card[:emails].map {|e| e[:address]}
email = Email.find_by_address(emails)
email.contact if email
end
def find_by_phones
phones = @card[:phones].map {|p| p[:number]}
phone = Phone.find_by_number(phones)
phone.contact if phone
end
def find_by_addresses
streets = @card[:addresses].map {|a| a[:street_1]}
address = Address.find_by_street_1(streets)
address.contact if address
end
def create_contact
@contact = Contact.create(contact_attributes.merge(:first_name => @card[:first_name],
:last_name => @card[:last_name]))
end
def process_associations
card_emails
card_phones
card_addresses
# only replace a contact's photo if they don't have one set
card_photo if @card[:photo] && @contact.photo.nil?
end
def card_addresses
streets = @contact.addresses.map(&:street_1)
addresses = @card[:addresses].reject {|a| a[:region_id].nil? || streets.include?(a[:street_1])}
addresses.each do |address|
@contact.addresses << Address.create(address)
end
end
def card_phones
contact_numbers = @contact.phones.map(&:number).map(&:numbers_only)
phones = @card[:phones].reject {|p| contact_numbers.include?(p[:number].numbers_only) }
phones.each do |phone|
@contact.phones << Phone.create(phone)
end
end
def card_emails
contact_emails = @contact.emails.map(&:address)
emails = @card[:emails].reject {|e| contact_emails.include?(e[:address]) }
emails.each do |email|
@contact.emails << Email.create(email)
end
end
def card_photo
photo_album = PhotoAlbum.find_or_create_by_name_with_access_level('users', 'admin')
access_level = AccessLevel.find_by_name('public')
@contact.photo = Photo.create(:image_file_string => @card[:photo],
:contact_id => @contact.id,
:name => "#{@contact.first_name} #{@contact.last_name}".strip,
:description => "User photo",
:access_level_id => access_level.id,
:photo_album_id => photo_album.id,
:server_format => "JPEG")
end
def get_label(obj)
obj.location.blank? ? nil : remove_newlines(obj.location).gsub("-","").strip
end
require 'spec_helper'
describe Vcard do
def reset_tables
Vcard.delete_all
Contact.all.map(&:destroy)
end
def country_mock
@usa = mock_model(Country, {:name => 'United States'})
Country.stub!(:find_by_name).with('United States').and_return(@usa)
end
def setup_contact
@contact = Contact.create!(:first_name => 'test', :last_name => 'test')
end
def get_card_data(card)
card_data = File.read(RAILS_ROOT + "/spec/fixtures/vcard/#{card}.vcf")
@vcard = Vcard.create!(:data => card_data)
end
def add_email(address)
email = Email.create!(:address => address)
@contact.emails << email
end
def add_address(street)
address = Address.create!(:street_1 => street)
@contact.addresses << address
end
def add_phone(number)
phone = Phone.create!(:number => number)
@contact.phones << phone
end
before(:all) do
reset_tables
country_mock
setup_contact
end
describe "vCard parsing tests" do
before(:all) do
get_card_data(:test)
@vcard.parse_card
end
context "finding a contact" do
it "should find the contact when an email for the contact exists" do
add_email("test@test.com")
end
it "should find the contact when a phone for the contact exists" do
add_phone("000-000-0000")
end
it "should find the contact when an address for the contact exists" do
add_address("123 test st.")
end
after(:each) do
@vcard.found_contact.should == @contact
end
end
context "sanitizing data" do
it "should remove all new lines and double backslashes from nested card data" do
test_data = {1=>"foo\n", 2=>[{3=>"bar\\"}]}
@vcard.filter_card_data(test_data).should == {1=>"foo", 2=>[{3=>"bar"}]}
end
end
context "finding the location" do
it "should default to United States if no country is provided" do
address = mock_model(Address)
address.stub(:country)
@vcard.get_country(address).should == @usa
end
end
end
describe "vcard importing tests" do
context "updating a contact" do
it "should update the attributes of a pre-existing contact" do
add_email("test@test.com")
get_card_data(:test)
@vcard.import
@contact.reload
@contact.notes.should_not be_nil
@contact.birthday.should_not be_nil
@contact.url.should_not be_nil
end
it "should add multiple emails to a pre-existing contact but not add one if it already exists" do
@contact = Contact.create(:first_name => 'James', :last_name => 'Kirk')
add_email("kirk@enterprise.com")
get_card_data(:kirk)
@vcard.import
@contact.emails.length.should == 3
end
it "should add multiple phones to a pre-existing contact but not add one if it already exists" do
@contact = Contact.create(:first_name => 'James', :last_name => 'Kirk')
add_phone("123-123-1234")
get_card_data(:kirk)
@vcard.import
@contact.phones.length.should == 3
end
it "should add multiple addresses to a pre-existing contact but not add one if it already exists" do
@contact = Contact.create(:first_name => 'James', :last_name => 'Kirk')
add_address("1000 space drive")
get_card_data(:kirk)
@vcard.import
@contact.addresses.length.should == 3
end
end
context "creating a contact" do
before(:each) do
Contact.all.map(&:destroy)
end
it "should create a contact when it cannot be found by email, phone, or address" do
get_card_data(:test)
@vcard.import
Contact.count.should == 1
end
it "should create a new contact and add multiple addresses" do
get_card_data(:kirk)
@vcard.import
Contact.find_by_first_name_and_last_name("James", "Kirk").addresses.length.should == 3
end
it "should create a new contact and add multiple emails" do
get_card_data(:kirk)
@vcard.import
Contact.find_by_first_name_and_last_name("James", "Kirk").emails.length.should == 3
end
it "should create a new contact and add multiple phones" do
get_card_data(:kirk)
@vcard.import
Contact.find_by_first_name_and_last_name("James", "Kirk").phones.length.should == 3
end
it "should create a photo for the contact" do
get_card_data(:felds)
access_level = mock_model(AccessLevel, {:name => "public"})
AccessLevel.should_receive(:find_by_name).with('public').and_return(access_level)
photo_album = mock_model(PhotoAlbum, {:name => "some photo album"})
PhotoAlbum.should_receive(:find_or_create_by_name_with_access_level).and_return(photo_album)
Photo.should_receive(:create).and_return(Photo.new)
@vcard.import
Contact.find_by_first_name_and_last_name("Lawrence", "Felds").photo.should_not be_nil
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment