Grails Flex Scaffold – GFSv0.2.2

December 15, 2009

GFSv0.2.2 Released!!

After the success of the last release, Cubika Labs is glad to announce that the new version of GFS in now available. GFSv0.2.2 includes new functionalities and some fixes.

Among important features you will find the spring-security integration, this was the feature most requested by GFS users.

Although other functionalities that we consider vital for a complete integration are still missing, this is an initial approach and the basis to continue working in the new versions.

Some included improvements in this version are:

  • spring-security Integration (we use stark-security)
  • Tabs group (Thank you so much Nicolas Domina)
  • actions into datagrid

If you want report a bug / improvement or vote our Backlog features’ you are welcome to do it in our Jira

See our Confluence for technical documentation

See our SVN if you want to try the code

See new Screencast

In order to explain how this features work, we are going to write some code for a dummy todo-list app

Prerequisites

  • Grails 1.1 or major
  • GFSv0.2.2
  • Flex 3.0.0 or major

FLEX_HOME and GRAILS_HOME must be defined as environment variable!!

Domain Model

1 – Create Project and Install Plugin


gfs@gfs $ grails create-app todo-list gfs@todo-list $ grails install-plugin \ [path-plugin]/grails-flex-scaffold-0.2.2.zip #or gfs@todo-list $ grails install-plugin flex-scaffold

2 - Generate User, Role and Task


#Generets User and Role for spring-security integration gfs@todo-list $ grails stark-security-install-full gfs@todo-list $ grails create-domain-class task

3 - Edit User

class User implements UserDetails {

    String name
    String lastName
    String username
    String password
	String email

    static transients = [
            'authorities',
            'accountNonExpired',
            'enabled',
            'credentialsNonExpired',
            'accountNonLocked'
    ]

    static hasMany = [ roles: Role ]

    //Generates TAB "ADM User" that contains User's tab
    static groupName = "ADM User" 

    // Need to eagerly fetch role relations, because they'll be asked for later
    // by Spring Security when the hibernate session is no longer available
    static mapping = {
      roles lazy:false

    }

    static constraints = {
      name(blank:false)
      lastName(blank:false)
	  email(email:true)	
      username(blank:false)
      //Generate 2 textinput to validate password.
      password(widget:"password")
      roles(inPlace:false)
    }

    GrantedAuthority[] getAuthorities() {
        return roles as GrantedAuthority[]
    }

    boolean isAccountNonLocked() {
        return true
    }
    def setAccountNonLocked(boolean nonLocked) {}

    boolean isCredentialsNonExpired() {
        return true
    }
    def setCredentialsNonExpired(boolean nonExpired) {}

    boolean isAccountNonExpired() {
        return true
    }
    def setAccountNonExpired(boolean acctNonExpired) {}

    boolean isEnabled() {
        return true
    }
    def setEnabled(boolean enabled) {}
}

4 - Edit Role

class Role implements GrantedAuthority {

    String description
    String authority

    static final ANONYMOUS = 'IS_AUTHENTICATED_ANONYMOUSLY'
    // Add your roles here so you can reference them, for instance:
    // static final ADMIN_USER = 'ROLE_ADMIN_USER'

    // This list holds all roles, convenient when you're declaring controller methods
    // that should be available to everybody (see AccessController for instance).  When
    // you add roles to your system, make sure you add them to this list as well.
    static final ALL_ROLES = [  ANONYMOUS ]

    //Generates TAB "ADM User" that contains Role's tab
    static groupName = "ADM User"

    int compareTo(Object o) {
        if (o instanceof Role) {
            return this.authority.compareTo(o.authority)
        }
         return 0
    }

    String toString() {
        return authority
    }
}

5 - Edit Task

class Task {

	String name
	String description
	Long priority

	//multiselection:true - A Checkbox appears in all rows on the first datagrid column
	//actions:["postpone"] - show an action button (label=postpone) in each row so you can 
	//add bussines logic into postpone method at TaskService
	//You could declare as many actions buttons as you need eg: actions:["postpone","complete"]
	static action = [datagrid:[multiselection:true,actions:["postpone"]]]

    static constraints = {
		name(blank:false)
		priority(widget:"hslider", range:1..10)
		description(minSize:5, maxSize:255)
    }
}

6 - Add to Config.groovy


gfs.security = true

7 - Edit StarkSecurityConfig.groovy and change:

    authorizations = [
        '/': Role.ALL_ROLES,
        '/js/**': Role.ALL_ROLES,
        '/css/**': Role.ALL_ROLES,
        '/images/**': Role.ALL_ROLES,
        '/j_spring_security_logout': Role.ALL_ROLES
    ]

to:

    authorizations = [
        '/**': Role.ALL_ROLES,
        '/js/**': Role.ALL_ROLES,
        '/css/**': Role.ALL_ROLES,
        '/images/**': Role.ALL_ROLES,
        '/j_spring_security_logout': Role.ALL_ROLES
    ]

8 - Edit BootStrap.groovy as below

    import org.codehaus.groovy.grails.plugins.starksecurity.PasswordEncoder

	class BootStrap {

	 def init = { servletContext ->

	  if (!User.findByUsername("admin")) {

      def superUser = new User(username: 'admin',
		           password: PasswordEncoder.encode('admin', 'SHA-256', true),name:"admin",lastName:"admin",
																			email:"admin@admin.com")

	   def role = new Role(authority:"ROLE_USER",description:"Role users")													

	   superUser.addToRoles(role)
	   
	   superUser.save()
	 }

	}

	def destroy = {
	}
   }

9 - Generate Flex Artifacts


gfs@todo-list $ grails generate-all-flex role gfs@todo-list $ grails generate-all-flex user gfs@todo-list $ grails generate-all-flex task

10 - Compile Flex and run app


gfs@todo-list $ grails flex-tasks gfs@todo-list $ grails run-app

11 - Access to application

On the login dialog enter the username and password already setted into BootStrap.groovy (admin/admin for this example).

Tips

For this version, we are not taking care of roles regarding functionality.

If you used FlexBuilder you must set-define=GFS::security,false to true ("-define=GFS::security,true") in the Flex Compiler options (flex arguments), this property will show login dialog if is setted to true. By default it's false and in command-line compiling (grails flex-tasks) this property will be retrieved from Config.groovy (gfs.security = [true|false]).

Grail Flex Scaffold – GFSv0.2 – Coming Soon!!

May 31, 2009

We are about to release a new version of GFS. This new release has tons of new features, bug fixes and an improved way to navigate CRUD operations.

Some of the new features for this release are:

- i18n Support. We now support all the languages that Google can translate. GFS builds up the message[_locale].properties files from the configured default language through Google Translator.

- Integrated reporting with DynamicJasper plugin http://www.grails.org/DynamicJasper+Plugin

- File Upload  (Image widget and snapshots through webcam)

- Package support

- Inheritance support (Perfrom CRUDs for subclasses)

- generate-all-flex now supports wildcards “*” (see documentation on how to use this feature)

- More widgets  (hslide, vslider, colorpicker, etc..)

We are also currently working on security features by including Spring Security (http://www.grails.org/AcegiSecurity+Plugin) to the GFS plugin. Since we had to modify several core classes to support the dynamic assignment of roles to services we still need to do some testing before we can release an alpha version.

You can see a new screencast on http://dist.codehaus.org/gfs/GFSv0.2-beta/gfs-screencast-0.2-beta.mov

comming soon Dynamic Jasper example screencast!!

You can download and try GFSv0.2-beta from: http://svn.codehaus.org/gfs/trunk/grails-flex-scaffold/tags/RELEASE_0_2-beta/grails-flex-scaffold-0.2-beta.zip

or see installation on http://grails.org/plugin/flex-scaffold 

You can also check the code here: http://svn.codehaus.org/gfs/

Documentation is also under development. You can check the updated documentation with all the GFSv0.2 new features documentation here: http://docs.codehaus.org/display/GFS/User+Guide+0.x

Please, report any bug on our jira 

Grails Flex Scaffold 0.1.1 Released

March 27, 2009

GFSv0.1.1 realease!!!

The only goal of this realease is adding Grails-1.1 support, so there are no new features included. We are putting all our efforts on GFSv0.2 (for more information on GFSv0.2 ->GFS-Jira)

We fixed issues of configuration, improved the FlexTasks script and refactor the commands to support the nomenclature of the generated applications

Included Features:

Relations

Supports one-to-one, one-to-many and many-to-one.

In one-to-one there doesn’t work update (it doesn’t update the record ), we don’t know why. If someone kwons or wants to check it is going to be of grear help for us.

Validations

GFS supports validations in FrontEnd from Grails’ constraint

Code Generation

CRUD’s views are generated from Grails Domain Class definition.

Adobe Flex Builder Integration

Once, first compilation via FlexTasks done, it is possible to Import the project into FlexBuilder (or Eclipse + FlexBuilder Plugin to support Grails too), that’s because FlexTasks generates Eclipse/FlexBuilder configuration property files. Obviously, FlexTasks generates an index.gsp that wraps the swf too.
Tip: “In order to debug flex application from FlexBuilder, it is necessary to modify the access url –>Open Debug’s configuration Dialog –> uncheck “Use Defaults” and remove main.html of three configurations.
We could not find any file of configuration that contains these parameters, that’s why the tip.”

Resources

GFS in Action (screencast)

Project Source: http://plugins.grails.org/grails-flex-scaffold

For more information about the use of GFS visit Official Docs (User Guide, Examples)

Bug reporting and versions’ status http://jira.codehaus.org/browse/GFS

Comming Soon GFSv0.2, to see the roadmap

Grails Flex Scaffold (GFS)

March 6, 2009

Grails Flex Scaffold (GFS) is a grails’ plugin that deals Flex code generation by scaffolding methodology, including support for presentation and service layers by providing embeded data in your domain classes as original Grails Scaffolding does with Ajax and HTML.

In the first version (GFS v0.1) we attempt to support some basic features in Flex’s CRUD generation staff (Relations, Validations, etc).

Inspired by our experience on Grails we thought it could be a very good idea the fact of adding RIA concept, in order to enhance the user experience.

This plugin DO NOT attempt to get down developers’ skill, instead It tries to make easy repetitive tasks we can find in a project and earn time for critical bussiness items.

In order to explain how this plugin works we are going to write some code for a dummy example app which contains three type of relations (one-to-many, many-to-one and one-to-one) between four domain classes.

Resources:GFSv0.1 plugin – Download

GFS example application – Download

GFS screencast – Download

Prerequisites

  • Grails 1.0.4 (last stable version)
  • Flex 3.0.0 or major

FLEX_HOME and GRAILS_HOME must be defined as environment variable!!

“In this version we should copy FLEX_HOME/ant/lib/flexTasks.jar into GRAILS_HOME/ant/lib”

Off to work!

Domain Model

Domain Model

1 – Grails Project creation

user@cubikalabs:~$ grails create-app gsf-test

2 – Plugin installation (link:download).

user@cubikalabs:~$ cd gfs-testuser@cubikalabs:~/gfs-test $ grails install-plugin [plugin-path]/grails-flex-scaffold-0.1.zip

3 – Generating domain class model

user@cubikalabs:~/gfs-test $ grails create-domain-class customeruser@cubikalabs:~/gfs-test $ grails create-domain-class company

user@cubikalabs:~/gfs-test $ grails create-domain-class phone

user@cubikalabs:~/gfs-test $ grails create-domain-class address

Let’s edit generated domain classes:

Company:

importorg.cubika.labs.scaffolding.annotation.FlexScaffoldProperty//@FlexScaffoldProperty(labelField=”name”) is

//The label field is displayed in edit-view

//of the relation external

@FlexScaffoldProperty(labelField=”name”)

classCompany

{

Stringname

Stringaddress

//One-to-Many

statichasMany = [customers:Customer]

staticmapping =

{

customers lazy:false, cascade:”none”

}

static constraints =

{

name(blank:false)

address(blank:false)

customers(display:false)//Not view customer in Company’s edit-view

}

}

Customer:

classCustomer

{

StringfirstName

StringlastName

Stringemail

DatedateOfBirth

Phonephone

Listaddresses

Companycompany

StringmaritalStatus

Integerage

Booleanenabled

statichasMany = [addresses:Address]

staticbelongsTo = Company

staticmapping =

{

addresses lazy:false, cascade:”all-delete-orphan”

company lazy:false, cascade:”none”

}

static constraints =

{

firstName(minSize:2, blank:false)

lastName(maxSize:20)

dateOfBirth()

age(range:18..99)

email(email:true, blank:false)

//if not declared widget, the default component is a ComboBox

maritalStatus(inList:["Single","Married","Divorce","Widower"],widget:”autocomplete”)

addresses()

//if inPlace:false, a ComboBox is created in the “edit-view”

//of the class containing it, and it’s filled with the information that makes a

// reference of it.

//Besides, it allows to create a new record from the edit-view of the referenced class

// through a button (“add”)

//by default inPlace is true

company(inPlace:false, nullable:true)

//The componente will be hidden when the form is setted CREATE_VIEW mode

//and sets the defaultValue, in this case the value is true

//If you wish, use the metaConstraint editView:false to hide component in EDIT_VIEW mode

enabled(createView:false,defaultValue:’true’)

}

}

Address:

classAddress{

Stringstreet

Integernumber

Stringzip

Stringobservation

Customercustomer

staticconstraints =

{

street(blank:false)

//if not declared widget, the default

//component is a NumericStepper

number(widget:”textinput”)

zip(blank:false)

observation(widget:”textarea”)

//Not view customer in Address’ edit-view

customer(display:false)

}

}

Phone:

classPhone{

Stringnumber

Stringtype

staticbelongsTo = Customer

staticconstraints =

{

//if not declared widget, the default

//component is a NumericStepper

number(widget:”textinput”)

type(inList:["Home","Movil"])

}

}

4 – Crud Company and Customer Generation

user@cubikalabs:~/gfs-test $ grails generate-all-flex companyuser@cubikalabs:~/gfs-test $ grails generate-all-flex customer

5 – Flex compilation

user@cubikalabs:~/gfs-test $ grails flex-tasks

6 – It’s time to start up our app-server and navigate our application

user@cubikalabs:~/gfs-text $ grails run-app

http://localhost:8080/projectName

Version details

In the first version (GFS v0.1), we put all our efforts in this kind of apps (relations, validations), and we didn’t mind about features as visual component suite and form (via metadata) settings. We have all this staff planned and more planned for future versions (we expect you to contribute your knowledge, questions or whatever you have to contribute).

In nearly future we will have Jira and Confluence apps in order to follow projects releases and bug reporting. On the other hand, we are going to open our SVN for annonimous checkouts, this way everybody will be able to view, modify, use, contribute or whatever you want to do with source code, beta versions,etc.

It is not our porpose to extend this post any more, but it really important for us to mention Marcel Overdijk who developed Grails-Flex-BlazeDS integration.

Success tips (Important information)

Relations

  • many-to-one supports only inPlace:false (this declaration is not required because it’s setted as a default)
  • one-to-many both cases are supported inPlace:false/inPlace:true.
  • one-to-one supports only inPlace:true (this declaration is not required because it’s setted as a default).
  • If relations are declared as inPlace:true, e.g: Customer <-> Address o Customer -> Phone, the included class (Address, Phone) must define the constraint diplay:false for property that is including “custormer(display:false)”. At this moment, this restriction is not valid in generation code time and ends by abort process
  • Relations must ever be lazy:false if not, BlazeDS throws a LazyInitialization exception (in future versions we are going to support this feature with DPHibernate or similar).

Constraints

  • Front-End supported constraints:
    • blank, email, size, minSize, maxSize, min, max, range, url, inList
    • For each Front-End constraint, a Flex validator is generated. This avoids user to persist the entity without the need of Back-End validation.
  • All other constraints (Grails constraints) follows Grails validation way, doing validation on Back-End side which have the responsibility of getting feedback about errors to Front-End. This kind of errors are supported by i18n.

Follow

Get every new post delivered to your Inbox.