"Dynamic" Domain Objects

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

"Dynamic" Domain Objects

Rob Rothwell
Ok...I'm not looking for an answer, just maybe a recommendation on how to proceed.

I created a very simple "dynamic object" (aren't all Smalltalk objects dynamic?!) with an addField method that would create a new instance variable and accessors so that I could dynamically add new input fields to a WebApplication (since a WebFormElement uses an aspect:for: an object to bind to--otherwise, I probably would just use a Dictionary).

Anyway, I just used my novice understanding to do something like:

ArbitraryObject>>class #addField
    | aFieldName|
   
    aFieldName := self newFieldName.
    self addInstVarName: aFieldName.
    self addGetter: aFieldName.
    self addSetter: aFieldName.
    ^aFieldName asSymbol.

creating an arbitrary field name "fieldN" with:

ArbitraryObject>>class #newFieldName
    ^'field',self newNumber asString.

and

ArbitraryObject>>class #newNumber
    Counter ifNil: [^Counter:=1].
    ^Counter:=Counter+1.

and compiling accessors with:

ArbitraryObject>>class #addGetter: fieldName
    |method|
   
    method:=fieldName,'
    ^',fieldName.

    self compile: method classified: 'field accessors'.

and

ArbitraryObject>>class #addSetter: fieldName
    |method |
   
    method:=fieldName,': anObject
    ^',fieldName,':=anObject'.
    self compile: method classified: 'field accessors'

(I know...there is a method SOMEWHERE that will do that for me!)

Anyway, it works fine if I simple execute the addField on the object from within a Workspace or something, but if I do it from within it's application (say in an action method for a button on the page), the addField method does not complete, the web page just dies, and I don't even get an error message.

So then I tried having such an object be contained within the domain object and had the same result.

So...

1.  Can you simply bind, say, a WebInputField to ANY object (not an aspect of an object), in which case I could store my dynamic fields in a collection of some kind, or
2.  Is there any reason you can think of why Aida wouldn't like it's objects being recompiled in the middle of a page refresh?

Thanks,

Rob

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Stefan Schmiedl
Hi Rob,

On Thu, 22 May 2008 18:57:51 -0400
"Rob Rothwell" <[hidden email]> wrote:

> Ok...I'm not looking for an answer, just maybe a recommendation on how to
> proceed.
>
> I created a very simple "dynamic object" (aren't all Smalltalk objects
> dynamic?!) with an addField method that would create a new instance variable
> and accessors so that I could dynamically add new input fields to a
> WebApplication (since a WebFormElement uses an aspect:for: an object to bind
> to--otherwise, I probably would just use a Dictionary).

FWIW, I *would* use a dictionary. An "aspect" is nothing more than
a reader and a writer with "compatible" names. If your ArbitraryObject
has a dictionary 'properties' as instance variable, you can create
"aspects" of your ArbitraryObject like, e.g.

        name
                ^properties at: #name ifAbsent: ['unnamed']

        name: aString
                properties at: #name put: aString

If you don't care too much for speed, you could also avoid defining all
of these methods by yourself (or explicitly in code) and instead prevent
the MNU exception being raised by implementing (in VW)
Object's method 'doesNotUnderstand: aMessage' which gives you access
to the selector, which in turn would point to the property you're
interested in.

HTH,
s.
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Nicolas Petton
In reply to this post by Rob Rothwell
Hi Rob,

Another solution, better IMHO, would be to use dynamic variables. Let me
explain a bit: If you store your custom variables in a dictionary, then
in Aida you can simply add an entry for each variable.

AObject>>dynamicVariables
    dynamicVariables isNil ifTrue: [self initDynamicVariables].
    ^dynamicVariables

AObject>>initDynamicVariables
   dynamicVariables := Dictionary new

To add a custom variable:

AObject>>addDynamicVariable: aString value: anObject
   self dynamicVariables at: aString put: [anObject]

Of course it can be improved, but that's the idea. Then in Aida you can
view or edit all of them:

AObjectApp>>viewMain
        ....
        self observee dynamicVariables do: [:each |
            e addtext: each key, ': ', each value; addBreak].
        ....

Cheers!

Nico
--
Nicolas Petton
http://nico.bioskop.fr
            ___
          ooooooo
         OOOOOOOOO
        |Smalltalk|
         OOOOOOOOO
          ooooooo
           \   /
            [|]
--------------------------------
Ma clé PGP est disponible ici :
http://nico.bioskop.fr/pgp-key.html

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida

signature.asc (204 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Nicolas Petton
In reply to this post by Stefan Schmiedl

Le vendredi 23 mai 2008 à 01:09 +0200, Stefan Schmiedl a écrit :

> Hi Rob,
>
> On Thu, 22 May 2008 18:57:51 -0400
> "Rob Rothwell" <[hidden email]> wrote:
>
> > Ok...I'm not looking for an answer, just maybe a recommendation on how to
> > proceed.
> >
> > I created a very simple "dynamic object" (aren't all Smalltalk objects
> > dynamic?!) with an addField method that would create a new instance variable
> > and accessors so that I could dynamically add new input fields to a
> > WebApplication (since a WebFormElement uses an aspect:for: an object to bind
> > to--otherwise, I probably would just use a Dictionary).
>
> FWIW, I *would* use a dictionary.
You was faster than me! :)

Nico

>  An "aspect" is nothing more than
> a reader and a writer with "compatible" names. If your ArbitraryObject
> has a dictionary 'properties' as instance variable, you can create
> "aspects" of your ArbitraryObject like, e.g.
>
> name
> ^properties at: #name ifAbsent: ['unnamed']
>
> name: aString
> properties at: #name put: aString
>
> If you don't care too much for speed, you could also avoid defining all
> of these methods by yourself (or explicitly in code) and instead prevent
> the MNU exception being raised by implementing (in VW)
> Object's method 'doesNotUnderstand: aMessage' which gives you access
> to the selector, which in turn would point to the property you're
> interested in.
>
> HTH,
> s.
> _______________________________________________
> Aida mailing list
> [hidden email]
> http://lists.aidaweb.si/mailman/listinfo/aida

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida

signature.asc (204 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Stefan Schmiedl
On Fri, 23 May 2008 01:22:00 +0200
Nicolas Petton <[hidden email]> wrote:

> You was faster than me! :)

heheh ... reading emails just before going to bed has its merits :-)

s.
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
In reply to this post by Nicolas Petton
On Thu, May 22, 2008 at 7:20 PM, Nicolas Petton <[hidden email]> wrote:
Hi Rob,

Another solution, better IMHO, would be to use dynamic variables. Let me
explain a bit: If you store your custom variables in a dictionary, then
in Aida you can simply add an entry for each variable.

AObject>>dynamicVariables
   dynamicVariables isNil ifTrue: [self initDynamicVariables].
   ^dynamicVariables

AObject>>initDynamicVariables
  dynamicVariables := Dictionary new

To add a custom variable:

AObject>>addDynamicVariable: aString value: anObject
  self dynamicVariables at: aString put: [anObject]

This part I can actually do myself, and in Aida, I even understand... 

AObjectApp>>viewMain
       ....
       self observee dynamicVariables do: [:each |
           e addtext: each key, ': ', each value; addBreak].
       ....

But how do I bind one of these dynamicVariables to a WebInputField/WebCheckBox/etc...?

AObjectApp>>viewMain
     | field |
     .....
     self observee dynamicVariables do: [:each |
          field := WebInputField new aspect: ??? for: self observee dynamicVariables size: 50.  
          e add: field.  e addBreak].

I think I am missing something fundamental again...I mean, I do not know the "aspect" until the user creates it...

Rob

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
In reply to this post by Stefan Schmiedl
On Thu, May 22, 2008 at 7:09 PM, Stefan Schmiedl <[hidden email]> wrote:
FWIW, I *would* use a dictionary. An "aspect" is nothing more than
a reader and a writer with "compatible" names. If your ArbitraryObject
has a dictionary 'properties' as instance variable, you can create
"aspects" of your ArbitraryObject like, e.g.

       name
               ^properties at: #name ifAbsent: ['unnamed']

       name: aString
               properties at: #name put: aString

I actually started to do just what you have written above, but in Aida, I STILL need the methods to be defined, so if I am creating new methods "on the fly," wouldn't I either still need to recompile an object or do what you mentioned below?

If you don't care too much for speed, you could also avoid defining all
of these methods by yourself (or explicitly in code) and instead prevent
the MNU exception being raised by implementing (in VW)
Object's method 'doesNotUnderstand: aMessage' which gives you access
to the selector, which in turn would point to the property you're
interested in.

Thanks for the doesNotUnderstand idea, though...I hadn't thought of that having listened to all the pro's and con's of such usage a while back on the lists!


_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Stefan Schmiedl
On Thu, 22 May 2008 19:31:28 -0400
"Rob Rothwell" <[hidden email]> wrote:

> I actually started to do just what you have written above, but in Aida, I
> STILL need the methods to be defined, so if I am creating new methods "on
> the fly," wouldn't I either still need to recompile an object or do what you
> mentioned below?

If I read your mind correctly, medidate about the following sentence:

You don't need to have a method defined for your objects, a receiver
only needs to be able to handle an incoming message.

In many other languages, this distinction does not exist, because of
static type checking and what not else. In Smalltalk (and friends, like
ruby), it's important.

Besides this, you're right. You have to define the accessors somewhere.

However, how about defining a method

        WebFormElement>>dynamicProperty:of:

similar to the way aspect:for: works, but instead of mapping the symbol
to an immediate accessor, use it to access the observee's property
dictionary.

> Thanks for the doesNotUnderstand idea, though...I hadn't thought of that
> having listened to all the pro's and con's of such usage a while back on the
> lists!

Yeah, well, just like most things, it's a powerful tool to be used with
care.

s.
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
On Fri, May 23, 2008 at 9:08 AM, Stefan Schmiedl <[hidden email]> wrote:
On Thu, 22 May 2008 19:31:28 -0400
"Rob Rothwell" <[hidden email]> wrote:

If I read your mind correctly, medidate about the following sentence:

You don't need to have a method defined for your objects, a receiver
only needs to be able to handle an incoming message.

In many other languages, this distinction does not exist, because of
static type checking and what not else. In Smalltalk (and friends, like
ruby), it's important.

I actually do understand that part, but because of the internals of how Aida works (if I understand it correctly), The XyzApp object that is "bound" to my Xyz object will load/store the WebFormElement data by sending them a [pre]defined message.  By definition, all my [dynamic] messages will be "undefined."
 
Besides this, you're right. You have to define the accessors somewhere.

However, how about defining a method

       WebFormElement>>dynamicProperty:of:

similar to the way aspect:for: works, but instead of mapping the symbol
to an immediate accessor, use it to access the observee's property
dictionary.

So, what I am working on is basically a combination of what you have suggested...a new dynamicProperty:of: method that gets activated by doesNotUnderstand: so that Aida can continue to function normally...

Thanks for the ideas!

Rob


 

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Stefan Schmiedl
On Fri, 23 May 2008 09:38:51 -0400
"Rob Rothwell" <[hidden email]> wrote:

> > However, how about defining a method
> >
> >        WebFormElement>>dynamicProperty:of:
> >
> > similar to the way aspect:for: works, but instead of mapping the symbol
> > to an immediate accessor, use it to access the observee's property
> > dictionary.
>
>
> So, what I am working on is basically a combination of what you have
> suggested...a new dynamicProperty:of: method that gets activated by
> doesNotUnderstand: so that Aida can continue to function normally...

I've just taken a look at how aspect:for: was implemented, which led me
to the method WebFormElement>>adapt

  adapt
  "set appropriate aspect adaptor for that element"
       
  (self aspect isKindOf: Symbol) ifTrue:
      [self adaptor: ((AIDAAspectAdaptor forAspect: self aspect) subject: self object)].
    (self aspect isKindOf: Number) ifTrue:
      ["adapt to an element of collection"
        self adaptor:  ((AIDAIndexedAdaptor forIndex: self aspect) subject: self object)]

which is kind of ugly (sorry Janko). I might be wrong, but it
looks like adapt is called on all WebFormElements, so you probably can't
just define your own adaptor like

        self adaptor:
                ((AIDAIndexedAdaptor forIndex: self aspect)
                        subject: self object properties

OTOH, it gives me an idea: It would work out of the box, if the object
behind the form is a collection indexable by numbers. Can you put your
dynamic properties into an OrderedCollection and use this collection as
model for a "dynamic property editor" component?

s.
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Janko Mivšek
In reply to this post by Rob Rothwell
Rob Rothwell wrote:

> I actually do understand that part, but because of the internals of how
> Aida works (if I understand it correctly), The XyzApp object that is
> "bound" to my Xyz object will load/store the WebFormElement data by
> sending them a [pre]defined message.  By definition, all my [dynamic]
> messages will be "undefined."

This "aspect adapting" is done with help of AIDAAspectAdaptor (in
Aida-Support). It is done by calling yourApp form
#registerFormElementsIn: anElement. All form elements are then
registered in instvar #fields in yourApp form.

Aspect adaptor is otherwise a pattern from MVC implementation in
VisualWorks by which Aida design is heavily influenced anyway. See
Smalltalk An Introduction to Application Development using VisualWorks,
Chapter 29 (page 312): Introduction to Models, Views and Controllers:
http://stephane.ducasse.free.fr/FreeBooks/HopkinsHoran/HopkinsHoran.pdf

I would say that with extending that aspect adapting pattern you can
actually achieve your goals easily. Even more because we already have
auto converting of values supported already (see WebFormElement class
auto type converting)

Janko


--
Janko Mivšek
AIDA/Web
Smalltalk Web Application Server
http://www.aidaweb.si
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
In reply to this post by Stefan Schmiedl
On Fri, May 23, 2008 at 10:05 AM, Stefan Schmiedl <[hidden email]> wrote:
OTOH, it gives me an idea: It would work out of the box, if the object
behind the form is a collection indexable by numbers. Can you put your
dynamic properties into an OrderedCollection and use this collection as
model for a "dynamic property editor" component?

Oh...I think you're right...it looks like if you pass a number as the aspect it assumes the object is an indexed collection?!

adapt
    "set appropriate aspect adaptor for that element"
    (self aspect isKindOf: Symbol) ifTrue:
        [self adaptor: ((AIDAAspectAdaptor forAspect: self aspect) subject: self object)].
    (self aspect  isKindOf: Number) ifTrue: 
        "adapt to an element of collection"
        [self adaptor: ((AIDAIndexedAdaptor forIndex: self aspect) subject: self object)]

I would be quite happy to map my fields to open slots in a collection for now!

Thanks...hope that works...

Rob



_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
In reply to this post by Janko Mivšek
On Fri, May 23, 2008 at 10:12 AM, Janko Mivšek <[hidden email]> wrote:
This "aspect adapting" is done with help of AIDAAspectAdaptor (in Aida-Support). It is done by calling yourApp form #registerFormElementsIn: anElement. All form elements are then registered in instvar #fields in yourApp form.

Aspect adaptor is otherwise a pattern from MVC implementation in VisualWorks by which Aida design is heavily influenced anyway. See      
Smalltalk An Introduction to Application Development using VisualWorks, Chapter 29 (page 312): Introduction to Models, Views and Controllers:
http://stephane.ducasse.free.fr/FreeBooks/HopkinsHoran/HopkinsHoran.pdf

I would say that with extending that aspect adapting pattern you can actually achieve your goals easily. Even more because we already have auto converting of values supported already (see WebFormElement class auto type converting)

Thanks for the references...I will try the simplest thing for now (indexed values) and see how that works!

Rob

_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida
Reply | Threaded
Open this post in threaded view
|

Re: "Dynamic" Domain Objects

Rob Rothwell
In reply to this post by Janko Mivšek
On Fri, May 23, 2008 at 10:12 AM, Janko Mivšek <[hidden email]> wrote:
I would say that with extending that aspect adapting pattern you can actually achieve your goals easily. Even more because we already have auto converting of values supported already (see WebFormElement class auto type converting)

Looking deeper, though, it looks like each WebFormElement can resolve it's bindings in it's own way.  For example, WebCheckBox sends the aspect message directly to the "bound" object even when I "bind" it to an object in a collection:

isChecked
    | value |
    self aspect notNil ifTrue:
        [value := self object perform: self aspect.
        ^value == true].  "so that false will be returned even if value is not so"
    self selected notNil ifTrue: [^self selected includes: self object].
    ^self checked

It sure looks like the least common denominator is a named message...the doesNotUnderstand route is looking better all the time...though probably not an elegant solution...!

Thanks again,

Rob
_______________________________________________
Aida mailing list
[hidden email]
http://lists.aidaweb.si/mailman/listinfo/aida