[grails-dev] Controller Validation

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
18 messages Options
Reply | Threaded
Open this post in threaded view
|

[grails-dev] Controller Validation

graemer
I've been thinking about controller validation a bit and what do you
guys think about having a separate closure that validates constraints
for an action instead of it being in the action itself. So for example
if you have a action called "login"

@Property login = {
     // process login
}

And you want to validate parameters to it you create a closure called
validateLogin like so

@Property validateLogin = {
            login(blank:false,redirect:"/user/home")
            pass(blank:false,redirect:"/user/home")
}

This way there is a nice separation between the 2 and you're not
mixing validation and controller logic.

This is actually similar to the interceptor concept (see
http://jira.codehaus.org/browse/GRAILS-10) and in fact you could do
this as an interceptor and support things like:

@Property validateAll = {
         user(blank:false,scope:session,redirect:"/user/home")
}

This would catch all request making sure the user is logged in and
redirect to "/user/home" if they're not

Anyway what do you guys think?

Graeme
Reply | Threaded
Open this post in threaded view
|

[grails-dev] Re: Controller Validation

graemer
Or possibly having a Validator artifact but this means a separate class:

class UserValidator {
    @Property login = {
             login(blank:false,redirect:"/user/home")
             pass(blank:false,redirect:"/user/home")
    }
}

On 22/11/05, Graeme Rocher <[hidden email]> wrote:

> I've been thinking about controller validation a bit and what do you
> guys think about having a separate closure that validates constraints
> for an action instead of it being in the action itself. So for example
> if you have a action called "login"
>
> @Property login = {
>      // process login
> }
>
> And you want to validate parameters to it you create a closure called
> validateLogin like so
>
> @Property validateLogin = {
>             login(blank:false,redirect:"/user/home")
>             pass(blank:false,redirect:"/user/home")
> }
>
> This way there is a nice separation between the 2 and you're not
> mixing validation and controller logic.
>
> This is actually similar to the interceptor concept (see
> http://jira.codehaus.org/browse/GRAILS-10) and in fact you could do
> this as an interceptor and support things like:
>
> @Property validateAll = {
>          user(blank:false,scope:session,redirect:"/user/home")
> }
>
> This would catch all request making sure the user is logged in and
> redirect to "/user/home" if they're not
>
> Anyway what do you guys think?
>
> Graeme
>
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Guillaume Laforge-2
In reply to this post by graemer
Good evening Graeme,

On 22/11/05, Graeme Rocher <[hidden email]> wrote:

> [...]
> @Property login = {
>     // process login
> }
> [...]
> @Property validateLogin = {
>             login(blank:false,redirect:"/user/home")
>             pass(blank:false,redirect:"/user/home")
> }
> [...]
> Anyway what do you guys think?

This sounds okay to me... however, I have a little idea...
I think it might be possible to set a specific metaclass for all
action closures in your controller.
This metaclass could allow us to set/get properties on it, and it
would hold the associated values... A sample code is better than a
thousand words:

So you'd have:

@Property login = {
    // process login
}

And it bit below:

login.validate = {
    login(blank:false,redirect:"/user/home")
    pass(blank:false,redirect:"/user/home")
}

The validate closure would be directly related to the login action closure.

It means implementing your own metaclass again of course ;-)
Particularly overriding get/setProperty, or potentiall a validate()
method taking a closure if you'd prefer that version:

login.validate {
    login(blank:false,redirect:"/user/home")
    pass(blank:false,redirect:"/user/home")
}

Is it more Groovy?

--
Guillaume Laforge
Groovy Project Manager
http://glaforge.free.fr/blog/groovy
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Guillaume Laforge-2
On 23/11/05, Graeme Rocher <[hidden email]> wrote:

> On 22/11/05, Guillaume Laforge <[hidden email]> wrote:
> > Good evening Graeme,
> >
> > On 22/11/05, Graeme Rocher <[hidden email]> wrote:
> > > [...]
> > > @Property login = {
> > >     // process login
> > > }
> > > [...]
> > > @Property validateLogin = {
> > >             login(blank:false,redirect:"/user/home")
> > >             pass(blank:false,redirect:"/user/home")
> > > }
> > > [...]
> > > Anyway what do you guys think?
> >
> > This sounds okay to me... however, I have a little idea...
> > I think it might be possible to set a specific metaclass for all
> > action closures in your controller.
> > This metaclass could allow us to set/get properties on it, and it
> > would hold the associated values... A sample code is better than a
> > thousand words:
> >
> > So you'd have:
> >
> > @Property login = {
> >     // process login
> > }
> >
> > And it bit below:
> >
> > login.validate = {
> >     login(blank:false,redirect:"/user/home")
> >     pass(blank:false,redirect:"/user/home")
> > }
> >
> > The validate closure would be directly related to the login action closure.
> >
> > It means implementing your own metaclass again of course ;-)
> > Particularly overriding get/setProperty, or potentiall a validate()
> > method taking a closure if you'd prefer that version:
> >
> > login.validate {
> >     login(blank:false,redirect:"/user/home")
> >     pass(blank:false,redirect:"/user/home")
> > }
> >
> > Is it more Groovy?
>
> Yeh that is pretty cool, there is little difference between the 2
> approaches (ie the method and the property).. but where would you put
> this code? inside the action closure? in a method? in the body of the
> class?

What I like in that approach is that the validation is somewhat
attached/associated with the closure. It certainly avoids some typos
between the name of the action closure and the name of the validation
closure.

Regarding the implementation, I think you'll have to attach a
metaclass to each instance of your controllers. So the metaclass
itself could hold the action / validation associations in some map,
and in the code handling the controllers, you'd retrieve the metaclass
to fetch the associated validation code.

--
Guillaume Laforge
Groovy Project Manager
http://glaforge.free.fr/blog/groovy
Reply | Threaded
Open this post in threaded view
|

RE: [grails-dev] Controller Validation

Dierk König
> > > > @Property validateLogin = {
> > > >             login(blank:false,redirect:"/user/home")
> > > >             pass(blank:false,redirect:"/user/home")
> > > > }

vs.

> > > login.validate = {
> > >     login(blank:false,redirect:"/user/home")
> > >     pass(blank:false,redirect:"/user/home")
> > > }

I like both ways, where the latter looks a bit more groovy
to me.

Is there a difference in handling for the user? I think
about
- stack traces in case of errors
- general code handling like search, replace and such...

I also think about graphical visualization of the
application like I did with putty for the pageflow.
How would the above alternatives fit into this
picture?

BTW: I currently run a little project that I first
implemented with GSQL and Groovlets. I ported this
to Hibernate (still using it from Groovy)
and now I think about a GRAILS port.
The speciality is that not only a webapp accesses the
db but also a second background process (also a
Groovy program).
Question: how can the second process share the
data model?

cheers
Mittie
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
On 23/11/05, Dierk Koenig <[hidden email]> wrote:

> > > > > @Property validateLogin = {
> > > > >             login(blank:false,redirect:"/user/home")
> > > > >             pass(blank:false,redirect:"/user/home")
> > > > > }
>
> vs.
>
> > > > login.validate = {
> > > >     login(blank:false,redirect:"/user/home")
> > > >     pass(blank:false,redirect:"/user/home")
> > > > }
>
> I like both ways, where the latter looks a bit more groovy
> to me.

Guillaume and I have actually been discussing this over GoogleTalk
(you should get an account btw or at least some IM medium! ;-) and the
feeling is now that the above syntax is nice but the problem remains
is where to set this up.

One approach is to have a validation closure that sets up all
validation for a controller:

@Property validation = {
 login.validate = {
     login(blank:false,redirect:"/user/home")
     pass(blank:false,redirect:"/user/home")
 }
}

another approach is to set it up in the controller action so you can
share validators:

@Property login = {
     this.validator = loginValidator
}
@Property loginValidator = {
     login(blank:false,redirect:"/user/home")
     pass(blank:false,redirect:"/user/home")
}

A finally the alternative is to use the coding by convention stuff,
but this is a bit error prone as a user could have a typo in the
convention and validation not happen:

@Property login = {  .. }
@Property validateLogin = {
     login(blank:false,redirect:"/user/home")
     pass(blank:false,redirect:"/user/home")
}
>
> Is there a difference in handling for the user? I think
> about
> - stack traces in case of errors
> - general code handling like search, replace and such...

exceptions shouldn't be thrown in validation, error codes and messages
get returned.

>
> I also think about graphical visualization of the
> application like I did with putty for the pageflow.
> How would the above alternatives fit into this
> picture?

I'm not sure I know what you mean here.

>
> BTW: I currently run a little project that I first
> implemented with GSQL and Groovlets. I ported this
> to Hibernate (still using it from Groovy)
> and now I think about a GRAILS port.
> The speciality is that not only a webapp accesses the
> db but also a second background process (also a
> Groovy program).
> Question: how can the second process share the
> data model?

This is currently possible but i want to make it a bit more slick as
it requires some work, but essentially you can configure your external
application in Spring to use the grails data model like so which will
give you access to the session factory instance etc.:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
        <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
            <property name="driverClassName">
                <value>org.hsqldb.jdbcDriver</value>
            </property>
            <property name="url">
                <value>jdbc:hsqldb:mem:grailsDB</value>
            </property>
            <property name="username">
                <value>sa</value>
            </property>
            <property name="password">
                <value></value>
            </property>
        </bean>
       
       
        <bean id="grailsApplication"
class="org.codehaus.groovy.grails.commons.GrailsApplicationFactoryBean">
                <description>Grails application factory bean</description>
                <property name="groovyFiles">
                        <value>somelocation/*.groovy</value>
                </property>
        </bean>
       
       
       
        <bean id="sessionFactory"
class="org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalsSessionFactoryBean">
                <property name="grailsApplication">
                        <ref bean="grailsApplication" />
                </property>
                <property name="configurationClass"> <value>org.codehaus.groovy.grails.orm.hibernate.cfg.DefaultGrailsDomainConfiguration</value>
                </property>
                <property name="hibernateProperties">
                        <props>
                                <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                                <prop key="hibernate.hbm2ddl.auto">create-drop</prop>
                        </props>
                </property>
                <property name="dataSource">
                        <ref bean="dataSource" />
                </property>
        </bean>
</beans>

To register the persistent methods and create the grails spring
context though you would need this code somewhere in the startup of
your app:

                SpringConfig springConfig = new SpringConfig(grailsApplication);
                ConfigurableApplicationContext appCtx = (ConfigurableApplicationContext)
                new XmlApplicationContextDriver().getApplicationContext(
                                springConfig.getBeanReferences(), super.applicationContext);

>
> cheers
> Mittie
>
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Jochen Theodorou
Graeme Rocher schrieb:
[...]

> One approach is to have a validation closure that sets up all
> validation for a controller:
>
> @Property validation = {
>  login.validate = {
>      login(blank:false,redirect:"/user/home")
>      pass(blank:false,redirect:"/user/home")
>  }
> }
>
> another approach is to set it up in the controller action so you can
> share validators:
>
> @Property login = {
>      this.validator = loginValidator
> }
> @Property loginValidator = {
>      login(blank:false,redirect:"/user/home")
>      pass(blank:false,redirect:"/user/home")
> }


how about both? I mean some kind of global check, that can be bypassed
with a local check.

bye blackdrag
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
On 23/11/05, Jochen Theodorou <[hidden email]> wrote:

> Graeme Rocher schrieb:
> [...]
> > One approach is to have a validation closure that sets up all
> > validation for a controller:
> >
> > @Property validation = {
> >  login.validate = {
> >      login(blank:false,redirect:"/user/home")
> >      pass(blank:false,redirect:"/user/home")
> >  }
> > }
> >
> > another approach is to set it up in the controller action so you can
> > share validators:
> >
> > @Property login = {
> >      this.validator = loginValidator
> > }
> > @Property loginValidator = {
> >      login(blank:false,redirect:"/user/home")
> >      pass(blank:false,redirect:"/user/home")
> > }
>
>
> how about both? I mean some kind of global check, that can be bypassed
> with a local check.

Yeh I think supporting both would be cool I'm going to go for
implementing the first approach (ie the one with the global validation
closure) first and then we can look at support explicit validators
later.

We could even do it so you could break it up nicely in the validation
closure like:

@Property validation = {
      login.validator = loginValidator
}
@Property loginValidator = {
      login(blank:false,redirect:"/user/home")
      pass(blank:false,redirect:"/user/home")
}

Or

@Property validation = {
      login.validator = {
           login(blank:false,redirect:"/user/home")
           pass(blank:false,redirect:"/user/home")
      }
}

Graeme

>
> bye blackdrag
>
Reply | Threaded
Open this post in threaded view
|

RE: [grails-dev] Controller Validation

Dierk König
In reply to this post by graemer
> Guillaume and I have actually been discussing this over GoogleTalk

ok.

> > Is there a difference in handling for the user? I think
> > about
> > - stack traces in case of errors
> > - general code handling like search, replace and such...
>
> exceptions shouldn't be thrown in validation, error codes and messages
> get returned.

I thought about unintentional exceptions like NPE's and such.
How easy can I spot the offending line in the StackTrace?
Is there a difference in this regard between the alternatives?

> > I also think about graphical visualization of the
> > application like I did with putty for the pageflow.
> > How would the above alternatives fit into this
> > picture?
>
> I'm not sure I know what you mean here.

I attach an image that shows the 'book' pageflow.
It's dynamically created by producing the attached .dot
file from the pageFlowBuilder and piping that through
the 'putty' program.
We may want to include validation in the picture.
Is there a difference in the alternatives regarding
easy of generating such an overview?
(but that's certainly a minor side-issue)

> > BTW: I currently run a little project that I first
[..]

cool. Thanks a lot for your answeres
Mittie
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
On 23/11/05, Dierk Koenig <[hidden email]> wrote:

> > Guillaume and I have actually been discussing this over GoogleTalk
>
> ok.
>
> > > Is there a difference in handling for the user? I think
> > > about
> > > - stack traces in case of errors
> > > - general code handling like search, replace and such...
> >
> > exceptions shouldn't be thrown in validation, error codes and messages
> > get returned.
>
> I thought about unintentional exceptions like NPE's and such.
> How easy can I spot the offending line in the StackTrace?
> Is there a difference in this regard between the alternatives?

Yes this is a concern of mine as the more stuff we do with meta class
the more difficult its gunna be to spot errors. We will have to make
sure we that functionality we expose to the user does all necessary
checks to avoid the scenario where the strack trace gets too deep and
difficult to debug.

>
> > > I also think about graphical visualization of the
> > > application like I did with putty for the pageflow.
> > > How would the above alternatives fit into this
> > > picture?
> >
> > I'm not sure I know what you mean here.
>
> I attach an image that shows the 'book' pageflow.
> It's dynamically created by producing the attached .dot
> file from the pageFlowBuilder and piping that through
> the 'putty' program.
> We may want to include validation in the picture.
> Is there a difference in the alternatives regarding
> easy of generating such an overview?
> (but that's certainly a minor side-issue)

Sounds cool, but there is no attachment :-(

>
> > > BTW: I currently run a little project that I first
> [..]
>
> cool. Thanks a lot for your answeres
> Mittie
>
Reply | Threaded
Open this post in threaded view
|

RE: [grails-dev] Controller Validation

Dierk König
> Sounds cool, but there is no attachment :-(

ah, the listserver removes it

I added it to the CVS.
grails/samples/books
Feel free to remove it.

cheers
Mittie
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Guillaume Laforge-2
On 23/11/05, Dierk Koenig <[hidden email]> wrote:
> > Sounds cool, but there is no attachment :-(
>
> ah, the listserver removes it
>
> I added it to the CVS.
> grails/samples/books
> Feel free to remove it.

Hmmm, that's pretty cool!

http://cvs.groovy.codehaus.org/viewrep/~raw,r=1.1/groovy/groovy/modules/grails/samples/books/pageFlow.png

--
Guillaume Laforge
Groovy Project Manager
http://glaforge.free.fr/blog/groovy
Reply | Threaded
Open this post in threaded view
|

RE: [grails-dev] Controller Validation

Dierk König
> Hmmm, that's pretty cool!
>
> http://cvs.groovy.codehaus.org/viewrep/~raw,r=1.1/groovy/groovy/mo
> dules/grails/samples/books/pageFlow.png

glad you like it.

Here's the code (pageFlowNodes is pageFlow called with
NodeBuilder instead of PageFlowBuilder):

----
flow = new BooksFlowGraphImpl().pageFlowNodes
println """
digraph G {
    node [shape=box, fontname=Helvetica];
    {rank=souce; ${flow.children()[0].name()}}"""
flow.children().each { source ->
    source.children().each { link ->
        println "\t${source.name()} -> ${link.value()}
[label=\"${link.name()}\"]"
    }
}
flow.children().each { node ->
    attributes = '' << ''
    node.attributes().each { key, value ->
        attributes << " ${key} : " << format(value) <<"\\l" // maybe better
info here
    }
    shape = ''
    if (node.attributes().keySet().contains('view')) shape =
',shape=ellipse,style=filled,color=".7 .3 1.0"'
    println "\t${node.name()}
[label=\"${node.name()}\\n${attributes.toString()}\"$shape]"
}
println '}'

def format(value){
    switch(value){
        case String  : return value
        case Closure : return 'Closure'
        case List    : return 'List'
        case Map     : return 'Map'
        default      : return value.toString()
    }
}
----

I just recognized it can be a bit slicker, e.g.
call to 'children()' shouldn't be necessary.
The version above is from August.
Seems like I learned something since then...

cheers
Mittie

Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
Very nice.. would be neat to include some visualisation tools in
Grails one day.. maybe when we start doing the IDE ;-)

Graeme

On 23/11/05, Dierk Koenig <[hidden email]> wrote:

> > Hmmm, that's pretty cool!
> >
> > http://cvs.groovy.codehaus.org/viewrep/~raw,r=1.1/groovy/groovy/mo
> > dules/grails/samples/books/pageFlow.png
>
> glad you like it.
>
> Here's the code (pageFlowNodes is pageFlow called with
> NodeBuilder instead of PageFlowBuilder):
>
> ----
> flow = new BooksFlowGraphImpl().pageFlowNodes
> println """
> digraph G {
>     node [shape=box, fontname=Helvetica];
>     {rank=souce; ${flow.children()[0].name()}}"""
> flow.children().each { source ->
>     source.children().each { link ->
>         println "\t${source.name()} -> ${link.value()}
> [label=\"${link.name()}\"]"
>     }
> }
> flow.children().each { node ->
>     attributes = '' << ''
>     node.attributes().each { key, value ->
>         attributes << " ${key} : " << format(value) <<"\\l" // maybe better
> info here
>     }
>     shape = ''
>     if (node.attributes().keySet().contains('view')) shape =
> ',shape=ellipse,style=filled,color=".7 .3 1.0"'
>     println "\t${node.name()}
> [label=\"${node.name()}\\n${attributes.toString()}\"$shape]"
> }
> println '}'
>
> def format(value){
>     switch(value){
>         case String  : return value
>         case Closure : return 'Closure'
>         case List    : return 'List'
>         case Map     : return 'Map'
>         default      : return value.toString()
>     }
> }
> ----
>
> I just recognized it can be a bit slicker, e.g.
> call to 'children()' shouldn't be necessary.
> The version above is from August.
> Seems like I learned something since then...
>
> cheers
> Mittie
>
>
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Jochen Theodorou
Graeme Rocher schrieb:

> Very nice.. would be neat to include some visualisation tools in
> Grails one day.. maybe when we start doing the IDE ;-)

as I am currently looking into the eclipse plugin... maybe one day isn't
so far away ;)

bye blackdrag
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
On 23/11/05, Jochen Theodorou <[hidden email]> wrote:
> Graeme Rocher schrieb:
>
> > Very nice.. would be neat to include some visualisation tools in
> > Grails one day.. maybe when we start doing the IDE ;-)
>
> as I am currently looking into the eclipse plugin... maybe one day isn't
> so far away ;)

Would be cool indeed. When I get a moment from Grails I may help you
out as I have quite a bit of experience writing Eclipse plugins and
using JFace/SWT

>
> bye blackdrag
>
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

Jochen Theodorou
Graeme Rocher schrieb:

> On 23/11/05, Jochen Theodorou <[hidden email]> wrote:
>
>>Graeme Rocher schrieb:
>>
>>>Very nice.. would be neat to include some visualisation tools in
>>>Grails one day.. maybe when we start doing the IDE ;-)
>>
>>as I am currently looking into the eclipse plugin... maybe one day isn't
>>so far away ;)
>
> Would be cool indeed. When I get a moment from Grails I may help you
> out as I have quite a bit of experience writing Eclipse plugins and
> using JFace/SWT

yes? very good.. because I have nearly no experience ;)

bye blackdrag
Reply | Threaded
Open this post in threaded view
|

Re: [grails-dev] Controller Validation

graemer
On 23/11/05, Jochen Theodorou <[hidden email]> wrote:

> Graeme Rocher schrieb:
>
> > On 23/11/05, Jochen Theodorou <[hidden email]> wrote:
> >
> >>Graeme Rocher schrieb:
> >>
> >>>Very nice.. would be neat to include some visualisation tools in
> >>>Grails one day.. maybe when we start doing the IDE ;-)
> >>
> >>as I am currently looking into the eclipse plugin... maybe one day isn't
> >>so far away ;)
> >
> > Would be cool indeed. When I get a moment from Grails I may help you
> > out as I have quite a bit of experience writing Eclipse plugins and
> > using JFace/SWT
>
> yes? very good.. because I have nearly no experience ;)

It is such a nicely designed API that its really easy (much more so
than Swing) but i'm sure you'll enjoy learning it!

>
> bye blackdrag
>