views:

479

answers:

2

Hi,

The following example of using the sendMail method provided by the grails mail plugin appears in this book.

sendMail {
    to "[email protected]"
    subject "Registration Complete"
    body view:"/foo/bar", model:[user:new User()]
}

I understand that the code within {} is a closure that is passed to sendMail as a parameter. I also understand that to, subject and body are method calls.

I'm trying to figure out what the code that implements the sendMail method would look like, and my best guess is something like this:

MailService {

    String subject
    String recipient
    String view
    def model

    sendMail(closure) {
        closure.call()
        // Code to send the mail now that all the 
        // various properties have been set
    }

    to(recipient) {
        this.recipient = recipient
    }

    subject(subject) {
        this.subject = subject;
    }

    body(view, model) {
        this.view = view
        this.model = model
    }
}

Is this reasonable, or am I missing something? In particular, are the methods invokedwithin the closure (to, subject, body), necessarily members of the same class as sendMail?

Thanks, Don

+2  A: 

I'm not sure exactly what the sendMail method does as I do not have the book you mention. The sendMail method does indeed take a closure as you describe but it probably uses a builder rather than being executed in the normal way. Essentially that would be a Domain Specific Language for describing an email to be sent.

The reason the class you defined would not work is that the scope of the closure is where it is declared not where it is run. So having your closure call the to() method, it would not be able to call the to method in MailService unless you passed the instance of mail service into the closure.

With a few modifications, your example could work though using a regular closure. The following changes to the call and

// The it-> can be omitted but I put it in here so you can see the parameter
service.sendMail {it->
    it.to "[email protected]"
    it.subject "Registration Complete"
    it.body view:"/foo/bar", model:[user:new User()]
}

The sendMail method in the class should look like this

def sendMail(closure) {
    closure(this)
    // Code to send the mail now that all the 
    // various properties have been set
}
Chris Dail
+1  A: 

MailService.sendMail closure delegating:

 MailMessage sendMail(Closure callable) {
    def messageBuilder = new MailMessageBuilder(this, mailSender)
    callable.delegate = messageBuilder
    callable.resolveStrategy = Closure.DELEGATE_FIRST
    callable.call()

    def message = messageBuilder.createMessage()
    initMessage(message)
    sendMail message
    return message
}

and for example, the method to at MailMessageBuilder:

void to(recip) {
    if(recip) {
        if (ConfigurationHolder.config.grails.mail.overrideAddress)
            recip = ConfigurationHolder.config.grails.mail.overrideAddress
        getMessage().setTo([recip.toString()] as String[])
    }
}
Bruno Fuster