Hello!,
I'm implementing an API for rendering Google Maps. I've decided to implement it as a brush. That's because I'm just generating javascript code. But now I have a problem: when adding support for callbacks, I need to hold some state; for example, the map the callback refers to. But I think brushes are not meant to hold state, that is something left for the components mechanism, isn't it? So I would like to know what would be the correct way of implementing it in the framework. Should I implement maps as components, or should I add state to my brushes; I may hold a state in the callback block too, but I don't think that's good. Thanks! Mariano _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Mariano,
I did a Seaside package for Google Maps. Check out http://seaside.gemstone.com/ss/GoogleMaps.html . If you'd like to contribute to that project just register as a developer and I'll add you to the project. James On Jun 18, 2009, at 5:51 AM, Mariano Montone wrote: > Hello!, > I'm implementing an API for rendering Google Maps. I've > decided to implement it as a brush. That's because I'm just > generating javascript code. But now I have a problem: when adding > support for callbacks, I need to hold some state; for example, the > map the callback refers to. But I think brushes are not meant to > hold state, that is something left for the components mechanism, > isn't it? So I would like to know what would be the correct way of > implementing it in the framework. Should I implement maps as > components, or should I add state to my brushes; I may hold a state > in the callback block too, but I don't think that's good. > > Thanks! > > Mariano > > > _______________________________________________ > seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Thanks James!
I think I looked at your code once, but you didn't have callbacks and implemented it only with brushes , didn't you? If you didn't, do you have callbacks? And how did you implemented them? I'm supporting callbacks now and am considering creating a component. Cheers Mariano On Thu, Jun 18, 2009 at 11:30 AM, James Foster <[hidden email]> wrote: Mariano, _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Mariano Montone
Hi Mariano, There are people who like implementing everything as brushes but the main functionality of brushes is that they can be selected in arbitrary orders to nest content within each other, e.g.:
html div: [ html span: [ html paragraph: 'foo' ] ]. Unless you plan to do be able to do: html div: [ html googleMap: [ html paragraph: 'foo' ] ] (i.e. unless the thing you are creating allows content to be put inside it) I don't think there's much advantage in making your own brush. (The other reason to consider using brushes of course is that they have more direct access to the document).
Even if you don't need the benefits of components (see http://blog.fitzell.ca/2009/05/when-to-use-seaside-component.html ), you can just create a renderable object by implementing #renderOn: and do:
html render: (GoogleMaps new configSomeStuff; yourself) This process is made much clearer in 2.9 where you can subclass WAPainter, implement #rendererClass to control what kind of renderer you get passed (you might possibly implement the google maps thing *using* one or more custom brushes and have your own renderer for them), and implement #renderContentOn: as you would for a component.
Hopefully that makes things clearer and not muddier. :) Julian On Thu, Jun 18, 2009 at 5:51 AM, Mariano Montone <[hidden email]> wrote: Hello!, _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Mariano Montone
If you trace the package history you can see that I tried a variety of approaches, including using a component. I settled on making GoogleMap a brush (subclass of WADivTag). See GoogleMaps-jgf.21.mcz for an example of calling back to Smalltalk.
James On Jun 18, 2009, at 7:47 AM, Mariano Montone wrote: Thanks James! _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Julian Fitzell-2
Thanks Julian. I think a component will be ok.
Mariano On Thu, Jun 18, 2009 at 1:22 PM, Julian Fitzell <[hidden email]> wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Mariano,
I'll be interested to see how this comes out. As I mentioned earlier, I started with a component and switched to a brush. I came to view the GoogleMap as a browser widget, something like a listbox, where you give it some data and let it draw itself. Yes, you can configure callbacks, but that isn't really different from other brushes. What sort of 'state' do you envision keeping with the map? Might that be better in a domain-specific component that wraps a map? I was able to implement over 40 examples and have not yet found a need to get more complex. James On Jun 18, 2009, at 2:58 PM, Mariano Montone wrote: Thanks Julian. I think a component will be ok. _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
James, I have no memory of whether there is anything that requires a component to keep state but it doesn't *seem* at first glance to me like something that needs to be a brush either. I don't mean to suggest you're wrong since you've obviously gone through the exercise and I haven't but there's quite a bit of debate now and then over when to use brushes. I'm just wondering whether you considered a renderable object (a Painter in 2.9) as an option or just component/brush. And if you ruled out the third option, is there a particular reason you think a brush is more appropriate?
Julian On Thu, Jun 18, 2009 at 4:00 PM, James Foster <[hidden email]> wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Julian,
I did the GoogleMaps stuff several months ago and was only comparing component and brush; I didn't consider a Painter mostly from lack of knowledge (and being on a 2.8 environment). I wasn't aware that there was a debate about when to use brushes; I thought it was just me who couldn't figure it out! I ended up with a Brush because it felt more like I was just defining a special <div> and I didn't want to require people to define a child component. I figured that until I needed something more complicated I'd stay with the simplest thing that could possibly work (tm), and the brush approach came together nicely. Once I started thinking about GoogleMaps as being little more than a fancy div/listbox/image, several things fell out quite cleanly. I discovered that I don't want to keep any state and that I want to treat the configuration as one does with other things. For example (where all of the messages to the brush are optional, but #setCenter:zoom: is most useful): MyComponent>>#renderContentOn: html html googleMap class: 'myMap'; setCenter: 45.5267 @ -122.8390 zoom: 11; enableGoogleBar; addType: GMapType physical; addControl: GControl largeMapControl; setUIToDefault; on: 'zoomEnd' do: [:x :y :z | 'alert("Zoom from ' , x printString , ' to ' , y printString , ' (see GMUsingLatLong>>renderContentOn:)");']; yourself. The only other requirement is that the component needs to allow some #updateRoot: behavior: MyComponent>>#updateRoot: anHtmlRoot super updateRoot: anHtmlRoot. self updateRootWithGoogleMaps: anHtmlRoot. The implementation involves creating various scripts and feeding them out, but it works fine in the brush. GoogleMap>>#with: anObject self ensureId. super with: [ anObject renderOn: canvas. canvas html: self mapScript. ]. self addLoadScript: self variable , 'Init()'. Overall it seemed less intrusive for the library client to use a brush rather than a component. Other than #updateRoot:, there isn't really much to do. With a component there were so many things that were brush- like, including setting the class, id, style, etc., and more issues (does it need to be included as a child?). I was able to do so much without state, that it just seemed nice this way. It seemed like I got further than any of the other GoogleMaps packages I found, but I'd love to see another approach or get expert feedback on what I've done. In general, while I'm heavily involved in Seaside from the GemStone point-of-view, there is still a great deal for me to learn about when to use different parts of the framework. This exercise was another opportunity to learn and I did learn something! James On Jun 18, 2009, at 5:15 PM, Julian Fitzell wrote: > James, > > It's been a while since I looked at the google maps stuff so I don't > recall... what do you actually render to the page? If memory serves, > you only need to output stuff that can be generated by the standard > brushes (divs and JS or something, right?) and you don't need to put > content inside it. > > I have no memory of whether there is anything that requires a > component to keep state but it doesn't *seem* at first glance to me > like something that needs to be a brush either. I don't mean to > suggest you're wrong since you've obviously gone through the > exercise and I haven't but there's quite a bit of debate now and > then over when to use brushes. I'm just wondering whether you > considered a renderable object (a Painter in 2.9) as an option or > just component/brush. And if you ruled out the third option, is > there a particular reason you think a brush is more appropriate? > > Julian > > On Thu, Jun 18, 2009 at 4:00 PM, James Foster > <[hidden email]> wrote: > Mariano, > > I'll be interested to see how this comes out. As I mentioned > earlier, I started with a component and switched to a brush. I came > to view the GoogleMap as a browser widget, something like a listbox, > where you give it some data and let it draw itself. Yes, you can > configure callbacks, but that isn't really different from other > brushes. What sort of 'state' do you envision keeping with the map? > Might that be better in a domain-specific component that wraps a > map? I was able to implement over 40 examples and have not yet found > a need to get more complex. > > James > > On Jun 18, 2009, at 2:58 PM, Mariano Montone wrote: > >> Thanks Julian. I think a component will be ok. >> >> Mariano >> >> On Thu, Jun 18, 2009 at 1:22 PM, Julian Fitzell >> <[hidden email]> wrote: >> Hi Mariano, >> >> Off the top of my head, if I were implementing a google maps >> package, I would do it as a component or a painter (see below). >> Brushes certainly aren't intended to be kept around so if you have >> state to persist between requests that's not the way to go. >> >> There are people who like implementing everything as brushes but >> the main functionality of brushes is that they can be selected in >> arbitrary orders to nest content within each other, e.g.: >> >> html div: [ html span: [ html paragraph: 'foo' ] ]. >> >> Unless you plan to do be able to do: >> >> html div: [ html googleMap: [ html paragraph: 'foo' ] ] >> >> (i.e. unless the thing you are creating allows content to be put >> inside it) I don't think there's much advantage in making your own >> brush. (The other reason to consider using brushes of course is >> that they have more direct access to the document). >> >> Even if you don't need the benefits of components (see http://blog.fitzell.ca/2009/05/when-to-use-seaside-component.html >> ), you can just create a renderable object by implementing >> #renderOn: and do: >> >> html render: (GoogleMaps new configSomeStuff; yourself) >> >> This process is made much clearer in 2.9 where you can subclass >> WAPainter, implement #rendererClass to control what kind of >> renderer you get passed (you might possibly implement the google >> maps thing *using* one or more custom brushes and have your own >> renderer for them), and implement #renderContentOn: as you would >> for a component. >> >> Hopefully that makes things clearer and not muddier. :) >> >> Julian >> >> On Thu, Jun 18, 2009 at 5:51 AM, Mariano Montone <[hidden email] >> > wrote: >> Hello!, >> I'm implementing an API for rendering Google Maps. I've >> decided to implement it as a brush. That's because I'm just >> generating javascript code. But now I have a problem: when adding >> support for callbacks, I need to hold some state; for example, the >> map the callback refers to. But I think brushes are not meant to >> hold state, that is something left for the components mechanism, >> isn't it? So I would like to know what would be the correct way of >> implementing it in the framework. Should I implement maps as >> components, or should I add state to my brushes; I may hold a state >> in the callback block too, but I don't think that's good. >> >> Thanks! >> >> Mariano >> >> >> >> _______________________________________________ >> seaside mailing list >> [hidden email] >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >> >> >> >> _______________________________________________ >> seaside mailing list >> [hidden email] >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >> >> >> _______________________________________________ >> seaside mailing list >> [hidden email] >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside > > > _______________________________________________ > seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside > > > _______________________________________________ > seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by jgfoster
James,
Our intention is to use a map in a mobile application. That is to say, both the smalltalk server and a browser are embedded in the same device. In that context, having some markers and moving them and receiving an update to the server whenever the marker moves, makes sense. So, the state is the map I'm referring to (there may be more than one map in the same page) and the markers of each map. This is what I have now, for example: renderContentOn: html |marker| self map center: 45.5267 @ -122.8390 zoom: 11; setUIToDefault. marker := (GMarker overlayAt: 45.5267 @ -122.8390) draggable: true. self map addOverlay: marker. marker onDrag: ( html gmrequest callback: [:latlng | self lat: latlng lat lng: latlng lng ]). That places a marker and callbacks whenever the user moves the marker across the map. I'm using callbacks, but that has some limitations if I want to hold the markers, so I'll be moving the callbacks and markers to a component. This would not make sense in a traditional client/server application, that's why you haven't found a need for something like this, I think. Mariano On Thu, Jun 18, 2009 at 8:00 PM, James Foster <[hidden email]> wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by jgfoster
So, with the need to update the root, I think this would work equally well as a simple renderable object in 2.8 but it wouldn't really be be much better than a component or brush.
With a 2.9 Painter, you can implement #updateRoot: on the Painter subclass itself so the map object would be able to encapsulate all of that. This does require, as you point out, that the Painter be added to #children, of course, but I think that's better than having to implement your own root updating behaviour and not unexpected for the user.
Your renderContentOn: would look basically the same as your version with either a painter or a renderable object: MyComponent>>#renderContentOn: html html render: (GoogleMap new class: 'myMap'; setCenter: 45.5267 @ -122.8390 zoom: 11; "..." yourself) So I think the result is slightly better with a Painter in the sense of implementing #children, rather then update root behaviour.
The other advantage is more theoretical and that is that a painter is not dependent on the encapsulating component using Canvas as its renderer. I say this is pretty theoretical because the only other renderer right now is the RSS one. :) From an architectural point of view, though, components are able to use any renderer they want and if they're using one that doesn't support your brush, well they couldn't easily use your implementation.
This is all pretty subtle. Thus the debate. :) Julian On Thu, Jun 18, 2009 at 5:49 PM, James Foster <[hidden email]> wrote: Julian, _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Thanks all for the interesting experience feedback :)
I think if implemented as a painter, this thread could br turned as a nice example/tutorial on how to use painter (instead of brush or compoenent). Just a question. If you want to embed a map with live points, overlay, etc... say you want to dynamically follow a path... Would you use state to keep this information (and therefore component) or would you build a component that embed the painter ? Thanks 2009/6/19 Julian Fitzell <[hidden email]>: > So, with the need to update the root, I think this would work equally well > as a simple renderable object in 2.8 but it wouldn't really be be much > better than a component or brush. > With a 2.9 Painter, you can implement #updateRoot: on the > Painter subclass itself so the map object would be able to encapsulate all of that. This does require, as you point out, that the Painter be added to #children, of course, but I think that's better than having to implement your own root updating behaviour and not unexpected for the user. > Your renderContentOn: would look basically the same as your version with > either a painter or a renderable object: > MyComponent>>#renderContentOn: html > html render: (GoogleMap new > class: 'myMap'; > setCenter: 45.5267 @ -122.8390 zoom: 11; > "..." > yourself) > > So I think the result is slightly better with a Painter in the sense of > implementing #children, rather then update root behaviour. > The other advantage is more theoretical and that is that a painter is not > dependent on the encapsulating component using Canvas as its renderer. I say > this is pretty theoretical because the only other renderer right now is the > RSS one. :) From an architectural point of view, though, components are able > to use any renderer they want and if they're using one that doesn't support > your brush, well they couldn't easily use your implementation. > This is all pretty subtle. Thus the debate. :) > Julian > On Thu, Jun 18, 2009 at 5:49 PM, James Foster <[hidden email]> > wrote: >> >> Julian, >> >> I did the GoogleMaps stuff several months ago and was only comparing >> component and brush; I didn't consider a Painter mostly from lack of >> knowledge (and being on a 2.8 environment). I wasn't aware that there was a >> debate about when to use brushes; I thought it was just me who couldn't >> figure it out! >> >> I ended up with a Brush because it felt more like I was just defining a >> special <div> and I didn't want to require people to define a child >> component. I figured that until I needed something more complicated I'd stay >> with the simplest thing that could possibly work (tm), and the brush >> approach came together nicely. Once I started thinking about GoogleMaps as >> being little more than a fancy div/listbox/image, several things fell out >> quite cleanly. I discovered that I don't want to keep any state and that I >> want to treat the configuration as one does with other things. For example >> (where all of the messages to the brush are optional, but #setCenter:zoom: >> is most useful): >> >> MyComponent>>#renderContentOn: html >> >> html googleMap >> class: 'myMap'; >> setCenter: 45.5267 @ -122.8390 zoom: 11; >> enableGoogleBar; >> addType: GMapType physical; >> addControl: GControl largeMapControl; >> setUIToDefault; >> on: 'zoomEnd' do: [:x :y :z | >> 'alert("Zoom from ' , x printString , ' to ' , y >> printString , >> ' (see GMUsingLatLong>>renderContentOn:)");']; >> yourself. >> >> The only other requirement is that the component needs to allow some >> #updateRoot: behavior: >> >> MyComponent>>#updateRoot: anHtmlRoot >> >> super updateRoot: anHtmlRoot. >> self updateRootWithGoogleMaps: anHtmlRoot. >> >> The implementation involves creating various scripts and feeding them out, >> but it works fine in the brush. >> >> GoogleMap>>#with: anObject >> >> self ensureId. >> super with: [ >> anObject renderOn: canvas. >> canvas html: self mapScript. >> ]. >> self addLoadScript: self variable , 'Init()'. >> >> Overall it seemed less intrusive for the library client to use a brush >> rather than a component. Other than #updateRoot:, there isn't really much to >> do. With a component there were so many things that were brush-like, >> including setting the class, id, style, etc., and more issues (does it need >> to be included as a child?). I was able to do so much without state, that it >> just seemed nice this way. It seemed like I got further than any of the >> other GoogleMaps packages I found, but I'd love to see another approach or >> get expert feedback on what I've done. >> >> In general, while I'm heavily involved in Seaside from the GemStone >> point-of-view, there is still a great deal for me to learn about when to use >> different parts of the framework. This exercise was another opportunity to >> learn and I did learn something! >> >> James >> >> On Jun 18, 2009, at 5:15 PM, Julian Fitzell wrote: >> >>> James, >>> >>> It's been a while since I looked at the google maps stuff so I don't >>> recall... what do you actually render to the page? If memory serves, you >>> only need to output stuff that can be generated by the standard brushes >>> (divs and JS or something, right?) and you don't need to put content inside >>> it. >>> >>> I have no memory of whether there is anything that requires a component >>> to keep state but it doesn't *seem* at first glance to me like something >>> that needs to be a brush either. I don't mean to suggest you're wrong since >>> you've obviously gone through the exercise and I haven't but there's quite a >>> bit of debate now and then over when to use brushes. I'm just wondering >>> whether you considered a renderable object (a Painter in 2.9) as an option >>> or just component/brush. And if you ruled out the third option, is there a >>> particular reason you think a brush is more appropriate? >>> >>> Julian >>> >>> On Thu, Jun 18, 2009 at 4:00 PM, James Foster <[hidden email]> >>> wrote: >>> Mariano, >>> >>> I'll be interested to see how this comes out. As I mentioned earlier, I >>> started with a component and switched to a brush. I came to view the >>> GoogleMap as a browser widget, something like a listbox, where you give it >>> some data and let it draw itself. Yes, you can configure callbacks, but that >>> isn't really different from other brushes. What sort of 'state' do you >>> envision keeping with the map? Might that be better in a domain-specific >>> component that wraps a map? I was able to implement over 40 examples and >>> have not yet found a need to get more complex. >>> >>> James >>> >>> On Jun 18, 2009, at 2:58 PM, Mariano Montone wrote: >>> >>>> Thanks Julian. I think a component will be ok. >>>> >>>> Mariano >>>> >>>> On Thu, Jun 18, 2009 at 1:22 PM, Julian Fitzell <[hidden email]> >>>> wrote: >>>> Hi Mariano, >>>> >>>> Off the top of my head, if I were implementing a google maps package, I >>>> would do it as a component or a painter (see below). Brushes certainly >>>> aren't intended to be kept around so if you have state to persist between >>>> requests that's not the way to go. >>>> >>>> There are people who like implementing everything as brushes but the >>>> main functionality of brushes is that they can be selected in arbitrary >>>> orders to nest content within each other, e.g.: >>>> >>>> html div: [ html span: [ html paragraph: 'foo' ] ]. >>>> >>>> Unless you plan to do be able to do: >>>> >>>> html div: [ html googleMap: [ html paragraph: 'foo' ] ] >>>> >>>> (i.e. unless the thing you are creating allows content to be put inside >>>> it) I don't think there's much advantage in making your own brush. (The >>>> other reason to consider using brushes of course is that they have more >>>> direct access to the document). >>>> >>>> Even if you don't need the benefits of components (see >>>> http://blog.fitzell.ca/2009/05/when-to-use-seaside-component.html ), you can >>>> just create a renderable object by implementing #renderOn: and do: >>>> >>>> html render: (GoogleMaps new configSomeStuff; yourself) >>>> >>>> This process is made much clearer in 2.9 where you can subclass >>>> WAPainter, implement #rendererClass to control what kind of renderer you get >>>> passed (you might possibly implement the google maps thing *using* one or >>>> more custom brushes and have your own renderer for them), and implement >>>> #renderContentOn: as you would for a component. >>>> >>>> Hopefully that makes things clearer and not muddier. :) >>>> >>>> Julian >>>> >>>> On Thu, Jun 18, 2009 at 5:51 AM, Mariano Montone >>>> <[hidden email]> wrote: >>>> Hello!, >>>> I'm implementing an API for rendering Google Maps. I've >>>> decided to implement it as a brush. That's because I'm just generating >>>> javascript code. But now I have a problem: when adding support for >>>> callbacks, I need to hold some state; for example, the map the callback >>>> refers to. But I think brushes are not meant to hold state, that is >>>> something left for the components mechanism, isn't it? So I would like to >>>> know what would be the correct way of implementing it in the framework. >>>> Should I implement maps as components, or should I add state to my brushes; >>>> I may hold a state in the callback block too, but I don't think that's good. >>>> >>>> Thanks! >>>> >>>> Mariano >>>> >>>> >>>> >>>> _______________________________________________ >>>> seaside mailing list >>>> [hidden email] >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>> >>>> >>>> >>>> _______________________________________________ >>>> seaside mailing list >>>> [hidden email] >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>> >>>> >>>> _______________________________________________ >>>> seaside mailing list >>>> [hidden email] >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>> >>> >>> _______________________________________________ >>> seaside mailing list >>> [hidden email] >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>> >>> >>> _______________________________________________ >>> seaside mailing list >>> [hidden email] >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >> >> _______________________________________________ >> seaside mailing list >> [hidden email] >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside > > > _______________________________________________ > seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside > > -- Cédrick _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Julian Fitzell-2
Julian,
Just to clarify your example a bit, let me know if I'm on the right track here... With a 2.9 Painter if you need to implement #updateRoot: then you need to be in the parent's #children. To be in the parent's children, you probably need to be in an instance variable and you probably need to be set up in the parent's #initialize method. Thus, your example below of creating the map during #renderContentOn: would not work. To use a Painter or Component: (1) add an instance variable to the parent for each map; (2) initialize each instance variable in #initialize; (3) add each instance variable to #children; and (4) use the map in #renderContentOn:. To use a Brush: (1) add one message send to your #updateRoot: for any number of maps; and (2) add any number of maps in #renderContentOn:. Given that the map itself does not need any state, creating it as a component (I'm not familiar with the painter implications) seems to encourage reuse by inheritance rather than by delegation. I mention these issues to describe my understanding and invite help, not to argue. I agree that the distinctions are a bit subtle and I don't need there to be a clear "right" answer. James On Jun 18, 2009, at 6:13 PM, Julian Fitzell wrote: So, with the need to update the root, I think this would work equally well as a simple renderable object in 2.8 but it wouldn't really be be much better than a component or brush. _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by cedreek
On Jun 19, 2009, at 1:54 AM, Cédrick Béler wrote:
> Thanks all for the interesting experience feedback :) > I think if implemented as a painter, this thread could br turned as a > nice example/tutorial on how to use painter (instead of brush or > compoenent). > > Just a question. If you want to embed a map with live points, overlay, > etc... say you want to dynamically follow a path... Would you use > state to keep this information (and therefore component) or would you > build a component that embed the painter ? > > Thanks My preference (from where I am right now; as you can see I'm learning more every day) would be treat the map as a brush and have it inform your component of map events and keep the state in your component or (even better) in your domain objects. In general I'd try to use delegation rather than inheritance. James > > 2009/6/19 Julian Fitzell <[hidden email]>: >> So, with the need to update the root, I think this would work >> equally well >> as a simple renderable object in 2.8 but it wouldn't really be be >> much >> better than a component or brush. >> With a 2.9 Painter, you can implement #updateRoot: on the >> Painter subclass itself so the map object would be able to >> encapsulate all of that. This does require, as you point out, that >> the Painter be added to #children, of course, but I think that's >> better than having to implement your own root updating behaviour >> and not unexpected for the user. >> Your renderContentOn: would look basically the same as your version >> with >> either a painter or a renderable object: >> MyComponent>>#renderContentOn: html >> html render: (GoogleMap new >> class: 'myMap'; >> setCenter: 45.5267 @ -122.8390 zoom: 11; >> "..." >> yourself) >> >> So I think the result is slightly better with a Painter in the >> sense of >> implementing #children, rather then update root behaviour. >> The other advantage is more theoretical and that is that a painter >> is not >> dependent on the encapsulating component using Canvas as its >> renderer. I say >> this is pretty theoretical because the only other renderer right >> now is the >> RSS one. :) From an architectural point of view, though, components >> are able >> to use any renderer they want and if they're using one that doesn't >> support >> your brush, well they couldn't easily use your implementation. >> This is all pretty subtle. Thus the debate. :) >> Julian >> On Thu, Jun 18, 2009 at 5:49 PM, James Foster >> <[hidden email]> >> wrote: >>> >>> Julian, >>> >>> I did the GoogleMaps stuff several months ago and was only comparing >>> component and brush; I didn't consider a Painter mostly from lack of >>> knowledge (and being on a 2.8 environment). I wasn't aware that >>> there was a >>> debate about when to use brushes; I thought it was just me who >>> couldn't >>> figure it out! >>> >>> I ended up with a Brush because it felt more like I was just >>> defining a >>> special <div> and I didn't want to require people to define a child >>> component. I figured that until I needed something more >>> complicated I'd stay >>> with the simplest thing that could possibly work (tm), and the brush >>> approach came together nicely. Once I started thinking about >>> GoogleMaps as >>> being little more than a fancy div/listbox/image, several things >>> fell out >>> quite cleanly. I discovered that I don't want to keep any state >>> and that I >>> want to treat the configuration as one does with other things. For >>> example >>> (where all of the messages to the brush are optional, but >>> #setCenter:zoom: >>> is most useful): >>> >>> MyComponent>>#renderContentOn: html >>> >>> html googleMap >>> class: 'myMap'; >>> setCenter: 45.5267 @ -122.8390 zoom: 11; >>> enableGoogleBar; >>> addType: GMapType physical; >>> addControl: GControl largeMapControl; >>> setUIToDefault; >>> on: 'zoomEnd' do: [:x :y :z | >>> 'alert("Zoom from ' , x printString , ' to >>> ' , y >>> printString , >>> ' (see >>> GMUsingLatLong>>renderContentOn:)");']; >>> yourself. >>> >>> The only other requirement is that the component needs to allow some >>> #updateRoot: behavior: >>> >>> MyComponent>>#updateRoot: anHtmlRoot >>> >>> super updateRoot: anHtmlRoot. >>> self updateRootWithGoogleMaps: anHtmlRoot. >>> >>> The implementation involves creating various scripts and feeding >>> them out, >>> but it works fine in the brush. >>> >>> GoogleMap>>#with: anObject >>> >>> self ensureId. >>> super with: [ >>> anObject renderOn: canvas. >>> canvas html: self mapScript. >>> ]. >>> self addLoadScript: self variable , 'Init()'. >>> >>> Overall it seemed less intrusive for the library client to use a >>> brush >>> rather than a component. Other than #updateRoot:, there isn't >>> really much to >>> do. With a component there were so many things that were brush-like, >>> including setting the class, id, style, etc., and more issues >>> (does it need >>> to be included as a child?). I was able to do so much without >>> state, that it >>> just seemed nice this way. It seemed like I got further than any >>> of the >>> other GoogleMaps packages I found, but I'd love to see another >>> approach or >>> get expert feedback on what I've done. >>> >>> In general, while I'm heavily involved in Seaside from the GemStone >>> point-of-view, there is still a great deal for me to learn about >>> when to use >>> different parts of the framework. This exercise was another >>> opportunity to >>> learn and I did learn something! >>> >>> James >>> >>> On Jun 18, 2009, at 5:15 PM, Julian Fitzell wrote: >>> >>>> James, >>>> >>>> It's been a while since I looked at the google maps stuff so I >>>> don't >>>> recall... what do you actually render to the page? If memory >>>> serves, you >>>> only need to output stuff that can be generated by the standard >>>> brushes >>>> (divs and JS or something, right?) and you don't need to put >>>> content inside >>>> it. >>>> >>>> I have no memory of whether there is anything that requires a >>>> component >>>> to keep state but it doesn't *seem* at first glance to me like >>>> something >>>> that needs to be a brush either. I don't mean to suggest you're >>>> wrong since >>>> you've obviously gone through the exercise and I haven't but >>>> there's quite a >>>> bit of debate now and then over when to use brushes. I'm just >>>> wondering >>>> whether you considered a renderable object (a Painter in 2.9) as >>>> an option >>>> or just component/brush. And if you ruled out the third option, >>>> is there a >>>> particular reason you think a brush is more appropriate? >>>> >>>> Julian >>>> >>>> On Thu, Jun 18, 2009 at 4:00 PM, James Foster <[hidden email] >>>> > >>>> wrote: >>>> Mariano, >>>> >>>> I'll be interested to see how this comes out. As I mentioned >>>> earlier, I >>>> started with a component and switched to a brush. I came to view >>>> the >>>> GoogleMap as a browser widget, something like a listbox, where >>>> you give it >>>> some data and let it draw itself. Yes, you can configure >>>> callbacks, but that >>>> isn't really different from other brushes. What sort of 'state' >>>> do you >>>> envision keeping with the map? Might that be better in a domain- >>>> specific >>>> component that wraps a map? I was able to implement over 40 >>>> examples and >>>> have not yet found a need to get more complex. >>>> >>>> James >>>> >>>> On Jun 18, 2009, at 2:58 PM, Mariano Montone wrote: >>>> >>>>> Thanks Julian. I think a component will be ok. >>>>> >>>>> Mariano >>>>> >>>>> On Thu, Jun 18, 2009 at 1:22 PM, Julian Fitzell <[hidden email] >>>>> > >>>>> wrote: >>>>> Hi Mariano, >>>>> >>>>> Off the top of my head, if I were implementing a google maps >>>>> package, I >>>>> would do it as a component or a painter (see below). Brushes >>>>> certainly >>>>> aren't intended to be kept around so if you have state to >>>>> persist between >>>>> requests that's not the way to go. >>>>> >>>>> There are people who like implementing everything as brushes but >>>>> the >>>>> main functionality of brushes is that they can be selected in >>>>> arbitrary >>>>> orders to nest content within each other, e.g.: >>>>> >>>>> html div: [ html span: [ html paragraph: 'foo' ] ]. >>>>> >>>>> Unless you plan to do be able to do: >>>>> >>>>> html div: [ html googleMap: [ html paragraph: 'foo' ] ] >>>>> >>>>> (i.e. unless the thing you are creating allows content to be put >>>>> inside >>>>> it) I don't think there's much advantage in making your own >>>>> brush. (The >>>>> other reason to consider using brushes of course is that they >>>>> have more >>>>> direct access to the document). >>>>> >>>>> Even if you don't need the benefits of components (see >>>>> http://blog.fitzell.ca/2009/05/when-to-use-seaside- >>>>> component.html ), you can >>>>> just create a renderable object by implementing #renderOn: and do: >>>>> >>>>> html render: (GoogleMaps new configSomeStuff; yourself) >>>>> >>>>> This process is made much clearer in 2.9 where you can subclass >>>>> WAPainter, implement #rendererClass to control what kind of >>>>> renderer you get >>>>> passed (you might possibly implement the google maps thing >>>>> *using* one or >>>>> more custom brushes and have your own renderer for them), and >>>>> implement >>>>> #renderContentOn: as you would for a component. >>>>> >>>>> Hopefully that makes things clearer and not muddier. :) >>>>> >>>>> Julian >>>>> >>>>> On Thu, Jun 18, 2009 at 5:51 AM, Mariano Montone >>>>> <[hidden email]> wrote: >>>>> Hello!, >>>>> I'm implementing an API for rendering Google Maps. I've >>>>> decided to implement it as a brush. That's because I'm just >>>>> generating >>>>> javascript code. But now I have a problem: when adding support for >>>>> callbacks, I need to hold some state; for example, the map the >>>>> callback >>>>> refers to. But I think brushes are not meant to hold state, that >>>>> is >>>>> something left for the components mechanism, isn't it? So I >>>>> would like to >>>>> know what would be the correct way of implementing it in the >>>>> framework. >>>>> Should I implement maps as components, or should I add state to >>>>> my brushes; >>>>> I may hold a state in the callback block too, but I don't think >>>>> that's good. >>>>> >>>>> Thanks! >>>>> >>>>> Mariano >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> seaside mailing list >>>>> [hidden email] >>>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>>> >>>>> >>>>> >>>>> _______________________________________________ >>>>> seaside mailing list >>>>> [hidden email] >>>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>>> >>>>> >>>>> _______________________________________________ >>>>> seaside mailing list >>>>> [hidden email] >>>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>> >>>> >>>> _______________________________________________ >>>> seaside mailing list >>>> [hidden email] >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>>> >>>> >>>> _______________________________________________ >>>> seaside mailing list >>>> [hidden email] >>>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >>> >>> _______________________________________________ >>> seaside mailing list >>> [hidden email] >>> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >> >> >> _______________________________________________ >> seaside mailing list >> [hidden email] >> http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside >> >> > > > > -- > Cédrick > _______________________________________________ > seaside mailing list > [hidden email] > http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside > _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by jgfoster
Hm... yes, you're right it would need to be in an instance variable... I had forgotten about that.
I may have to go back and look at that... I intended Painter to be useable as a throw-away object. Along the way, updateRoot: got pulled up because it didn't have any dependencies on anything further down the class hierarchy but updateRoot: just can't work unless it gets called and currently that is via the #children mechanism unless you want to call it yourself.
So I'm not sure whether it's better to push #updateRoot: back down to WAPresenter or leave it on WAPainter with the caveat that if the painter uses updateRoot: it needs to be listed in #children. The latter seems to add a confusing ambiguity. Could also split WAPainter into two classes but that's starting to get a little excessive, I think. Back to the architecture design board for a bit... (opinions appreciated) Forgetting updateRoot: then for a second, though, you could still implement simply with a Painter or renderable object by having a class-side method that does the root modification.
MyComponent>>updateRoot: aRoot super updateRoot: aRoot. GoogleMaps updateRoot: aRoot.
MyComponent>>#renderContentOn: html html render: (GoogleMap new class: 'myMap';
setCenter: 45.5267 @ -122.8390 zoom: 11; "..." yourself) With that pattern, I think any object that implements #renderOn: now works essentially the same as a brush? Assuming the root updating is not component-specific (you don't show what's in it but I gather it's a class extension on Component added by your package?). WAPainter adds the benefit that the component doesn't have to be using the same Renderer that GoogleMaps expects (which the other two options depend on).
Julian On Fri, Jun 19, 2009 at 7:11 AM, James Foster <[hidden email]> wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Julian,
As you were going through this my thought was eventually expressed when you said that the painter approach "works essentially the same as the brush" approach. And Yes, my updateRoot: code was simply an extension to WAComponent that adds some JavaScript. Switching it to a class-side method on GoogleMaps as you describe would be equivalent and perhaps more obvious and less intrusive. The part I'm not yet following or seeing much value in is the idea that using a different renderer would be desirable. I don't see much point of rendering a GoogleMap on anything other than an HTML canvas. Or are you envisioning two renderers (HTML for the GoogleMap thing and something else for the rest)? At the moment, my understanding of the alternate renderers is so shallow that I don't see the use/value. For my purposes, at least, I'll probably leave my implementation as a Brush subclass since it seems to work for all use cases I understand. Which is not to say that I mind the discussion! James On Jun 19, 2009, at 8:44 AM, Julian Fitzell wrote: Hm... yes, you're right it would need to be in an instance variable... I had forgotten about that. _______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by Mariano Montone
Mario,
Comments below... On Jun 18, 2009, at 5:57 PM, Mariano Montone wrote: James,
If you wanted to capture the current selection from a drop-down list, would you create a component to wrap the drop-down list or would you have the drop-down list notify the component of its current value. I don't see any substantive distinction between a GoogleMap and a drop-down list. They each have state and can notify the server when that state changes. Yes, the map has more states (multiple markers) and the states are more complex (two floating-point numbers for the location of a marker instead of an integer for the current selection). But, fundamentally, the concept is the same. Something changed in the browser and we want to record that in the server. I could easily imagine that you would create a reusable component that includes a map, but the map itself does not need to be a component any more than a drop-down list needs to be a component.
James
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
In reply to this post by jgfoster
On Fri, Jun 19, 2009 at 10:50 AM, James Foster <[hidden email]> wrote:
James, The discussion is clarifying for me as well so let's carry on... :) First, I do think your design would be improved slightly by putting that method on the class side of GoogleMap.
The idea of having different Renderers is that, for example, somebody might eventually decide they'd really like to have templates in Seaside and that would probably be implemented as a different Renderer. Or maybe they want to embed a google map in a Component that is using the RSS Renderer (actually, that Renderer still uses Brushes, so it may not be the best example). As I said, this is relatively theoretical at the moment given the lack of other Renderers but I'm interested in it from the architectural angle.
I believe (and this is where I may be wrong) that an implementation using Brushes is (slightly) less flexible and, while there are things that would be easier using Brushes, I don't see that this problem does any of those things.
Or to look at it another way, just as you say that GoogleMap doesn't need to *be* a Component but can be rather *used by* a Component, I don't think it needs to *be* a Brush but can instead *use* a Brush; there's nothing you need to output to the Document that can't easily be done with the existing Brushes.
So you might end up with MyComponent (uses) any Renderer (to render a) GoogleMap (a Painter) (which uses its)
HtmlCanvas (a Renderer) (to get a) DivTag (a Brush) (which writes to) HtmlDocument (a Document) (which writes to) Stream When I say it "works essentially the same as the brush", what I mean is, the Painter implementation is equally convenient to use and no harder to implement but provides added flexibility. It also requires no class method extensions and means you can leverage WAScriptTag instead of duplicating stuff to make sure the CDATA is added and so on. Since I can't (yet) see an advantage to the Brush implementation, my suggestion is that the Painter implementation is "more correct" architecturally.
I still think "uses a Brush" (actually two) is a clearer relationship for this than "is a Brush" even in 2.8, but without Painter there's really no added benefit (though you could easily duplicate the very limited amount of functionality Painter has in its #renderOn: method and you'd be in the same shape as 2.9).
So the architectural question for me comes down to (sorry for the long message), is there an additional benefit to doing it as a Brush over a Painter that I'm missing? Julian
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Julian,
I think the only think I'm equipped to give even a weak opinion on is that as I was doing this some months ago I found a Brush to be more natural to use (as a client) than a Canvas. As to the relative merits of Painter and Renderer, I'm just not sufficiently informed to venture an educated opinion (though that isn't usually an impediment ;-). I like your point that a Brush can be embedded in another Brush and this is a feature that the GoogleMap does not need. Given that sending #with: to a GoogleMap does not make sense, I'll agree that making it a Brush is a bit awkward. Also, there is a sense in which subclassing from WADivTag comes back to the question of delegation vs. inheritance, and in principal I'd prefer delegation. In an effort to contribute more intelligently to this discussion, I'd like to try refactoring my library using a Painter. I'll fire up a Seaside 2.9 environment and look at WAPainter with an eye toward what would be required to refactor my GoogleMap in a Seaside 2.8 environment. James On Jun 19, 2009, at 12:20 PM, Julian Fitzell wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
That should provide a good comparison.
Looking at the 2.9 code now I realize it may be hard for you to simply lift the code because of the refactoring in the way walking the component tree happens.
As a starting point for 2.8, try making GoogleMap subclass Object and implement something like: renderOn: aRenderer |html| html := WAHtmlCanvas
context: aRenderer context callbacks: (aRenderer context callbacksFor: self). self renderContentOn: html. I think you should then be able to implement #renderContentOn: to output your content and allow a GoogleMap to be passed to the #render: method.
That code is untested, just typed into gmail, but it should be something like that. :) Julian On Fri, Jun 19, 2009 at 4:43 PM, James Foster <[hidden email]> wrote:
_______________________________________________ seaside mailing list [hidden email] http://lists.squeakfoundation.org/cgi-bin/mailman/listinfo/seaside |
Free forum by Nabble | Edit this page |