Monday, May 6, 2013

I'm starting to understand functional programming better.

I've made an object that both has a method for sending an email via a web service and some state for reporting on how it went. An instance for this object will exist for every email address in a list of addresses to which a canned email message will be broadcast. The methods for the instance objects will get spooled up in a Func and then fired off at once.

using System;
namespace Funky.Core
{
   public class EmailAttempt
   {
      public string Error { get; set; }
      public EmailSendingStatus Status { get; set; }
      public string ToAddress { get; private set; }
      
      public EmailAttempt(string email)
      {
         ToAddress = email;
         Status = EmailSendingStatus.Unsent;
         Error = null;
      }
      
      public DateTime Attempt(Tuple<string, string, string> htmlPasswordAndSubject,
            IEmailSender emailSender, IClock clock)
      {
         try
         {
            emailSender.Send(ToAddress, htmlPasswordAndSubject.Item1,
                  htmlPasswordAndSubject.Item2, htmlPasswordAndSubject.Item3);
            Status = EmailSendingStatus.Successful;
         }
         catch (Exception exception)
         {
            Status = EmailSendingStatus.Failed;
            Error = exception.Message;
         }
         return clock.Time();
      }
   }
}

 
 

Here is an enum used by my object. This is for delineating if a would-be attempt has been processed and how the processing faired.

namespace Funky.Core
{
   public enum EmailSendingStatus
   {
      Failed,
      Successful,
      Unsent
   }
}

 
 

Here is a static method for populating and then executing the Func mentioned. It will return a list of objects which denote how each would-be outbound email faired and also a note about when the whole of the greater operation ended.

using System;
using System.Collections.Generic;
using System.Linq;
namespace Funky.Core
{
   public static class EmailSenderWrapper
   {
      public static Tuple<List<EmailAttempt>, string> SendToList(List<string> emails,
            Tuple<string, string, string> htmlPasswordAndSubject, IEmailSender
            emailSender, IClock clock)
      {
         List<EmailAttempt> attempts = emails.Select(email => new
               EmailAttempt(email)).ToList();
         string endStatus = CommonMagicStrings.EmailSpoolEmptyMessage();
         if (attempts.Count > 0)
         {
            Func<Tuple<string, string, string>, IEmailSender, IClock, DateTime> queue;
            queue = attempts[0].Attempt;
            if (attempts.Count > 1)
            {
               int counter = 1;
               while (counter < attempts.Count)
               {
                  queue += attempts[counter].Attempt;
                  counter++;
               }
            }
            DateTime endTime = queue(htmlPasswordAndSubject, emailSender, clock);
            endStatus =
                  String.Format(CommonMagicStrings.EmailSpoolCompletionMessage(),
                  endTime.ToString());
         }
         return new Tuple<List<EmailAttempt>, string>(attempts, endStatus);
      }
   }
}

 
 

Here is an example of use.

using System;
using System.Collections.Generic;
using System.Web.Mvc;
using Funky.Core;
using StructureMap;
namespace Funky.UserInterface.Controllers
{
   public class HomeController : Controller
   {
      public ActionResult Index()
      {
         IEmailSender emailSender = ObjectFactory.GetInstance<IEmailSender>();
         IClock clock = ObjectFactory.GetInstance<IClock>();
         Tuple<string, string, string> htmlPasswordAndSubject = new Tuple<string, string,
               string>("Body", "Xenu", "Header");
         List<string> emails = new List<string>() {"fevercheese@gmail.com",
               "tomjaeschke@tomjaeschke.com"};
         Tuple<List<EmailAttempt>, string> detailsAndStatusOfEmailCampaign =
               EmailSenderWrapper.SendToList(emails, htmlPasswordAndSubject,
               emailSender, clock);
         return View(detailsAndStatusOfEmailCampaign);
      }
   }
}

No comments:

Post a Comment