[SOLUTION] Secret Santas (#2)

this solution parses stdin or an input file. the input may contain blank line
and comments. the input is first hashed last name -> first name -> email
using the Family class. next a Family::GiftPool for the current state of the
Family class is created and all families and their members iterated over; the
pool's 'draw_name' method randomly choses a santa not in the same family and
not the same person - the selection is destructive, meaning after someone has
been chosen they can no longer be chosen again. this method requires one pass
over the data (not counting the copy) and no sorting. mailing also included.

#!/usr/bin/env ruby
require 'net/smtp'

class EmailList < Array
#{{{
   def initialize port
#{{{
     buf =
       if port.respond_to? 'read'
         port.read
       else
         "#{ port }"
       end
     parse_buf buf
#}}}
   end
   def parse_buf buf
#{{{
     buf.each do |line|
       line.gsub! %r/^\s*|\s*$|#.*$/, ''
       next if line.empty?
       name, address = line.split %r/[<>]/
       raise "syntax error in <#{ line }>" unless
         name and address
       tokens = name.strip.split %r/\s+/
       last, first = tokens.pop, tokens.join(' ')
       name = [first, last]
       self << [name, address]
     end
#}}}
   end
#}}}
end
class Family < Hash
#{{{
   class << self
#{{{
     def families last = nil
#{{{
       @families ||= {}
       if last
         @families[last] ||=
           Hash::new{|h,last| h[last] ||= Family::new}
       else
         @families
       end
#}}}
     end
     alias families
     def each(*a, &b); families.each(*a, &b); end
     def gift_pool; GiftPool::new families; end
#}}}
   end
   class GiftPool
#{{{
     def initialize families
#{{{
       @pool = Hash::new{|h,k| h[k] = Hash::new}
       families.each do |last, members|
         members.each do |first, email|
           @pool[last][first] = email
         end
       end
#}}}
     end
     def draw_name last, first #{{{
       not_in_family = @pool.keys - [last]
       family = not_in_family[rand(not_in_family.size)]

       members = @pool[family].keys - [first]
       member = members[rand(members.size)]

       name = [family, member]
       email = @pool[family][member]
       santa = [name, email]

       @pool[family].delete member
       @pool.delete family if @pool[family].empty?

       santa
#}}}
     end
#}}}
   end
#}}}
end
module Mailer
#{{{
   def self.mail msg, to, from, opts = {} #{{{
     Net::SMTP.start('localhost') do |smtp|
       email = ''
       opts.each do |k, v|
         email << "#{ k.capitalize }: #{ v }\r\n"
       end
       email << "\r\n#{ msg }"
       smtp.send_message email, from, to
     end
#}}}
   end
#}}}
end

port = (ARGV.empty? ? STDIN : open(ARGV.shift))

list = EmailList::new port

list.each do |name, email|
   Family[name.last][name.first] = email
end

gift_pool = Family.gift_pool

Family.each do |last, members|
   members.each do |first, email|
     santa = gift_pool.draw_name last, first
     sname, semail = santa
     msg = "you are the secret santa for <#{ sname.join ', ' }>"
     Mailer::mail msg, email, nil, 'subject' => 'secret santa'
   end
end

-a

santa.rb (2.54 KB)

···

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Ara.T.Howard wrote:

class EmailList < Array
#{{{
  [snip]
#}}}
end

hello Ara,

Just out of interest, please do not understand this as an offence:
to which end are you using all those #{{{ comments? Is it to make your editor understand indentation, or what is its purpose?

Regards,

Brian

···

--
Brian Schröder
http://ruby.brian-schroeder.de/

vim is a folding editor. everything inbetween the markers appear as one line
as something like

   +-- 3 lines: >> --------------------------------------------

in otherwords each class in my solution looks, to me, as

   class Foo
   +-- n lines: >> ------------
   end

and each method likewise. if you don't use folding i highly reccomend it -
folds can be cut, pasted, operated on, etc. and, of course, it makes 10,000
source file a breeze to navigate.

cheers.

-a

···

On Tue, 5 Oct 2004, [ISO-8859-1] Brian Schröder wrote:

Ara.T.Howard wrote:

class EmailList < Array
#{{{
  [snip]
#}}}
end

hello Ara,

Just out of interest, please do not understand this as an offence:
to which end are you using all those #{{{ comments? Is it to make your
editor understand indentation, or what is its purpose?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

That's neat. Would you care to share the relevant lines from your
.vimrc?

Best Regards //Anders

···

On Tue, Oct 05, 2004 at 05:04:52AM +0900, Ara.T.Howard@noaa.gov wrote:

On Tue, 5 Oct 2004, [ISO-8859-1] Brian Schröder wrote:

>Ara.T.Howard wrote:
>
>>class EmailList < Array
>>#{{{
>> [snip]
>>#}}}
>>end
>
>hello Ara,
>
>Just out of interest, please do not understand this as an offence:
>to which end are you using all those #{{{ comments? Is it to make your
>editor understand indentation, or what is its purpose?

vim is a folding editor. everything inbetween the markers appear as one
line
as something like

  +-- 3 lines: >> --------------------------------------------

in otherwords each class in my solution looks, to me, as

  class Foo
  +-- n lines: >> ------------
  end

and each method likewise. if you don't use folding i highly reccomend it -
folds can be cut, pasted, operated on, etc. and, of course, it makes 10,000
source file a breeze to navigate.

--
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. Anders Engström aengstrom@gnejs.net
. http://www.gnejs.net PGP-Key: ED010E7F
. [Your mind is like an umbrella. It doesn't work unless you open it.]

Ahh,

thanks for the pointer. I found the same for xemacs. But I think I personally like hide-show better. Entering this additional comments seem like too much work to me.

Regards,

Brian

···

Ara.T.Howard@noaa.gov wrote:

On Tue, 5 Oct 2004, [ISO-8859-1] Brian Schr�der wrote:

Ara.T.Howard wrote:

class EmailList < Array
#{{{
  [snip]
#}}}
end

hello Ara,

Just out of interest, please do not understand this as an offence:
to which end are you using all those #{{{ comments? Is it to make your
editor understand indentation, or what is its purpose?

vim is a folding editor. everything inbetween the markers appear as one line
as something like

  +-- 3 lines: >> --------------------------------------------

in otherwords each class in my solution looks, to me, as

  class Foo
  +-- n lines: >> ------------
  end

and each method likewise. if you don't use folding i highly reccomend it -
folds can be cut, pasted, operated on, etc. and, of course, it makes 10,000
source file a breeze to navigate.

cheers.

-a
--

> EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
> PHONE :: 303.497.6469
> A flower falls, even though we love it;
> and a weed grows, even though we do not love it. | --Dogen

--
Brian Schröder
http://ruby.brian-schroeder.de/

set foldmethod=marker
     set commentstring=#%s

to use: highlight visually using 'shift-v' and motion keys then type

   z-f -> fold create
   z-o -> fold open
   z-c -> fold close
   z-d -> fold delete

all the fold command start with z because it looks like a fold :wink:

if you add those settings and open my script you'll see how it works quickly -
because the folds are marked they come up folded.

cheers.

-a

···

On Tue, 5 Oct 2004, Anders [iso-8859-1] Engström wrote:

That's neat. Would you care to share the relevant lines from your
vimrc?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

but then they are lost when you close the file... if they are 'marked' they
are restored on open.

-a

···

On Tue, 5 Oct 2004, [UTF-8] Brian Schröder wrote:

thanks for the pointer. I found the same for xemacs. But I think I
personally like hide-show better. Entering this additional comments seem
like too much work to me.

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Isn't it possible to fold based on syntax/indentation, thus getting
automatic Ruby folding without special markers?

Gavin

···

On Tuesday, October 5, 2004, 7:44:53 AM, Ara wrote:

On Tue, 5 Oct 2004, [UTF-8] Brian Schröder wrote:

thanks for the pointer. I found the same for xemacs. But I think I
personally like hide-show better. Entering this additional comments seem
like too much work to me.

but then they are lost when you close the file... if they are 'marked' they
are restored on open.

set foldmethod=syntax

it folds like mad though.

-a

···

On Tue, 5 Oct 2004, Gavin Sinclair wrote:

On Tuesday, October 5, 2004, 7:44:53 AM, Ara wrote:

On Tue, 5 Oct 2004, [UTF-8] Brian Schröder wrote:

thanks for the pointer. I found the same for xemacs. But I think I
personally like hide-show better. Entering this additional comments seem
like too much work to me.

but then they are lost when you close the file... if they are 'marked' they
are restored on open.

Isn't it possible to fold based on syntax/indentation, thus getting
automatic Ruby folding without special markers?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================

Can you impose limits on the madness?

Gavin

···

On Tuesday, October 5, 2004, 10:44:51 AM, Ara wrote:

On Tue, 5 Oct 2004, Gavin Sinclair wrote:

On Tuesday, October 5, 2004, 7:44:53 AM, Ara wrote:

On Tue, 5 Oct 2004, [UTF-8] Brian Schröder wrote:

thanks for the pointer. I found the same for xemacs. But I think I
personally like hide-show better. Entering this additional comments seem
like too much work to me.

but then they are lost when you close the file... if they are 'marked' they
are restored on open.

Isn't it possible to fold based on syntax/indentation, thus getting
automatic Ruby folding without special markers?

   set foldmethod=syntax

it folds like mad though.

set foldlevel=n

-a

···

On Tue, 5 Oct 2004, Gavin Sinclair wrote:

On Tuesday, October 5, 2004, 10:44:51 AM, Ara wrote:

On Tue, 5 Oct 2004, Gavin Sinclair wrote:

On Tuesday, October 5, 2004, 7:44:53 AM, Ara wrote:

On Tue, 5 Oct 2004, [UTF-8] Brian Schröder wrote:

thanks for the pointer. I found the same for xemacs. But I think I
personally like hide-show better. Entering this additional comments seem
like too much work to me.

but then they are lost when you close the file... if they are 'marked' they
are restored on open.

Isn't it possible to fold based on syntax/indentation, thus getting
automatic Ruby folding without special markers?

   set foldmethod=syntax

it folds like mad though.

Can you impose limits on the madness?

--

EMAIL :: Ara [dot] T [dot] Howard [at] noaa [dot] gov
PHONE :: 303.497.6469
A flower falls, even though we love it;
and a weed grows, even though we do not love it. --Dogen

===============================================================================