Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Identifiers trait #16

Open
bambuchaAdm opened this issue May 11, 2014 · 6 comments
Open

Remove Identifiers trait #16

bambuchaAdm opened this issue May 11, 2014 · 6 comments

Comments

@bambuchaAdm
Copy link
Contributor

Remove unneeded trait Identifiers. All class from him is not depend on cake classes, so they could be top level.

@yurishkuro
Copy link

Hi,
Unfortunately, the fix #15 to remove HasJdbcDriver from Identifiers did not do the trick, because as long as the Identifiers is a trait, even if I implement it with some helper object, the WithId and BaseId traits become incompatible with the classes defining repositories, i.e. I get this error

type arguments [com.example.model.UserId,com.example.model.User] do not conform to class
IdTable's type parameter bounds [Id <: Tables.this.BaseId,Entity <: Tables.this.WithId[Id]]

There is a general problem with the sample usage of Unicorn shown in the README:

import package com.example.Unicorn._
import package com.example.Unicorn.driver.simple._

case class UserId(id: Long) extends AnyVal with BaseId

In order to use BaseId I already need to have a concrete instance of the Unicorn object, but all I am doing is defining a data model, I don't know which JdbcDriver I will end up using.

I think the simplest solution is to remove Identifiers traits and make WithId / BaseId into top level traits.

@bambuchaAdm
Copy link
Contributor Author

@javajumbo Resolving this issue could be problematic in terms of unicorn 0.6.x.

We introduce parameter to Unicorn for Underlying type. It's easy to resolve definition of data in this use case.
When you want to avoid choosing the driver your implementation of unicorn could be like this

package com.example

import org.virtuslab.unicorn.{HasJdbcDriver, UnicornCore}
import scala.slick.driver.JdbcDriver

object Unicorn extends UnicornCore[Long] with HasJdbcDriver { 
   override val driver: JdbcDriver = ??? 
}

using this would be same as using in core examples.

import package com.example.Unicorn._
import package com.example.Unicorn.driver.simple._

case class UserId(id: Long) extends AnyVal with BaseId

@yurishkuro
Copy link

Hi - I am not sure if I follow your comment fully, but I've looked at the commits linked to this issue, and they look pretty much along the lines I was expecting, so should solve my issue. I will give it a try by replacing my custom hack with 0.6 version.

@yurishkuro
Copy link

@bambuchaAdm

do you guys have a working example of configuring UnicornCore without static driver selection? I used to be able to define my model classes without any cake pattern, simply as

import org.virtuslab.unicorn._

case class UserId(id: Long) extends BaseId

Not it seems even for a simple data model I have to import from an object extending unicorn. In such object I assume I cannot just leave the driver member as ???, can I?

It seems in 0.6.x the cake pattern was extended way more than needed.

@bambuchaAdm
Copy link
Contributor Author

We want to make usage of unicorn as simple as possible. The main reason that cake pattern is used in project is that slick use it heavily.

The core example shows how to use unicorn without using cake in project. Right after you define your bake cake

package com.example

import org.virtuslab.unicorn.{HasJdbcDriver, LongUnicornCore}
import scala.slick.driver.H2Driver

object Unicorn extends LongUnicornCore with HasJdbcDriver {
  val driver = H2Driver
}

the usage in code is simply as you describe

import package com.example.Unicorn._
import package com.example.Unicorn.driver.simple._

Not it seems even for a simple data model I have to import from an object extending unicorn. In such object I assume I cannot just leave the driver member as ???, can I?

If this solution is temporally, ex. database will be chosen later but still in project) yes, but you will be unable to perform any runtime test (exception on loading class will be thrown. I was tested this scenario on previous response).

Moreover you could make your implementation like this

object ExampleUnicorn
    extends LongUnicornCore
    with HasJdbcDriver {

  override lazy val driver = {
    // here load database driver using config and reflection
  }
}

Implementation like this is used in play-slick in Config and dynamical choose which config should be used.

So, for development (differed choose of static driver) you could use ??? for driver. For more dynamic solution you could use lazy val.

It seems in 0.6.x the cake pattern was extended way more than needed.

I didn't see other way how to implement parametrization for arbitrary Underlaying type for BaseId and don't brake signature for those class in client code. Unfortunately API has changed but there is easy migration way.

@yurishkuro
Copy link

Hm, I think we're back to my original problem. Even with the parametrization, the data model should not need anything more than static imports

import org.virtuslab.unicorn._

case class UserId(id: Long) extends BaseId[Long]
case class PictureId(id: String) extends BaseId[String]

Then when defining the Unicorn class, I wouldn't expect to provide a parameter at the top level, because the business function of the Unicorn class is to wire in the driver implementation. Which ID type to use on the tables is already known when we use the types defined above in the IdTable:

class Pictures(tag: Tag) extends IdTable[PictureId, Picture](tag, "PICTURES")

Here we know that the ID type is String.

The problem I have with the Play pattern is it forces the driver selection too early. Yes, you can delay it by reading it from the config, but it means I need to have a config even to run the unit tests. With 0.5.x (after I hacked it to remove the Identities trait) I simply had a trait SlickTestSupport I needed to mix into the unit test classes, where the DAL object was initialized with the underlying Unicorn object.

trait SlickTestSupport {
    val db = SlickTestSupport.database
    val dal = SlickTestSupport.dal
}

object TestUnicorn extends LongUnicornCore with HasJdbcDriver {
    override val driver = H2Driver
}

object SlickTestSupport extends Logging {
    val database = Database.forURL("jdbc:h2:mem:test1;DB_CLOSE_DELAY=-1", driver = "org.h2.Driver")

    val dal = {
        val dal = new MyDAL {
            override lazy val unicorn = TestUnicorn
            def db = database
        }
        try {
            dal.createSchema
        } catch {
            case t: Throwable => {
                log.error("Failed to create schema", t)
                throw t
            }
        }
        dal
    }
}

Then in the real application, I would instantiate MyDAL with a driver loaded based on the configuration and let Spring inject it where needed. As a result, I have data model independent of the driver, all knowledge of Unicorn and Slick is encapsulated inside the MyDAL class.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants