
Serving 1.2M request per day
GaelykCategory @CompileStatic wherever possible// use(GaelykCategory){ // no longer compileMyPogo mypogo = new MyPogo(a: 'a', b: 'b', c: 'c')Entity entity = mypogo as Entityentity.save()// }
class ScriptExtension {void ifAuthenticated(Script self, Closure body){self.with{if(user){body(user)}}}}
// someTemplate.gtpl<% ifAuthenticated { user -> %>Hello $user.nickname!<% } %>
dependencies {
classpath 'org.gradle.api.plugins:gradle-gaelyk-plugin:0.4.1'
classpath 'org.gradle.api.plugins:gradle-gae-plugin:0.8', {
exclude module: "gradle-fatjar-plugin"
}
classpath 'eu.appsatori:gradle-fatjar-plugin:0.2'
classpath 'org.gradle.api.plugins:gradle-gae-geb-plugin:0.3'
}
TaskHandle handle = defaultQueue << [url: '/task.groovy']
defaultQueue << [url: '/task.groovy']
Future<TaskHandle> future = defaultQueue << [url: '/task.groovy']
@Entity class Book {// magic long id property is still added// @Key Long id// no more version property added automatically// @Version Long version@Indexed String title}Book book = new Book(title: 'It')assert book.hasProperty('id')assert !book.hasProperty('version')
get '/agent/*/@id', forward: '/agent.groovy?id=@id''/agent/bond/7' => '/agent.groovy?id=7'//before 2.0 the '*' in routes was greedy'/entry/james/bond/7' => '/agent.groovy?id=7'//since 2.0 the '*' matches just to the closest slash'/entry/james/bond/7' => '/agent.groovy?id=james/7'// use '**' to get the old behaviour backget '/agent/**/@id', forward: '/agent.groovy?id=@id'
// use method call with closurelifecycle.shutdown{...}// instead of assigning closure as variable// lifecycle.shutdown = {...}
// use method call xml()message.xml()// instead of property access// message.xml
Be sure you're running HRD!
Most of the examples only works when deployed
search.index('restaurants').put { document(id: 'pakwaan') { title text: 'Pakwaan Indian Restaurant'rating number: 4 opensAt number: 10 closesAt number: 24averagePrice number: 7 menu text: ''' Chicken Tikka 6 pcs - Marinated boneless chicken are with yoghurt, spices and cooked in tandoor.Chicken Seekh Kebab - Minced chicken with ginger flavour, lemon, and butter with green coriander'''// new tags atom: ['lunch menu', 'indian', 'take away'] location geoPoint: [50.098009,14.403806] }}
QueryOptions.Builder queryOptions = QueryOptions.newBuilder()queryOptions.idsOnly = truequeryOptions.numberFoundAccuracy = 1000SortOptions.Builder sortOptions = SortOptions.newBuilder()sortOptions.limit = 1000SortExpression.Builder exp = SortExpression.newBuilder() exp.expression = 'distance(location,geopoint(50.07543,14.436936))'exp.defaultValueNumeric = 0exp.direction = SortDirection.DESCENDINGsortOptions.addSortExpression(exp)queryOptions.sortOptions = sortOptionsQuery.Builder queryBuilder = Query.newBuilder()queryBuilder.options = queryOptionsQuery query = queryBuilder.build('tags: "take away")
SearchService search = SearchServiceFactory.searchServiceIndex index = search.getIndex(IndexSpec.newBuilder().setName(indexName).build())Results<ScoredDocument> results = index.search(query)def ids = results.results*.id
def results = search.search {select ids from restaurants where tags =~ 'take away' sort asc by distance(location, geopoint(50.07543,14.436936)), 0number found accuracy 1000limit sort to 1000 }
def now = 22.5 // 22.30
def results = search.search {
select title, opensAt, closesAt
from restaurants
where opensAt <= now
and closesAt >= now
sort desc by rating, 0
}
results.results*.title
def searchText = 'chicken' def results = search.search { select snippet: snippet(query, menu),distance: distance(location, geopoint(50.07543,14.436936)) from restaurants where menu =~ ~searchText sort asc by distance(location,geopoint(50.07543,14.436936)), Double.MAX_VALUE } for(result in results.results){ println "$result.title: $result.snippet ($result.distance)" }
def results = search.searchAsync(3) { select ids from restaurants where tags =~ 'take away' sort asc by distance(location, geopoint(50.07543,14.436936)), 0number found accuracy 1000limit sort to 1000 }
Future results = 3 * {search.searchAsync { select ids from restaurants where tags =~ 'take away' } }
Future key = 3 * { entity.asyncSave() }
QueryResultList<Entity> restaurants = datastore.execute {from Restaurantif(params.cursor){ startAt params.cursor }limit Math.min((params.limit ?: 10) as int, 100)}request.restaurants = restaurants
<ul><% for(r in request.restaurants){ %><li>$r</li><% } %></ul><a href="/restaurants?cursor=${request.restaurants}">Next</>
// works only for iterate methoddef result = datastore.iterate {from Restaurantrestart automatically}for(Entity r in restaurants){if(!r.processed){r.processed = true}r.save()}
QueryBuilder builder = datastore.build {from Restaurants}def results = nullint counter = 0int curusor = nullwhile(!results || counter < 10){counter++try {if(cursor){builder.startAt cursor}results = builder.execute()} catch(e) {log.warning "$e"}}
@Entity class Meal {@Parent Key restaurant@Key String name@Indexed Double price@Indexed Double weight}Meal meal = Meal.get(['Restaurant','pakwaan'] as Key, 'chicken masala')List<Meal> meals = Meal.findAll {ancestor ['Restaurant','pakwaan'] as Keywhere weight > 200}Meal.delete(['Restaurant','pakwaan'] as Key, 'chicken masala')
DatastoreEntity interface to skip relfection@Entity class Person { String firstName String lastName Date born Double height }1000.times{ (person as Entity) as Person }
Only works on HRD
datastore.withTransaction(true){Entity payment = new Entity('Payment')payment.amount = priceKey paymentKey = payment.save()Entity order = new Entity('Order')order.payment = paymentKey.idorder.save()}
class POGO implements Serializable {// @Ignore no longer neededstatic final long serialVersionUID = 1L}
entity.name = 'Vladimir' // set indexed propertyprintln entity.unindexed.name // no longer throws exception
get '/posts/@year?/@month?/@day?/page-@page?', forward: '/posts.groovy'
get '/posts/@year/@month/@day/page-@page', forward: '/posts.groovy?year=@year&month=@month&day=@day&page=@page'get '/posts/@year/@month/page-@page', forward: '/posts.groovy?year=@year&month=@month&page=@page'get '/posts/@year/page-@page', forward: '/posts.groovy?year=@year&page=@page'get '/posts/page-@page', forward: '/posts.groovy?page=@page'// to be continued
get '/posts/@year/@month/@day', forward: '/posts.groovy?year=@year&month=@month&day=@day'get '/posts/@year/@month', forward: '/posts.groovy?year=@year&month=@month'get '/posts/@year', forward: '/posts.groovy?year=@year'get '/posts', forward: '/posts.groovy'
get '/home', forward: 'home.groovy' // index: 0get '/about', forward: 'about.groovy' // index: 1...// new notation for extension matching// these routes will be evaluated firstall '/**/*.gtpl', ignore: true, index: -101all '/**/*.groovy', ignore: true, index: -100
after {if(result instanceof Map){result.each{ k,v ->request[k] = v}}}
after {
if(request.renderAsJson) {
response.contentType = 'application/json'
if(result instanceof Map && result?.status) {
response.status = result.remove('status')
}
JsonBuilder json = new JsonBuilder()
json result
json.writeTo(response.writer)
}
}if(!users.userLoggedIn){return [error: 'You must be logged in first', status: 401]}if(!users.userAdmin){return [error: 'You must be admin!', status: 401]}[result: datastore.execute{from Stuffwhere owner == user}]
get "/@username", forward: "/profile.groovy?username=@username"
startRoutingAt -30000 get "/_ah/gaelyk-console/",forward: "/org/gaelyk/console/script.gtpl" // index: -30000get "/_ah/gaelyk-console/render/",forward: "/org/gaelyk/console/template.gtpl" // index: -29999
// ?tag=one&tag=two assert params.tag.collect { it } == ['one', 'two']// ?tag=one assert params.tag.collect { it } == ['o', 'n', 'e']// ?limit=100 assert params.limit as int == 100// ?limit=100&limit=200 try { params.limit as int assert false } catch(ClassCastException e) { // cannot cast String[] to int assert true }
// ?tag=one&tag=two assert (params.tag as String[]).collect { it } == ['one', 'two']// ?tag=one assert (params.tag as String[]).collect { it } == ['one']// ?limit=100 assert params.limit as int == 100// ?limit=100&limit=200 assert params.limit as int == 100