I'd like to let you know that the grails community has one more place to find answers! This time it's a discussion group for polish speakers, GUG Poland.
Come join and let's bring the community the best help we can!
static mapping = {
id composite: [ 'reportId', 'managerId' ]
}
class CarController {
def index = { ... }
def setCarAvailability = {
def car = Car.get(params.id)
if (car && params.available) {
car.avaiable = params.available
car.save(flush: true)
redirect action: 'index'
} else {
flash.message = "Error: either no valid car id, available not given or saving failed"
redirect action: 'index'
}
}
}
class CarController {
def index = { ... }
def setCarAvailability = {
SetCarAvailabilityCommand command ->
if (command.validate()) {
command.execute()
redirect action: 'index'
} else {
flash.message =
command.errors.allErrors.collect { error ->
error -> error.field
}.join(", ")
redirect action: 'index'
}
}
}
class SetCarAvailabilityCommand {
Car car
Boolean available
static constraints = {
car nullable: false, validator: { it?.attached }
available blank: false, required: false
}
def execute() {
car.available = available
car.save(flush: true)
}
}
How about that, ha? Nice separation of concerns (actual entity that does the job separated from controller that should only care about controlling what should go next), automatic parameter validation, clean naming and if you put those two into the same file then even automatic reloading works! <g:form action="setCarAvailability">
<g:textField name="car.id"/>
<g:checkBox name="available"/>
<br/><br/>
<g:submitButton name="action" />
</g:form>
grails create-app exampleStep 2: Install the grails-easyb plugin.
grails install-plugin easybStep 3: Install selenium rc.
java -jar selenium-server.jarStep 4: Create the home controller.
grails create-controller homeStep 5: Add some meat to the index action.
[ message: "Hello, world!" ]Step 6: Create a functional (yes! functional test at last!) in test/functional (this folder does not exist at first so you have to create it yourself). I'll call it MyFirstFunctional.story
import com.thoughtworks.selenium.*
before "start selenium", {
given "selenium is up and running", {
selenium = new DefaultSelenium(
"localhost", 4444, "*iexplore",
"http://localhost:8080/example/"
)
selenium.start()
}
}
scenario "Will show 'hello, world!' message", {
when "home/index opens", {
selenium.open("home/index")
}
then "the text 'Hello, world! appears on the page", {
selenium.isTextPresent("Hello, world!")
}
}
after "stop selenium", {
then "selenium should be shutdown", {
selenium.stop()
}
}
WARNING: you might naturally want to move the "after" section right below the "before" section. DON'T EVER DO THAT! EasyB test segments are executed in the order they have been declared so if you do that selenium will get stopped before any scenario can be executed!grails test-app functional:easybStep 8: Create the view (/views/home/index.gsp).
Message: ${message}Step 9: Execute the test and see it pass:
grails test-app functional:easyb
class HomeController {
def index = { }
def save = { LoginCommand login ->
println "login.username: ${login.username}"
println "login.password: ${login.password}"
println "comment.text: ${comment.text}"
redirect action: "index"
}
}
class LoginCommand {
String username
String password
}
<g:form action="save">
<label for="login.username">Username: <g:textField name="login.username"/><br/>
<label for="login.password">Password: </label>
<g:textField name="login.password"/><br/>
<label for="comment.text">Comment</label>
<g:textField name="comment.text"/><br/>
<br/>
<input type="submit"/>
</g:form>
class HomeController {
def index = { }
def save = { LoginCommand login, CommentCommand comment ->
println "login.username: ${login.username}"
println "login.password: ${login.password}"
println "comment.text: ${comment.text}"
redirect action: "index"
}
}
class LoginCommand {
String username
String password
}
class CommentCommand {
String text
}
try {
LdapContext context = new InitialLdapContext((Hashtable) [
(Context.INITIAL_CONTEXT_FACTORY): "com.sun.jndi.ldap.LdapCtxFactory",
(Context.PROVIDER_URL) : "ldap://ldap.localdomain.com",
(Context.SECURITY_AUTHENTICATION): "simple",
(Context.SECURITY_PRINCIPAL) : "DOMAIN\\invalid",
(Context.SECURITY_CREDENTIALS) : "invalid",
(Context.REFERRAL) : "follow",
(Context.BATCHSIZE) : "30"
], (Control[]) []);
println "Logged in!"
} catch (AuthenticationException e) {
println "NOT lOGGED IN"
}