Add sample project for Groovy templates

This commit is contained in:
Dave Syer
2014-05-20 13:32:44 +01:00
parent 334c8142c4
commit 523956e2fe
19 changed files with 11325 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.ui;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
/**
* @author Dave Syer
*/
public class InMemoryMessageRespository implements MessageRepository {
private static AtomicLong counter = new AtomicLong();
private final ConcurrentMap<Long, Message> messages = new ConcurrentHashMap<Long, Message>();
@Override
public Iterable<Message> findAll() {
return this.messages.values();
}
@Override
public Message save(Message message) {
Long id = message.getId();
if (id == null) {
id = counter.incrementAndGet();
message.setId(id);
}
this.messages.put(id, message);
return message;
}
@Override
public Message findMessage(Long id) {
return this.messages.get(id);
}
}

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package sample.ui;
import java.util.Date;
import org.hibernate.validator.constraints.NotEmpty;
/**
* @author Rob Winch
*/
public class Message {
private Long id;
@NotEmpty(message = "Message is required.")
private String text;
@NotEmpty(message = "Summary is required.")
private String summary;
private Date created = new Date();
public Long getId() {
return this.id;
}
public void setId(Long id) {
this.id = id;
}
public Date getCreated() {
return this.created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getText() {
return this.text;
}
public void setText(String text) {
this.text = text;
}
public String getSummary() {
return this.summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package sample.ui;
/**
* @author Rob Winch
*/
public interface MessageRepository {
Iterable<Message> findAll();
Message save(Message message);
Message findMessage(Long id);
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2012-2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sample.ui;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
@Configuration
@EnableAutoConfiguration
@ComponentScan
public class SampleGroovyTemplateApplication {
@Bean
public MessageRepository messageRepository() {
return new InMemoryMessageRespository();
}
@Bean
public Converter<String, Message> messageConverter() {
return new Converter<String, Message>() {
@Override
public Message convert(String id) {
return messageRepository().findMessage(Long.valueOf(id));
}
};
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleGroovyTemplateApplication.class, args);
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package sample.ui.mvc;
import java.util.HashMap;
import java.util.Map;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import sample.ui.Message;
import sample.ui.MessageRepository;
/**
* @author Rob Winch
*/
@Controller
@RequestMapping("/")
public class MessageController {
private final MessageRepository messageRepository;
@Autowired
public MessageController(MessageRepository messageRepository) {
this.messageRepository = messageRepository;
}
@RequestMapping
public ModelAndView list() {
Iterable<Message> messages = this.messageRepository.findAll();
return new ModelAndView("messages/list", "messages", messages);
}
@RequestMapping("{id}")
public ModelAndView view(@PathVariable("id") Message message) {
return new ModelAndView("messages/view", "message", message);
}
@RequestMapping(params = "form", method = RequestMethod.GET)
public String createForm(@ModelAttribute Message message) {
return "messages/form";
}
@RequestMapping(method = RequestMethod.POST)
public ModelAndView create(@Valid Message message, BindingResult result,
RedirectAttributes redirect) {
if (result.hasErrors()) {
ModelAndView mav = new ModelAndView("messages/form");
mav.addObject("formErrors", result.getAllErrors());
mav.addObject("fieldErrors", getFieldErrors(result));
return mav;
}
message = this.messageRepository.save(message);
redirect.addFlashAttribute("globalMessage", "Successfully created a new message");
return new ModelAndView("redirect:/{message.id}", "message.id", message.getId());
}
private Map<String, ObjectError> getFieldErrors(BindingResult result) {
Map<String, ObjectError> map = new HashMap<String, ObjectError>();
for (FieldError error : result.getFieldErrors()) {
map.put(error.getField(), error);
}
return map ;
}
@RequestMapping("foo")
public String foo() {
throw new RuntimeException("Expected exception in controller");
}
}

View File

@@ -0,0 +1,2 @@
# Allow templates to be reloaded at dev time
spring.groovy.template.cache: false

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<include resource="org/springframework/boot/logging/logback/base.xml"/>
<logger name="org.springframework.web" level="DEBUG"/>
</configuration>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,27 @@
html {
head {
title(title)
link(rel:'stylesheet', href:'/css/bootstrap.min.css')
}
body {
div(class:'container') {
div(class:'navbar') {
div(class:'navbar-inner') {
a(class:'brand',
href:'http://beta.groovy-lang.org/docs/groovy-2.3.0/html/documentation/markup-template-engine.html') {
yield 'Groovy - Layout'
}
ul(class:'nav') {
li {
a(href:'/') {
yield 'Messages'
}
}
}
}
}
h1(title)
div { content() }
}
}
}

View File

@@ -0,0 +1,25 @@
layout 'layout.tpl', title: 'Messages : Create',
content: contents {
div (class:'container') {
form (id:'messageForm', action:'/', method:'post') {
if (formErrors) {
div(class:'alert alert-error') {
formErrors.each { error ->
p error.defaultMessage
}
}
}
div (class:'pull-right') {
a (href:'/', 'Messages')
}
label (for:'summary', 'Summary')
input (name:'summary', type:'text', value:message.summary?:'',
class:fieldErrors?.summary ? 'field-error' : 'none')
label (for:'text', 'Message')
textarea (name:'text', class:fieldErrors?.text ? 'field-error' : 'none', message.text?:'')
div (class:'form-actions') {
input (type:'submit', value:'Create')
}
}
}
}

View File

@@ -0,0 +1,31 @@
layout 'layout.tpl', title: 'Messages : View all',
content: contents {
div(class:'container') {
div(class:'pull-right') {
a(href:'/?form', 'Create Message')
}
table(class:'table table-bordered table-striped') {
thead {
tr {
td 'ID'
td 'Created'
td 'Summary'
}
}
tbody {
if (messages.empty) { tr { td(colspan:'3', 'No Messages' ) } }
messages.each { message ->
tr {
td message.id
td "${message.created}"
td {
a(href:"/${message.id}") {
yield message.getSummary()
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,21 @@
layout 'layout.tpl', title:'Messages : View',
content: contents {
div(class:'container') {
if (globalMessage) {
div (class:'alert alert-success', globalMessage)
}
div(class:'pull-right') {
a(href:'/', 'Messages')
}
dl {
dt 'ID'
dd(id:'id', message.id)
dt 'Date'
dd(id:'created', "${message.created}")
dt 'Summary'
dd(id:'summary', message.summary)
dt 'Message'
dd(id:'text', message.text)
}
}
}