javalite icon indicating copy to clipboard operation
javalite copied to clipboard

Reusing helper methods in controllers and filters (like HttpSupport class does)

Open mppfiles opened this issue 5 years ago • 7 comments

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:

  1. Being AW an opinionated framework, is there any "recommended" place (package) to put interfaces, implementation, and modules (like Greeter, GreeterImpl and GreeterModule)?

  2. How can I implement such module? Guice modules need to extend AbstractModule, but in this case, I would need to extend HttpSupport for example.

It would be nice to have an "official", documented way, to do this. Thoughts?

mppfiles avatar Aug 13 '20 16:08 mppfiles

@mppfiles can you please provide more info on what kind of utility class methods you want to reuse?

ipolevoy avatar Aug 13 '20 16:08 ipolevoy

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 avatar Aug 13 '20 17:08 mppfiles

@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?

ipolevoy avatar Aug 17 '20 14:08 ipolevoy

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.

mppfiles avatar Aug 18 '20 17:08 mppfiles

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

ipolevoy avatar Aug 18 '20 18:08 ipolevoy

I already use a BaseController... as well as a BaseFilter... :smile: The trick is to share code between them!

mppfiles avatar Aug 18 '20 18:08 mppfiles

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

ipolevoy avatar Aug 18 '20 18:08 ipolevoy