Skip to content

Instantly share code, notes, and snippets.

@patrick99e99
Created April 25, 2010 22:21
Show Gist options
  • Save patrick99e99/378778 to your computer and use it in GitHub Desktop.
Save patrick99e99/378778 to your computer and use it in GitHub Desktop.
class Contact < ActiveRecord::Base
with_options :dependent => :destroy do |parent|
parent.has_many :phones
parent.has_many :emails
parent.has_many :addresses
parent.has_one :photo
parent.has_one :user
end
accepts_nested_attributes_for :emails, :allow_destroy => true, :reject_if => lambda {|email| email["address"].blank? }
accepts_nested_attributes_for :phones, :allow_destroy => true, :reject_if => lambda {|phone| phone["number"].blank? }
accepts_nested_attributes_for :addresses, :allow_destroy => true, :reject_if => lambda {|address| address["street_1"].blank? }
validates_presence_of :first_name
validates_presence_of :last_name
...
class Photo < ActiveRecord::Base
belongs_to :photo_album
belongs_to :contact
belongs_to :access_level
acts_as_fleximage
....
def import
@card = Vpim::Vcard.decode(self.data).first
get_data
# find the contact if it already exists by email
@email = Email.find_by_address(@emails)
if @email
update_contact
else
create_contact
end
process_associations
end
private
def update_contact
@contact = @email.contact
@contact.update_attributes(contact_attributes)
end
def contact_attributes
{:url => @url,
:notes => @notes,
:birthday => @birthday}
end
def create_contact
@contact = Contact.create(contact_attributes.merge(:first_name => @first_name,
:last_name => @last_name))
end
def process_associations
card_emails
card_phones
card_addresses
card_photo
end
def get_data
@first_name = @card.name.given.blank? ? nil : remove_newlines(@card.name.given.capitalize)
@last_name = @card.name.family.blank? ? nil : remove_newlines(@card.name.family.capitalize)
@first_name = @card.name.fullname if (@first_name.nil? && @last_name.nil? && !@card.name.fullname.blank?)
@birthday = @card.birthday.blank? ? nil : @card.birthday
@url = @card.url.blank? ? nil : remove_newlines(@card.url.uri.gsub("\\",""))
@notes = @card.note.blank? ? nil : remove_newlines(@card.note)
@phones = @card.telephones.map {|p| remove_newlines(p)}
@streets = @card.addresses.map {|a| remove_newlines(a.street)}
@emails = @card.emails.map {|e| remove_newlines(e.to_s.downcase)}
end
def card_addresses
street_numbers = @contact.addresses.map(&:street_1).map(&:numbers_only)
@card.addresses.each do |@address|
# don't bother saving the address if there is no region (state)
# assume that if the contact currently has an address with the same numbers that this address is already stored
unless @address.region.blank? || street_numbers.include?(@address.street.numbers_only)
@country = get_country || Country.find_by_name("United States")
region = get_region
next if region.nil?
# labels retrieved from the vcard are only home or work.. custom does not work.
@contact.addresses << Address.create(:country_id => @country.id,
:region_id => region.id,
:street_1 => remove_newlines(@address.street),
:street_2 => remove_newlines(@address.pobox),
:city => remove_newlines(@address.locality),
:postal_code => remove_newlines(@address.postalcode),
:label => get_label(@address))
end
end
end
def get_country
if !@address.country.blank?
card_country = @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)
end
end
def get_region
# best way I could deal with nev. => nev
card_region = (@address.region.last == "." ? @address.region.chop : @address.region)
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) }
end
def card_phones
@card.telephones.each do |phone|
# phone is something like: #<Vpim::Vcard::Telephone: "123-456-7890", pref, work
# doing phone.to_s will extract just the number
unless @contact.phones.map(&:number).map(&:numbers_only).include?(phone.to_s.numbers_only)
# labels retrieved from the vcard are only home or work.. custom does not work.
p = Phone.create(:label => get_label(phone), :number => remove_newlines(phone.to_s))
@contact.phones << p
end
end
end
def card_emails
@card.emails.each do |email|
# email is something like: #<Vpim::Vcard::Email: "test@yahoo.com", pref, home
# doing email.to_s will extract just the email address
unless @contact.emails.map(&:address).map(&:downcase).include?(email.to_s.downcase)
# labels retrieved from the vcard are only home or work.. custom does not work.
e = Email.create(:label => get_label(email), :address => remove_newlines(email.to_s.downcase))
@contact.emails << e
end
end
end
def card_photo
unless @card.photos.size.zero?
if @contact.photo.nil?
photo_album = PhotoAlbum.find_or_create_by_name_with_access_level('users', 'admin')
description = "User photo"
access = AccessLevel.find_by_name('public')
@contact.photo = Photo.create(:image_file_string => @card.photos.first,
:contact_id => @contact.id,
:name => "#{@contact.first_name} #{@contact.last_name}".strip,
:description => "User photo",
:access_level_id => access,
:photo_album_id => photo_album.id,
:server_format => "JPEG")
end
end
end
def get_label(obj)
obj.location.blank? ? nil : remove_newlines(obj.location).gsub("-","").strip
end
def remove_newlines(obj)
obj.to_s.gsub("\n", " ")
end
require 'spec_helper'
describe Vcard do
describe "importing a vcard" do
Vcard.delete_all
Region.delete_all
Region.create!(:name => "CA", :country_id => 237)
def mock_country
@country = mock_model(Country, {:id => 237, :name => "United States"})
Country.stub!(:find_by_name).with("United States").and_return(@country)
end
context "it has a birthday, url and an invalid address" do
before(:all) do
card_data = File.read(RAILS_ROOT + "/spec/fixtures/vcard/kirk_no_photo.vcf")
@vcard = Vcard.create!(:data => card_data)
end
before(:each) do
Contact.all.map(&:destroy)
mock_country
# could not get this to work...
#
# photo_content = File.read(RAILS_ROOT + "/spec/fixtures/vcard/image/kirk.jpg")
# @photo = mock_model(Photo, {:image_file_string => photo_content})
# Photo.should_receive(:create).and_return(@photo)
end
it "should update an existing contact when an email is found" do
@contact = Contact.create!(:first_name => "James", :last_name => "Kirk")
@contact.emails << Email.create!(:address => 'kirk@enterprise.com')
@vcard.import
@contact.reload
@contact.birthday.should_not be_blank
@contact.url.should_not be_blank
# because I could not get the photo to work in the spec..
# @contact.photo.should == @photo
end
it "should create a new contact when an email is not found" do
@vcard.import
Contact.find_by_first_name_and_last_name("James", "Kirk").should_not be_nil
end
it "should not create an address if the state cannot be found" do
@vcard.import
Contact.last.addresses.should be_blank
end
end
context "it has 3 valid phones, addresses, and emails" do
before(:all) do
mock_country
card_data = File.read(RAILS_ROOT + "/spec/fixtures/vcard/felds_no_photo.vcf")
@vcard = Vcard.create!(:data => card_data)
@vcard.import
end
it "should create 3 new addresses" do
Contact.last.addresses.count.should == 3
end
it "should create 3 new phones" do
Contact.last.phones.count.should == 3
end
it "should create 3 new emails" do
Contact.last.emails.count.should == 3
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment