SPACEPORT DOCS

Groovy Luminary Certification

Welcome, Cadet. You've embarked on a journey to master Groovy—the dynamic, powerful language that drives Spaceport. Whether you've completed the Developer Onboarding and built your Tic-Tac-Toe application, or you're the type who prefers to dive straight into deep technical waters, this certification course will transform you into a Groovy expert.

This is your Starfleet Academy for Groovy. By the end of this training program, you'll have mastered the concepts of everything from basic syntax to advanced metaprogramming, earning your certification as a Groovy Luminary—ready to command sophisticated Spaceport applications with confidence and elegance.

Course Philosophy: Groovy is designed to make developers more productive. It reduces boilerplate, embraces dynamic typing where helpful, and provides powerful features built on the solid foundation of the JVM. Whether you're coming from Java, JavaScript, or you jumped straight into Spaceport, this course will show you the full power of the language underneath.

# Course Structure

This certification is organized as a series of missions, each building on the last:

Core Groovy syntax and fundamentals will be covered here, ensuring everyone has a solid foundation. Groovy concepts like closures, Groovy Truth, collections, and object-oriented programming will be explained in detail.

How Groovy powers Spaceport's architecture is fundamental to your success. This mission covers how Groovy works with Spaceport's Source Modules, Alerts, and Class Enhancements.

# Prerequisites

Before beginning this certification, you should have:

Note: If you haven't gone through the Developer Onboarding yet, that's okay! Many developers prefer to "jump in" and learn by doing. This course will give you the theoretical foundation while you experiment with code.

# Mission Brief: What is Groovy?

Apache Groovy is a powerful, optionally typed, and dynamic JVM language designed for the Java platform. It compiles to Java bytecode and runs on the Java Virtual Machine, which means:

For Java developers, Groovy feels like Java with the friction removed. For JavaScript developers, Groovy offers familiar dynamic features with the power and safety of the JVM. For hands-on learners who built the Tic-Tac-Toe game, this course will explain the "magic" you've already been using.

# Mission 01: First Contact

Mission Briefing: Before we engage the warp drive, we must ensure all systems are nominal. This mission covers Groovy's core syntax and fundamental concepts—the "plasma" that fuels the Spaceport engine.

Whether you're a seasoned Java developer, a JavaScript wizard, or you learned by building the Tic-Tac-Toe game, this module ensures everyone starts with the same solid foundation.

Objectives:

## Core Syntax Essentials

### Semicolons are Optional

Unlike Java, semicolons at the end of statements are optional in Groovy.

// Java style (still valid)
def name = "Spaceport";
println(name);

// Groovy style (preferred)
def name = "Spaceport"
println name

### Parentheses are Often Optional

When calling methods, you can often omit parentheses, making code read more naturally.

// Traditional style
println("Greetings, Commander!")

// Groovy style
println "Greetings, Commander!"

// Multiple parameters still need parentheses around arguments
Math.max(5, 10)

### Return Statements are Implicit

The last expression in a method or closure is automatically returned.

// Explicit return (Java style)
def multiply(a, b) {
    return a * b
}

// Implicit return (Groovy style - preferred)
def multiply(a, b) {
    a * b
}

## Variables and Types

### Dynamic Typing with def

The def keyword declares a dynamically-typed variable. The type is determined at runtime.

def name = "Commander"        // String
def age = 35                  // Integer
def isActive = true           // Boolean
def coordinates = [10, 20]    // List

### Static Typing

You can still use explicit types when you want compile-time type checking and better IDE support.

String rank = "Captain"
Integer shipId = 74205
List<String> crewMembers = ["Sisko", "Kira", "Dax"]

### Type Coercion

Groovy provides explicit type conversion using the as operator or conversion methods.

def number = "42"

// Explicit coercion using 'as'
Integer result = number as Integer

// Or use conversion methods
Integer result = number.toInteger()
Double decimal = number.toDouble()

Note: Spaceport features like Transmissions and Cargo provide additional type-safe getter methods that handle coercion automatically:

These methods are especially useful when working with form data or client-side transmissions where values arrive as strings, but more on that later.

## Strings

Groovy provides multiple ways to work with strings, each with its own superpowers.

### Single-Quoted Strings

Simple, literal strings without interpolation.

def message = 'Hello, World!'

### Double-Quoted Strings with GString Interpolation

Use ${} to embed expressions inside strings. This is similar to JavaScript template literals.

def name = "Captain"
def rank = "Commander"

// GString interpolation
def greeting = "Greetings, ${rank} ${name}!"
println greeting  // "Greetings, Commander Captain!"

// For simple variables, you can omit the braces
def simple = "Hello, $name"

### Multi-Line Strings

Use triple quotes for multi-line strings.

def report = """
Mission Status: Active
Coordinates: Alpha Quadrant
Ship Status: Operational
"""

### Slashy Strings

Useful for regular expressions, as backslashes don't need escaping.

// No need to escape backslashes, unlike "\\d{3}-\\d{2}-\\d{4}"
def pattern = /\d{3}-\d{2}-\d{4}/

## Collections: Lists and Maps

Groovy makes working with collections remarkably simple.

### Lists

Lists in Groovy are dynamic arrays, similar to JavaScript arrays or Java's ArrayList.

// Creating a list
def crew = ["Sisko", "Kira", "Dax"]

// Accessing elements (zero-based index)
println crew[0]        // "Sisko"
println crew[-1]       // "Dax" (negative index from end)

// Adding elements
crew << "Bashir"       // Append using left-shift operator
crew.add("O'Brien")    // Using add method

// List operations
println crew.size()    // 5
println crew.contains("Kira")  // true

### Maps

Maps are key-value pairs, similar to JavaScript objects or Java's HashMap.

// Creating a map
def ship = [
    name: "Defiant",
    registry: "NX-74205",
    captain: "Sisko"
]

// Accessing values
println ship.name              // "Defiant" (property notation)
println ship['registry']       // "NX-74205" (subscript notation)
println ship.get('captain')    // "Sisko" (method notation)

// Adding/updating entries
ship.crew = 50
ship['class'] = "Escort"

// Checking for keys
if (ship.containsKey('captain')) {
    println "Captain: ${ship.captain}"
}

## The Universal Translator: Closures

In Groovy, a closure is the single most important concept to master. It's an anonymous, nestable block of code that can be passed around as a variable, just like an object. Think of it as a "command packet" that you can hand off to other parts of your system to be executed at a later time.

In Spaceport, you use closures everywhere: - As Server Actions in Launchpad (e.g., on-click=${ _{ ... }}) - For iterating over lists (.each { }, .collect { }) - In conditional logic (.find { }, .any { })

### Basic Closure Syntax

A closure is defined by curly braces {}.

// A simple closure assigned to a variable
def greeting = {
    println "Hello from inside a closure!"
}

// You execute it just like a method
greeting()
// Output: Hello from inside a closure!

// Closures can take parameters
def personalizedGreeting = { name ->
    println "Hello, ${name}!"
}

personalizedGreeting("Cadet")
// Output: Hello, Cadet!

### The it Parameter

If a closure takes only one parameter, you don't even need to name it. Groovy provides an implicit parameter named it. This is extremely common in Spaceport.

def numbers = [1, 2, 3, 4]

// Using a named parameter 'n'
def doubled = numbers.collect { n ->
    return n * 2
}

// Using the implicit 'it' parameter (much cleaner)
def tripled = numbers.collect {
    return it * 3
}

// Groovy also has implicit 'return' on the last line
def quadrupled = numbers.collect { it * 4 }

// doubled == [2, 4, 6, 8]
// tripled == [3, 6, 9, 12]
// quadrupled == [4, 8, 12, 16]

This concise syntax is why on-click=${ _{ ... }} in Launchpad is so powerful. The it (or t in Spaceport conventions) in that context is the Transmission object, giving you direct access to the client request data.

### Closures as Method Parameters

Closures can be passed as arguments to methods, enabling powerful functional programming patterns.

def crew = ["Sisko", "Kira", "Dax"]

// Filter with findAll
def commanders = crew.findAll { it.startsWith("S") }
println commanders  // ["Sisko"]

// Transform with collect
def lengths = crew.collect { it.length() }
println lengths  // [5, 4, 3]

// Custom iteration
def announceAll = { list, closure ->
    list.each { item ->
        closure(item)
    }
}

announceAll(crew) { member ->
    println "Crew member on deck: $member"
}

### Closure Scope and Variables

Closures can access variables from their surrounding scope.

def multiplier = 2

def multiply = { number ->
    number * multiplier  // Accesses 'multiplier' from outer scope
}

println multiply(5)  // 10

multiplier = 3
println multiply(5)  // 15 (uses updated value)

## Groovy Truth: "It's True, but Not as We Know It"

In Java, a conditional if (...) statement must evaluate a boolean (true or false). This leads to lots of if (myString != null && !myString.isEmpty()) or if (myList.size() > 0).

Groovy simplifies this with Groovy Truth. Many different types can be evaluated as true or false.

This is false in Groovy: - null - The boolean false - The number 0 (or 0.0) - An empty String ("") - An empty Collection ([], [:]) - An empty Matcher ("abc" =~ /xyz/ with no matches)

Everything else is true!

### Practical Application

This dramatically cleans up your code, especially in Launchpad templates.

Java-style (Verbose):

if (user != null && user.getRole() != null && user.getRole().equals("admin")) {
    // ... show admin button
}
if (errors != null && !errors.isEmpty()) {
    // ... show error messages
}

Groovy-style (Clean & Idiomatic):

// The '?' is the safe-navigation operator.
// If 'user' or 'user.role' is null, the expression stops and returns null (which is false).
if (user?.role == "admin") {
    // ... show admin button
}

// If 'errors' is null OR an empty list, this is false.
if (errors) {
    // ... show error messages
}

You will use this constantly to check for data before rendering HTML in Launchpad templates.

## Control Structures

### Elvis Operator

The ?: operator provides a concise way to handle null or false values (similar to JavaScript's ||).

def name = null
def displayName = name ?: "Unknown"
println displayName  // "Unknown"

// Longer equivalent
def displayName = name ? name : "Unknown"

### Safe Navigation Operator

The ?. operator prevents NullPointerExceptions by short-circuiting if the left side is null.

def user = null
println user?.name  // null (doesn't throw exception)

def ship = [captain: [name: "Sisko"]]
println ship.captain?.name  // "Sisko"
println ship.crew?.size()   // null (crew doesn't exist)

### Loops

#### Enhanced For Loop
// Iterating over a list
def crew = ["Sisko", "Kira", "Dax"]
for (member in crew) {
    println member
}

// Iterating over a range
for (i in 1..5) {
    println "Count: $i"
}

// Iterating over a map
def ship = [name: "Defiant", registry: "NX-74205"]
for (entry in ship) {
    println "${entry.key}: ${entry.value}"
}
#### Each Method

The .each() method is more idiomatic in Groovy and takes a closure.

def crew = ["Sisko", "Kira", "Dax"]

// With implicit parameter 'it'
crew.each {
    println it
}

// With explicit parameter
crew.each { member ->
    println "Crew member: $member"
}

// Maps with key and value
def ship = [name: "Defiant", registry: "NX-74205"]
ship.each { key, value ->
    println "$key: $value"
}

## Ranges

Ranges are a powerful feature for creating sequences.

// Inclusive range (1, 2, 3, 4, 5)
def range1 = 1..5

// Exclusive range (1, 2, 3, 4)
def range2 = 1..<5

// Using ranges in loops
for (i in 1..3) {
    println "Iteration $i"
}

// Converting to list
def numbers = (1..5).toList()  // [1, 2, 3, 4, 5]

// Character ranges
def letters = ('a'..'e').toList()  // ['a', 'b', 'c', 'd', 'e']

## Operators

### Spaceship Operator

The <=> operator compares two values and returns -1, 0, or 1 (useful for sorting).

println 5 <=> 3   // 1 (5 is greater)
println 3 <=> 5   // -1 (3 is less)
println 5 <=> 5   // 0 (equal)

// Great for sorting
def numbers = [3, 1, 4, 1, 5, 9]
numbers.sort { a, b -> a <=> b }
println numbers  // [1, 1, 3, 4, 5, 9]

### Spread Operator

The *. operator calls a method or accesses a property on all items in a collection.

def crew = [
    [name: "Sisko", rank: "Captain"],
    [name: "Kira", rank: "Major"],
    [name: "Bashir", rank: "Doctor"]
]

// Get all names
def names = crew*.name
println names  // ["Sisko", "Kira", "Bashir"]

// Call a method on all items
def lengths = names*.length()
println lengths  // [5, 4, 6]

## The GDK (Groovy Development Kit)

The GDK is Groovy's "away team" equipment. Groovy takes standard Java classes (like java.util.List, java.lang.String, java.lang.Object) and adds dozens of helpful new methods to them.

This means that even when you're working with a plain Java object, you have a whole new set of "Groovy tools" available on it.

### Example: java.util.List

def crew = ['Sisko', 'Kira', 'Dax', 'Bashir']

// GDK method: .each
crew.each {
    println "${it} reporting for duty!"
}

// GDK method: .find
def doctor = crew.find { it == 'Bashir' }
// doctor == "Bashir"

// GDK method: .join
def manifest = crew.join(', ')
// manifest == "Sisko, Kira, Dax, Bashir"

// GDK method: .last
def lastOnBridge = crew.last()
// lastOnBridge == "Bashir"

### Example: java.lang.String

def myString = "spaceport"

// GDK method: .capitalize()
println myString.capitalize() // "Spaceport"

// GDK method: .isInteger()
println "123".isInteger() // true
println "abc".isInteger() // false

Spaceport takes this concept one step further with its own Class Enhancements, adding even more methods that are specific to web development. We'll cover those in Mission 02.

## Object-Oriented Programming

### Classes and Objects

Groovy classes are similar to Java classes but with less boilerplate.

class Starship {
    String name
    String registry
    Integer crew
    
    // Constructor (automatically generated for properties)
    // Groovy creates getters, setters, and a constructor
    
    def launch() {
        println "$name ($registry) is launching with $crew crew members"
    }
}

// Creating and using objects
def defiant = new Starship(
    name: "Defiant", 
    registry: "NX-74205", 
    crew: 50
)

defiant.launch()
println defiant.name  // Uses auto-generated getter

### Properties vs Fields

In Groovy, public fields automatically become properties with getters and setters.

class Officer {
    String name        // Property (public, with getter/setter)
    private int age    // Field (truly private)
    
    def introduce() {
        println "I am $name, age $age"
    }
}

def officer = new Officer()
officer.name = "Dax"       // Uses generated setter
// officer.age = 35        // Compile error! Private field

officer.introduce()

### Methods

Methods in Groovy have optional return types and can use implicit returns.

class Calculator {
    // Method with explicit return type
    Integer add(Integer a, Integer b) {
        a + b  // Implicit return
    }
    
    // Method with def (dynamic return type)
    def multiply(a, b) {
        a * b
    }
    
    // Method with optional parameters and default values
    def greet(String name = "Officer") {
        "Hello, $name"
    }
}

def calc = new Calculator()
println calc.add(5, 3)           // 8
println calc.multiply(4, 2)      // 8
println calc.greet()             // "Hello, Officer"
println calc.greet("Commander")  // "Hello, Commander"

### Inheritance

Groovy supports standard object-oriented inheritance.

class Vehicle {
    String type
    
    def move() {
        println "$type is moving"
    }
}

class Starship extends Vehicle {
    String name
    
    Starship(String name) {
        this.type = "Starship"
        this.name = name
    }
    
    @Override
    def move() {
        println "$name is warping through space"
    }
}

def ship = new Starship("Defiant")
ship.move()  // "Defiant is warping through space"

### Traits

Traits are similar to interfaces but can contain method implementations (like Java 8+ default methods).

trait Communicator {
    def sendMessage(String message) {
        println "Transmitting: $message"
    }
}

trait Navigator {
    def plotCourse(String destination) {
        println "Course set for $destination"
    }
}

class Starship implements Communicator, Navigator {
    String name
}

def ship = new Starship(name: "Defiant")
ship.sendMessage("We come in peace")
ship.plotCourse("Bajor")

## Mission Debrief

You have recalibrated your understanding of Groovy's core mechanics. You've mastered:

These concepts are the "plasma" that fuels the Spaceport engine. With this foundation, you are ready to proceed to the next module and see how Groovy powers the Spaceport framework itself.

# Mission 02: The Spaceport Core

Mission Briefing:

Cadet, you've passed your fundamental review. It's time to leave the simulators and report to Engineering. Your assignment is to understand the "warp core" of Spaceport—how it manages, loads, and enhances your Groovy code.

In this mission, we'll examine the three systems that form the heart of Spaceport's power: Source Modules (the "nacelles" that house your logic), Alerts (the "internal comms" that let systems talk), and Class Enhancements (the "subspace shortcuts" that make the ship fly faster).

Understanding this core architecture is the difference between being a passenger and being an engineer.

Objectives:

# Source Modules: The "Decoupled Nacelles"

In a starship, the engines (nacelles) are separate from the main hull. They provide the power, but they are self-contained, modular, and can be serviced or even swapped.

Source Modules are the "nacelles" of your Spaceport application. They are Groovy classes that Spaceport dynamically loads from your /modules directory. This is where you write all your core application logic.

If you built the Tic-Tac-Toe game, you already created a Source Module (Game.groovy). Now you'll understand how it really works under the hood.

## Static vs. Instance: The Critical Difference

This is a core concept that developers often stumble over. How you define your variables and methods in a Groovy Source Module has a massive impact on how your application behaves.

### static (Shared State - The "Ship's Computer")

A static variable or method belongs to the class itself. In Spaceport, this means there is only one instance of it for the entire running application.

Use Case: Caches, application-wide settings, shared game states (like in the Tic-Tac-Toe tutorial), or utility methods.

Analogy: It's a "ship-wide" system. When one crew member updates the ship's main computer, it's updated for everyone.

Be careful! Since the state is shared, it is not "thread-safe" by default. Multiple users accessing it at the same time can cause conflicts. For thread-safe shared state, use Java's concurrent collections like ConcurrentHashMap, CopyOnWriteArrayList, or wrap access with proper synchronization. Groovy also provides @Synchronized annotations and the Collections.synchronizedMap() utility for simpler cases.
// Located in /modules/ShipSystems.groovy

class ShipSystems {
    
    // This variable is shared across ALL users and ALL requests because it's STATIC.
    static def system = [ 
            shieldFrequency:  50.0,
            enginePower:     75.0,
            frequencyStable: true
        ] as ConcurrentHashMap
    
    // This is a utility method, it doesn't need instance data.
    static def isFrequencyStable(def frequency) {
        return system.frequency > 40.0
    }
    
    static def changeShieldFrequency(def newFrequency) {
        system.shieldFrequency = newFrequency
        // Also set stability based on new frequency
        system.frequencyStable = isFrequencyStable(newFrequency)
    }
    
}

### instance (Request-Scoped State - "Your PADD")

An instance variable (any variable defined without static) belongs to an object, or instance, of the class. Spaceport's default behavior is to create a new instance of your Source Module for every incoming HTTP request.

Use Case: Handling form data, managing a specific user's request, or performing any operation that should be isolated to a single request.

Analogy: It's your personal "PADD" (tablet). You can write notes on it, and it doesn't affect anyone else's PADD. When your "mission" (the request) is over, the PADD is wiped clean.

// Located in /modules/RequestProcessor.groovy
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.*

class RequestProcessor {
    // This variable is NEW for every request
    String userName

    // This method can access the instance variable
    def greetUser() {
        println "Hello, ${userName}"
    }
    
    // This Alert handles the request, creating a new instance
    @Alert("on /greet hit")
    static _process(HttpResult r) {
        // 1. Create a new instance of this class
        def processor = new RequestProcessor()
        
        // 2. Populate its INSTANCE data
        processor.userName = r.context.data.name
        
        // 3. Call its INSTANCE method
        processor.greetUser()
        
        // 4. Send a response
        r.writeToClient("User ${processor.userName} greeted.")
    }
}

### When to Use Static vs. Instance

Scenario Use Static Use Instance
Application-wide cache
Game state shared by all players
Utility/helper methods
Request-specific form data
User session data (when not in Cargo)
Temporary calculation variables
Tic-Tac-Toe Example: In the tutorial, you used static variables for the game board because there was only one game shared by everyone. For a multi-player version, you'd want instance variables or Cargo objects to isolate each game.

Mastering this distinction is the key to managing state and preventing data "leaks" between user sessions.

# Alerts: The "Internal Comms"

A ship's computer doesn't have one giant main method. Instead, it reacts to events: "Red Alert!", "Intruder Alert!", "Incoming Transmission!"

Alerts are Spaceport's event system. They are the "internal communications" that allow your decoupled modules to talk to each other without being directly tied together. This is what you use to handle HTTP requests, respond to server startup, or process data.

An Alert handler is just a static Groovy method annotated with @Alert.

## Basic Alert Usage

// Located in /modules/CommsOfficer.groovy
import spaceport.computer.alerts.Alert
import spaceport.computer.alerts.results.*

class CommsOfficer {

    // This method "listens" for requests to the root path "/"
    @Alert("on / hit")
    static _handleHomepage(HttpResult r) {
        // Set the response
        r.writeToClient("Welcome to the Spaceport, Captain!")
    }
    
    // This method "listens" for requests to "/bridge"
    @Alert("on /bridge hit")
    static _handleBridge(HttpResult r) {
        // You can also "fire" new, custom Alerts
        // Another module might be listening for this!
        Alert.fire("INTRUDER_ON_BRIDGE")
        
        r.writeToClient("Bridge access granted.")
    }
}

## Custom Alerts

You can create your own custom alerts to build an event-driven architecture.

// Module A: Security.groovy
import spaceport.computer.alerts.Alert

class Security {
    @Alert("INTRUDER_ON_BRIDGE")
    static _handleIntruder(Result r) {
        println "SECURITY ALERT: Intruder detected on bridge!"
        // Log to database, notify admins, etc.
    }
}

Using Alerts lets you build complex applications where logic is neatly separated. Your Security module can listen for INTRUDER_ON_BRIDGE without the CommsOfficer module even knowing it exists. This is powerful decoupling.

For more on Alerts and routing, see the Alerts Documentation.

# Class Enhancements: "Subspace Shortcuts"

You learned about the GDK in Mission 01—how Groovy adds methods to standard Java classes.

Spaceport takes this to the next level. Class Enhancements are special methods Spaceport adds to the GDK, specifically designed for web development. Think of them as "subspace shortcuts" that our engineers built into the ship's computer to make common tasks faster.

You've already seen some of these if you built the Tic-Tac-Toe game or went through the onboarding guide.

## String Enhancements

Commonly used in Launchpad templates for formatting and URL handling.

def myString = "Captain Sisko"
def myUrl = "My Great Blog Post!"

// .kebab() -> Converts to kebab-case (perfect for CSS classes)
println myString.kebab() // "captain-sisko"

// .slugify() -> Converts to a URL-friendly "slug"
println myUrl.slugify() // "my-great-blog-post"

// .clean() -> Sanitizes HTML to prevent XSS attacks
def userInput = "<script>alert('xss')</script>Hello"
println userInput.clean() // "Hello"

// .quote() -> Wraps in double quotes and escapes internal quotes
def jsString = "Hello \"World\""
println jsString.quote() // "\"Hello \\\"World\\\"\""

## Number Enhancements

Commonly used for formatting display values.

def price = 47.5
def rank = 4

// .money() -> Formats as currency
println price.money() // "$47.50" (in US Locale)

// .ordinal() -> Gets the ordinal string
println rank.ordinal() // "4th"

// .days(), .hours(), .minutes(), .seconds() -> Convert to milliseconds
def timeout = 5.minutes()
println timeout // 300000

## Conditional Enhancements

The .if() enhancement is a Spaceport superpower. It's a clean replacement for ternary operators or if blocks inside strings, and it understands Groovy Truth.

def userRole = "admin"
def noUser = null

// The .if() method takes a closure.
// If the closure is "true", it returns the original string.
// If "false", it returns an empty string.

// 'userRole == "admin"' is true, so it returns the string
def adminLink = "<a href='/admin'>Admin Panel</a>".if { userRole == "admin" }

// 'noUser' is null, which is "false" in Groovy Truth.
// This returns an empty string.
def userLink = "<a href='/user'>Profile</a>".if { noUser }

/*
adminLink == "<a href='/admin'>Admin Panel</a>"
userLink == ""
*/

This enhancement is invaluable for building clean Launchpad Templates without cluttering them with <% if(...) { } %> blocks.

Example in a Template:

<div class="navigation">
    ${ "<a href='/admin'>Admin</a>".if { user?.role == "admin" }}
    ${ "<a href='/profile'>Profile</a>".if { user }}
</div>

## Collection Enhancements

def crew = ["Sisko", "Kira", "Dax"]

// .combine() -> Collect results and join into a string
def html = crew.combine { "<li>${it}</li>" }
// html == "<li>Sisko</li><li>Kira</li><li>Dax</li>"

// .snap() -> Temporarily add an item for a duration
def messages = []
messages.snap(5000, "This disappears in 5 seconds")
// After 5 seconds, the item is automatically removed

// .including() -> Fluently add items and return the collection
def updated = crew.including("Bashir").including("O'Brien")
println updated // ["Sisko", "Kira", "Dax", "Bashir", "O'Brien"]
For a complete list of Class Enhancements, see the Class Enhancements Documentation.

# Mission Debrief

You now understand the "engine core" of Spaceport. You've mastered:

With this knowledge, you understand how your Tic-Tac-Toe game (or any Spaceport app) actually works under the hood. You're ready to explore how Groovy compares to other languages you may know.

Mission 03: Language Interoperability(mission-03)

Mission Briefing: You've learned Groovy's syntax and how it powers Spaceport. But you're not starting from zero—you already know Java, JavaScript, or you've been learning by doing. This mission will show you exactly how Groovy relates to the languages you already know.

This comparative approach will help you leverage your existing knowledge while understanding Groovy's unique advantages.

Objectives: - Understand Groovy's relationship to Java - Compare Groovy with JavaScript concepts - Master interoperability patterns - Know when to use which language features

# Groovy for Java Developers

If you're coming from Java, Groovy will feel like Java with all the friction removed. Every valid Java class is also a valid Groovy class, but Groovy adds powerful features that make your code more concise and expressive.

## Key Differences from Java

Feature Java Groovy
Semicolons Required Optional
Parentheses Required for method calls Optional for top-level calls
Return Statement Required Optional (implicit)
Getters/Setters Manual (or IDE-generated) Auto-generated for properties
Type Declaration Always required Optional with def
Default Access Modifier package-private public
Operator Overloading Not supported Fully supported
Closures Lambda expressions (Java 8+) First-class, powerful closures
String Interpolation String concatenation GString with ${}
Collection Literals Verbose constructors [] for lists, [:] for maps

## Side-by-Side Comparison

Creating a List

Java:

List<String> crew = new ArrayList<>();
crew.add("Sisko");
crew.add("Kira");
crew.add("Dax");

Groovy:

def crew = ["Sisko", "Kira", "Dax"]

Iterating Over a Collection

Java:

for (String member : crew) {
    System.out.println("Crew member: " + member);
}

Groovy:

crew.each { println "Crew member: $it" }

Creating a POJO/POGO

Java:

public class Starship {
    private String name;
    private Integer crew;
    
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
    
    public Integer getCrew() {
        return crew;
    }
    
    public void setCrew(Integer crew) {
        this.crew = crew;
    }
    
    public void launch() {
        System.out.println(name + " is launching with " + crew + " crew");
    }
}

Groovy:

class Starship {
    String name
    Integer crew
    
    def launch() {
        println "$name is launching with $crew crew"
    }
}

## Java Interoperability

Groovy code can seamlessly use Java libraries and vice versa. This is one of Groovy's greatest strengths.

// Using Java collections
import java.util.ArrayList
import java.util.HashMap

def list = new ArrayList<String>()
list.add("Sisko")
list.add("Kira")

// Using Java date/time API
import java.time.LocalDate
def today = LocalDate.now()
println "Stardate: $today"

// Calling Java static methods
def max = Math.max(10, 20)

// Using any Java library
import org.apache.commons.lang3.StringUtils
println StringUtils.capitalize("spaceport")

## When to Use Static Typing in Groovy

Even though Groovy supports dynamic typing with def, there are times when explicit Java-style types are better:

Use Static Types When: - Working with Java APIs that require specific types - You want compile-time type checking - Performance is critical (static typing can be faster) - You need better IDE autocomplete and refactoring support

// Dynamic typing - flexible but no compile-time checks
def processData(data) {
    return data.size() * 2
}

// Static typing - safer and clearer
Integer processData(List<String> data) {
    return data.size() * 2
}

## Migrating from Java to Groovy

If you're converting existing Java code to Groovy:

1. Start by just renaming .java to .groovy - It will work! 2. Remove semicolons - Clean up line endings 3. Replace getters/setters with properties - Let Groovy generate them 4. Use GStrings instead of concatenation - "Hello $name" instead of "Hello " + name 5. Replace loops with closures - .each{}, .collect{}, etc. 6. Add def for local variables - Unless you want static typing

## The Power of Groovy's Dynamic Nature

While Java requires everything to be known at compile time, Groovy can make runtime decisions:

// Groovy can handle unknown types gracefully
def process(input) {
    if (input instanceof List) {
        return input.size()
    } else if (input instanceof String) {
        return input.length()
    } else if (input instanceof Number) {
        return input * 2
    }
    return "Unknown type"
}

println process([1, 2, 3])      // 3
println process("Spaceport")    // 9
println process(5)              // 10

This flexibility is what makes Groovy perfect for rapid web development in Spaceport.

# Groovy for JavaScript Developers

If you're coming from JavaScript, you'll find many familiar concepts in Groovy. Both languages embrace dynamic typing, first-class functions, and developer productivity. However, Groovy runs on the JVM, giving you type safety when you need it and access to the entire Java ecosystem.

## Similarities to JavaScript

Feature JavaScript Groovy
Dynamic Typing let x = 5 def x = 5
String Interpolation Hello ${name} "Hello ${name}"
Closures/Lambdas (x) => x * 2 { x -> x * 2 }
Optional Parameters Default parameters Default parameters
Collections Arrays, Objects Lists, Maps
Truthiness Falsy values concept Groovy Truth
First-Class Functions Functions as values Closures as values

## Side-by-Side Comparison

Array/List Operations

JavaScript:

const crew = ['Sisko', 'Kira', 'Dax'];

// Filter
const captains = crew.filter(m => m === 'Sisko');

// Map
const lengths = crew.map(m => m.length);

// ForEach
crew.forEach(m => console.log(m));

Groovy:

def crew = ['Sisko', 'Kira', 'Dax']

// Filter
def captains = crew.findAll { it == 'Sisko' }

// Map
def lengths = crew.collect { it.length() }

// Each
crew.each { println it }

Object/Map Literals

JavaScript:

const ship = {
    name: 'Defiant',
    registry: 'NX-74205',
    captain: 'Sisko'
};

console.log(ship.name);        // Dot notation
console.log(ship['registry']); // Bracket notation

Groovy:

def ship = [
    name: 'Defiant',
    registry: 'NX-74205',
    captain: 'Sisko'
]

println ship.name        // Dot notation
println ship['registry'] // Bracket notation

Functions/Closures

JavaScript:

// Function expression
const greet = function(name) {
    return Hello, ${name}!;
};

// Arrow function
const double = x => x * 2;

// Callback
numbers.forEach(n => console.log(n));

Groovy:

// Closure
def greet = { name ->
    "Hello, ${name}!"
}

// Single parameter (implicit 'it')
def double = { it * 2 }

// Callback
numbers.each { println it }

## Key Differences from JavaScript

### 1. Class-Based OOP

JavaScript uses prototype-based inheritance (even with ES6 classes). Groovy uses traditional class-based OOP like Java.

JavaScript:

class Starship {
    constructor(name, crew) {
        this.name = name;
        this.crew = crew;
    }
    
    launch() {
        console.log(${this.name} launching);
    }
}

Groovy:

class Starship {
    String name
    Integer crew
    // Constructor auto-generated
    
    def launch() {
        println "$name launching"
    }
}

### 2. No Hoisting

Unlike JavaScript, Groovy doesn't hoist variable declarations. Variables must be declared before use.

JavaScript (works due to hoisting):

console.log(message); // undefined (hoisted)
var message = "Hello";

Groovy (error):

println message  // Error! Variable not declared yet
def message = "Hello"

### 3. Type Coercion is More Explicit

JavaScript's type coercion is often surprising. Groovy's is more predictable.

JavaScript:

"5" - 2    // 3 (string coerced to number)
"5" + 2    // "52" (number coerced to string)
[] + []    // "" (weird)
[] + {}    // "[object Object]"

Groovy:

"5" - 2    // Error! No automatic coercion
"5".toInteger() - 2  // 3 (explicit)
"5" + 2    // "52" (string concatenation)

### 4. Static Typing Option

JavaScript (without TypeScript) is always dynamically typed. Groovy lets you choose.

Groovy gives you both:

// Dynamic typing (like JavaScript)
def name = "Sisko"

// Static typing (compile-time checks)
String rank = "Captain"
Integer age = 45

### 5. No undefined - Only null

JavaScript has both null and undefined. Groovy only has null.

JavaScript:

let x;           // undefined
let y = null;    // null

Groovy:

def x            // null
def y = null     // null

## Promise/Async Patterns in Groovy

JavaScript developers are used to Promises and async/await. Groovy handles asynchrony differently:

JavaScript:

async function fetchData() {
    const response = await fetch('/api/data');
    const data = await response.json();
    return data;
}

Groovy (using GPars for async):

import groovyx.gpars.GParsPool

// Parallel processing
GParsPool.withPool {
    def results = data.collectParallel { item ->
        processItem(item)
    }
}

In Spaceport, most operations are synchronous since you're handling individual HTTP requests. Complex async patterns are less common.

## The Console Object

JavaScript developers use console.log() everywhere. In Groovy/Spaceport:

JavaScript:

console.log("Debug message");
console.error("Error occurred");

Groovy:

println "Debug message"           // Standard output
System.err.println "Error"        // Error output

// Or use Spaceport's logging
log.info("Info message")
log.error("Error message")

## Truthy/Falsy Comparison

Both languages have truthiness concepts, but with slight differences:

Value JavaScript Groovy
false ❌ Falsy ❌ False
true ✅ Truthy ✅ True
0 ❌ Falsy ❌ False
1 ✅ Truthy ✅ True
"" ❌ Falsy ❌ False
"text" ✅ Truthy ✅ True
null ❌ Falsy ❌ False
undefined ❌ Falsy (doesn't exist)
[] ✅ Truthy ❌ False
{} ✅ Truthy ❌ False
[1, 2] ✅ Truthy ✅ True

Notice that empty collections are truthy in JavaScript but falsy in Groovy!

# Using Java Libraries from Groovy

One of Groovy's superpowers is seamless access to Java's vast ecosystem.

## Adding Dependencies

In Spaceport, you can use @Grab annotations to pull in Java libraries:

@Grab('org.apache.commons:commons-lang3:3.12.0')
import org.apache.commons.lang3.StringUtils

// Now use any Java library method
def result = StringUtils.reverse("Spaceport")
println result  // "tropsecapS"

## Common Java Libraries in Groovy

// JSON processing with Jackson
@Grab('com.fasterxml.jackson.core:jackson-databind:2.15.0')
import com.fasterxml.jackson.databind.ObjectMapper

def mapper = new ObjectMapper()
def json = mapper.writeValueAsString([name: "Defiant", crew: 50])

// HTTP client
@Grab('org.apache.httpcomponents:httpclient:4.5.14')
import org.apache.http.client.methods.HttpGet
import org.apache.http.impl.client.HttpClients

def client = HttpClients.createDefault()
def request = new HttpGet("https://api.example.com/data")
def response = client.execute(request)

// Working with dates
import java.time.LocalDate
import java.time.format.DateTimeFormatter

def today = LocalDate.now()
def formatter = DateTimeFormatter.ofPattern("MM/dd/yyyy")
println today.format(formatter)

# Mission Debrief

You now understand how Groovy relates to the languages you already know:

Whether you came from Java or JavaScript, you can now leverage your existing knowledge while taking advantage of Groovy's unique strengths in the Spaceport framework.

Mission 04: Engineering (Metaprogramming)(mission-04)

Mission Briefing: Cadet, you've inspected the "core systems" and understand how Spaceport operates. It's time for your advanced engineering certification. Welcome to the "warp core" itself.

This mission is about Metaprogramming: Groovy's ability to modify its own classes and objects at runtime. This is the "magic" that powers Groovy. It's like having the ability to add new buttons to your "command console" or rewrite a ship's blueprints while it's already in flight.

This is arguably the most powerful—and dangerous—feature in your toolkit. It requires precision, skill, and a deep understanding of the consequences. Master it, and you can solve problems that are "impossible" in other languages.

Objectives: - Learn to modify any class (even Java's) at runtime using ExpandoMetaClass - Understand how to create Builders and Domain-Specific Languages (DSLs) - Recognize AST Transformations and their power

# The "Warp Core": Runtime Metaprogramming

Groovy's ExpandoMetaClass (EMC) allows you to "open up" any class—yours, one from a library, or even java.lang.String—and add new methods, properties, and constructors to it.

This is exactly how Spaceport adds its own Class Enhancements like .kebab() and .money(). They aren't magic; they are just Groovy metaprogramming at work.

## Example: Augmenting a String

Let's say our ship's communication system needs a new "red alert" format (all caps, with an exclamation). We can't rewrite java.lang.String, but we can enhance it.

// This code is typically run once at startup (e.g., in an 'on initialize' Alert)

// 1. We access the 'metaClass' of the String class
String.metaClass.toRedAlert = {
    // 2. 'delegate' refers to the String instance ("Priority One")
    return delegate.toUpperCase() + "!"
}

// 3. Now, EVERY string in the entire application has this new method
def message = "Priority One"
println message.toRedAlert()
// Output: PRIORITY ONE!

## Adding Properties at Runtime

You can also add properties to classes dynamically:

// Add a property to Integer
Integer.metaClass.isEven = {
    delegate % 2 == 0
}

println 4.isEven()  // true
println 5.isEven()  // false

## Intercepting Method Calls

You can intercept method calls using methodMissing:

class DynamicShip {
    def methodMissing(String name, args) {
        println "Called non-existent method: $name with args: $args"
        return "Method $name executed"
    }
}

def ship = new DynamicShip()
ship.launch("warp 9")  
// Output: Called non-existent method: launch with args: [warp 9]

## Why is This Useful in Spaceport?

1. Clean Data Models

Add helper methods to your Document classes dynamically:

// Add a permission check to all User documents
UserDocument.metaClass.hasPermission = { perm ->
    delegate.permissions?.contains(perm) ?: false
}

// Now you can use it anywhere
if (user.hasPermission('admin')) {
    // Show admin panel
}

2. Fixing Libraries

Need a third-party Java library to behave differently? Patch it at runtime without forking:

// Make all Lists have a 'random' method
List.metaClass.random = {
    delegate[new Random().nextInt(delegate.size())]
}

def crew = ['Sisko', 'Kira', 'Dax']
println crew.random()  // Random crew member

3. Fluent APIs

Create incredibly readable code:

// Add a 'times' method to Integer for Ruby-like syntax
Integer.metaClass.times = { closure ->
    for (int i = 0; i < delegate; i++) {
        closure(i)
    }
}

5.times {
    println "Launching torpedo ${it + 1}"
}
// Output:
// Launching torpedo 1
// Launching torpedo 2
// ...

# Builders & DSLs: Your Own "Command Language"

A Domain-Specific Language (DSL) is a "mini-language" you create to solve one specific problem. Instead of writing complex, nested Groovy code, you create a simple, readable syntax.

Groovy's Builder pattern, combined with closures, makes this incredibly easy.

## Example: MarkupBuilder

Instead of mashing strings together, a builder gives you a clean DSL for "describing" the HTML you want.

def writer = new StringWriter()
// MarkupBuilder is a standard Groovy class
def html = new groovy.xml.MarkupBuilder(writer)

// This is a Groovy DSL.
// 'body', 'h1', and 'p' are not real methods...
// The 'builder' intercepts them and turns them into tags.
html.body {
    h1("Mission Briefing")
    p(class: 'intel', "Warning: Metaprogramming is powerful.")
}

def htmlOutput = writer.toString()
/* htmlOutput is:
<body>
  <h1>Mission Briefing</h1>
  <p class='intel'>Warning: Metaprogramming is powerful.</p>
</body>
*/

## How Builders Work

Builders use methodMissing to intercept method calls and convert them into structured data:

class SimpleBuilder {
    def output = []
    
    def methodMissing(String name, args) {
        output << "Called: $name with ${args}"
        return this  // Return this for chaining
    }
}

def builder = new SimpleBuilder()
builder.launch('Defiant').setSpeed('Warp 9').engage()

println builder.output
// [Called: launch with [Defiant], Called: setSpeed with [Warp 9], Called: engage with []]

## Creating a Validation DSL

Here's a practical example: creating a clean DSL for validating Documents in Spaceport.

The "Old" Way (Verbose Code):

def user = Document.get(userId, 'users')
def errors = []

if (user.fields.email == null || !user.fields.email.contains("@")) {
    errors.add("Email is invalid")
}
if (user.fields.age < 18) {
    errors.add("User must be 18 or older")
}
if (user.fields.password?.length() < 8) {
    errors.add("Password must be at least 8 characters")
}

The "DSL" Way (Clean & Readable):

class Validator {
    def errors = []
    def doc
    
    Validator(doc) {
        this.doc = doc
    }
    
    def check(String field, Closure... rules) {
        def value = doc.fields[field]
        rules.each { rule ->
            def result = rule(value)
            if (result) errors << result
        }
    }
    
    // Helper rule builders
    static Closure isRequired() {
        return { value ->
            value ? null : "Field is required"
        }
    }
    
    static Closure isEmail() {
        return { value ->
            value?.contains("@") ? null : "Must be a valid email"
        }
    }
    
    static Closure min(int minValue) {
        return { value ->
            value >= minValue ? null : "Must be at least ${minValue}"
        }
    }
    
    static Closure minLength(int length) {
        return { value ->
            value?.length() >= length ? null : "Must be at least ${length} characters"
        }
    }
    
    static validate(doc, Closure definition) {
        def validator = new Validator(doc)
        definition.delegate = validator
        definition()
        return validator.errors
    }
}

// Now use the DSL
def errors = Validator.validate(user) {
    check 'email', isRequired(), isEmail()
    check 'age', isRequired(), min(18)
    check 'password', isRequired(), minLength(8)
}

if (errors) {
    println "Validation failed:"
    errors.each { println "  - $it" }
}

This is the hallmark of a Groovy Luminary: Don't just write code; design the language for your problem, then write in that.

# AST Transformations: "Genetic Engineering"

If ExpandoMetaClass is "modifying the ship at runtime," then Abstract Syntax Tree (AST) Transformations are "rewriting the DNA in the blueprints before the ship is even built."

This is the most advanced form of metaprogramming. You write an annotation (like @...), and Groovy will physically rewrite your source code during the compilation phase.

## Common AST Transformations

You use these all the time, perhaps without knowing it:

@Singleton - Groovy rewrites your class to implement the full Singleton pattern:

@Singleton
class Database {
    def connection = "db://localhost"
}

// Only one instance exists
def db1 = Database.instance
def db2 = Database.instance
println db1 == db2  // true

@ToString - Groovy writes a toString() method for you:

@ToString
class Officer {
    String name
    String rank
}

def odo = new Officer(name: "Odo", rank: "Constable")
println odo  // Officer(Odo, Constable)

@Immutable - Groovy rewrites your class to be read-only and thread-safe:

@Immutable
class Coordinates {
    int x
    int y
}

def coord = new Coordinates(10, 20)
// coord.x = 30  // Compile error!

@Delegate - Groovy forwards method calls to another object:

class Engine {
    def start() { println "Engine starting" }
    def stop() { println "Engine stopping" }
}

class Starship {
    @Delegate Engine engine = new Engine()
}

def ship = new Starship()
ship.start()  // Calls engine.start()
ship.stop()   // Calls engine.stop()

## The Power of AST Transformations

AST Transformations enable the language itself to evolve without changing its core syntax. They're the "Prime Directive" of Groovy.

While we won't be building our own ASTs in this course (that's a topic for post-graduates), it's your duty as an engineer to know they exist and recognize them when you see them.

# Mission Debrief

You have been inside the "warp core" and seen the raw power of Groovy's dynamic nature. You've learned:

You are not limited by the "rules" of the language—you can change the rules. This is the true power of Groovy, and what makes it perfect for a framework like Spaceport.

Mission 05: The Bridge (Groovy in Launchpad)(mission-05)

Mission Briefing: You've mastered the engine room. Now it's time to take command of the bridge—where Groovy directly controls your user interface through Launchpad.

Groovy isn't just for the backend in Spaceport; it's the command language for your frontend. Through Launchpad templates, you can embed Groovy code directly in your HTML, creating dynamic, reactive user interfaces with the full power of the language at your fingertips.

Objectives: - Understand how Groovy integrates into Launchpad templates - Master server-side rendering with embedded Groovy - Use closures for Server Actions and Transmissions - Build reactive UI components with Cargo

# Groovy in Templates

Launchpad templates (.ghtml files) allow you to embed Groovy code directly alongside your HTML. This is similar to JSP, ERB, or PHP, but with Groovy's elegant syntax.

## Basic Template Syntax

<%
    // Scriptlet - Any Groovy code
    def crew = ["Sisko", "Kira", "Dax", "Bashir"]
    def shipName = "Defiant"
%>

<div>
    <h1>Welcome aboard the <%= shipName %></h1>
    
    <ul>
        <% crew.each { member -> %>
            <li>Commander <%= member %></li>
        <% } %>
    </ul>
</div>

## Expression Output

Use ${ ... } to output expressions directly into HTML:

<%
    def user = [name: "Sisko", rank: "Captain"]
%>

<div>
    <h2>${ user.name }</h2>
    <p>Rank: ${ user.rank }</p>
    
    <!-- Groovy expressions can include logic -->
    <p class="${ user.rank == 'Captain' ? 'command' : 'crew' }">
        Access Level: ${ user.rank }
    </p>
</div>

## Using Class Enhancements in Templates

This is where Spaceport's Class Enhancements really shine:

<%
    def user = context.data.user
    def isAdmin = user?.role == 'admin'
%>

<nav>
    <a href="/">Home</a>
    
    <!-- Only show admin link if user is admin -->
    ${ "<a href='/admin'>Admin Panel</a>".if { isAdmin }}
    
    <!-- Only show login if no user -->
    ${ "<a href='/login'>Login</a>".if { !user }}
</nav>

# Server Actions with Closures

One of Launchpad's most powerful features is Server Actions—the ability to execute Groovy code on the server in response to client events.

## Basic Server Action

<%
    def counter = 0
    
    def increment = {
        counter++
        return "Count: $counter"
    }
%>

<div>
    <button on-click=${ _{ increment() }} target="#display">
        Click Me
    </button>
    <div id="display">Count: 0</div>
</div>

## Using the Transmission Object

The t parameter (or whichever name you choose) gives you access to client data:

<%
    def greet = { t ->
        def name = t.getString('username')
        return "Hello, ${name.clean()}!"
    }
%>

<form on-submit=${ _{ t -> greet(t) }} target="#greeting">
    <input name="username" placeholder="Enter your name">
    <button type="submit">Greet</button>
</form>
<div id="greeting"></div>

## Returning Transmissions

Server Actions can return Maps to perform multiple operations:

<%
    def processForm = { t ->
        def name = t.getString('name')
        def email = t.getString('email')
        
        // Validate
        if (!email.contains('@')) {
            return [
                '#error': 'Invalid email address',
                '+error': 'it',
                'disabled': false
            ]
        }
        
        // Success
        return [
            '#message': "Welcome, ${name.clean()}!",
            '+success': 'it',
            'disabled': true,
            '@hide': '#error'
        ]
    }
%>

<form on-submit=${ _{ t -> processForm(t) }} target="self">
    <input name="name" required>
    <input name="email" type="email" required>
    <button type="submit">Submit</button>
    <div id="error" style="display:none"></div>
    <div id="message"></div>
</form>

# Working with Cargo

Cargo objects are Groovy Maps with superpowers—they provide reactive state management in Spaceport.

## Basic Cargo Usage in Templates

<%
    import spaceport.computer.memory.virtual.Cargo
    
    // Get or create a Cargo object from the store
    def cart = Cargo.fromStore('shopping-cart')
    
    def addItem = { t ->
        def itemName = t.getString('item')
        cart.inc('count')
        cart.append('items', itemName.clean())
        return [ '@reload' : null ]
    }
%>

<div>
    <h2>Shopping Cart (${ cart.getInteger('count') ?: 0 } items)</h2>
    
    <form on-submit=${ _{ t -> addItem(t) }}>
        <input name="item" placeholder="Item name">
        <button>Add Item</button>
    </form>
    
    <ul>
        <% (cart.getList('items') ?: []).each { item -> %>
            <li>${ item }</li>
        <% } %>
    </ul>
</div>

## Reactive Updates with Cargo

Use the ${{ ... }} syntax for reactive subscriptions:

<%
    import spaceport.computer.memory.virtual.Cargo
    
    def counter = Cargo.fromStore('live-counter')
    counter.counter.getDefaulted('value', 0)
    
    def increment = {
        counter.inc('value')
        // No need to return anything - reactive update happens automatically!
    }
%>

<div>
    <h2>Live Counter</h2>
    
    <!-- This updates automatically when counter changes -->
    <div class="display">
        Count: ${{ counter.getInteger('value') }}
    </div>
    
    <button on-click=${ _{ increment() }}>+1</button>
</div>

When counter changes on the server, Launchpad automatically pushes just the new value to the client and updates only that part of the DOM. No full page reload needed!

# Advanced Template Patterns

## Combining Static and Dynamic Content

<%
    import spaceport.computer.memory.virtual.Cargo
    
    def posts = Cargo.fromDocument(blogDoc)
    
    def publishPost = { t ->
        def title = t.getString('title')
        def content = t.getString('content')
        
        posts.setNext([
            title: title.clean(),
            content: content.clean(),
            timestamp: System.currentTimeMillis()
        ])
        
        return ['@reload': null]
    }
%>

<div class="blog">
    <!-- Static header -->
    <h1>Deep Space Nine Blog</h1>
    
    <!-- Dynamic post list -->
    <div class="posts">
        <% posts.values().sort { a, b -> 
            b.timestamp <=> a.timestamp 
        }.each { post -> %>
            <article>
                <h2>${ post.title }</h2>
                <p>${ post.content }</p>
                <time>${ post.timestamp.date() }</time>
            </article>
        <% } %>
    </div>
    
    <!-- Interactive form -->
    <form on-submit=${ _{ t -> publishPost(t) }}>
        <input name="title" placeholder="Post title">
        <textarea name="content" placeholder="Write your post..."></textarea>
        <button>Publish</button>
    </form>
</div>

## Using Groovy Helper Methods

Define reusable methods at the top of your template:

<%
    // Helper method to format rank with insignia
    def formatRank = { rank ->
        def insignia = [
            'Captain': '✦✦✦✦',
            'Commander': '✦✦✦',
            'Lieutenant': '✦✦',
            'Ensign': '✦'
        ]
        return "${insignia[rank] ?: ''} $rank"
    }
    
    def crew = [
        [name: 'Sisko', rank: 'Captain'],
        [name: 'Kira', rank: 'Commander'],
        [name: 'Dax', rank: 'Lieutenant']
    ]
%>

<div class="crew-roster">
    <% crew.each { member -> %>
        <div class="officer">
            <strong>${ member.name }</strong>
            <span class="rank">${ formatRank(member.rank) }</span>
        </div>
    <% } %>
</div>

# Mission Debrief

You've learned how to command the bridge—using Groovy to create dynamic, interactive user interfaces through Launchpad. You've mastered:

With Groovy powering both your backend logic and frontend templates, you have complete control over your Spaceport application from engine room to bridge.

Final Examination(final-exam)

To earn your Groovy Luminary Certification, you must complete this practical examination. These challenges will test your mastery of Groovy in real-world Spaceport scenarios.

# Challenge 1: Data Transformation

Given this list of starship data:

def fleet = [
    [name: "Defiant", registry: "NX-74205", crew: 50],
    [name: "Rio Grande", registry: "NCC-72452", crew: 4],
    [name: "Rubicon", registry: "NCC-72936", crew: 4]
]

Write Groovy code to: 1. Find all ships with crew larger than 5 2. Create a list of just the ship names 3. Calculate the total crew across all ships 4. Sort ships by crew size (descending) 5. Create a Map with registry numbers as keys and ship names as values

# Challenge 2: Closure Mastery

Create a closure called processReports that: 1. Takes a list of report maps (each with keys status and details) 2. Filters out any reports with status "success" 3. Returns a formatted string of all remaining reports: "Status: [STATUS] - [DETAILS]" 4. Each report should be on its own line

Test it with this data:

def reports = [
    [status: "success", details: "Mission complete"],
    [status: "warning", details: "Low fuel"],
    [status: "error", details: "Engine failure"],
    [status: "warning", details: "Hull breach on deck 4"]
]

# Challenge 3: Class Design

Create a CrewMember class with: - Properties: name, rank, station - A method report() that returns: "[RANK] [NAME] reporting from [STATION]" - A static method getAllByRank(rank) that filters a list of crew members - Use the @ToString annotation

Create at least 3 crew members and demonstrate all functionality.

# Challenge 4: Metaprogramming

Use metaprogramming to:

1. Add a isOfficer() method to the CrewMember class that returns true if rank is "Captain", "Commander", or "Lieutenant" 2. Add a reverse() method to String that returns the string backwards 3. Demonstrate both additions work correctly

# Challenge 5: Spaceport Integration

Create a simple Source Module that:

1. Handles requests to /crew/list 2. Maintains a static list of crew members (use Cargo) 3. Renders the crew list using a Launchpad template 4. Provides a Server Action to add a new crew member 5. Uses Class Enhancements to format the output

Show the complete Source Module code and template code.

Certification Achievement(certification)

Congratulations, Cadet!

Upon completing the course and final examination, you have demonstrated exceptional proficiency in:

You are now certified as a Groovy Luminary.

Your certification code: GL-${new Date().format('yyyyMMdd')}-${(1000..9999).random()}

You have earned the knowledge and skills necessary to build powerful, elegant, and dynamic applications using Groovy in the Spaceport framework. Your training has prepared you to:

Whether you came from Java, JavaScript, or jumped straight in by building the Tic-Tac-Toe game, you now understand the full power and elegance of Groovy as Spaceport's command language.

Welcome to the fleet, Groovy Luminary. Your mission to explore the possibilities of dynamic JVM programming has just begun.

# Additional Resources

Continue your journey with these recommended resources:

Official Groovy Resources

Spaceport Documentation

Books

May your code be elegant, your closures powerful, and your applications ever-dynamic.

Safe travels, Luminary. 🚀