Found Pages

Entity

Data models in WebDSL are defined using entity definitions. An entity definition consists of the entity’s name, possibly a super-entity from which it inherits, 0 or more properties and 0 or more entity functions:

entity User {
  name     :: String (length = 25)
  email    :: Email
  password :: Secret
  homepage :: URL
  pages    -> Set<Page>
  function checkPassword(String s) : Bool { 
    return password.check(s);
  }
  predicate sameUser(u:User){ this == u } 
}

A property consists of 4 parts:

  • a name
  • a property kind, which can either be value (::), reference (->) or composite (<>)

The difference between reference and composite property kinds is that composite indicates that the referred entity is part of the one referring to it. The only effect this currently has is that composite cascades delete (deleting the entity will also delete the referred entity).

  • a property type, e.g. value types String, Int, Long, Text or reference/composite types which refer to other entities, such as Person, Set, and List.

For a complete overview of the available types, see Types.

  • a set of annotations, for instance declaring inverse properties, lengths, validation.

An example data model for a blogging site:

entity Author {
  name     :: String
  email    :: Email
  password :: Secret
  posts    -> Set<Post> (inverse=Post.author)
}

entity Post {
  author   -> Author
  title    :: String
  text     :: Text
  comments -> Set<Comment> (inverse=Comment.post)
}

entity Comment {
  post     -> Post
  author   :: String
  text     :: Text
}
Entity Inheritance

Entities can inherit properties and functions from other entities, like subclassing in Object-Oriented programming.

Example:

entity Sub : Super {
  str :: String
}
entity Super {
  i :: Int
}
function test(){
  var e1 := Sub{ i := 1 str := "sdf" };
  var e2 := Super{ i := 1 };
}

Subclass entities can be passed whenever an argument of one of its super types is expected.

Example:

function test(){
  var e1 := Sub{ i := 1 str := "sdf" };
  test(e1);
}
function test(s:Super){
  log(s.i);
}

Checking the dynamic type of an entity can be done using isa and casting is performed using as.

Example:

function test(s:Super){
  if(s isa Sub){
    var su :Sub := s as Sub;
    log(su.str);
  }
}

When specifically want to call a function from the Superclass, use the ‘super’ keyword.

Example:

entity Sub : Super {
  function foo() : Int {
    return super.foo();
  }
}
entity Super {
  function foo() : Int {
    return 42;
  }
}
Session Entity

Storing data in the session context on the server is done using session entities. Example:

session shoppingcart {
  products -> List<Product>
}

A session entity name is a globally visible variable in the application code. The entity object is automatically instantiated and saved, one for each browser session accessing the application.

Typically, session data is used for keeping track of authentication state, but it can also be used for temporarily storing data for anonymous users. A common oversight with session data is that it is shared between tabs in a browser.

Declaring an access control principle, e.g. principal is User with credentials name,password, automatically creates a securityContext session entity. For more information about access control see the Access Control section.

Session entities can also be extended with extra properties. Example:

extend session shoppingcart{
  lastSearchQuery :: String
}

Session data times out by default, this timeout length can be adjusted in the application.ini file, e.g. sessiontimeout=10080. This time is specified in minutes. More information about application settings is shown on the Application Configuration page.

Instantiating Entity Objects

Instantiating new entity objects is done with the following expression:

Entity{ [property := value]* }

The entity name followed by an optional list of property assignments between curly brackets.

Example:

User{}
User{ name := "Alice" }
User{ name := "Bob" age := 34 }

Default initialization (what you would put into the constructor of an object in e.g. the Java programming language), can be added by extending the constructor function that is implicitly called.

Example:

entity A : B{
  extend function A(){
    name := name +"A";
  } 
}

entity B{
  extend function B(){
    name := name +"B";
  } 
}

test constructors {
  var t := A{};
  assert(t.name == "BA");
}

Creating an empty entity which doesn’t call the constructor extensions can be done using createEmptyEntity, e.g. createEmptyUser()

Ajax Validation

WebDSL provides input components that validate the inputs using ajax.

built-in value types:

inputajax(String/Secret/URL/Email/Text/WikiText)
inputajax(Int/Bool/Float/Long)

reference types:

inputajax(Entity/List<Entity>/Set<Entity>)
selectajax(Entity/Set<Entity>)
radioajax(Entity)

provide selection options:

inputajax(Entity/List<Entity>/Set<Entity>,List<Entity>)
selectajax(Entity/Set<Entity>,List<Entity>)
radioajax(Entity,List<Entity>)

Selection options can also be provided using the allowed annotation on an entity property. Example:

entity Person{
  parent -> Person (allowed=from Person as p where p != this)
} 
List

Represents an ordered list of items of a certain type. Example:

var l : List<Int> := [1, 2, 3, 4];

Sorted output of lists can be created using the for loop filter in templates or actions:

for(u:User in [u1,u2,u3] order by u.name desc){
  output(u)
}

Fields

length

Gives the number of items in the list.


List Creation Expressions

List()

Creates an empty list of type Entity.

List(…, Entity, …)

Creates a list of type Entity with the elements resulting from the comma separated argument expressions.

Example:

var list := List<User>(User{},SubSubUser{},SubUser{})

[Entity, …]

Creates a list of type Entity (type of first element) with the elements resulting from the comma separated expressions between the [ ].

Example:

var list := [User{ name := "test" },SubUser{},uservar]

Functions

add(Entity)

Adds the entity to this list.

remove(Entity)

Removes the first occurence of Entity in this list.

clear()

Removes all entities in this list.

addAll(List/Set)

Adds all entities of the List/Set to this list.

set() : Set

Creates a Set containing the unique elements in this list.

indexOf(Entity) : Int

Returns the index of the first occurence of Entity in this list. Returns -1 if the Entity is not in this list.

get(Int)

Returns the element at location Int in this list.

set(Int,Entity)

Sets the list element at Int to Entity. If the Int is not within bounds, nothing is set, and a warning is shown in the log.

insert(Int,Entity)

Inserts the Entity at location Int in this list. If the Int is not within bounds, nothing is set, and a warning is shown in the log.

removeAt(Int)

Removes the element at location Int in this list. If the Int is not within bounds, nothing is set, and a warning is shown in the log.

subList(from:Int,to:Int):List

Returns a portion of this list between the specified from, inclusive, and to, exclusive.

Set

Represents an unordered collection of unique items of a certain type. Example

var s : Set<Int> := {1, 2, 3, 4};

Sorted output of sets can be created using the for loop filter in templates or actions.

for(u:User in [u1,u2,u3] order by u.name desc){
  output(u)
}

Fields

length

Gives the number of items in the set.


Set Creation Expressions

Set()

Creates an empty set of type Entity.

Set(…, Entity, …)

Creates a set of type Entity with the elements resulting from the comma separated argument expressions.

Example:

var set := Set<Person>(Person{},SubSubPerson{},SubPerson{})

{Entity, …}

Creates a set of type Entity (type of first element) with the elements resulting from the comma separated expressions.

Example:

var set : Set<Person> := {Person{ name := "test" },personvar}

Functions

add(Entity)

Adds the entity to this set.

remove(Entity)

Removes Entity in this set.

clear()

Removes all entities in this set.

addAll(List/Set)

Adds all entities of the List/Set to this set.

list() : List

Creates a List containing the elements in this set.

Generated Functions for Entities

For defined entities, a number of global functions are automatically generated. Replace Entity with the defined entity name below.

Property with id annotation

If the Entity has an id annotation on a property, the following functions are generated (idtype is the type of the id property):

getUniqueEntity

getUniqueEntity(id : idtype) : Entity

If the Entity with the given id already exists, it is returned. If it did not exist, it is created once and a flush to the database is performed (this will commit any changes made to the entities in memory, e.g. the changes from data binding of input fields), repeated calls to this function with the same argument will keep returning that created Entity.

isUniqueEntity

isUniqueEntity(ent : Entity) : Bool

This function returns false when the value of the id property of ent is already taken. The function returns true when the id property is not taken, but will do so only once, subsequent calls with different entities but the same id will then return false (which makes this function suitable for processing a batch of entities in an action).

isUniqueEntityId

isUniqueEntityId(id : idtype, ent : Entity) : Bool

This function returns false when the entity would not be unique when given the id argument. The function returns true when the entity would be unique, but will do so only once for a given id, checking a different entity with the same id will return false in the rest of the action handling.

isUniqueEntityId(id : idtype) : Bool

This function returns false when the given id is not available for the Entity type. The function will return true only once, to cope with batch processing.

Note that these functions use one collection per entity to determine whether an id is available, so a call to isUniqueUserId(id) can influence the result of isUniqueUser(ent).

findEntity

findEntity(id : idtype) : Entity

This function returns the Entity with the given id value, null if it does not exist.

String property

For each String property in an Entity, a find function is generated (repace Property with the property name):

findEntityByProperty

findEntityByProperty(val : String) : List<Entity>

This function returns a list of all Entitys with the exact given Property value, an empty list if there are none.

findEntityByPropertyLike

findEntityByPropertyLike(val : String) : List<Entity>

This function returns a list of all Entitys with the given Property value as substring, an empty list if there are none.

Entity Name

Every entity has a name, which is always a string. This name can be retrieved by the automatically generated getName() function.

The name of an entity is determined as follows:

  1. If a property of the entity has the name annotation, the name of the entity equals this property. This property must be of type String.

  2. If a property of the entity is called ‘name’ and is of type String, this property determines the entity name.

  3. Otherwise, the id of the entity (converted to its string-value) is used.

Example

A typical scenario where these functions come in handy is a create/edit page for an entity. In the following example the isUniquePage function is used to verify that the new page has a unique identifier property:

entity Page {
  identifier :: String  (id, validate(isUniquePage(this), "Identifier is taken")
}
define page createPage(){ 
  var p := Page{}
  form{
    label("Identifier"){input(p.identifier)}
    action("save",save())
    action save(){
      p.save();
      message("New page created.");
      return home();
    }
  }  
}
Input

input(<expression>) creates an input form element. Can be applied directly to the properties of an entity (e.g., input(user.name)) or to page variables.

Input widgets are determined by the type of the property passed to the input template call:

  • String, Email, Int, Float, URL, Patch -> textfield
  • Text, WikiText -> textarea
  • Bool -> checkbox
  • Date, DateTime, Time -> date picker
  • List, Set -> multiselect (bug: List actually requires a different type of input, to allow duplicates and control ordering)
  • Entity -> select

For example, to get a checkbox, use:

define root(){
 var x : Bool := false
 form{
   input(x)
   submit action{ log(x); } { "log result" }
 }
}

or:

entity TestEntity {
 x :: Bool
}
define editTestEntity (e:TestEntity){
 form{
   input(e.x)
   submit action{ } { "update entity" }
 }
}
Name Property

The ‘name’ property is special, it is declared for each entity. By default it is a derived property that simply returns the id of the entity (which is also a special property declared for each entity, id:UUID is set automatically). The name can be customized by declaring a real name property:

name :: String

Or derived name property:

name :: String := firstname + lastname

Or by declaring a property as the name using an annotation:

someproperty :: String (name)

The name property is used in input and select template elements to refer to an entity. Example:

application exampleapp
init{
  var u := User{};
  u.save();
  u := User{};
  u.save();
  u := User{};
  u.save();
}
entity User{} 
entity UserList{
  users -> List<User>
}
var globalList := UserList{}

define page root(){
  for(u:User in globalList.users){
    output(u.name) //there is always a name property
  }
  form{ 
    input(globalList.users) //this will show three UUIDs as options
    submit("save",action{})
  }
}

If the name is not a real property, you cannot create an input for it or assign to it.