0

All methods in my DAO class that handles folders (the web application is about cloud storage, like google drive) has User as first parameter. This parameter is used so that the current user may only access folders that he or she owns.

Some of examples:

class FolderDAO {
    def findRoot(user: User): Future[Option[DBFolder]] = { /* implementation */}

    def findById(user: User, folderId: Long): Future[Option[DBFolder]] = { /* implementation */}

    def findChildren(user: User, folder: DBFolder): Future[List[DBFolder]] = { /* implementation */}
}

and so on.

There is a possibility that one day I will need a method there that does not need a User parameter, but it will still be in minority.

As you might imagine, the use of such methods is quite cumbersome.

I identified 3 solutions to the problem:

  1. Leave it as it is as readability is above all
  2. User service store current user and is accessible to all other services (and DAOs) (this looks like a global variable, but the service is injectable, so tests are ok)
  3. Implicit user parameter for these methods

With implicits:

def findRoot()(implicit user: User): Future[Option[DBFolder]]

def findById(folderId: Long)(implicit user: User): Future[Option[DBFolder]]

def findChildren(folder: DBFolder)(implicit user: User): Future[List[DBFolder]]

What do you think is the best option here?

This StackOverflow post gives some insights on the question, but doesn't help a lot, unfortunately

Community
  • 1
  • 1
Mironor
  • 1,157
  • 10
  • 25
  • 2
    While I like reading questions like yours, I fear that it's not meeting the requirements of SO as the answer is opinionated. Anyways... if `user` is accessed in almost all methods of `DAO`, I think it should be a member variable of `DAO`. If this isn't an option, I'd prefer your first alternative, as the second requires you to maintain a separate state in the service store, and the third requires you to have the correct implicit value in the scope - both options are prone to subtle errors. – Kulu Limpa Dec 11 '14 at 03:36
  • Thank you for your reply, at the end of the question I linked another question that is also opinionated, but was answered nevertheless. I'm trying to find a use for implicit variables as they are used all over Play framework and slick. The error in the third case is compile-time, which is not so bad in my opinion (as it's quite hard to make it run-time not intentionally) – Mironor Dec 11 '14 at 07:37

1 Answers1

2

I would probably use a fourth option:

trait UserService {
  def findRoot(): Future[Option[DBFolder]]
  def findById(folderId: Long): Future[Option[DBFolder]]
  def findChildren(folder: DBFolder): Future[List[DBFolder]]
}

def userService(user: User): UserService = ???

I'm generally skeptical to using implicit parameters just for saving some characters when calling the method. It decreases clarity for little benefit.

Jesper Nordenberg
  • 2,104
  • 11
  • 15
  • You mean "UserService" => "Folder service" (I edited the question accordingly) ? This is quite strange that no one is found of using implicit parameters (even myself, just checking if it is indeed a "scalish" way of doing things), I mean Play Framework and Slick are using them seamlessly, there should be a good use case for them. Anyway thanks a lot for the answer. – Mironor Dec 11 '14 at 15:40
  • 2
    Use whatever name you see fit :) Implicit parameters definitely have at least one valuable use case: emulating typeclasses. The main purpose of typeclasses is to facilitate usage of functions that use constrained data type polymorphism. When the function is not polymorphic the benefit of using implicit parameters are greatly reduced, and the reduced readability in call sites makes the slight benefit questionable. – Jesper Nordenberg Dec 11 '14 at 16:00