Reusing helper methods in controllers and filters (like HttpSupport class does)
As stated in an old issue but in a more general way, I am looking for options to share some utility methods in filters and controllers, since clearly it's not a good practice to duplicate methods in both classes.
It seems like Dependency Injection is the way to go, although a bit overkill for this simple case. But the documentation lacks a real world example, and some questions arose:
-
Being AW an opinionated framework, is there any "recommended" place (package) to put interfaces, implementation, and modules (like
Greeter,GreeterImplandGreeterModule)? -
How can I implement such module? Guice modules need to extend
AbstractModule, but in this case, I would need to extendHttpSupportfor example.
It would be nice to have an "official", documented way, to do this. Thoughts?
@mppfiles can you please provide more info on what kind of utility class methods you want to reuse?
a simple but common case is to encapsulate access to session objects in controllers and filters, to make some DSL'ish api:
public ShoppingCart getShoppingCart() {
return (ShoppingCart)session().get("shopping_cart");
}
some other examples (somewhat silly, but you get the point):
protected HttpBuilder respondCustomJson(String custom_msg) {
String json = JsonHelper.toJsonString(Collections.map("mycode", 123, "mymessage", custom_msg));
return respond(json).contentType("application/json");
}
@mppfiles this has come into view a couple of times before. You are correct assessing that there is no convenient way of building helpers that may respond to a request and yet are usable in controllers and filters alike. The one solutions to this would be augmenting the HttpSupport class. If you look closely, this class is a facade to as class RequestContext, which in turn is a collection of ThreadLocal objects. These objects are what drives a single HTTP request in ActiveWeb. Basically, if most methods in HttpSupport are converted from instance to static you will be able to use this class to write universal; helpers. Such helpers should still be working in tests.
However, the HttpSupport class having most methods static will look odd. Also, it should not break backwards compatibility.
thoughts?
Agreed, it will look odd. And I prefer not to mess with the TL's.
I'm currently doing something like this, it works so far:
public class SessionHelper extends HttpSupport {
public ShoppingCart getShoppingCart() {
return (ShoppingCart)session().get("shopping_cart");
}
// ... more utility methods...
}
public class CheckoutController extends AppController {
public void index() {
SessionHelper sh = new SessionHelper(); // have to repeat this on EVERY method...
view("cart_total", sh.getShoppingCart().size()); // example of use
// ... more controller logic...
}
}
This is really simple and almost clean. But, although DI is a little overkill, at least I can inject the helper instance just once... Still testing which one is best.
if this is all you want, why not create a super controller? We do this all the time. The trick is to reuse the same function in controllers and filters
I already use a BaseController... as well as a BaseFilter... :smile:
The trick is to share code between them!
for session objects, you are replacing one line of code with three:
ShoppingCart sc = (ShoppingCart)session("shopping_cart");
for common code to respond from controllers and filters, I see your point. I wonder if we can cook something up with default methods in the interfaces: https://docs.oracle.com/javase/tutorial/java/IandI/defaultmethods.html
What could really be useful is Ruby modules, lol: http://ruby-for-beginners.rubymonstas.org/advanced/modules.html