Tag Archives: Play

PSPS

At CodeWorth.io we create many web applications. Unless there’s a very compelling reason not to, we’re using the Scala flavor of Play Framework, which we find performant, fun to work with, and generally easy to maintain (static typing FTW! There. I’ve said it.).

Now, Play is a very non-opinionated framework that’s happy to work with multiple database servers, front-end kits, etc. It’s great since it provides us with the flexibility a software studio like us needs. On the flip side, though, we found ourselves re-implementing various functionalities, such as nav-bars, page layouts, and logins (including “forgot password” flow and such). The textbooks say that re-implementing functionality is wrong. That’s only partially right – when you implement a functionality for the fourth time, you’re doing a much better job at it. But enough is enough – one would also like to write some new functionality from time to time.

Enter PSPS. This is a very opinionated application seed. We built it by collecting our best implementations, and then cleaning and brushing them up one more last final time (hmm, actually, we could improve this a bit by…) . PSPS is based on technologies we found to be reliable, and easy to work with and extend. Nothing fancy or surprising, really: PostgreSQL, Bootstrap4, and Slick.

Out of its proverbial box, PSPS supports many features common to web applications:

  • User management
  • Public and user-only sections
  • Navigation bars (including menus, and created using type-safe Scala code)
  • Database connectivity
  • UI Components and functionality (paging, page headers, buttons, JS alerts…)
  • Page templates that support localization, CSRF, informationals, etc.
  • Basic localization
  • Ajax functionality (wrapping Play’s JS Routers)
  • Scripts for deploying on UNIX boxes
  • Documentation (yay AsciiDoc and antora)

We have been testing PSPS internally for a while now, and believe it’s ready human consumption. It might even be good for said human, e.g. if they’re a part of the Play Framework community.

So feel free to try it out – it’s open sourced under Apache v2.0. We’re happily accepting improvements, suggestions, and pull-requests. Hopefully, together we can get the Play community an excellent application seed, so we can all focus on writing new things.

Anorm and Duplicate Column Names

A bit of a technical post, just in case someone else hits this problem, e.g. while working through Play for Scala book (which I heartily recommend). That’s how I stumbled on this issue.

Anorm is an interesting alternative to ORMs. This is pretty much going against the flow of “let us write your SQL for you”, so they have some explainin’ to do. Anorm sticks to native SQL, and make it easier to parse result rows into objects. One way of making it easy are SQL row parser combinators.

The idea is this: parsers take column values from an SQL result row, and return a Scala object. Combining the parsers yields tuples, that can later be parsed into Scala objects (normally, some case class).

So that’s all fine and dandy, but the code below produces unwanted results:


val prodsAndItems =  SQL("""
    SELECT p.*, s.* FROM products p INNER JOIN stock_items s 
    ON p.id = s.product_id
    ORDER BY p.id
    """)

  val productParser: RowParser[Product] = {
    long("id")~long("ean")~str("name")~str("description") map {
      case id~ean~name~description => Product(id, ean, name, description)
    }
  }
  
  val productsParser: ResultSetParser[List[Product]] = { 
    productParser *
  }

  val stockItemParser: RowParser[StockItem] = {
    long("id")~long("product_id")~long("warehouse_id")~long("quantity") map {
      case id~prodId~wareHouseId~quantity => StockItem(id,prodId,wareHouseId,quantity)
    }
  }

  val stockItemsParser: ResultSetParser[List[StockItem]] = {
    stockItemParser*
  }
  
  val productStockItemParser: RowParser[(Product, StockItem)] = {
    productParser ~ stockItemParser map (flatten)
  }

  def selectAllProductsAndItems: Map[Product,Seq[StockItem]] = DB.withConnection{ implicit c =>
    val res:List[(Product, StockItem)] = prodsAndItems.as( productStockItemParser * )
    res.groupBy( _._1 ).mapValues( _.map(_._2) )
  }

This is a classic case of products, warehouses and stock items (product quantities that are stocked in warehouses). You would expect the call to selectAllProductsAndItems to return a map object mapping products to their stock items. Instead, it returns a set of new products that don’t exist in the database, and maps a single stock item to each one.

Now, I love creating new products. I just don’t want my data access layer to do it for me. Enough of this “machines replacing people” zeitgeist.

Why was Anorm doing this? (spoiler alert from here on)
Both stock_items and products have a column called “id”. The basic parsers (defined in stockItemParser and productParser) call the columns by their names, and so get ambiguous results.

The solution turns out to be pretty simple (as always) – reference the column by its full name: “products.id” instead of “id”. Over to you, Peg:

P.S commit link on github