JSONExclusionMarshaller / test / integration / stonebraker / jxm / TestStudentTests.groovy

package stonebraker.jxm

import static org.junit.Assert.*
import org.junit.*
import grails.converters.JSON

class TestStudentTests {
    // Logger log = LoggerFactory.getLogger(TestStudentTests)

    void testCanPrintATestStudentAsJSON() {
        // Given
        def json,

        student = new TestStudent([
            firstName: "Tobias",
            lastName: "Funke",
            gradePointAverage: 3.6,
            studentID: "FS-210-7312",
            socialSecurityNumber: "555-55-5555"

        // The meta programming technique being used here is synthesized method injection. It's cool, but
        // it may warrant some explanation. Though hopefully not a lot.

        // In order to exclude class properties from JSON output, we create 'excluders.' Don't go looking for
        // an 'Excluder' class, you won't find it. That's just what I call ObjectMarshallers that exclude
        // properties from rendering.

        // Excluders are created using: excludeFor*(Class class, List<String> propertiesToExclude)
        // Each excluder needs an excluder name, the target class name, and a list of properties to exclude from that class.

        // We'll look at the 'excludeForTeachers' excluder.
        // Name: "excludeForTeachers" <--- You can use anything you want after "excludeFor", in this case I
        //       chose "Teachers" since in my domain, teachers aren't allowed to see a student's social
        //       security number. In this way, I'm able to use domain specific language in code.
        // Class: TestStudent <--- The domain class being marshalled
        // List<String>: ['socialSecurityNumber', 'id', 'class'] <--- The properties that will be excluded
        //       from the JSON output.

        // Usage: Once you've created an excluder, you simply call JSON.use('excludeForWhatever') { ... }
        // ... and of course do stuff within the closure per the example below.
        // As seen here, we can create multiple excluders and use them as needed.

        JSON.excludeForTeachers(TestStudent, ['socialSecurityNumber', 'id', 'class'])
        JSON.excludeForOtherStudents(TestStudent, ['gradePointAverage', 'studentID', 'socialSecurityNumber', 'id', 'class'])
        JSON.excludeForTestAdministrators(TestStudent, ['gradePointAverage', 'socialSecurityNumber', 'id', 'class'])

        // When
        JSON.use('excludeForTeachers') {
            json = new JSON(student)
        resultTeachersWillSee = json.toString()

        JSON.use('excludeForOtherStudents') {
            json = new JSON(student)
        resultOtherStudentsWillSee = json.toString()

        // We can also embed the excluder within use('deep')
        // though it has no affect our simple class.
        JSON.use('deep') {
            JSON.use('excludeForTestAdministrators') {
                json = new JSON(student)
                json.prettyPrint = false
                resultTestAdministratorsWillSee = json.toString()

        // Then
        assert resultTeachersWillSee == '{"firstName":"Tobias","gradePointAverage":3.6,"lastName":"Funke","studentID":"FS-210-7312"}'
        assert resultOtherStudentsWillSee == '{"firstName":"Tobias","lastName":"Funke"}'
        assert resultTestAdministratorsWillSee == '{"firstName":"Tobias","lastName":"Funke","studentID":"FS-210-7312"}'


What's actually happening? Basically the excludeFor*() method creates a Named Configuration with a name of
'excludeForWhateverYouDecideToNameIt'. The Named Configuration then registers a custom ObjectMarshaller for the
TestStudent domain class. The custom object marshaller is built into the excludesFor* sythesized method. It
removes the properties we provided to it in the list. The JSON converter then outputs the desired JSON formatted string.