Re: Extending the foreach directive
Gary Bentley wrote: Hi Jonathan, The original aim of the request was to allow a JoSQL statement to be used directly within Velocity. More specifically it would be that the WHERE clause, ORDER BY clause and potentially the GROUP BY clause (and as a further extension the LIMIT and GROUP BY ORDER clauses) could be used. Well, okay, but then, frankly, you didn't formulate your original question very well. What you're wanting above is something very general, and I answered the question as specifically how do you do the specific example you gave in FreeMarker. The thing is that it so happened that the specific example you used could be written directly using FreeMarker without exposing anything extra from the java side -- or interacting with josql, and of course, your whole motive is to interact with josql, but that was not at all clear from the question. Never mind, nobody's perfect. :-) The freemarker code I gave simply used ?sort_by which is a freemarker built-in See: http://freemarker.org/docs/ref_builtins_sequence.html#ref_builtin_sort_by FreeMarker currently doesn't have built-ins that replicate all of an SQL SELECT statement, like GROUP BY and HAVING and so on. (Of course, as a practical matter, a lot of SELECT queries are just about like that one, with a sort key and a condition.) But okay, what you want to do is expose josql functionality to FreeMarker... To do this in Velocity I was going to do (I've written, the code but due to personal reasons not had chance to go further with it yet): #foreach ($v in $josql.filter ("name LIKE '/opt/%' order by dir, name", $files)) This isn't as elegant as I'd like but it will work. I had looked at modifying the Velocity grammar to support N arguments to a tool but I ran into a number of problems with it. Unfortunately, from what I've seen, the Velocity grammar has hard coded support for the "foreach" statement. Personally I'd like to see the grammar in the form: [args] Well, the basic way you could have that syntax in FreeMarker is along the lines of the example I gave in the last message. Here is an approximate possibility: <#macro josqlIterator sequence where=[] order_by=[] group_by=[] having=[] descending=false > <#local results=josql.filter(where, order_by, group_by, having, descending)> <#list results as result> <#nested result result_index+1 result_count> I guess it is assumed that you receive a sequence as a required parameter and the other named parameters are optional and default to the empty sequence, except for descending which defaults to false. So here would be a possible invocation of the above macro. <@josqlIterator sequence=files where=["LIKE '/opt/%'"] order_by=["dir", "name"] ; row, index > ${index}. ${flogger.getPromo(row)} Note that in the above example, unlike the one I gave in the last messsage, I had the invocation of the loop pass in three loop variables, the row and its 1-based index, and the total row count. This, besides being a fairly realistic scenario that you'd need something like that, gives you an idea of the flexibility of the macro system. And then have the tool invoked with the arguments passed to it. But that's not for me to decide. What do you mean, it's not for you to decide? If you mean that it's not technically feasible with Velocity, well, use a tool that has the flexibility to do it how you want to. As for it's implementation in FreeMarker, I would like to do that in time (I wasn't aware of FreeMarker before), but again having a "native" implementation for the statement would be preferable to the special macro you defined. When I get around to a FreeMarker implementation I'll let you know and you can tell me the possibilities! Well, I've given you a flavor of it here. Obviously you have a lot more possibilities than you do with Velocity. Frankly, I think it's like night and day... But let me say, that if you get into doing this with FreeMarker, in this case, I really think you'd be better off using the code from the SVN HEAD. You can get the latest greatest here: http://freemarker.org:8085/browse/FM-TRUNK And then you click on the "COMPLETED BUILDS" tab and choose the latest one. (At the moment that is:) http://freemarker.org:8085/browse/FM-TRUNK-153 and then you click on the Artifacts tab and pick up the freemarker.jar. The reason to use the 2.4 codebase (the stable release is 2.3.x) is that there are specifically some very nice new features for exposing java methods to the template layer. It's annotation based, so that, even though java doesn't support optional and default parameters, or invoking parameters by name,you can add an annotation on the java side so that it effectively has optional and default parameters and invocation by name when you call it from the template layer. Lik
Re: Extending the foreach directive
Hi Jonathan, The original aim of the request was to allow a JoSQL statement to be used directly within Velocity. More specifically it would be that the WHERE clause, ORDER BY clause and potentially the GROUP BY clause (and as a further extension the LIMIT and GROUP BY ORDER clauses) could be used. To do this in Velocity I was going to do (I've written the code but due to personal reasons not had chance to go further with it yet): #foreach ($v in $josql.filter ("name LIKE '/opt/%' order by dir, name", $files)) This isn't as elegant as I'd like but it will work. I had looked at modifying the Velocity grammar to support N arguments to a tool but I ran into a number of problems with it. Unfortunately, from what I've seen, the Velocity grammar has hard coded support for the "foreach" statement. Personally I'd like to see the grammar in the form: [args] And then have the tool invoked with the arguments passed to it. But that's not for me to decide. As for it's implementation in FreeMarker, I would like to do that in time (I wasn't aware of FreeMarker before), but again having a "native" implementation for the statement would be preferable to the special macro you defined. When I get around to a FreeMarker implementation I'll let you know and you can tell me the possibilities! Gary Jonathan Revusky wrote: Gary Bentley wrote: Hi, I'm the developer of JoSQL (http://josql.sf.net) and I've had a request from a user to extend the foreach directive to include JoSQL processing facilities. Along the lines of: #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by $mud.price) $flogger.getPromo( $mud ) Hi, Gary, since you wrote me that note about trying to get in with ASF (thanks but no thanks) I looked back on your participation in this list. I see you posed the above question. Here's how you do the above in FreeMarker: <#-- Special foreach replacement macro that sorts the sequence by a fieldname (the orderByField parameter) and executes the associated nested block if the condition is satisfied, where the condition parameter is a function or method that operates on the item in the sequence and returns a boolean --> <#macro specialForeach sequence condition orderByField> <#list sequence?sort_by(orderByField) as item> <#if condition(item)> <#nested item> Example usage: <@specialForeach sequence=mud condition=customer.hasPurchased orderByField="price" ; item> ${flogger.getPromo(item)} Now, I didn't have time to look into how you do this in Velocity. There's a fairly long discussion after this. Could you summarize it? How do you do the above in Velocity? You can answer in private or on the list, but really, it should be the latter, since other people might want to know, and after all, that's what these lists are for, right? Cheers, Jonathan Revusky - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Gary Bentley wrote: Hi, I'm the developer of JoSQL (http://josql.sf.net) and I've had a request from a user to extend the foreach directive to include JoSQL processing facilities. Along the lines of: #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by $mud.price) $flogger.getPromo( $mud ) Hi, Gary, since you wrote me that note about trying to get in with ASF (thanks but no thanks) I looked back on your participation in this list. I see you posed the above question. Here's how you do the above in FreeMarker: <#-- Special foreach replacement macro that sorts the sequence by a fieldname (the orderByField parameter) and executes the associated nested block if the condition is satisfied, where the condition parameter is a function or method that operates on the item in the sequence and returns a boolean --> <#macro specialForeach sequence condition orderByField> <#list sequence?sort_by(orderByField) as item> <#if condition(item)> <#nested item> Example usage: <@specialForeach sequence=mud condition=customer.hasPurchased orderByField="price" ; item> ${flogger.getPromo(item)} Now, I didn't have time to look into how you do this in Velocity. There's a fairly long discussion after this. Could you summarize it? How do you do the above in Velocity? You can answer in private or on the list, but really, it should be the latter, since other people might want to know, and after all, that's what these lists are for, right? Cheers, Jonathan Revusky -- lead developer, FreeMarker project, http://freemarker.org/ Velocity or FreeMarker: Looking at 5 Years of Practical Experience http://freemarker.blogspot.com/2007/12/velocity-of-freemarker-looking-at-5.html In essence to allow the collection to be filtered and ordered prior to being processed by the foreach directive. Alternatively it may be that I would add it via a custom directive. To make this work though I have a couple of questions: 1. How do I make a new directive available to velocity? I am currently, for testing purposes, using the "ant docs" command to process a file that has the extended syntax in it. I have modified the "directive.properties" file to add my new class, it is loaded and I've modified the value returned by the "getName" method of my class however when I then use the directive in my test file it isn't being used. 2. How do I get the arguments for the directive from the Node? When I modify the Foreach class directly I find that only 4 child nodes are added to the Node, basically no more children are available after the "where" keyword. (To make argument processing work I've modified the Parser.jjt file to allow for more args for the foreach directive). How can I find out what the arguments are and how can I get the parser to make them available? They are available if I iterate through the tokens associated with the Node. Many thanks, Gary - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
On Jan 17, 2008 7:34 AM, Christopher Schultz <[EMAIL PROTECTED]> wrote: > Gary, > > Gary Bentley wrote: > > Actually, just so I know could you let me know what the life-cycle is > > for tool objects? Is a new tool object created per request and > > hence discarded after the request has been made. Or is a pool of > > objects created and then an object assigned from the pool to each > > request? > > No pools AFAIK: you get a new object per-[scope]. If it's application > scope, then the object gets created at webapp startup and lives for the > life of the app. Same for session or request. Actually, I think VelTools > 2.0 does lazy loading so you won't get a JoSQL object for a particular > request if none is ever requested. Just to clear up a little grammatical ambiguity above, the request or session tools do not live for the life of the app (as could be read above), but rather for the life of the request or session, respectively. And yes, VelocityTools doesn't pool, it's just not worth the overhead for most tools. And yes, VelTools 2 only loads tools when they're used. > > I am trying to avoid the relatively costly statement parse, but I > > think it may be unavoidable anyway since to identify the relevant > > Query object (that will do the actual work) I would have to parse and > > then normalize the query string anyway. > > I think what you want to do is parse the query string on demand, and let > the template author figure out the rest. > > Finally, don't forget that a tool instance does not equal one of your > query instances. You could write a tool that creates a new query for > every single "request" made to the tool. In that case, you don't have a > threading issue (but may have a performance issue). If you wanted to be > really anal about it, you could keep a cache of query string -> query > objects in your tool, though i doubt very much it would be really worth > it. Parsing those queries shouldn't take /that/ long. > > Honestly, I think the explosion of "tools" for velocity is starting to > look like ColdFusion where everything is a view tag instead of being put > into the controller like it really should be. :( we've never claimed to be an MVC purists. :) MVC is just another pattern that is ideal sometimes, ok sometimes, and just unnecessary sometimes. it's not hard to ignore any tools you don't like. > I realize that you are simply trying to cater to your users, but I'm not > sure if this tool is really in their best interest. nor could you be sure. ;) there are many different apps and needs out there. and many perfectly reasonable and helpful ways of developing them. > > -chris > > > - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Cool. If it doesn't do what you need (like access the velocity context or velocity engine), you can bet one of the other tools probably does. On Jan 16, 2008 10:03 PM, Gary Bentley <[EMAIL PROTECTED]> wrote: > Thanks for that Nathan, I'll probably use the LoopTool as a template, it > appears to have the items you are talking about. > > Gary > > > > Nathan Bubna wrote: > > On Jan 16, 2008 2:27 PM, Christopher Schultz > > <[EMAIL PROTECTED]> wrote: > > > >> Gary, > >> > >> Gary Bentley wrote: > >> > >>> For the class name, it's needed because during the parse it uses the > >>> class name to resolve method/field accesses. Without it resolution > >>> would be needed at execution time. In theory it's not needed but it > >>> also allows the query to be parsed into the relevant tree of objects so > >>> that it can be reused for execution. > >>> > >> Gotcha. > >> > >> > >>> It also has the benefit of making > >>> the query more readable to humans, so if you pass the query around > >>> others can tell what class it is operating on. > >>> > >> When I was reading the "quick description" on the website, I was > >> confused at first because it wasn't clear that a collection of existing > >> objects were being passed-in as a context to evaluate the query (until I > >> kept reading, of course). At first, I thought that "SELECT * FROM > >> java.io.File" would magically read from the filesystem. Silly me! > >> > >> > >>> There is one issue, you > >>> said that the tool is put into the application scope, however JoSQL > >>> isn't thread safe, is it possible to get the tool to be created per > >>> thread? (I could however use a query pool and normalize the query > >>> string if not) > >>> > >> That's really up to you. Velocity Tools itself will not create a new > >> tool per thread, but you can specify that a tool should be > >> request-scoped (that is, a new one is created for each request). Since > >> Velocity Tools was really created for webapp-use, and webapps should > >> only use a single thread for each request, this works out just fine. > >> > >> If the JoSQL processors are not thread safe, you can simply direct your > >> users to specify "request" scope when configuring the tool in the > >> Velocity Tools toolbox. > >> > > > > Definitely use request scope if there's any thread safety concerns. > > With VelocityTools 2.0 (still in beta, but final release is largely > > waiting on feedback and finishing docs), you can even add a > > ValidScope(Scope.REQUEST) annotation to your tool class, to prevent > > users from using the tool in any other scope. Also, if your tool will > > be needing access to the current context (in VelocityTools 2, just add > > a setVelocityContext(Context ctx) method and it will automatically be > > given to the tool each request), then be sure to use request scope. > > > > (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want > > to be sure people are motivated to upgrade from 1.4 soon :) > > > > > >>> I do have plans for a future enhancement to allow a bind variable to be > >>> used of a class name instead so that it can be more dynamic. > >>> > >> Speaking of bind variables, I found myself a little thrown by their > >> syntax for the simple reason that it differs from JDBC. Perhaps you > >> could offer separate parsers at runtime for those who prefer the > >> JDBC-style syntax. > >> > >> Good luck, > >> -chris > >> > >> > >> > > > > > - > > To unsubscribe, e-mail: [EMAIL PROTECTED] > > For additional commands, e-mail: [EMAIL PROTECTED] > > > > > - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Gary, Gary Bentley wrote: Actually, just so I know could you let me know what the life-cycle is for tool objects? Is a new tool object created per request and hence discarded after the request has been made. Or is a pool of objects created and then an object assigned from the pool to each request? No pools AFAIK: you get a new object per-[scope]. If it's application scope, then the object gets created at webapp startup and lives for the life of the app. Same for session or request. Actually, I think VelTools 2.0 does lazy loading so you won't get a JoSQL object for a particular request if none is ever requested. I am trying to avoid the relatively costly statement parse, but I think it may be unavoidable anyway since to identify the relevant Query object (that will do the actual work) I would have to parse and then normalize the query string anyway. I think what you want to do is parse the query string on demand, and let the template author figure out the rest. Finally, don't forget that a tool instance does not equal one of your query instances. You could write a tool that creates a new query for every single "request" made to the tool. In that case, you don't have a threading issue (but may have a performance issue). If you wanted to be really anal about it, you could keep a cache of query string -> query objects in your tool, though i doubt very much it would be really worth it. Parsing those queries shouldn't take /that/ long. Honestly, I think the explosion of "tools" for velocity is starting to look like ColdFusion where everything is a view tag instead of being put into the controller like it really should be. :( I realize that you are simply trying to cater to your users, but I'm not sure if this tool is really in their best interest. -chris signature.asc Description: OpenPGP digital signature
Re: Extending the foreach directive
Gary, Gary Bentley wrote: For the bind variables is it possible to use "?" as the placeholder like you can for JDBC. Is that what you meant? Yeah, that's what I meant. -chris signature.asc Description: OpenPGP digital signature
Re: Extending the foreach directive
Le jeudi 17 janvier 2008 à 17:01 +1000, Gary Bentley a écrit : > Hi Nathan, > > Just on the annotation, if I set it as request will that mean that there > is 1 JoSQL object per request? Yes. > For example if a template is processed in a single request that has 2 > josql.execute calls (say) will it be the same JoSQL object that is used > for both? Yes, but the two josql.execute calls will be called sequentially by the same thread. > I just need to know since it is the query that isn't thread safe but if > the same object is used for both then I need to create 2 query objects. You should not need two different objects in this particular case. Claude > Thanks, > > Gary > > > Gary Bentley wrote: > > Thanks for that Nathan, I'll probably use the LoopTool as a template, > > it appears to have the items you are talking about. > > > > Gary > > > > > > Nathan Bubna wrote: > >> On Jan 16, 2008 2:27 PM, Christopher Schultz > >> <[EMAIL PROTECTED]> wrote: > >> > >>> Gary, > >>> > >>> Gary Bentley wrote: > >>> > For the class name, it's needed because during the parse it uses the > class name to resolve method/field accesses. Without it resolution > would be needed at execution time. In theory it's not needed but it > also allows the query to be parsed into the relevant tree of > objects so > that it can be reused for execution. > > >>> Gotcha. > >>> > >>> > It also has the benefit of making > the query more readable to humans, so if you pass the query around > others can tell what class it is operating on. > > >>> When I was reading the "quick description" on the website, I was > >>> confused at first because it wasn't clear that a collection of existing > >>> objects were being passed-in as a context to evaluate the query > >>> (until I > >>> kept reading, of course). At first, I thought that "SELECT * FROM > >>> java.io.File" would magically read from the filesystem. Silly me! > >>> > >>> > There is one issue, you > said that the tool is put into the application scope, however JoSQL > isn't thread safe, is it possible to get the tool to be created per > thread? (I could however use a query pool and normalize the query > string if not) > > >>> That's really up to you. Velocity Tools itself will not create a new > >>> tool per thread, but you can specify that a tool should be > >>> request-scoped (that is, a new one is created for each request). Since > >>> Velocity Tools was really created for webapp-use, and webapps should > >>> only use a single thread for each request, this works out just fine. > >>> > >>> If the JoSQL processors are not thread safe, you can simply direct your > >>> users to specify "request" scope when configuring the tool in the > >>> Velocity Tools toolbox. > >>> > >> > >> Definitely use request scope if there's any thread safety concerns. > >> With VelocityTools 2.0 (still in beta, but final release is largely > >> waiting on feedback and finishing docs), you can even add a > >> ValidScope(Scope.REQUEST) annotation to your tool class, to prevent > >> users from using the tool in any other scope. Also, if your tool will > >> be needing access to the current context (in VelocityTools 2, just add > >> a setVelocityContext(Context ctx) method and it will automatically be > >> given to the tool each request), then be sure to use request scope. > >> > >> (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want > >> to be sure people are motivated to upgrade from 1.4 soon :) > >> > >> > I do have plans for a future enhancement to allow a bind variable > to be > used of a class name instead so that it can be more dynamic. > > >>> Speaking of bind variables, I found myself a little thrown by their > >>> syntax for the simple reason that it differs from JDBC. Perhaps you > >>> could offer separate parsers at runtime for those who prefer the > >>> JDBC-style syntax. > >>> > >>> Good luck, > >>> -chris > >>> > >>> > >>> > >> > >> - > >> To unsubscribe, e-mail: [EMAIL PROTECTED] > >> For additional commands, e-mail: [EMAIL PROTECTED] > >> > >> > > > > - > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Actually, just so I know could you let me know what the life-cycle is for tool objects? Is a new tool object created per request and hence discarded after the request has been made. Or is a pool of objects created and then an object assigned from the pool to each request? I am trying to avoid the relatively costly statement parse, but I think it may be unavoidable anyway since to identify the relevant Query object (that will do the actual work) I would have to parse and then normalize the query string anyway. Thanks, Gary Gary Bentley wrote: Thanks for that Nathan, I'll probably use the LoopTool as a template, it appears to have the items you are talking about. Gary Nathan Bubna wrote: On Jan 16, 2008 2:27 PM, Christopher Schultz <[EMAIL PROTECTED]> wrote: Gary, Gary Bentley wrote: For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. Gotcha. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. When I was reading the "quick description" on the website, I was confused at first because it wasn't clear that a collection of existing objects were being passed-in as a context to evaluate the query (until I kept reading, of course). At first, I thought that "SELECT * FROM java.io.File" would magically read from the filesystem. Silly me! There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) That's really up to you. Velocity Tools itself will not create a new tool per thread, but you can specify that a tool should be request-scoped (that is, a new one is created for each request). Since Velocity Tools was really created for webapp-use, and webapps should only use a single thread for each request, this works out just fine. If the JoSQL processors are not thread safe, you can simply direct your users to specify "request" scope when configuring the tool in the Velocity Tools toolbox. Definitely use request scope if there's any thread safety concerns. With VelocityTools 2.0 (still in beta, but final release is largely waiting on feedback and finishing docs), you can even add a ValidScope(Scope.REQUEST) annotation to your tool class, to prevent users from using the tool in any other scope. Also, if your tool will be needing access to the current context (in VelocityTools 2, just add a setVelocityContext(Context ctx) method and it will automatically be given to the tool each request), then be sure to use request scope. (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want to be sure people are motivated to upgrade from 1.4 soon :) I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Speaking of bind variables, I found myself a little thrown by their syntax for the simple reason that it differs from JDBC. Perhaps you could offer separate parsers at runtime for those who prefer the JDBC-style syntax. Good luck, -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Hi Nathan, Just on the annotation, if I set it as request will that mean that there is 1 JoSQL object per request? For example if a template is processed in a single request that has 2 josql.execute calls (say) will it be the same JoSQL object that is used for both? I just need to know since it is the query that isn't thread safe but if the same object is used for both then I need to create 2 query objects. Thanks, Gary Gary Bentley wrote: Thanks for that Nathan, I'll probably use the LoopTool as a template, it appears to have the items you are talking about. Gary Nathan Bubna wrote: On Jan 16, 2008 2:27 PM, Christopher Schultz <[EMAIL PROTECTED]> wrote: Gary, Gary Bentley wrote: For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. Gotcha. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. When I was reading the "quick description" on the website, I was confused at first because it wasn't clear that a collection of existing objects were being passed-in as a context to evaluate the query (until I kept reading, of course). At first, I thought that "SELECT * FROM java.io.File" would magically read from the filesystem. Silly me! There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) That's really up to you. Velocity Tools itself will not create a new tool per thread, but you can specify that a tool should be request-scoped (that is, a new one is created for each request). Since Velocity Tools was really created for webapp-use, and webapps should only use a single thread for each request, this works out just fine. If the JoSQL processors are not thread safe, you can simply direct your users to specify "request" scope when configuring the tool in the Velocity Tools toolbox. Definitely use request scope if there's any thread safety concerns. With VelocityTools 2.0 (still in beta, but final release is largely waiting on feedback and finishing docs), you can even add a ValidScope(Scope.REQUEST) annotation to your tool class, to prevent users from using the tool in any other scope. Also, if your tool will be needing access to the current context (in VelocityTools 2, just add a setVelocityContext(Context ctx) method and it will automatically be given to the tool each request), then be sure to use request scope. (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want to be sure people are motivated to upgrade from 1.4 soon :) I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Speaking of bind variables, I found myself a little thrown by their syntax for the simple reason that it differs from JDBC. Perhaps you could offer separate parsers at runtime for those who prefer the JDBC-style syntax. Good luck, -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Thanks for that Nathan, I'll probably use the LoopTool as a template, it appears to have the items you are talking about. Gary Nathan Bubna wrote: On Jan 16, 2008 2:27 PM, Christopher Schultz <[EMAIL PROTECTED]> wrote: Gary, Gary Bentley wrote: For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. Gotcha. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. When I was reading the "quick description" on the website, I was confused at first because it wasn't clear that a collection of existing objects were being passed-in as a context to evaluate the query (until I kept reading, of course). At first, I thought that "SELECT * FROM java.io.File" would magically read from the filesystem. Silly me! There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) That's really up to you. Velocity Tools itself will not create a new tool per thread, but you can specify that a tool should be request-scoped (that is, a new one is created for each request). Since Velocity Tools was really created for webapp-use, and webapps should only use a single thread for each request, this works out just fine. If the JoSQL processors are not thread safe, you can simply direct your users to specify "request" scope when configuring the tool in the Velocity Tools toolbox. Definitely use request scope if there's any thread safety concerns. With VelocityTools 2.0 (still in beta, but final release is largely waiting on feedback and finishing docs), you can even add a ValidScope(Scope.REQUEST) annotation to your tool class, to prevent users from using the tool in any other scope. Also, if your tool will be needing access to the current context (in VelocityTools 2, just add a setVelocityContext(Context ctx) method and it will automatically be given to the tool each request), then be sure to use request scope. (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want to be sure people are motivated to upgrade from 1.4 soon :) I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Speaking of bind variables, I found myself a little thrown by their syntax for the simple reason that it differs from JDBC. Perhaps you could offer separate parsers at runtime for those who prefer the JDBC-style syntax. Good luck, -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Hi Chris, Thanks for the info. For the bind variables is it possible to use "?" as the placeholder like you can for JDBC. Is that what you meant? Gary Christopher Schultz wrote: Gary, Gary Bentley wrote: For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. Gotcha. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. When I was reading the "quick description" on the website, I was confused at first because it wasn't clear that a collection of existing objects were being passed-in as a context to evaluate the query (until I kept reading, of course). At first, I thought that "SELECT * FROM java.io.File" would magically read from the filesystem. Silly me! There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) That's really up to you. Velocity Tools itself will not create a new tool per thread, but you can specify that a tool should be request-scoped (that is, a new one is created for each request). Since Velocity Tools was really created for webapp-use, and webapps should only use a single thread for each request, this works out just fine. If the JoSQL processors are not thread safe, you can simply direct your users to specify "request" scope when configuring the tool in the Velocity Tools toolbox. I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Speaking of bind variables, I found myself a little thrown by their syntax for the simple reason that it differs from JDBC. Perhaps you could offer separate parsers at runtime for those who prefer the JDBC-style syntax. Good luck, -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
On Jan 16, 2008 2:27 PM, Christopher Schultz <[EMAIL PROTECTED]> wrote: > Gary, > > Gary Bentley wrote: > > For the class name, it's needed because during the parse it uses the > > class name to resolve method/field accesses. Without it resolution > > would be needed at execution time. In theory it's not needed but it > > also allows the query to be parsed into the relevant tree of objects so > > that it can be reused for execution. > > Gotcha. > > > It also has the benefit of making > > the query more readable to humans, so if you pass the query around > > others can tell what class it is operating on. > > When I was reading the "quick description" on the website, I was > confused at first because it wasn't clear that a collection of existing > objects were being passed-in as a context to evaluate the query (until I > kept reading, of course). At first, I thought that "SELECT * FROM > java.io.File" would magically read from the filesystem. Silly me! > > > There is one issue, you > > said that the tool is put into the application scope, however JoSQL > > isn't thread safe, is it possible to get the tool to be created per > > thread? (I could however use a query pool and normalize the query > > string if not) > > That's really up to you. Velocity Tools itself will not create a new > tool per thread, but you can specify that a tool should be > request-scoped (that is, a new one is created for each request). Since > Velocity Tools was really created for webapp-use, and webapps should > only use a single thread for each request, this works out just fine. > > If the JoSQL processors are not thread safe, you can simply direct your > users to specify "request" scope when configuring the tool in the > Velocity Tools toolbox. Definitely use request scope if there's any thread safety concerns. With VelocityTools 2.0 (still in beta, but final release is largely waiting on feedback and finishing docs), you can even add a ValidScope(Scope.REQUEST) annotation to your tool class, to prevent users from using the tool in any other scope. Also, if your tool will be needing access to the current context (in VelocityTools 2, just add a setVelocityContext(Context ctx) method and it will automatically be given to the tool each request), then be sure to use request scope. (Apologies for all my not-so-subtle plugs for VelocityTools 2; i want to be sure people are motivated to upgrade from 1.4 soon :) > > I do have plans for a future enhancement to allow a bind variable to be > > used of a class name instead so that it can be more dynamic. > > Speaking of bind variables, I found myself a little thrown by their > syntax for the simple reason that it differs from JDBC. Perhaps you > could offer separate parsers at runtime for those who prefer the > JDBC-style syntax. > > Good luck, > -chris > > - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Gary, Gary Bentley wrote: For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. Gotcha. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. When I was reading the "quick description" on the website, I was confused at first because it wasn't clear that a collection of existing objects were being passed-in as a context to evaluate the query (until I kept reading, of course). At first, I thought that "SELECT * FROM java.io.File" would magically read from the filesystem. Silly me! There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) That's really up to you. Velocity Tools itself will not create a new tool per thread, but you can specify that a tool should be request-scoped (that is, a new one is created for each request). Since Velocity Tools was really created for webapp-use, and webapps should only use a single thread for each request, this works out just fine. If the JoSQL processors are not thread safe, you can simply direct your users to specify "request" scope when configuring the tool in the Velocity Tools toolbox. I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Speaking of bind variables, I found myself a little thrown by their syntax for the simple reason that it differs from JDBC. Perhaps you could offer separate parsers at runtime for those who prefer the JDBC-style syntax. Good luck, -chris signature.asc Description: OpenPGP digital signature
Re: Extending the foreach directive
Hi Chris, For the class name, it's needed because during the parse it uses the class name to resolve method/field accesses. Without it resolution would be needed at execution time. In theory it's not needed but it also allows the query to be parsed into the relevant tree of objects so that it can be reused for execution. It also has the benefit of making the query more readable to humans, so if you pass the query around others can tell what class it is operating on. There is one issue, you said that the tool is put into the application scope, however JoSQL isn't thread safe, is it possible to get the tool to be created per thread? (I could however use a query pool and normalize the query string if not) I do have plans for a future enhancement to allow a bind variable to be used of a class name instead so that it can be more dynamic. Thanks again for all the advice, I'll let you know when the tool is available. Gary Christopher Schultz wrote: Gary, Gary Bentley wrote: Many thanks for the responses. They are both very helpful. I think at this point I'll go for Chris's suggestion since that will be easier to implement. There are some differences between the way that JoSQL and Velocity works so there will be limitations but overall I think it will be easier this way. I think they can complement each other very well: JoSQL seems very self-contained, so you can easily create a re-usable "tool" that is shareable across Context instances (Velocity Tools will put a tool like this into "application" scope for a webapp), and Velocity operates on any collection/iterator/array of objects generically. So, you can simply write a method that takes one collection of objects and emits another one (or an iterator) and use it directly. Show us what you've got when you've got it. Your tool definitely looks very interesting (though I'm not sure why you have to SELECT * FROM [class name]... I suppose you needed something in there!). Good luck, -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Gary, Gary Bentley wrote: Many thanks for the responses. They are both very helpful. I think at this point I'll go for Chris's suggestion since that will be easier to implement. There are some differences between the way that JoSQL and Velocity works so there will be limitations but overall I think it will be easier this way. I think they can complement each other very well: JoSQL seems very self-contained, so you can easily create a re-usable "tool" that is shareable across Context instances (Velocity Tools will put a tool like this into "application" scope for a webapp), and Velocity operates on any collection/iterator/array of objects generically. So, you can simply write a method that takes one collection of objects and emits another one (or an iterator) and use it directly. Show us what you've got when you've got it. Your tool definitely looks very interesting (though I'm not sure why you have to SELECT * FROM [class name]... I suppose you needed something in there!). Good luck, -chris signature.asc Description: OpenPGP digital signature
Re: Extending the foreach directive
Nathan/Chris, Many thanks for the responses. They are both very helpful. I think at this point I'll go for Chris's suggestion since that will be easier to implement. There are some differences between the way that JoSQL and Velocity works so there will be limitations but overall I think it will be easier this way. Thanks, Gary Christopher Schultz wrote: Gary, Gary Bentley wrote: Along the lines of: #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by $mud.price) This looks like mixing SQL-ish syntax with Java collections, which is precisely what JoSQL was designed to do. Why extend Velocity when you can simply use JoSQL as it was intended: #foreach($mud in $josql.execute("SELECT [whatever] FROM mud.Type WHERE $customer.hasPurchased($mud)", $mudsOnSpecial)) ... with with your muds #end then you just have to implement a JoSQL tool and shove it into the velocity context. You tool can do something like this: public Iterator execute(String query, Object (or List?) source) { Query q = new Query(); q.parse(query); QueryResults qr = q.execute(source); List res = qr.getResults(); return res.iterator(); } I think this would be a whole lot easier than re-writing Velocity. Since this capability is orthogonal to Velocity's, it seems to be a better choice for architecture, too. -chris - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Extending the foreach directive
Gary, Gary Bentley wrote: Along the lines of: #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by $mud.price) This looks like mixing SQL-ish syntax with Java collections, which is precisely what JoSQL was designed to do. Why extend Velocity when you can simply use JoSQL as it was intended: #foreach($mud in $josql.execute("SELECT [whatever] FROM mud.Type WHERE $customer.hasPurchased($mud)", $mudsOnSpecial)) ... with with your muds #end then you just have to implement a JoSQL tool and shove it into the velocity context. You tool can do something like this: public Iterator execute(String query, Object (or List?) source) { Query q = new Query(); q.parse(query); QueryResults qr = q.execute(source); List res = qr.getResults(); return res.iterator(); } I think this would be a whole lot easier than re-writing Velocity. Since this capability is orthogonal to Velocity's, it seems to be a better choice for architecture, too. -chris signature.asc Description: OpenPGP digital signature
Re: Extending the foreach directive
Hi Gary, So far as i know, you are in fairly unique territory. The #if and #foreach directives were never really meant to be extended or replaced, so far as i know. Their roots go deep into the parser and will almost certainly require a custom build of Velocity, not just some configuration and extensions. You may be the first to try something much like this. But brave experiments usually teach a lot, so i'm certainly curious to see how it works out and hear if there are ways we can make things easier for such efforts down the road. In the meantime, i do have a somewhat lateral suggestion: have you considered using a "Tool" type of syntax instead? I know it probably wouldn't be as clean, but it might be a lot easier to implement. I've actually been heading down a parallel road myself lately with the work i've been doing on the LoopTool (http://svn.apache.org/viewvc/velocity/tools/trunk/src/main/java/org/apache/velocity/tools/generic/LoopTool.java?view=markup) Rather than mess with the parser and core directives, i've made it relatively simple to have a #foreach watched by the LoopTool to automatically skip or stop before specific elements. The syntax is currently: #foreach( $foo in $loop.watch($myFoos).skip('badfoo').stop('prematureEndFoo') ) Here's a $foo #end The above would skip any $foo that equals 'badfoo' and stop iteration when the next foo equals 'prematureEndFoo'. Pretty rudimentary, but not too far from your example, particularly if combined with the SortTool. The tricky part of making a $josql tool to do #foreach( $mud in $josql.filter($mudsOnSpecial).where('$customer.hasPurchased($mud)').orderBy('price') ) would be working out the $customer.hasPurchased($mud) part. Though i think that is feasible if you give the tool access to the current context and VelocityEngine so you can evaluate the condition. Then the orderBy can be implemented to sort on $mud properties, just like SortTool can. Anyway, if you're curious about taking the tool route, i'm much more capable of helping out than with modifying the #foreach directive, which would be totally new to me. :) Others here would be of more help with that, if they're not too busy at the moment. On Jan 15, 2008 4:45 AM, Gary Bentley <[EMAIL PROTECTED]> wrote: > Hi, > > I'm the developer of JoSQL (http://josql.sf.net) and I've had a request > from a user to extend the foreach directive to include JoSQL processing > facilities. > > Along the lines of: > > #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by > $mud.price) > > > > $flogger.getPromo( $mud ) > > > > In essence to allow the collection to be filtered and ordered prior to being > processed by the foreach directive. > Alternatively it may be that I would add it via a custom directive. > > To make this work though I have a couple of questions: > > 1. How do I make a new directive available to velocity? I am currently, for > testing purposes, using the "ant docs" command > to process a file that has the extended syntax in it. I have modified the > "directive.properties" file to add my new > class, it is loaded and I've modified the value returned by the "getName" > method of my class however when I then use the > directive in my test file it isn't being used. > > 2. How do I get the arguments for the directive from the Node? When I modify > the Foreach class directly I find that only 4 > child nodes are added to the Node, basically no more children are available > after the "where" keyword. (To make argument > processing work I've modified the Parser.jjt file to allow for more args for > the foreach directive). How can I find > out what the arguments are and how can I get the parser to make them > available? They are available if I iterate through > the tokens associated with the Node. > > Many thanks, > > Gary > > > > > - > To unsubscribe, e-mail: [EMAIL PROTECTED] > For additional commands, e-mail: [EMAIL PROTECTED] > > - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Extending the foreach directive
Hi, I'm the developer of JoSQL (http://josql.sf.net) and I've had a request from a user to extend the foreach directive to include JoSQL processing facilities. Along the lines of: #foreach( $mud in $mudsOnSpecial where $customer.hasPurchased($mud) order by $mud.price) $flogger.getPromo( $mud ) In essence to allow the collection to be filtered and ordered prior to being processed by the foreach directive. Alternatively it may be that I would add it via a custom directive. To make this work though I have a couple of questions: 1. How do I make a new directive available to velocity? I am currently, for testing purposes, using the "ant docs" command to process a file that has the extended syntax in it. I have modified the "directive.properties" file to add my new class, it is loaded and I've modified the value returned by the "getName" method of my class however when I then use the directive in my test file it isn't being used. 2. How do I get the arguments for the directive from the Node? When I modify the Foreach class directly I find that only 4 child nodes are added to the Node, basically no more children are available after the "where" keyword. (To make argument processing work I've modified the Parser.jjt file to allow for more args for the foreach directive). How can I find out what the arguments are and how can I get the parser to make them available? They are available if I iterate through the tokens associated with the Node. Many thanks, Gary - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]