Grails Flex Scaffold (GFS)

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.
About these ads

Tags: , , , ,

53 Responses to “Grails Flex Scaffold (GFS)”

  1. Graeme Rocher Says:

    Wow this looks pretty awesome. You planning on publishing it to the Grails central plugin repository?

    • gclavell Says:

      Hi, I’m Gonzalo Clavell from Cubika Labs, first of all thank you very much for your comment, it is very important for us .
      We are expecting for Grails central answer, while developing some other features as Reporting, I18n, and scripts improvements.
      This improvements will be integrated in next version GFSv0.2, which is coming soon.
      I hope you could enjoy this plugin, whatever support you need post your doubts, we will be answering as soon as posible.
      Regards,
      Gonzalo

      • Graeme Rocher Says:

        Your account has been approved.

      • gclavell Says:

        Graeme Rocher:
        Thank you very much for your support.
        Next week we will move all GFS’s staff to codehaus (wiki,svn,jira,etc).
        We hope you follow helping us as you do normally.

  2. Anatoliy Says:

    When are you going to release this plugin for grails-1.1?

  3. Anatoliy Says:

    There are some error’s in your current version.
    1. Setting FLEX_HOME to “D:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0″ doesn’t work. (OS: Window XP Professional)

    2. Maybe it will be better if flex_src will generate under “${basedir}/src/flex” and maybe flex_lib under “${basedir}/lib/flex” (like gwt-plugin does)

    3. IMHO configure files under “${basedir}/conf/flex”.

    4. In my project I use mx:ModuleLoader. In Flex builder Modules compiled automatic. Is it possible to add this behaviour in your plugin?

    You did a great work!!! I’m waiting for release under grails 1.1.

    Sorry for English.

    • Ezequiel Apfel Says:

      Hi, thanks for your post!
      Here are the answers for your questions

      1 – It’s strange. Make sure you changed the file on $(basedir)/flex.properties and not $(flexScaffoldBaseDir)/src/ flex/flex.properties. Obviously, we are going to take a look at this point.

      2 – We understand your point of view, but we decided to copy the Adobe’s configurations for projects “Flex/j2ee”.
      We omitted to mention in this post something really important, Grails’ project can be import into eclipse and it will take (when run for the first time the flex-generate-all this generates the files .flexProperties and .actionScriptProperties) the “Flex Nature”

      3 – the same as for point 2, but we can organize it in “$(basedir)/config/flex”
      4 – Have in mind to support modules, not for version 0.2 but certainly in nexts.

      We have planned to be testing GFSv0.1 on Grails-1.1 the next week, as soon as posible we will upload tested version.

    • Ezequiel Apfel Says:

      Sorry, also we ommited to tell you in the last comment that to run the script “flex-tasks” you should copy FLEX_HOME/ant/lib/flexTask.jar into GRAILS_HOME/ant/lib
      We updated the post explaining it.
      Regards,
      Ezequiel

    • Anatoliy Says:

      Thank you for your support.

      About this question
      >>Setting FLEX_HOME to “D:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0″ doesn’t work. (OS: Window XP Professional)

      It was my mistake. The path must be:
      FLEX_HOME = “D:/Program Files/Adobe/Flex Builder 3/sdks/3.2.0″

  4. Anatoliy Says:

    One more thing about $(flexScaffoldBaseDir)/scripts/FlexTasks.groovy
    May be it’ll be better to change ${grailsHome} to ${flexHome}

    You current version (string number 22,23) file: $(flexScaffoldBaseDir)/scripts/FlexTasks.groovy:
    Ant.taskdef (name:’mxmlc’, classname:’flex.ant.MxmlcTask’, classpath:”${grailsHome}/ant/lib/flexTasks.jar”)
    Ant.taskdef (name:’gspWrapper’, classname:’flex.ant.HtmlWrapperTask’, classpath:”${grailsHome}/ant/lib/flexTasks.jar”)

    My version:
    ant.taskdef(name:’mxmlc’, classname: ‘flex.ant.MxmlcTask’, classpath: “${flexHome}/ant/lib/flexTasks.jar”)
    ant.taskdef(name:’gspWrapper’, classname: ‘flex.ant.HtmlWrapperTask’, classpath: “${flexHome}/ant/lib/flexTasks.jar”)

    • Ezequiel Apfel Says:

      Thanks for your improvement.
      We fixed it for the next version and also removed the configuration of FLEX_HOME on flex.properties to avoid this errors.

  5. Anatoliy Says:

    It’s me again.
    $(flexScaffoldBaseDir)/scripts/FlexTasks.groovy
    string 73:
    target(compile:’Compile Flex project into SWF file’)
    {

    depends(clean)
    ….

    I suppose depends(clean) is useless in this line.

    Because Adobe say that FlexTask can determinate dependences and it is smart enough to decide the needs to recompile the swf (compiler create main.swf.cache).

    Maybe this features available in last version (I have 3.2.0)

    Here it is what I get in my console:
    >grails flex-tasks compile
    Welcome to Grails 1.1 – http://grails.org/
    Licensed under Apache Standard License 2.0
    Grails home is set to: C:\Program Files\Grails

    Base Directory: D:\project
    Running script D:\project\.\plugins\flex-scaffold-0.1\scripts\FlexTasks.groovy
    Loading configuration file D:\Program Files\Adobe\Flex Builder 3\sdks\3.2.0\frameworks\flex-config.xml
    Recompile: D:\project\flex\src\view\LoginScreen.mxml
    Reason: The source file or one of the included files has been updated.
    Recompile: D:\project\flex\src\Main.mxml
    Reason: The source file or one of the included files has been updated.
    Files changed: 2 Files affected: 0
    D:\project\web-app\main.swf (759458 bytes)
    Flex compile Done!

  6. Michael Walker Says:

    Just what I have been looking for!

    This is a hugely significant initiative. I can’t help feeling that HTML/Javascript are “old hat” and that we must break away from the limitations imposed on us by web 1.0 and the challenges of code running and being rendered on many different browsers. The AVM (FlashPlayer) supporting Flex/Flash/AS seems a natural solution to this – just as Java/JVM was a natural solution to C/C++ and the issues of compiled code not running on different CPUs.

    Do you have any ideas when a version that runs under Grails 1.1 will be ready?

    I wish you every success with this.
    Great work guys!

    • Ezequiel Apfel Says:

      Thanks for you comment, next week we plan to begin tests with Grails-1.1 and published as soon as possible

  7. Michael Walker Says:

    Excellent. I look forward to trying it out…

  8. Anatoliy Says:

    Congratulation!!!
    I found this plugins on grails.org.

    The “Cairngorm Deepdive” presentation says that developer have to use cairngorm ServiceLocator because:
    “… It also handles security credentials for the services …”

    IMHO it’ll be good features to connect your plugin with “Spring Security plugin”. Or give a tutorial how they can be connected together.

    Sorry for bed English.

    • Ezequiel Apfel Says:

      Hi Anatoliy, yesterday we published!!!. We’re preparing everything to migrate to codehaus (source, documentation, examples, screencast).

      Regarding your question about ServiceLocator, although we believe that Cairngorn is the best framewrok to develop Flex applications, we found it difficult to declare each service via MXML. That’s why we chose a “static private var _service” in each concret BusinesDelegate. Anyway we’re planning to extend the ServiceLocator (tied to the priority: we’ll analyze in which version will be it) and register the services in runtime such as the events/commands in the FrontController.

      The RemoteObject has the methods of “setCredentials()” and “logout()” which allow us to generate the same functionality that the ServiceLocator. We could wrap these methods in the BusinessDelegate.

      Security is also a task pending and we have thought to realice the integration as soon as we established the plugin (users, roles and permissions).

      With more time we’ll do a howto of Flex integration with Acegi.

      Next week we’re going to have the Jira Project available so we all could vote the features we would release in future versions and the functionalities that are essential for the plugin to be useful.

      We hope you could join us with your participation.
      Regards,
      Ezequiel

      • Anatoliy Says:

        Thank you for your invitation.
        I’d like to join to this project.

        Unfortunate I can’t do it right now. I have a project, it must be done at the end of march. As soon as I finish it, I’m yours :)

  9. Anatoliy Says:

    The first is:
    Adobe’s published “AdvancED Flex 3 excerpt: Integrating via data and media services”. You can download for free. I think this information can be very usefully for you. It describe the architecture of BlazeDS with examples.

    The second:
    I have a question. I use some codes from your plugin in my project.
    Do you do something special for casting from Object to ValueObject?
    I can’t force to working type casting.

    Generated code from yours plugin:
    new CustomerCRUDEvent(CustomerCRUDEvent.SELECT_EVENT,dg.selectedItem as CustomerVO).dispatch()

    I mean “dg.selectedItem as CustomerVO”
    I have similar code and I always get this error :(:
    TypeError: Error #1034: Type Coercion failed: cannot convert Object@1102a6f1 to

    • Ezequiel Apfel Says:

      Hi Anatoly, Thanks for the link, I’ll read

      With regard to cast, we do nothing special
      We always work with specific types of objects such as: Customer, Address, etc.

      Flex does not allow cast objects to concret objects, it will always throw this error at runtime.

      We always fill the dataProvider with concrete objects (the DataGrid always shows a list of concret object) that is why it works.

      Check whether the objects that you have in the DataGrid are the data type that you want cast.

      If you fill dataProvider with a service, check if your Remote Class is not well configured, check [RemoteClass(alias='.....')]

      Regards,
      Ezequiel

    • Anatoliy Says:

      I find out why it’s some time happen.

      Flex compile have optimization if you import some class but don’t use it by default it remove it.
      And in debug session under FlexBuilder if you return array list from grails.
      Array will be the array of object.

      To remove it you have to include dummy variable like:
      private var dummyPerson:PersonVO;

      I hope this information can help someone to safe his time

  10. Anatoliy Says:

    Thank you for your soon answer.
    I get what you mean. I think you right.

    How do you do this?
    I mean the algorithm which cast remote ArrayCollection to specific types.

    I suppose the algorithm hides from me in com.cubika.labs.pagination.PageFilter.

    Is it possible to view it (of course if this code is open source)?

    • Ezequiel Apfel Says:

      Hi Anatoliy, us do nothing, the whole algorithm to convert the objects makes BlazeDS.

      PageFilter just paging logic.

      if you got a RemoteObject with the following method:

      def getCustomers()
      {
      Customer.list()
      }

      BlazeDS will transform your Customer List in an ArrayCollection of CustomerVO only if your VO (CustomerVO) has the metadata RemoteClass(alias =”…”) correctly defined.
      Obviously, the request must be via BlazeDS.

      In the gfs-test are the services we use to perform CRUD
      grails-app/services/CustomerService.groovy and
      grails-app/services/CompanyService.groovy can you notice that there’s a method called list() that filled the combo of many-to-one inPlace:false
      The command is flex_src /command/company/ListComanyCommand.as

      Anything, send me an e-mail with the error code and I can help you to resolve it

      • Anatoliy Says:

        I’m sorry for bothering you.
        I found what was wrong in my code.

        In my project I develop a grid in flex (similar features EXTJS). I’m going to give it to you team as soon as I finish it. I think you can adapt it in you architecture.

      • Ezequiel Apfel Says:

        Anatoliy, good for you. We hope your component

  11. Anatoliy Says:

    It’s just like idea.

    I use Flex Builder (Eclipse) and often use shortcut CTRL+SHIFT+R for searching needed class.

    What we have now:
    {flex_src}/model/customer/CustomerModel.as
    {flex_src}/service/customer/CustomerBusinessDelegate.as
    {flex_src}/model/customer/CustomerModel.as
    {flex_src}/event/customer/CustomerCRUDEvent.as

    They are all begin from “Customer”. And now we are lookin on commands class. They aren’t start with “Customer”

    {flex_src}/event/customer/
    CreateCustomerCommand.as
    DeleteCustomerCommand.as
    GetPaginationListCustomerCommand.as
    ListCustomerCommand.as
    SaveOrUpdateCustomerCommand.as
    SelectCustomerCommand.as

    IMHO it’ll be better for agile developing to rename this class:
    CustomerCreateCommand.as
    CustomerDeleteCommand.as
    CustomerGetPaginationListCommand.as
    CustomerListCommand.as
    CustomerSaveOrUpdateCommand.as
    CustomerSelectCommand.as

    What do you think about this?

  12. Eduardo Méndez Says:

    Hi | Hola

    What about the 0.2 version or the SVN repo?. I have tried the 0.1 version and found a couple of glitches but overall it has been an impressive experience. ¡Enhorabuena!.

    Eduardo.

    • gclavell Says:

      Hi thanks for your comment.
      Tomorow we release GFSv0.1.1 for Grails 1.1 support and some bugs were fixed.
      Next week we’re going to publish all code into codehaus svn, docs, and jira. Last one will carry with the responsibility of containing future releases plan where you can see releases state. So you can report your “glitches” as bugs too.
      Regards

  13. worknet Says:

    Hello! I’m Brazilian. How to display new Date() in forms? I just like put new Date() in field timestamp ( Postgresql 8.3 ).

    Thanks!

    Sucess!

    • Ezequiel Apfel Says:

      Hello, I do not understand your question.

      Maybe I it can help.

      At the moment GFS doesn’t support timestamp to generate code.

      If you want to use a property of the type timestamp so you can do the following:

      Set DomanClass’ property like Date and then run generate-all-flex you may modify a timestamp.

      GFS will generate the DateField in your form and you will have no problem using the timestamp via BlazeDS.

      We are planning to support it in the next version.

      Regards.

  14. worknet Says:

    Thank you Ezekiel! I understand. Another question, how can I hide some classes? I have many tables and would like some auxiliaries were charged only on relationships.

    The work of you is amazing. I would like to contribute, I do not have much, but I would like to contribute in some way. How could I make a donation to the project?

    Again, thank you! And success.

    • Ezequiel Apfel Says:

      GFS only generate CRUD for class that you put in “generate-all-flex”.

      GFS not support generate all DomainClass (at the moment).

      If you need to hide a DomainClass inside a relationship you must be set display:false into property’s constraint.

      For example:

      class Company
      {
      Customer customer

      static constraints = {

      customer(display:false)
      }
      }
      //Customer is not going to be visible in Company’s form

      You can contribute with us using GFS and to report the issues that you find and also the improvements we should develop to have a useful plugin.

      We expect to see your application working.

      You can see documentation of the plugin in:
      http://docs.codehaus.org/display/GFS

      Also you can visit the plugin in the page of grails:
      http://grails.org/plugin/flex-scaffold

      Any doubt that you have does not hesitate to ask us

      Thank you very much.

  15. worknet Says:

    Ok! With respect to the classes, I changed the. /Flex_src/Main.mxml and gave everything right, perfect!

  16. worknet Says:

    I am really impressed … I’m not really a programmer, and so little knew the groovy or grails, but with only 2 days could develop a good system with many relationships, with the help of GFS plugin. Fantastic!

  17. worknet Says:

    Ok! You could tell me where I could change to get the timestamp?

    • Ezequiel Apfel Says:

      in your domainClass

      class aClass
      {
      Date time
      }

      run
      grails generate-all-flex

      edit your Class

      class aClass
      {
      Timestamp time
      }

      run
      grails flex-tasks
      grails run-app

      if this is not what you need, you could explain to me more about what you want to do?
      Regards

  18. worknet Says:

    Ok! I need to get the system date as default in my field. Or even the pattern of groovy.

    Thank you!

  19. worknet Says:

    List of Values – LOV

    Hello! Friend, Is possible use the JSON and JQUERY to assemble a list of values, return to the fields?

  20. Doug Says:

    This is great!
    Is there a plan to support the scaffolding for custom validation?
    For example: defining grails’ custom validator with backend validation logic and have GFS generates the Flex view with that custom validator?

    Thanks

    • Ezequiel Apfel Says:

      Hi, thanks for your comment.
      We’re planning to support custom validation but not in the next version. Probably it’ll be on version 0.3.
      GFSv0.2 will be release this week, we haven’t plan the date of GFSv0.3 release.
      The time that take us to update each version is 20 days more or less.

      Regards,
      Ezequiel

  21. Roger Says:

    Hi guys,

    congratulations! Good job.

    I’m just reporting a strange behavior in the example. After created a customer I double clicked on the same customer and update it marital status. When I selected the same customer the phone is missing.

    • Ezequiel Apfel Says:

      Hi,
      I could not reproduce this error.
      On the other hand, we have a bug when we try to update the relations one-to-one that even we could not solve
      Regards

  22. lramos Says:

    How difficult would it be to make this work with domain classes defined in Java under grails-app/src/. Grails generate-all works for hibernate-mapped classes. Would anyone be able to point me in the right direction on how I can modify this? Thanks.

    • Ezequiel Apfel Says:

      Hello,
      GFS does not support hibernate-mapping.
      At the moment we are not going to focus our efforts on that.
      You can start watching GenerateAll also I’m not sure that everything works as it works with Grails Domain classes (Thinking about the metacosntraint).
      Perhaps you can create a script that transform your Java’s classes to Groovy, then you run “generate-all-flex” and then you use it in your java configuration (just you create Groovy’s model to generate the Scaffold)
      Regards

  23. Manu Garcia Says:

    great work. Thanks for sharing.

  24. Israel Martinez Says:

    Hello Ezequiel, Development on GFS has stoped?

  25. 百度收录技巧 Says:

    wow, awesome post, I was wondering the same thing. and found your site by yahoo, learned a lot, now i’m a bit clear. I’ve bookmark your site and also add rss. keep us updated.

  26. Michael Shields Says:

    Looks pretty cool, though I got a NPE (Cannot get property ‘starksecurity’ on null) when I ran the example.
    Is this project dead now?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: