Skip to content

CRUD trait for Slick 2.0

This post was similar with what I’ve used in the past months before upgrading Slick to 2.0. From this version, some refactoring were needed.

For each of my case class tables I have a RichTable abstract class from which I extend in order to not repeat id column definition for all the tables.


abstract class RichTable[T](tag: Tag, name: String) extends Table[T](tag, name) {
 def id = column[Int]("id", O.PrimaryKey)

and an example of a case class to describe table is:

class Locations(tag:Tag) extends RichTable[LocationRow](tag, "location") with Logging {
def description = column[String]("description")
def addressId = column[Int]("address_id")
def partyId = column[Int]("party_id")
def contactPersonId = column[Int]("contact_person_id", O.Nullable)
def mainLocation = column[Boolean]("main_location")

//fks
def address = foreignKey("fk_address", addressId, Addresses.tableQuery)(_.id)
def contactPerson = foreignKey("fk_contact_person", contactPersonId, Users.tableQuery)(_.id)
def party = foreignKey("fk_party", partyId, Parties.tableQuery)(_.id)

def * = (id.?, description, addressId, partyId, contactPersonId.?, mainLocation) <> (LocationRow.tupled, LocationRow.unapply _)
}

As you noticed in documentation, we have to use TableQuery of type of our case class so I define this in a companion object of the above case class:

object Locations extends GenericTableUtils[Locations, LocationRow] with Logging {

val tableQuery = TableQuery[Locations]

/**
* Override insert in order to generate id dynamically. Return id of inserted entity.
*/
def insert(model: LocationRow)(implicit db: Session): Int = {
model.id match {
case None => {
val generatedId = getNextId(classOf[LocationRow].getSimpleName())
tableQuery += model.copy(id = Some(generatedId))
generatedId
}
case Some(id) => {
tableQuery += model
id
}
}
}

// other specific methods

}

and here it comes my generic trait:

trait GenericTableUtils[T <: RichTable[A], A] {

val tableQuery: TableQuery[T, T#TableElementType]

/**
 * To dynamically generate ids for insert, use sequences (they are created when schema is created).
 */
 def getNextId(seqName: String)(implicit session: Session) =
 (StaticQuery[Int] + "select nextval('" + seqName + "_seq') ").first

/**
 * Find a specific entity by id.
 */
 def findById(id: Int)(implicit session: Session): Option[A] = {
 val byId = tableQuery.findBy(_.id)
 byId(id).list.headOption
 }

/**
 * Delete a specific entity by id. If successfully completed return true, else false
 */
 def delete(id: Int)(implicit session: Session): Boolean =
 findById(id) match {
 case Some(entity) => { tableQuery.where(_.id === id).delete; true }
 case None => false
 }

/**
 * Update a specific entity by id. If successfully completed return true, else false
 */
 def update(id: Int, entity: A)(implicit session: Session): Boolean = {
 findById(id) match {
 case Some(e) => { tableQuery.where(_.id === id).update(entity); true }
 case None => false
 }
 }
 }

Insert method is more tricky to move it in generic trait but other methods works pretty cool there.
Ps: I use Postgresql, that’s why I generate ids in that way.

Export content as static file in Spray

If you want to export some text as a file using Spray scala library, here is an example:


respondWithHeader(`Content-Type`(new ContentType(MediaTypes.`text/plain`, None))) {
   respondWithHeader(`Content-Disposition`("attachment", Map(("filename", fileName)))) {
      complete {
         "text"
      }
   }
}

and use these imports:


import spray.http.HttpHeaders._
import spray.http.HttpCharsets._
import spray.http.MediaTypes._
import spray.http.ContentType
import spray.http.MediaTypes

Code-First Useful Commands

Create Migration:

Add-Migration “Name”

Generate Script From Migration

Update-Database -Script -TargetMigration:”Name”

Revert Applied Migration:

Update-Database -TargetMigration:”Name_of_previous_migration”

 

Spray default config

For REST services in Scala I use spray. Default configuration (hard to find documentation about this), by listing it programmatically:

spray.can.client.idle-timeout="10 s"
 spray.can.client.parsing.max-chunk-ext-count=16
 spray.can.client.parsing.max-chunk-ext-name-length=64
 spray.can.client.parsing.max-chunk-ext-value-length=256
 spray.can.client.parsing.max-chunk-size="1m"
 spray.can.client.parsing.max-content-length="8m"
 spray.can.client.parsing.max-header-count=64
 spray.can.client.parsing.max-header-name-length=64
 spray.can.client.parsing.max-header-value-length="8k"
 spray.can.client.parsing.max-response-reason-length=64
 spray.can.client.parsing.max-uri-length="2k"
 spray.can.client.reaping-cycle="100 ms"
 spray.can.client.request-size-hint=512
 spray.can.client.request-timeout="5 s"
 spray.can.client.response-chunk-aggregation-limit="1m"
 spray.can.client.user-agent-header="spray-can/1.1-M7"
 spray.can.parsing.max-chunk-ext-count=16
 spray.can.parsing.max-chunk-ext-name-length=64
 spray.can.parsing.max-chunk-ext-value-length=256
 spray.can.parsing.max-chunk-size="1m"
 spray.can.parsing.max-content-length="8m"
 spray.can.parsing.max-header-count=64
 spray.can.parsing.max-header-name-length=64
 spray.can.parsing.max-header-value-length="8k"
 spray.can.parsing.max-response-reason-length=64
 spray.can.parsing.max-uri-length="2k"
 spray.can.server.chunkless-streaming="off"
 spray.can.server.idle-timeout="120 s"
 spray.can.server.parsing.max-chunk-ext-count=16
 spray.can.server.parsing.max-chunk-ext-name-length=64
 spray.can.server.parsing.max-chunk-ext-value-length=256
 spray.can.server.parsing.max-chunk-size="1m"
 spray.can.server.parsing.max-content-length="8m"
 spray.can.server.parsing.max-header-count=64
 spray.can.server.parsing.max-header-name-length=64
 spray.can.server.parsing.max-header-value-length="8k"
 spray.can.server.parsing.max-response-reason-length=64
 spray.can.server.parsing.max-uri-length="2k"
 spray.can.server.pipelining-limit=8
 spray.can.server.reaping-cycle="100 ms"
 spray.can.server.remote-address-header="off"
 spray.can.server.request-chunk-aggregation-limit="1m"
 spray.can.server.request-timeout="30 s"
 spray.can.server.response-size-hint="1k"
 spray.can.server.server-header="spray-can/1.1-M7"
 spray.can.server.ssl-encryption="off"
 spray.can.server.stats-support="on"
 spray.can.server.timeout-handler=""
 spray.can.server.timeout-timeout="500 ms"
 spray.can.server.transparent-head-requests="on"
 spray.can.server.verbose-error-messages="off"
 spray.io.io-bridge-dispatcher.type="akka.spray.io.IOBridgeDispatcherConfigurator"
 spray.io.parallelism=1
 spray.io.read-buffer-size="4k"
 spray.io.tcp.keep-alive=0
 spray.io.tcp.no-delay=0
 spray.io.tcp.receive-buffer-size=0
 spray.io.tcp.send-buffer-size=0
 spray.routing.file-chunking-chunk-size="512k"
 spray.routing.file-chunking-threshold-size="1m"
 spray.routing.relaxed-header-parsing="off"
 spray.routing.render-vanity-footer="yes"
 spray.routing.verbose-error-messages="off"
 spray.util.log-actor-paths-with-dots="off"
 spray.util.log-actor-system-name="off"
 spray.version="1.1-M7"

Starting with Slick – Part 1

I have chosen slick to query a PostgreSql database for a scala + play 2 project I’m working on. Also, because I needed something stable I’ve chosen lifted embedding api because it’s stable (direct embedding is still in experimental state).

In order to help others (documentation has some limitations) I will post here some code I’ve written.

To map a specific table which looks like:

CREATE TABLE geo_location
(
 latitude double precision,
 longitude double precision NOT NULL,
 altitude double precision NOT NULL,
 id serial NOT NULL,
 CONSTRAINT geo_location_pkey PRIMARY KEY (id)
)

we have to write a mapper class:

import scala.slick.driver.PostgresDriver.simple._

case class GeoLocation(id: Option[Int], latitude: Option[Double], longitude: Double, altitude: Double)

/**
 * Define table "geo_location".
 */
object GeoLocations extends RichTable[GeoLocation]("geo_location") {
  def latitude = column[Double]("latitude")
  def longitude = column[Double]("longitude")
  def altitude = column[Double]("altitude")

   def * = id.? ~ latitude.? ~ longitude ~ altitude <> (GeoLocation, GeoLocation.unapply _)
  def forInsert = latitude.? ~ longitude ~ altitude <> ({ (lat, long, alt) => GeoLocation(None, lat, long, alt) },
    { g: GeoLocation => Some((g.latitude, g.longitude, g.altitude)) })

}

As you see, I had to declare id as Optional and also a project named forInsert (consider it a hack in order to force PostgreSql driver to auto-inc our id for the follwing case, an INSERT statement):

GeoLocations.forInsert.insert(GeoLocation(None, 22.23, 25.36, 22.22))

Also, RichTable is an abstract class in order to not declare ids for each Table I have but just extend this::

abstract class RichTable[T](name: String) extends Table[T](name) {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)

  val byId = createFinderBy(_.id)
}

5 tips to make your customers happy

I’m a freelancer in my free time.

I do some projects for fun to keep in touch with new technologies. I had the opportunity to work for clients living in US or Europe. And I tried to make great stuff to see them happy. Most of the times, I think I’ve succeded.

Here are a few tips I recommend (entirely from my experience).

1. Be honest

Yes, this is extremely important. Be honest about your skills. Be honest about what you know and what you don’t. Be honest about what you promise. Don’t say “I’m an expert” if you don’t are. I met so-called “experts” who wrote code which was very close to ruin the business of the client. It’s better to say “I’m not an expert” and write great code than to say “I’m an expert” and write poor code. Seriously.

2. Meet deadlines

Sometimes your client ask you for a time estimation. If you say 1 month, make sure you finish it a few days earlier. If you say one week, finish it after 5 days. Your client will really apreciate this.

3. Be available

Sometimes my clients have some urgent things to do. When I say urgent I don’t mean to add a feature which takes 12 days of work. I mean that my client found an ugly bug and wants to see it solved asap. Maybe you’re reading a programming book or play a game or just rest. But if you have the chance to hear skype/email buzz from one of your clients just answer them and be available. I promise you’ll get a great feedback when your project is done.

4. Communicate

I’ve seen a lof of complaints from clients who were dissapointed about their freelancers. In most of the situations, the biggest issue was communication. If you have a missunderstanding just ask your client. Don’t go on with the project spending client’s money on wrong direction and in the end tell “Sorry but I tought that…”. No! Just communicate. Ask. Answer.

5. Keep in touch

I’ve seen job postings like: “You need to keep in touch with me daily”. Do you know why this requests are there? Because that client met freelancers who was working 15 days on the product and sent to the client no mail about the progress. Please, don’t do this. Just keep him in touch with your progress. Your client will really apreciate this!.

Have fun and don’t forget: if you’re a great programmer, you will be first concerned about your client’s satisfaction and his product. Not money first. Not you first. Client first.

Happy programming.

Play Framework 2 – tutorials

As most of you may already know, I am a huge fan of Play Framework since 2010 (I remember when I discovered it and other colleagues I shown the demo laughed at me, not trusting this framework then, now I am sure they no longer laugh:).

Since 2011, I have done about 4 projects in this framework, mostly in my free time (what a pity that in 2010 I was not looking for a full-time job in Play… I would have been an expert now:). A few weeks ago, I started a new project in Play 2(check it here if you want), using Java (soon I ‘ll switch to Scala). Well, I just wanted to put down here some links to some great resources you may need to start with Play 2 quickly:

VIDEO: recording of James Ward JavaOne 2012 presentation, “Client/Server Apps with HTML5 and Java”

Text:

Books:

Have fun!!!

Ps: For Spring, Struts, JSF (even SEAM!) etc developers, a small advice for your future: the time has come to make a choice:)

Attach pdf invoice to completed orders’ emails in nopcommerce

As you know, when an order is completed, the user of any nopcommerce shop receives an email about this but that email does not contain any pdf invoice the user would like to see.

My approach to this situation was not to attach directly the pdf to the email because this require some headache (emails being saved in DB means you need to save pdf in DB which is ugly) but:

1. Include a link in the orderConfirmationEmail like this:

Please download your invoice from <a href=”http://www.myshop.com/orderdetails/pdfasguest/%Order.OrderId%”>here</a&gt;

(you do this in admin – messages template)

2. Add a new route for this link in RouterProvider.cs in //orders section

routes.MapLocalizedRoute("GetOrderPdfInvoiceAsGuest",
                           "orderdetails/pdfasguest/{orderId}",
                           new { controller = "Order", action = "GetPdfInvoiceAsGuest" },
                           new[] { "Nop.Web.Controllers" });

3. Add a method for this route in OrderController.cs

public ActionResult GetPdfInvoiceAsGuest(string orderId)
        {
            string orderIdDecoded = DecodeFrom64(orderId.ToString());

            var order = _orderService.GetOrderById(Int32.Parse(orderIdDecoded));
            if (order == null || order.Deleted)
                return new HttpUnauthorizedResult();

            string fileName = string.Format("order_{0}_{1}.pdf", order.OrderGuid, DateTime.Now.ToString("yyyy-MM-dd-HH-mm-ss"));
            string filePath = string.Format("{0}content\\files\\ExportImport\\{1}", this.Request.PhysicalApplicationPath, fileName);
            _pdfService.PrintOrderToPdf(order, _workContext.WorkingLanguage, filePath);
            var pdfBytes = System.IO.File.ReadAllBytes(filePath);
            return File(pdfBytes, "application/pdf", fileName);
        }

– as you notice, the order id in the link is decoded from base64 because I put orderId in the link to be base64 encoded to not be plain text easy-readable by a user (of couse, you can increase the security here if you want)

4. Last step, build Order.Id parameter for the link where rest of the parameters are built: MessageTokenProviders.cs, AddOrderTokes method:

 tokens.Add(new Token("Order.OrderId", EncodeTo64(order.Id.ToString())));

That’s all….

Hope to be helpfull for someone.

Basic commands for Entity Framework – Code First

When using EF – Code First approach in a .NET project, usually you have to follow these steps.

1. Add the EntityFramework NuGet package.

Project –> Manage NuGet Packages…
Note: If you don’t have the Manage NuGet Packages… option you should install the latest version of NuGet
Select the Online tab
Select the EntityFramework package
Click Install

2. Create derived context.

public class BloggingContext : DbContext
{
public DbSet Blogs { get; set; }
public DbSet Posts { get; set; }
}

3. Create Models

4. Create a connection string in the App.Config file with the same name as your context.

Dealing with Model Changes

1. Enable Code First Migrations for our BloggingContext.

  • Tools -> Library Package Manager -> Package Manager Console
  • Run the Enable-Migrations command in Package Manager Console

2. Make necessary changes to the model

3. Run the Add-Migration AddUrl command in Package Manager Console.

4. Run the Update-Database command in Package Manager Console.

 

Ps: This post is mostly a sum-up from here

Play Framework: Get checkbox form value in controller

I’ll post a short help for those who are struggling with this.

I have a Model class called Term which has two fields: name and selected

To show the state of a specific term (actually there are more terms, think about a Project having multiple terms, that’s why you see the name being selectedTerms) in a view I did:

#{form @ProjectController.update(project.alias), enctype:'multipart/form-data', class:'well form-horizontal'}
#{list terms, as:'term'}
<input type="checkbox" name="selectedTerms" value="${term.name}"/>
<span>${term.name}<span>
#{/list}
#{/form}

Now the main question is: how do I know which of these terms were selected by the user?

Well, Play let me to define these terms as a List, check below:

public static void update(String alias, List<String> selectedTerms) {
//play with selected terms
}

Please note: selectedTerms list will contain ONLY the terms which are selected (TRUE) by the user. Because I have their names (or ids or what you want) all the problems are solved:)

Ps: You will probably ask: how do you show these in the view after you saved them in db?

#{list terms, as:'term'}
<input type="checkbox" name="selectedTerms" value="${term.name}" ${term.selected ? 'checked':''}/>
<span>${term.name}</span>
#{/list}

My route entry for this is simple:

POST /projects/{alias}/update ProjectController.update

Good luck!