OT: some thoughts on customization of libraries

classic Classic list List threaded Threaded
3 messages Options
Reply | Threaded
Open this post in threaded view
|

OT: some thoughts on customization of libraries

david bovill
This is a copy of the same post sent to the Rev list - I thought it would be
of interest to this group - besides which it has been held for moderation as
it was too long :)

---

This is somewhat off-topic. I'm putting this out for comment because it may
be useful to people who are structuring libraries they want people to be
able to cusomise - or maybe the technique I'm using is not such a good idea
and I can get some feedback on it before implementing it more widely? Skip
this if libraries and their problems are not your thing :)

*Background*
I put a lot of effort into abstracting out code into generic libraries, and
there is always a battle between making the code simple and generic, and
making it flexible enough to all the uses it could be put to. What tends to
happen is that the handlers steadily sprout parameters, and what started
simple ends up requiring a big manual to get your head around.

I separate my code now into a semi-strict structure with the visual code in
the controls - libraries in used stacks, and the program logic on the card
script. This works well, but made me wary of associating the library code
with any data or rev controls. One "requirement" for the library code is
that you can cut and paste it to another used stack and it still works - any
links to data must be through handlers that connect to a distinct source
(the model). For this reason was uncomfortable using getprop / setprop
handlers in libraries. This is wrong I think, or rather there is a way to
use getprop / setprop handlers in libraries that I had not taken advantage
of, and that is end user customisation of library functionality.

I am not talking about just setting custom properties of the library - this
I try to avoid - I'd set the propoerty of a the data source (model) instead,
but something much more akin to the sort of customisation you can do in
object oriented langauges, where if you don't like the default behaviour you
can take an object (function) and overide it with your own code. using
custom properties in libraries can achieve the same sort of behavior. How?
Simply replace a function call with a getprop handler that refer to the
target!


*libOPN_Graph:  an example*
To take an example of a library that I am working on for graphing
relationships:

function graph_ConstructNode nodeID, someColour, someShape, someRect,
> someLinks, someThingElse
>     -- do your stuff and put the params together to create the text that
> determines the way a node looks and behaves
>
>     return formattedNode
> end graph_ConstructNode


I use it for instance to graph the relationships between handlers, where I
want function coloured one colour, and commands another etc. The library is
complex with a hundred or so handlers, and there are so many parameters it
gets hard to read. Now what if I want to change the colour of the function
nodes? Well I can pass a different colour as a parameter when I call the
library and bingo - right? Well yes - but the same goes for shape and around
40 other parameters, and for each one you end up passing parameters around.

So you can use local properties right? I tend not to, or at least not to
have lots of them. They make coding harder, and harder to read, and really
break portability as you have to remeber to copy the local declaration with
the handler - all of which ends up making things a mess over time. OK - so
you can use custom properties? Yes, with the reservations above about
seprating data. (To make the code more readable, and modifiable I nearly
always wrap custom property call in getprop / setprop handlers). Also what
happens when you have two or more graphs? You now have a real problem, where
you have to store the data distinctly for each graph, and your simple custom
properties become a complex mess - now multiply that for some 30 or 40
properties of the graph!

And what if there is a radically new use for the graph - say where the
colour depends on some complex replationships that you need to code? So we
are back to passing parameters to the library - no? Well no - at least I
think not -the thought is why not use getprop / setprop calls from the
library to the target which you can optionally intercept and override? In
our example it works like this:

function graph_ConstructNode nodeID, someShape, someRect, someLinks,

> someThingElse
>
>     put the graph_NodeColour [nodeID] of the target into someColour
>
>     -- do your stuff and put the params together to create the text that
> determines the way a node looks and behaves
>
>     return formattedNode
> end graph_ConstructNode
>
> getprop graph_NodeColour [nodeID]
>     return the uOPN ["NodeColour"] of me
> end graph_NodeColour
>
> setprop graph_NodeColour [nodeID] someColour
>     set the uOPN ["NodeColour"] of me to someColour
> end graph_NodeColour
>

So now web have one less parameter to pass around, and better readability.
If in the future you get some great idea to customise the way the colouring
works, its easy to find the one place in the code this happens and modify
it. But best of all the end users can customise the behaviour for each graph
simply by intercepting the getprop call, and putiing whatever code they want
there. In the end (depending on how far you want to take it), you end up
with code that looks like this:



> function graph_ConstructNode nodeID, someLinks, someThingElse
>
>     put the graph_NodeColour [nodeID] of the target into someColour
>     put the graph_NodeShape [nodeID] of the target into someShape
>     put the graph_NodeRect [nodeID] of the target into someRect
>
>     -- do your stuff and put the params together to create the text that
> determines the way a node looks and behaves
>
>     return formattedNode
> end graph_ConstructNode
>
> getprop graph_NodeColour [nodeID]
>     return the uOPN ["NodeColour"] of the model_Object of the target
> end graph_NodeColour
>
> setprop graph_NodeColour [nodeID] someColour
>     set the uOPN ["NodeColour"] of the model_Object of the target to
> someColour
> end graph_NodeColou
>
> getprop graph_NodeShape [nodeID]
>     return the uOPN ["NodeColour"] of the model_Object of the target
> end graph_NodeShape
> setprop graph_NodeShape [nodeID] someShape
>     set the uOPN ["NodeColour"] of the model_Object of the target to
> someShape
> end graph_NodeShape
>
> getprop graph_NodeRect [nodeID]
>     return the uOPN ["NodeColour"] of the model_Object of the target
> end graph_NodeRect
> setprop graph_NodeRect [nodeID] someRect
>     set the uOPN ["NodeColour"] of the model_Object of the target to
> someRect
> end graph_NodeRect
>
> getprop model_Object
>     return the long id of me
> end model_Object
>

The last part is something I don't (yet) do very often. Defining a
"model_Object" like this is something I use for storing data in models
(usually custom properties). I do this to allow switching to databases or
external text files at a later stage. In this case it is used so that if a
script say in a GUI graph preference stack needs to control several graphs
it can switch the model_Object for each graph so that all the custom
properties are stored with the graph and not in the library.


*Problems
*Speed? My initial worry* *was that all these messages flying around would
significantly slow things down. Frankly they don't. I sue them all the time
and have never yet found a speed problem. In fact I am not sure if there is
any speed penalty over using a function call to calling a custom property -
at least not one that is of general significance. Still taking the last
example - I tend to do this:

getprop graph_NodeRect [nodeID]
>     return the uOPN ["NodeColour"] of the model_Object of me
> end graph_NodeRect
> getprop model_Object
>     return the uOPN ["model_Object"] of me
> end graph_NodeRect
> setprop model_Object someObject
>     set the uOPN ["model_Object"] of me to someObject
> end model_Object
>

rather than this:

getprop graph_NodeRect [nodeID]
>     return the uOPN ["NodeColour"] of the model_Object of the target
> end graph_NodeRect
> getprop model_Object
>     return the long id of me
> end model_Object
>

because it gets called so often, and is unlikely to need the end user to
customise extensively via scripting, I figure to speed it up by storing the
data locally.


*Feedback*
I've got in toal over 50 libraries and some of them may be useful enough to
publish, so before I work on them your thought and comments would be more
than welcome!


[Non-text portions of this message have been removed]

Reply | Threaded
Open this post in threaded view
|

Re: OT: some thoughts on customization of libraries

Ken Ray
Thanks for posting this, David... it's a good topic for this group.

> *Background*
> I put a lot of effort into abstracting out code into generic libraries, and
> there is always a battle between making the code simple and generic, and
> making it flexible enough to all the uses it could be put to. What tends to
> happen is that the handlers steadily sprout parameters, and what started
> simple ends up requiring a big manual to get your head around.
>
> I separate my code now into a semi-strict structure with the visual code in
> the controls - libraries in used stacks, and the program logic on the card
> script.

Can you clarify what you mean by this? When you say "the visual code in the
controls", what do you consider "visual code"? And when you say you're
separating your code, you're talking about separating it for a full project,
and not just for libraries, right? (Just trying to be clear.)

> This works well, but made me wary of associating the library code
> with any data or rev controls. One "requirement" for the library code is
> that you can cut and paste it to another used stack and it still works - any
> links to data must be through handlers that connect to a distinct source
> (the model). For this reason was uncomfortable using getprop / setprop
> handlers in libraries. This is wrong I think, or rather there is a way to
> use getprop / setprop handlers in libraries that I had not taken advantage
> of, and that is end user customisation of library functionality.


> So you can use local properties right? I tend not to, or at least not to
> have lots of them. They make coding harder, and harder to read, and really
> break portability as you have to remeber to copy the local declaration with
> the handler - all of which ends up making things a mess over time. OK - so
> you can use custom properties? Yes, with the reservations above about
> seprating data. (To make the code more readable, and modifiable I nearly
> always wrap custom property call in getprop / setprop handlers). Also what
> happens when you have two or more graphs? You now have a real problem, where
> you have to store the data distinctly for each graph, and your simple custom
> properties become a complex mess - now multiply that for some 30 or 40
> properties of the graph!
>
> And what if there is a radically new use for the graph - say where the
> colour depends on some complex replationships that you need to code? So we
> are back to passing parameters to the library - no? Well no - at least I
> think not -the thought is why not use getprop / setprop calls from the
> library to the target which you can optionally intercept and override? In
> our example it works like this:
>
> function graph_ConstructNode nodeID, someShape, someRect, someLinks,
> someThingElse
>
>     put the graph_NodeColour [nodeID] of the target into someColour
>
>     -- do your stuff and put the params together to create the text that
> determines the way a node looks and behaves
>
>     return formattedNode
> end graph_ConstructNode
>
> getprop graph_NodeColour [nodeID]
>     return the uOPN ["NodeColour"] of me
> end graph_NodeColour
>
> setprop graph_NodeColour [nodeID] someColour
>     set the uOPN ["NodeColour"] of me to someColour
> end graph_NodeColour
>
> So now web have one less parameter to pass around, and better readability.
> If in the future you get some great idea to customise the way the colouring
> works, its easy to find the one place in the code this happens and modify
> it. But best of all the end users can customise the behaviour for each graph
> simply by intercepting the getprop call, and putiing whatever code they want
> there.


So if I'm understanding you properly, the reason why this:

   put the graph_NodeColour [nodeID] of the target into someColour

is preferable to this:

   put the uOPN["NodeColour"] of the target into someColour

is because the end user can intercept the getProp to run custom code if they
wanted to do something other than just set the one `uOPN["NodeColour"]` of
the target?

If so, then why couldn't the end user simply intercept the getProp on the
`uOPN["NodeColour"]` instead of intercepting the getProp of
`graph_NodeColour[nodeID]` ?

Or am I missing something... ?

> In the end (depending on how far you want to take it), you end up

>> getprop graph_NodeShape [nodeID]
>>     return the uOPN ["NodeColour"] of the model_Object of the target
>> end graph_NodeShape
>> setprop graph_NodeShape [nodeID] someShape
>>     set the uOPN ["NodeColour"] of the model_Object of the target to
>> someShape
>> end graph_NodeShape
>>
>> getprop graph_NodeRect [nodeID]
>>     return the uOPN ["NodeColour"] of the model_Object of the target
>> end graph_NodeRect
>> setprop graph_NodeRect [nodeID] someRect
>>     set the uOPN ["NodeColour"] of the model_Object of the target to
>> someRect
>> end graph_NodeRect

I'm assuming the custom props above should be `uOPN["NodeShape"]` and
`uOPN["NodeRect"]` instead of `uOPN["NodeColour"]`, right? (I'm assuming
it's just a copy-and-paste error... something I do all the time ;-)

> The last part is something I don't (yet) do very often. Defining a
> "model_Object" like this is something I use for storing data in models
> (usually custom properties). I do this to allow switching to databases or
> external text files at a later stage. In this case it is used so that if a
> script say in a GUI graph preference stack needs to control several graphs
> it can switch the model_Object for each graph so that all the custom
> properties are stored with the graph and not in the library.
>
>
> *Problems
> *Speed? My initial worry* *was that all these messages flying around would
> significantly slow things down. Frankly they don't. I sue them all the time
> and have never yet found a speed problem. In fact I am not sure if there is
> any speed penalty over using a function call to calling a custom property -
> at least not one that is of general significance.

Custom properties are indeed very fast to access...
 
> *Feedback*
> I've got in toal over 50 libraries and some of them may be useful enough to
> publish, so before I work on them your thought and comments would be more
> than welcome!

Well, here's my two cents... and keep in mind that I may very well be
reacting from an engrained development approach... but it *seems* to me to
be a bit more abstracted than it needs to be (IMHO). I like the idea of
custom property-trapping in order to allow the end-user/developer to add
custom behaviors, but I guess I'm struggling with the additional
abstraction.

Here's an example: I have a library called "libSTSControls" that manages
custom controls (like a splitter object or a search box), that exist as
groups on the card of the main stack of the library. When a new "splitter"
is needed, I have a handler that can be called which will create a copy of
that splitter control on the current card:

  stsControls_Create "splitter"

Which in turn generates an "stsControls_NewControl" message with params
about the type of object being created, and if it is not trapped by any
user's code, it hits the "backstop" in the library:

  on stsControls_newControl
  end stsControls_newControl

Each custom control (group) has a uRIP["type"] that corresponds to the type
of control (like "STSSplitter") so that if I have any code that needs to see
how many splitters are on a card, say, it can quickly iterate through the
groups on the card and check the uRIP["type"] of each group.

Attached to the group are any custom properties that either (a) the splitter
needs to reference in order to operate, or (b) that trigger other changes in
the splitter. Currently these are not in a custom property set, but perhaps
they should be. So for example, the splitter maintains the object references
to what objects are to the left or right of (in the case of a vertical
splitter) with the props:

  uSTSSplitter_LeftObjects
  uSTSSplitter_RightObjects

These are used for reference, and no action is taken when they are retrieved
or set. On the other hand, whether the splitter is oriented horizontally or
vertically is stored in the prop:

  uSTSSplitter_Orientation

Which when set, causes the splitter to redraw in its new orientation.

The getprop/setprop for this property lives in the library, not in the group
itself - in fact, I prefer to keep my custom controls codeless so they are
only a group of objects and custom properties to control them.

The code in the library always refers to "the target", so if there are
multiple splitter controls on a card (let's say you have a standed "3-pane"
interface, so you'd have one vertical and one horizontal splitter), the
library can know which one to deal with.

If someone wanted to do something special when a property was retrieved or
changed, they could always intercept it before the library trapped the
getProp/setProp.

So I guess I'm still struggling with the desire to abstract the calls one
more level...

Just my two cents,

Ken Ray
Sons of Thunder Software, Inc.
Email: [hidden email]
Web Site: http://www.sonsothunder.com/


Reply | Threaded
Open this post in threaded view
|

Re: OT: some thoughts on customization of libraries

david bovill
2008/8/31 Ken Ray <[hidden email]>

> Thanks for posting this, David... it's a good topic for this group.
>
> > *Background*
> > I put a lot of effort into abstracting out code into generic libraries,
> and
> > there is always a battle between making the code simple and generic, and
> > making it flexible enough to all the uses it could be put to. What tends
> to
> > happen is that the handlers steadily sprout parameters, and what started
> > simple ends up requiring a big manual to get your head around.
> >
> > I separate my code now into a semi-strict structure with the visual code
> in
> > the controls - libraries in used stacks, and the program logic on the
> card
> > script.
>
> Can you clarify what you mean by this? When you say "the visual code in the
> controls", what do you consider "visual code"?


Any code that relates to the visual appearance of the view displaying data
or interacting with the user. Typically code relating to things like
geometry, or colours or other visual styles. This is contrasted with code
that fetches or manipulates the actual data, or code involved in the logic
of the particular application. So yes - I keep this visual code separate -
either in the "views" themselves (usually groups taken from a library), or
abstracted into a library that multiple views can share.

separating your code, you're talking about separating it for a full project,
> and not just for libraries, right? (Just trying to be clear.)


Yes - in particular I am finding the separation of visual code from the
program logic helpful. Not just used stacks or front / back scripts - but
more interestingly I have started to put all program logic in the card
script (sometimes stack script) - but never in the card based groups or
controls. The acid test here is always can I replace a control on the card
with an updated version, or a different style of control and everything
still keep working. Early on I experimented with having custom scripts
sections that would be saved and restored when the view is updated /
replaced - but now I find I nearly always can do what I want and keep the
code on the card script.

So if I'm understanding you properly, the reason why this:

>
>   put the graph_NodeColour [nodeID] of the target into someColour
>
> is preferable to this:
>
>   put the uOPN["NodeColour"] of the target into someColour
>
> is because the end user can intercept the getProp to run custom code if
> they
> wanted to do something other than just set the one `uOPN["NodeColour"]` of
> the target?
>
> If so, then why couldn't the end user simply intercept the getProp on the
> `uOPN["NodeColour"]` instead of intercepting the getProp of
> `graph_NodeColour[nodeID]` ?


Not really. The main point of the post was the use of custom property calls
from the library down to the visual controls as a form of end user
customisation. The choice of doing this with explicitly wrapped handlers is
not important. I do this for readability - I find that if I don;t use
getprop / setprop handlers I cannot tell by looking at the code - I miss
part of the program logic visually. I prefer to be able to see this by
looking at the organisation of the handler in the handler list. I use uOPN
to indicate that this is an actual custom property and to ensure
compatability in cases where there may be other people code mixed in.

>
> >> end graph_NodeRect
>
> I'm assuming the custom props above should be `uOPN["NodeShape"]` and
> `uOPN["NodeRect"]` instead of `uOPN["NodeColour"]`, right? (I'm assuming
> it's just a copy-and-paste error... something I do all the time ;-)


Yes sorry - just a typo.

Well, here's my two cents... and keep in mind that I may very well be
> reacting from an engrained development approach... but it *seems* to me to
> be a bit more abstracted than it needs to be (IMHO). I like the idea of
> custom property-trapping in order to allow the end-user/developer to add
> custom behaviors,


Thats the main point really.


> but I guess I'm struggling with the additional
> abstraction.


 I did not mean to emphasise that.

Here's an example: I have a library called "libSTSControls" that manages
> custom controls (like a splitter object or a search box), that exist as
> groups on the card of the main stack of the library. When a new "splitter"
> is needed, I have a handler that can be called which will create a copy of
> that splitter control on the current card:
>
>  stsControls_Create "splitter"


Similar.  I have a database of views. Each view is in its own stack, and
stored together in a folder. They are named so that they are structured
hiearchicly - so the are Title Views ("View|Title|Simple" or
"View|Title|Icon"), there are layout views, form views, teree views, image
views, etc. Then I have a generic library to insert, delete, and update the
views. So in your example above I would.

set the view_Insert of group "layout" of card 2 to "View|Simple|Tree"

Which in turn generates an "stsControls_NewControl" message with params
> about the type of object being created, and if it is not trapped by any
> user's code, it hits the "backstop" in the library:
>
>  on stsControls_newControl
>  end stsControls_newControl


I think that the same - I use a front script which checks the uRIPs and if
its a view - stores or updates info in the database. The front script is
passed and does not affect the ability to trap the messages locally.

Each custom control (group) has a uRIP["type"] that corresponds to the type
> of control (like "STSSplitter") so that if I have any code that needs to
> see
> how many splitters are on a card, say, it can quickly iterate through the
> groups on the card and check the uRIP["type"] of each group.


I use uRIP["type"] for "library" or "view" or "menu" - its underused really
and I use the uRIP["name"] for a full path like "View|Title|Simple". Maybe I
should use "type" for that.

The getprop/setprop for this property lives in the library, not in the group
> itself - in fact, I prefer to keep my custom controls codeless so they are
> only a group of objects and custom properties to control them.


Yes. I also have stopped storing data in the views that may change. I use
the card script now as a default. I do however put the logic for things like
geometry inside the view (group). I like to be able to go into the code and
tweak it, and I don't like the Rev based way of using libraries and custom
properties - again I like the readability of the code. So I leave code in
the view that has to do with visual presentation and is relatively stable.

The code in the library always refers to "the target",


Again I use custom properties for how you refer to things - so instead of:

 put the text of the target

 you'd either have

 put the tree_Text of the target

, or

put the text of the tree_Field of the target.


[Non-text portions of this message have been removed]