Serving 1.2M request per day
GaelykCategory
@CompileStatic
wherever possible// use(GaelykCategory){ // no longer compile
MyPogo mypogo = new MyPogo(a: 'a', b: 'b', c: 'c')
Entity entity = mypogo as Entity
entity.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 back
get '/agent/**/@id', forward: '/agent.groovy?id=@id'
// use method call with closure
lifecycle.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: 24
averagePrice 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 = true
queryOptions.numberFoundAccuracy = 1000
SortOptions.Builder sortOptions = SortOptions.newBuilder()
sortOptions.limit = 1000
SortExpression.Builder exp = SortExpression.newBuilder() exp.expression = 'distance(location,geopoint(50.07543,14.436936))'
exp.defaultValueNumeric = 0
exp.direction = SortDirection.DESCENDING
sortOptions.addSortExpression(exp)
queryOptions.sortOptions = sortOptions
Query.Builder queryBuilder = Query.newBuilder()
queryBuilder.options = queryOptions
Query query = queryBuilder.build('tags: "take away")
SearchService search = SearchServiceFactory.searchService
Index 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)), 0
number found accuracy 1000
limit 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)), 0
number found accuracy 1000
limit 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 Restaurant
if(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 method
def result = datastore.iterate {
from Restaurant
restart automatically
}
for(Entity r in restaurants){
if(!r.processed){
r.processed = true
}
r.save()
}
QueryBuilder builder = datastore.build {
from Restaurants
}
def results = null
int counter = 0
int curusor = null
while(!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 Key
where 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 = price
Key paymentKey = payment.save()
Entity order = new Entity('Order')
order.payment = paymentKey.id
order.save()
}
class POGO implements Serializable {
// @Ignore no longer needed
static final long serialVersionUID = 1L
}
entity.name = 'Vladimir' // set indexed property
println 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: 0
get '/about', forward: 'about.groovy' // index: 1
...
// new notation for extension matching
// these routes will be evaluated first
all '/**/*.gtpl', ignore: true, index: -101
all '/**/*.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 Stuff
where owner == user
}]
get "/@username", forward: "/profile.groovy?username=@username"
startRoutingAt -30000 get "/_ah/gaelyk-console/",
forward: "/org/gaelyk/console/script.gtpl" // index: -30000
get "/_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