Source

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)

    @Test
    void testCanPrintATestStudentAsJSON() {
        // Given
        def json,
            resultTeachersWillSee,
            resultOtherStudentsWillSee,
            resultTestAdministratorsWillSee,

        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.

*/
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.