Yay, I think I just made my first, hopefully functional, piece of ruby code :)
Took me about 2.5 hours, including finding out about how to use
Comparable and define <=> :) ... including a long time to realize
delete_if is destructive :(

The solution does the following:
- groups persons by family, 
- orders the families descending by number of members 
- gets a santa as a member from the family with the most number of members
- for this santa, finds a santee as a member from the family with the
mose number of members, that is not the santa's family
rinse, repeat until there are no more santas and all is ok or, until
we can't find a santee for a santa and decide we can't hook these
people up

this seems ok, acts ok, but i can't really prove that it actually
works across all possible cases :)

Here's the code, sorry, it doesn't read from stdin, but it generates
it's own random input.
beware, this thing is probably badly coded :)

#####################

#!ruby
# evilchelu@irc
# mental@gmail

def generate_input(persons=10, families=3)
	fams = Array.new(families) {|i| 1}
	srand Time.now.to_i
	(persons-families).times do
		fams[rand(families)] += 1
	end
	s = ''
	fams.each_with_index do |fam, famnr|
		fam.times do |i|
			s += (97+i).chr + ' ' + (90-famnr).chr + ' ' + '<' + (97+i).chr +
'@' +  (90-famnr).chr + ".fam>\n"
		end
	end
	return s.sort_by {rand}
end

class Object
	def deep_clone
		Marshal::load(Marshal.dump(self))
	end
end

class Families < Array
	def sum_persons
		s = 0
		self.each { |el| s+= el.persons.size}
		return s
	end
	def get_person(exceptfamily='')
		self.find_all{ |fam| fam.name != exceptfamily
}.sort.reverse.first.persons.shift
	end
end

class Family
	include Comparable
	attr_accessor :name, :persons
	def initialize(name)
		@name = name
		@persons = Array.new
	end
	def to_s
		s = "Family: #{@name}\n"
		persons.each {|p| s+= "  " + p.to_s + "\n"}
		return s
	end
	def <=>(other)
		persons.size <=> other.persons.size
	end
end

class Person
	include Comparable
	attr_accessor :name, :family, :email
	def initialize(args)
		@name = args[0]
		@family = args[1]
		@email = args[2]
	end
	def to_s
		return [@name, @family, @email].join(" ").strip
	end
	def <=>(other)
		tmp = family <=> other.family
		tmp = name <=> other.name if tmp == 0
		return tmp
	end
end

def read_data(input)
	puts "Input\n#{input}\n"
	fams = Families.new
	persons = Families.new
	input.each do |line|
		persons.push Person.new(line.split(/\s/, 3))
		fam = fams.find { |fam| fam.name == persons.last.family}
		if fam.nil?
			fam = Family.new(persons.last.family)
			fams.push fam
		end
		fam.persons.push persons.last unless fam.persons.find{ |pers|
pers.name == persons.last.name}
	end
	return fams
end

def make_santas(fams)
	fams = fams.sort.reverse
	
	santas = fams.deep_clone
	santees = fams.deep_clone
	s = "List of santas (SANTA - SANTEE)\n"
	while santas.sum_persons > 0
		begin
			santa = santas.get_person
			santee = santees.get_person(santa.family)
		rescue
		end
		if (!santa || !santee)
			puts "Bad input. Red Sleigh Down"
			return
		end
		s += santa.to_s + ' - ' + santee.to_s + "\n"
	end
	puts s
end

make_santas(read_data(generate_input()))

#####################

-- 
Cristi BALAN