RE: Fluent API and StructureBuilder
Hi all, Based on Lars's StructureBuilder and Yuri's fluent Configurator, I've started thinking about a solution that would take the best of both approaches. The goal is to provide an optional package in NRE ("com.noelios.restlet.ext.builder") implementing the fluent design pattern. I also want to take advantage of Java's strong typing and IDEs code completion as much as possible. This will imply having a tree of specialized builders. As a result, the code of tutorial 11 will look like this :-) // Build and start the container new RestletContainerBuilder("My container") .addServer(Protocols.HTTP, "My server", 8182) .attachLog("com.noelios.restlet.example") .attachStatus(true, "[EMAIL PROTECTED]", "http://www.mysite.org";) .attachHost(8182) .attachGuard("/docs/", "com.noelios.restlet.example", true, ChallengeSchemes.HTTP_BASIC , "Restlet tutorial", true) .authorize("scott", "tiger") .attachDirectory("D:/Restlet/www/docs/api/", true, "index") .addExtension("html", MediaTypes.TEXT_HTML) .addExtension("css", MediaTypes.TEXT_CSS) .addExtension("gif", MediaTypes.IMAGE_GIF) .up() .up().toMaplet(). .attachMaplet("/users") .attachMaplet("/[a-z]+") .attachRestlet("$", userRestlet) .up().toMaplet() .attachRestlet("/orders$", ordersRestlet) .root().toComponent().start(); The attach*() methods will move the focus to the attached/created node. Other methods will keep the focus on the current node. The up() and root() will change the focus in the tree of builder nodes. The to*() methods will allow the cast the builder to the best type (maybe upMaplet() and rootComponent() are even better?). Any thought? Thanks, Jerome > -Message d'origine- > De : news [mailto:[EMAIL PROTECTED] De la part de Yuri de Wit > Envoyé : mercredi 17 mai 2006 19:50 > À : discuss@restlet.tigris.org > Objet : Re: Fluent API and StructureBuilder > > > Interesting... > > The approach I was taking was to use Java itself as the stack > by using a > "new XXX()" to create/"push" a new entry to the "stack". > .attach("/a", new Maplet() > .attach("/b", new Maplet()) > .attach("/c", > new Restlet() > ) > ) > ) > > To abstract the new Maplet() even further I was configuring > this inside > a Configurator class that had methods such as map(), resource() that > would hide the new Maplet(), new resource() instantiation, > etc cleaning up > the code a bit more: > new Configurator(container){ > public Restlet build(){ >map() > .attach("/a", > new MyChainlet() > .map() > .attach("/b", > > resource().handle("GET", redirect("/WEB-INF/...")) > .attach("/c", > redirect("/home" > } > }.build(); > > > Each ")" above would correspond to the .back(1) in Lars solution, I > guess. The generic back(?) seems more flexible and I would > interested in > knowing what kind of scenarios does it enable. > Thanks, > > -- yuri > > Lars Heuer <[EMAIL PROTECTED]> wrote: > >Hi all, > > > >[...] > >> I think this may be a better way to support your fluent > design pattern. We > >> could > >> implement more complex things like multiple extractions, > etcs. Let me think > >> more > >> about this. > >[...] > > > >To explain a bit further: The StrutureBuilder holds an internal stack > >of Restlets and pushes always the last attached Restlet to the top of > >the stack. Exceptions are: addServer, addClient, attach(String, > >Class). > >Once I am finished with one path in my tree, I can call > back() to jump > >to the parent leaf or back(int) to jump to any higher level of the > >tree. > >Once the user has made a jump back to a higher level it is not > >possible to go to a lower level (because I never needed it, but it is > >trivial to implement). > > > >I can attach the code to the issue. Tomorrow. > > > >Best regards, > >Lars > >-- > >http://semagia.com > > > > > > > > -- > > >
Re: Fluent API and StructureBuilder
Hi Jerome, [...] > The attach*() methods will move the focus to the attached/created node. > Other methods will keep the focus on the current node. > The up() and root() will change the focus in the tree of builder nodes. > The to*() methods will allow the cast the builder to the best type (maybe > upMaplet() and rootComponent() are even better?). As long as the generic attach methods are also provided I like the proposal. :) What I do not understand: How does the toMaplet() method help for code completion? If you return a specialized Maplet here (that keeps a reference to the builder) the code assistent would assume that only "attach(String, Restlet)" and "attach(String, Class)" are allowed. I the "to*" methods are not for code completion I'd leave them out. Why should the user be forced to do the cast? The builder can to the cast transparently (see StructureBuilder). I like the DWIM (Do What I Mean) approach more than the DWIS (Do What I Say) approach. :) Or do I miss something here? Best regards, Lars -- http://semagia.com
Re: Fluent API and StructureBuilder
Hi Yuri, > Interesting... > The approach I was taking was to use Java itself as the stack by using a > "new XXX()" to create/"push" a new entry to the "stack". > .attach("/a", new Maplet() > .attach("/b", new Maplet()) > .attach("/c", > new Restlet() > ) > ) > ) Yep, I thought about the same approach but it has the disadvantage that you cannot easily comment out a section and you have to keep track about the braces (or the IDE does it). I am very happy that I could do the following: builder .attach(SomeChainlet()) .attach(Someother) And if I don't need a chainlet or maplet I can easily comment it out without breaking the flow. builder // .attach(SomeChainlet()) .attach(Someother) [...] > Each ")" above would correspond to the .back(1) in Lars solution, I > guess. The generic back(?) seems more flexible and I would interested in > knowing what kind of scenarios does it enable. I use it just to save some .back().back() chains if I've to jump to a higher level. It is build in because I am lazy. ;) Best regards, Lars -- http://semagia.com
RE: Fluent API and StructureBuilder
Hi Lars, > As long as the generic attach methods are also provided I like the > proposal. :) Absolutely, the other attach*() methods are just shortcut methods. But the generic attach() methods would only be available for ChainletBuilder, MapletBuilder and RestletContainerBuilder nodes. > What I do not understand: How does the toMaplet() method help for code > completion? If you return a specialized Maplet here (that keeps a > reference to the builder) the code assistent would assume that only > "attach(String, Restlet)" and "attach(String, Class)" are allowed. The toMaplet() would return a MapletBuilder instance actually :-) therefore the code assistant will be able to propose the correct methods. > If the "to*" methods are not for code completion I'd leave them out. > Why should the user be forced to do the cast? They are for code completion indeed. I was also thinking about merging them with the up method in case a cast is needed: .up().toMaplet() => .upMaplet() or .parentMaplet() .root().toComponent().start() => .topComponent().start() Of course, the generic .up(), .up(levels) and .root() would still be available. > The builder can to the > cast transparently (see StructureBuilder). I like the DWIM (Do What I > Mean) approach more than the DWIS (Do What I Say) approach. :) I'm indeed trying to take advantage of compile-time error detection and code completion here. The only drawback is the need to specify casting when doing up() or root() calls. Regards, Jerome
Re: Fluent API and StructureBuilder
Hi Jerome, [...] >> What I do not understand: How does the toMaplet() method help for code >> completion? If you return a specialized Maplet here (that keeps a >> reference to the builder) the code assistent would assume that only >> "attach(String, Restlet)" and "attach(String, Class)" are allowed. > The toMaplet() would return a MapletBuilder instance actually :-) therefore > the code assistant will be able to propose the correct methods. Ah! :) Cool! :) [...] >> The builder can to the >> cast transparently (see StructureBuilder). I like the DWIM (Do What I >> Mean) approach more than the DWIS (Do What I Say) approach. :) > I'm indeed trying to take advantage of compile-time error detection and code > completion here. > The only drawback is the need to specify casting when doing up() or root() > calls. Fine, fine. :) It seems to be a good mix-in. Now I understand it better. The user can either use the generic attach* methods and the builder will to the cast transparently or the user can take the advantage of specialized builders and will be assisted. :) Best regards, Lars -- http://semagia.com
CompressChainlet issue
Hi Jerome, I discovered that the CompressChainlet compresses the output even if the accepted-encoding header is empty. Actually I've not tested this scenario at the beginning of this week. I am not sure who is responsible for the failure (the compress chainlet or the preferences). I'll investigate it further if I find some time. I seems that more JUnit tests would be a good idea. Are all test cases in the SVN? Or do you have more? Best regards, Lars -- http://semagia.com
RE: CompressChainlet issue
Hi Lars, According to the HTTP specs: "If no Accept-Encoding field is present in a request, the server MAY assume that the client will accept any content coding. In this case, if "identity" is one of the available content-codings, then the server SHOULD use the "identity" content-coding, unless it has additional information that a different content-coding is meaningful to the client." I read this part too quickly and decided to assume that all encodings were accepted in this case. It seems safer to only accept Identity encoding. Just change line 254 of PreferenceUtils to: preference.getEncodings().add(new EncodingPref(Encodings.IDENTITY)); Let me know if that solves the issue. Thanks, Jerome > -Message d'origine- > De : Lars Heuer [mailto:[EMAIL PROTECTED] > Envoyé : jeudi 18 mai 2006 16:29 > À : Jerome Louvel > Objet : CompressChainlet issue > > Hi Jerome, > > I discovered that the CompressChainlet compresses the output even if > the accepted-encoding header is empty. Actually I've not tested this > scenario at the beginning of this week. > I am not sure who is responsible for the failure (the compress > chainlet or the preferences). > I'll investigate it further if I find some time. > > I seems that more JUnit tests would be a good idea. Are all test cases > in the SVN? Or do you have more? > > Best regards, > Lars > -- > http://semagia.com
Re: Fluent API and StructureBuilder
Hi Lars, fair enough. The back() (or up() as proposed by Jerome) is indeed more flexible (even though I am not sure how an IDE will auto format that - minor detail) and I'll be happy to use it as soon as Jerome incorporate it instead of what I have been using. regards, -- yuri Lars Heuer <[EMAIL PROTECTED]> wrote: >Hi Yuri, > >> Interesting... > >> The approach I was taking was to use Java itself as the stack by using a >> "new XXX()" to create/"push" a new entry to the "stack". >> .attach("/a", new Maplet() >> .attach("/b", new Maplet()) >> .attach("/c", >> new Restlet() >> ) >> ) >> ) > >Yep, I thought about the same approach but it has the disadvantage >that you cannot easily comment out a section and you have to keep >track about the braces (or the IDE does it). >I am very happy that I could do the following: > > builder > .attach(SomeChainlet()) > .attach(Someother) > >And if I don't need a chainlet or maplet I can easily comment it out >without breaking the flow. > > builder >// .attach(SomeChainlet()) > .attach(Someother) > > > >[...] >> Each ")" above would correspond to the .back(1) in Lars solution, I >> guess. The generic back(?) seems more flexible and I would interested in >> knowing what kind of scenarios does it enable. > >I use it just to save some .back().back() chains if I've to jump to a >higher level. It is build in because I am lazy. ;) > >Best regards, >Lars >-- >http://semagia.com > --
Re: Fluent API and StructureBuilder
One thing that I forgot to mention is that using new XXX() as the new "stack" instead of up()/back() still allows you to comment/uncomment what you want and you get some auto indentation for free when using IDEs. :-) regards, -- yuri Yuri de Wit<[EMAIL PROTECTED]> wrote: > >Hi Lars, > >fair enough. The back() (or up() as proposed by Jerome) is indeed more >flexible (even though I am not sure how an IDE will auto format that - >minor detail) and I'll be happy to use it as soon as Jerome incorporate it >instead of what I have been using. >regards, > >-- yuri > > >Lars Heuer <[EMAIL PROTECTED]> wrote: >>Hi Yuri, >> >>> Interesting... >> >>> The approach I was taking was to use Java itself as the stack by using a >>> "new XXX()" to create/"push" a new entry to the "stack". >>> .attach("/a", new Maplet() >>> .attach("/b", new Maplet()) >>> .attach("/c", >>> new Restlet() >>> ) >>> ) >>> ) >> >>Yep, I thought about the same approach but it has the disadvantage >>that you cannot easily comment out a section and you have to keep >>track about the braces (or the IDE does it). >>I am very happy that I could do the following: >> >> builder >> .attach(SomeChainlet()) >> .attach(Someother) >> >>And if I don't need a chainlet or maplet I can easily comment it out >>without breaking the flow. >> >> builder >>// .attach(SomeChainlet()) >> .attach(Someother) >> >> >> >>[...] >>> Each ")" above would correspond to the .back(1) in Lars solution, I >>> guess. The generic back(?) seems more flexible and I would interested in >>> knowing what kind of scenarios does it enable. >> >>I use it just to save some .back().back() chains if I've to jump to a >>higher level. It is build in because I am lazy. ;) >> >>Best regards, >>Lars >>-- >>http://semagia.com >> > > > >-- > > > --