I'm migrating from play 2.3 to 2.5
Originally I have "DAOFactory" object
object DAOFactory {
def categoryDAO: CategoryDAO = AnormCategoryDAO
def itemDAO: ItemDAO = AnormItemDAO
def bidDAO: BidDAO = AnormBidDAO
def userDAO: UserDAO = AnormUserDAO
def feedStatsDAO: FeedStatsDAO = AnormFeedStatsDAO
}
and let's take "AnormCategoryDAO" as a example, and I have to change the "object" into a "Class"
object AnormCategoryDAO extends CategoryDAO {
val category = {
int("id") ~ str("display_name") ~ str("url_name") map {
case id~displayName~urlName => Category(id, displayName, urlName)
}
}
def create(displayName: String, urlName: String) = DB.withConnection { implicit c =>
SQL("INSERT INTO category(display_name, url_name) VALUES({displayName}, {urlName})").on(
'displayName -> displayName, 'urlName -> urlName).executeUpdate()
}
def findById(id: Int): Option[Category] = DB.withConnection { implicit c =>
SQL("SELECT * FROM category WHERE id = {id}").on('id -> id).as(category singleOpt)
}
def findByName(urlName: String): Option[Category] = DB.withConnection { implicit c =>
SQL("SELECT * FROM category WHERE url_name = {urlName}").on('urlName -> urlName).as(category singleOpt)
}
def all(): List[Category] = DB.withConnection { implicit c =>
SQL("SELECT * FROM category ORDER BY display_name").as(category *)
}
}
So I changed the OBJECT to CLASS and annotated with SINGLETON as below, and I changed "DB.withConnection" to "db.withConnection"
#Singleton
class AnormCategoryDAO #Inject()(db: Database) extends CategoryDAO {
val category = {
int("id") ~ str("display_name") ~ str("url_name") map {
case id~displayName~urlName => Category(id, displayName, urlName)
}
}
...
Now, "AnormCategoryDAO" is a Class. So I need to figure out a way to instantiate it with a default database.
But I don't know how to instantiate it.
object DAOFactory {
//def categoryDAO: CategoryDAO = AnormCategoryDAO
def userDAO: UserDAO = AnormUserDAO
def itemDAO: ItemDAO = AnormItemDAO
}
The question is, how do I inject the database and instantiate it?
I don't like to use guice or similar to di. with compile time di I can achieve that by using something like:
import play.api.db.slick.{DbName, SlickComponents}
trait TablesComponents extends BaseComponent with SlickComponents {
lazy val dbConf = api.dbConfig[JdbcProfile](DbName("default"))
lazy val myTable = new MyTable(dbConf.db)
lazy val otherTable = new OtherTable(dbConf.db)
}
You either have the dependency that is to be injected ready, in which case you may call new AnormCategoryDAO(myDb) directly, or you inject the AnormCategoryDAO wherever it is required (this could mean that dependency injection propagates all the way to the controllers, which are instantiated by Play).
For Example:
class CategoryService #Inject() (categoryDao: CategoryDAO) {
def findAll() = categoryDao.findAll()
}
Note that in this example, I used the abstract type CategoryDAO to refer to the categoryDAO. For this, you'll have to tell the dependency injection framework (typically Guice) which concreate class it should inject (binding). Alternatively, you could depend on AnormCategoryDAO directly.
How you can define custom bindings is documented here: https://www.playframework.com/documentation/2.5.x/ScalaDependencyInjection
Note that there is an alternative approach to dependency injection named compile time: https://www.playframework.com/documentation/2.5.x/ScalaCompileTimeDependencyInjection
Related
I need to write two functions to get the output format and the output index for file conversion. As part of this, I wrote a TransformSettings class for these methods and set the default value. And in the transformer class, I created a new object of TransformSettings class to get the default values for each job run. Also, I have another class called ParquetTransformer that extends Transformer where I want to change these default values. So I implemented like below.
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer{
def getTransformSettings: TransformSettings = {
new TransformSettings
}
def posttransform(table: AWSGlueDDL.Table):Dateframe ={
val indexAccess = getTransformSettings.getOuputIndex(table: AWSGlueDDL.Table)
........
}
}
class ParquetTransformer extends Transformer{
override def getTransformSettings: TransformSettings = {
val transformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
}
}
Is there a way to avoid creating a brand new object of TransformSettings in Transfomer class every time this is called?
Also is there a way to rewrite the code using Scala value class?
As #Dima proposed in the comments try to make TransformSettings a field / constructor parameter (a val) in the class Transformer and instantiate them outside
class TransformSettings{
def getOuputFormat: String = {
"orc"
}
def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("orc.column.index.access")
}
}
class Transformer(val transformSettings: TransformSettings) {
def posttransform(table: AWSGlueDDL.Table): DataFrame ={
val indexAccess = transformSettings.getOuputIndex(table: AWSGlueDDL.Table)
???
}
}
val parquetTransformSettings = new TransformSettings {
override def getOuputFormat: String = {
"parquet"
}
override def getOuputIndex(table: AWSGlueDDL.Table): Option[String] = {
table.StorageDescriptor.SerdeInfo.Parameters.get("parquet.column.index.access")
}
}
class ParquetTransformer extends Transformer(parquetTransformSettings)
You don't seem to need value classes (... extends AnyVal) now. They are more about unboxing, not about life-cycle management. TransformSettings and Transformer can't be value classes because they are not final (you're extending them in class ParquetTransformer extends Transformer... and new TransformSettings { ... }). By the way, value classes have many limatations
https://failex.blogspot.com/2017/04/the-high-cost-of-anyval-subclasses.html
https://github.com/scala/bug/issues/12271
Besides value classes, there are scala-newtype library in Scala 2 and opaque types in Scala 3.
My use case has case classes something like
case class Address(name:String,pincode:String){
override def toString =name +"=" +pincode
}
case class Department(name:String){
override def toString =name
}
case class emp(address:Address,department:Department)
I want to create a DSL like below.Can anyone share the links about how to create a DSL and any suggestions to achieve the below.
emp.withAddress("abc","12222").withDepartment("HR")
Update:
Actual use case class may have more fields close to 20. I want to avoid redudancy of code
I created a DSL using reflection so that we don't need to add every field to it.
Disclamer: This DSL is extremely weakly typed and I did it just for fun. I don't really think this is a good approach in Scala.
scala> create an Employee where "homeAddress" is Address("a", "b") and "department" is Department("c") and that_s it
res0: Employee = Employee(a=b,null,c)
scala> create an Employee where "workAddress" is Address("w", "x") and "homeAddress" is Address("y", "z") and that_s it
res1: Employee = Employee(y=z,w=x,null)
scala> create a Customer where "address" is Address("a", "b") and "age" is 900 and that_s it
res0: Customer = Customer(a=b,900)
The last example is the equivalent of writing:
create.a(Customer).where("address").is(Address("a", "b")).and("age").is(900).and(that_s).it
A way of writing DSLs in Scala and avoid parentheses and the dot is by following this pattern:
object.method(parameter).method(parameter)...
Here is the source:
// DSL
object create {
def an(t: Employee.type) = new ModelDSL(Employee(null, null, null))
def a(t: Customer.type) = new ModelDSL(Customer(null, 0))
}
object that_s
class ModelDSL[T](model: T) {
def where(field: String): ValueDSL[ModelDSL2[T], Any] = new ValueDSL(value => {
val f = model.getClass.getDeclaredField(field)
f.setAccessible(true)
f.set(model, value)
new ModelDSL2[T](model)
})
def and(t: that_s.type) = new { def it = model }
}
class ModelDSL2[T](model: T) {
def and(field: String) = new ModelDSL(model).where(field)
def and(t: that_s.type) = new { def it = model }
}
class ValueDSL[T, V](callback: V => T) {
def is(value: V): T = callback(value)
}
// Models
case class Employee(homeAddress: Address, workAddress: Address, department: Department)
case class Customer(address: Address, age: Int)
case class Address(name: String, pincode: String) {
override def toString = name + "=" + pincode
}
case class Department(name: String) {
override def toString = name
}
I really don't think you need the builder pattern in Scala. Just give your case class reasonable defaults and use the copy method.
i.e.:
employee.copy(address = Address("abc","12222"),
department = Department("HR"))
You could also use an immutable builder:
case class EmployeeBuilder(address:Address = Address("", ""),department:Department = Department("")) {
def build = emp(address, department)
def withAddress(address: Address) = copy(address = address)
def withDepartment(department: Department) = copy(department = department)
}
object EmployeeBuilder {
def withAddress(address: Address) = EmployeeBuilder().copy(address = address)
def withDepartment(department: Department) = EmployeeBuilder().copy(department = department)
}
You could do
object emp {
def builder = new Builder(None, None)
case class Builder(address: Option[Address], department: Option[Department]) {
def withDepartment(name:String) = {
val dept = Department(name)
this.copy(department = Some(dept))
}
def withAddress(name:String, pincode:String) = {
val addr = Address(name, pincode)
this.copy(address = Some(addr))
}
def build = (address, department) match {
case (Some(a), Some(d)) => new emp(a, d)
case (None, _) => throw new IllegalStateException("Address not provided")
case _ => throw new IllegalStateException("Department not provided")
}
}
}
and use it as emp.builder.withAddress("abc","12222").withDepartment("HR").build().
You don't need optional fields, copy, or the builder pattern (exactly), if you are willing to have the build always take the arguments in a particular order:
case class emp(address:Address,department:Department, id: Long)
object emp {
def withAddress(name: String, pincode: String): WithDepartment =
new WithDepartment(Address(name, pincode))
final class WithDepartment(private val address: Address)
extends AnyVal {
def withDepartment(name: String): WithId =
new WithId(address, Department(name))
}
final class WithId(address: Address, department: Department) {
def withId(id: Long): emp = emp(address, department, id)
}
}
emp.withAddress("abc","12222").withDepartment("HR").withId(1)
The idea here is that each emp parameter gets its own class which provides a method to get you to the next class, until the final one gives you an emp object. It's like currying but at the type level. As you can see I've added an extra parameter just as an example of how to extend the pattern past the first two parameters.
The nice thing about this approach is that, even if you're part-way through the build, the type you have so far will guide you to the next step. So if you have a WithDepartment so far, you know that the next argument you need to supply is a department name.
If you want to avoid modifying the origin classes you can use implicit class, e.g.
implicit class EmpExtensions(emp: emp) {
def withAddress(name: String, pincode: String) {
//code omitted
}
// code omitted
}
then import EmpExtensions wherever you need these methods
I would like to understand this error:
found : row.type (with underlying type _#TableElementType)
required: _1#TableElementType
Looks like I was very close, but what is this "1" in _1#TableElementType? Can I convert one in the other?
Edit: useful bits of codes for context (Play + Slick):
abstract class GenericDAO[T <: AbstractTable[_]](...) {
def table: TableQuery[T]
def insert(model: T#TableElementType) = db run (table += model)
}
trait TableObject[T <: AbstractTable[_]] {
def rowFromJson(jsObject: JsObject): T#TableElementType
def dao(driver: JdbcProfile, db: Database): GenericDAO[T]
}
// Controller Action with an instance implementing `tableObject` above:
val tableObject = tableObjectFactory("test")
val row = tableObject.rowFromJson(request.body.asJson.get)
val dao = tableObject.dao(driver, db) // tableObject has a DOA extending GenericDAO
dao.insert(row)
Example of tableObject:
object TestTable extends TableObject[Test] {
def dao(driver: JdbcProfile, db: Database) = new TestDAO(driver, db)
def rowFromJson(j: JsObject): TestRow = { TestRow(...) }
class TestDAO(...) extends GenericDAO[Test](driver, db) { ... }
}
I use a factory to get the right one from the url:
object TableObjectFactory {
def tableObjectFactory(name: String) = {
name match {
case "test" => TestTable
case "projects" => ProjectsTable
case "people" => PeopleTable
...
}
}
}
Although it doesn't explain much, it works if I make the DAO parse the request body and insert, instead of producing the row object separately and applying one of the DAO's methods on it.
I got all kinds of similar errors with names such as _$1#TableElementType, _1$u#TableElementType etc., but I think they are compiler aliases for different instances of the same class.
So the solution was to do
val j: JsValue = request.body.asJson.get
val tableObject: TableObject[_] = tableObjectFactory(table)
val dao = tableObject.dao(driver, db)
val res: Future[Int] = dao.insert(j)
where this new insert method now is abstract in GenericDAO, and in the concrete implementations takes a JsValue and parses it, then inserts:
class TestDAO(override val driver: JdbcProfile, override val db: Database) extends GenericDAO[Test](driver, db) {
import this.driver.api._
val table = TableQuery[Test]
//def insert(model: TestRow) = db run (table += model) // NO!
def insert(j: JsValue): Future[Int] = {
val row = TestRow(
(j \ "id").as[Int],
(j \ "name").as[String],
(j \ "value").as[Float],
(j \ "other").as[String]
)
db run (table += row)
}
}
At the same time, it makes Play forms completely useless, which is a good thing anyway.
I still get the same error, I have defined the marshaller (and imported it); it appears that the case class entry is not in context when the function is polymorphic. and this throws a Cannot find JsonWriter or JsonFormat type class for Case Class. Is there a reason why spray-json can not find the implicit marshaller for the case class, (even when defined) is this case class in context? Link to marshaller
import spray.json._
import queue.MedusaJsonProtocol._
object MysqlDb {
...
}
case class UserDbEntry(
id: Int,
username: String,
countryId: Int,
created: LocalDateTime
)
trait MysqlDb {
implicit lazy val pool = MysqlDb.pool
}
trait HydraMapperT extends MysqlDb {
val FetchAllSql: String
def fetchAll(currentDate: String): Future[List[HydraDbRow]]
def getJson[T](row: T): String
}
object UserHydraDbMapper extends HydraMapperT {
override val FetchAllSql = "SELECT * FROM user WHERE created >= ?"
override def fetchAll(currentDate: String): Future[List[UserDbEntry]] = {
pool.sendPreparedStatement(FetchAllSql, Array(currentDate)).map { queryResult =>
queryResult.rows match {
case Some(rows) =>
rows.toList map (x => rowToModel(x))
case None => List()
}
}
}
override def getJson[UserDbEntry](row: UserDbEntry): String = {
HydraQueueMessage(
tableType = HydraTableName.UserTable,
payload = row.toJson.toString()
).toJson.toString()
}
private def rowToModel(row: RowData): UserDbEntry = {
UserDbEntry (
id = row("id").asInstanceOf[Int],
username = row("username").asInstanceOf[String],
countryId = row("country_id").asInstanceOf[Int],
created = row("created").asInstanceOf[LocalDateTime]
)
}
}
payload = row.toJson.toString() Can't find marshaller for UserDbEntry
You have defined UserDbEntry locally and there is no JSON marshaller for that type. Add the following:
implicit val userDbEntryFormat = Json.format[UserDbEntry]
I'm not sure how you can call row.toJson given UserDbEntry is a local case class. There must be a macro in there somewhere, but it's fairly clear that it's not in scope for the local UserDbEntry.
Edit
Now that I see your Gist, it looks like you have a package dependency problem. As designed, it'll be circular. You have defined the JSON marshaller in package com.at.medusa.core.queue, which imports UserDbEntry, which depends on package com.at.medusa.core.queue for marshalling.
I'm trying to write a custom SPickler / Unpickler pair to work around some the current limitations of scala-pickling.
The data type I'm trying to pickle is a case class, where some of the fields already have their own SPickler and Unpickler instances.
I'd like to use these instances in my custom pickler, but I don't know how.
Here's an example of what I mean:
// Here's a class for which I want a custom SPickler / Unpickler.
// One of its fields can already be pickled, so I'd like to reuse that logic.
case class MyClass[A: SPickler: Unpickler: FastTypeTag](myString: String, a: A)
// Here's my custom pickler.
class MyClassPickler[A: SPickler: Unpickler: FastTypeTag](
implicit val format: PickleFormat) extends SPickler[MyClass[A]] with Unpickler[MyClass[A]] {
override def pickle(
picklee: MyClass[A],
builder: PBuilder) {
builder.beginEntry(picklee)
// Here we save `myString` in some custom way.
builder.putField(
"mySpecialPickler",
b => b.hintTag(FastTypeTag.ScalaString).beginEntry(
picklee.myString).endEntry())
// Now we need to save `a`, which has an implicit SPickler.
// But how do we use it?
builder.endEntry()
}
override def unpickle(
tag: => FastTypeTag[_],
reader: PReader): MyClass[A] = {
reader.beginEntry()
// First we read the string.
val myString = reader.readField("mySpecialPickler").unpickle[String]
// Now we need to read `a`, which has an implicit Unpickler.
// But how do we use it?
val a: A = ???
reader.endEntry()
MyClass(myString, a)
}
}
I would really appreciate a working example.
Thanks!
Here is a working example:
case class MyClass[A](myString: String, a: A)
Note that the type parameter of MyClass does not need context bounds. Only the custom pickler class needs the corresponding implicits:
class MyClassPickler[A](implicit val format: PickleFormat, aTypeTag: FastTypeTag[A],
aPickler: SPickler[A], aUnpickler: Unpickler[A])
extends SPickler[MyClass[A]] with Unpickler[MyClass[A]] {
private val stringUnpickler = implicitly[Unpickler[String]]
override def pickle(picklee: MyClass[A], builder: PBuilder) = {
builder.beginEntry(picklee)
builder.putField("myString",
b => b.hintTag(FastTypeTag.ScalaString).beginEntry(picklee.myString).endEntry()
)
builder.putField("a",
b => {
b.hintTag(aTypeTag)
aPickler.pickle(picklee.a, b)
}
)
builder.endEntry()
}
override def unpickle(tag: => FastTypeTag[_], reader: PReader): MyClass[A] = {
reader.hintTag(FastTypeTag.ScalaString)
val tag = reader.beginEntry()
val myStringUnpickled = stringUnpickler.unpickle(tag, reader).asInstanceOf[String]
reader.endEntry()
reader.hintTag(aTypeTag)
val aTag = reader.beginEntry()
val aUnpickled = aUnpickler.unpickle(aTag, reader).asInstanceOf[A]
reader.endEntry()
MyClass(myStringUnpickled, aUnpickled)
}
}
In addition to the custom pickler class, we also need an implicit def which returns a pickler instance specialized for concrete type arguments:
implicit def myClassPickler[A: SPickler: Unpickler: FastTypeTag](implicit pf: PickleFormat) =
new MyClassPickler