Skip to content

Commit

Permalink
feat(scala3): WIP working on scala 3 cross compilation. playframework…
Browse files Browse the repository at this point in the history
  • Loading branch information
brbrown25 committed Jan 4, 2022
1 parent ea8d3c5 commit 64097c4
Show file tree
Hide file tree
Showing 19 changed files with 1,109 additions and 41 deletions.
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ jobs:
- SCALA_VERSION=2.13.7
- ADOPTOPENJDK=11

- name: "Run tests with Scala 3 and AdoptOpenJDK 11"
script: scripts/test-code.sh
env:
- TRAVIS_SCALA_VERSION=3.1.0
- ADOPTOPENJDK=11

- name: "Run tests with Scala 2.12 and AdoptOpenJDK 8"
script: scripts/test-code.sh
env:
Expand All @@ -40,6 +46,12 @@ jobs:
- SCALA_VERSION=2.13.7
- ADOPTOPENJDK=8

- name: "Run tests with Scala 3 and AdoptOpenJDK 8"
script: scripts/test-code.sh
env:
- TRAVIS_SCALA_VERSION=3.1.0
- ADOPTOPENJDK=8

- stage: docs
script: scripts/validate-docs.sh
name: "Validate documentation"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl.api

import java.util.Optional
import scala.collection.immutable
import scala.jdk.CollectionConverters._
import scala.reflect.ClassTag

case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) {
// The overloaded methods are here for speed. The compiled templates
// can take advantage of them for a 12% performance boost
def _display_(x: AnyVal): T = format.escape(x.toString)
def _display_(x: String): T = if (x eq null) format.empty else format.escape(x)
def _display_(x: Unit): T = format.empty
def _display_(x: scala.xml.NodeSeq): T = if (x eq null) format.empty else format.raw(x.toString())
def _display_(x: T): T = if (x eq null) format.empty else x

def _display_(o: Any)(implicit m: ClassTag[T]): T = {
o match {
case escaped if escaped != null && escaped.getClass == m.runtimeClass => escaped.asInstanceOf[T]
case () => format.empty
case None => format.empty
case Some(v) => _display_(v)
case key: Optional[_] =>
(if (key.isPresent) Some(key.get) else None) match {
case None => format.empty
case Some(v) => _display_(v)
case _ => format.empty
}
case xml: scala.xml.NodeSeq => format.raw(xml.toString())
case escapeds: immutable.Seq[_] => format.fill(escapeds.map(_display_))
case escapeds: TraversableOnce[_] => format.fill(escapeds.iterator.map(_display_).toList)
case escapeds: Array[_] => format.fill(escapeds.view.map(_display_).toList)
case escapeds: java.util.List[_] =>
format.fill(escapeds.asScala.map(_display_).toList)
case string: String => format.escape(string)
case v if v != null => format.escape(v.toString)
case _ => format.empty
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl.api

import scala.language.implicitConversions

/**
* Imports for useful Twirl helpers.
*/
object TwirlHelperImports {

/** Allows Java collections to be used as if they were Scala collections. */
implicit def twirlJavaCollectionToScala[T](x: java.lang.Iterable[T]): Iterable[T] = {
import scala.jdk.CollectionConverters._
x.asScala
}

/** Allows inline formatting of java.util.Date */
implicit class TwirlRichDate(date: java.util.Date) {
def format(pattern: String): String = {
new java.text.SimpleDateFormat(pattern).format(date)
}
}

/** Adds a when method to Strings to control when they are rendered. */
implicit class TwirlRichString(string: String) {
def when(predicate: => Boolean): String = {
predicate match {
case true => string
case false => ""
}
}
}
}
47 changes: 47 additions & 0 deletions api/shared/src/main/scala-2.13/play/twirl/api/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl

import scala.reflect.ClassTag

package object api {

/**
* Brings the template engine as a
* [[http://docs.scala-lang.org/overviews/core/string-interpolation.html string interpolator]].
*
* Basic usage:
*
* {{{
* import play.twirl.api.StringInterpolation
*
* val name = "Martin"
* val htmlFragment: Html = html"&lt;div&gt;Hello \$name&lt;/div&gt;"
* }}}
*
* Three interpolators are available: `html`, `xml` and `js`.
*/
implicit class StringInterpolation(val sc: StringContext) extends AnyVal {
def html(args: Any*): Html = interpolate(args, HtmlFormat)

def xml(args: Any*): Xml = interpolate(args, XmlFormat)

def js(args: Any*): JavaScript = interpolate(args, JavaScriptFormat)

def interpolate[A <: Appendable[A]: ClassTag](args: Seq[Any], format: Format[A]): A = {
StringContext.checkLengths(args, sc.parts)
val array = Array.ofDim[Any](args.size + sc.parts.size)
val strings = sc.parts.iterator
val expressions = args.iterator
array(0) = format.raw(strings.next())
var i = 1
while (strings.hasNext) {
array(i) = expressions.next()
array(i + 1) = format.raw(strings.next())
i += 2
}
new BaseScalaTemplate[A, Format[A]](format)._display_(array)
}
}
}
43 changes: 43 additions & 0 deletions api/shared/src/main/scala-3/play/twirl/api/BaseScalaTemplate.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl.api

import java.util.Optional
import scala.collection.immutable
import scala.jdk.CollectionConverters._
import scala.reflect.ClassTag

case class BaseScalaTemplate[T <: Appendable[T], F <: Format[T]](format: F) {
// The overloaded methods are here for speed. The compiled templates
// can take advantage of them for a 12% performance boost
def _display_(x: AnyVal): T = format.escape(x.toString)
def _display_(x: String): T = if (x eq null) format.empty else format.escape(x)
def _display_(x: Unit): T = format.empty
def _display_(x: scala.xml.NodeSeq): T = if (x eq null) format.empty else format.raw(x.toString())
def _display_(x: T): T = if (x eq null) format.empty else x

def _display_(o: Any)(implicit m: ClassTag[T]): T = {
o match {
case escaped if escaped != null && escaped.getClass == m.runtimeClass => escaped.asInstanceOf[T]
case () => format.empty
case None => format.empty
case Some(v) => _display_(v)
case key: Optional[_] =>
(if (key.isPresent) Some(key.get) else None) match {
case None => format.empty
case Some(v) => _display_(v)
case null => format.empty
}
case xml: scala.xml.NodeSeq => format.raw(xml.toString())
case escapeds: immutable.Seq[_] => format.fill(escapeds.map(_display_))
case escapeds: TraversableOnce[_] => format.fill(escapeds.iterator.map(_display_).toList)
case escapeds: Array[_] => format.fill(escapeds.view.map(_display_).toList)
case escapeds: java.util.List[_] =>
format.fill(escapeds.asScala.map(_display_).toList)
case string: String => format.escape(string)
case v if v != null => format.escape(v.toString)
case null => format.empty
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl.api

import scala.language.implicitConversions

/**
* Imports for useful Twirl helpers.
*/
object TwirlHelperImports {

/** Allows Java collections to be used as if they were Scala collections. */
implicit def twirlJavaCollectionToScala[T](x: java.lang.Iterable[T]): Iterable[T] = {
import scala.jdk.CollectionConverters._
x.asScala
}

/** Allows inline formatting of java.util.Date */
implicit class TwirlRichDate(date: java.util.Date) {
def format(pattern: String): String = {
new java.text.SimpleDateFormat(pattern).format(date)
}
}

/** Adds a when method to Strings to control when they are rendered. */
implicit class TwirlRichString(string: String) {
def when(predicate: => Boolean): String = {
predicate match {
case true => string
case false => ""
}
}
}
}
47 changes: 47 additions & 0 deletions api/shared/src/main/scala-3/play/twirl/api/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
* Copyright (C) Lightbend Inc. <https://www.lightbend.com>
*/
package play.twirl

import scala.reflect.ClassTag

package object api {

/**
* Brings the template engine as a
* [[http://docs.scala-lang.org/overviews/core/string-interpolation.html string interpolator]].
*
* Basic usage:
*
* {{{
* import play.twirl.api.StringInterpolation
*
* val name = "Martin"
* val htmlFragment: Html = html"&lt;div&gt;Hello \$name&lt;/div&gt;"
* }}}
*
* Three interpolators are available: `html`, `xml` and `js`.
*/
implicit class StringInterpolation(val sc: StringContext) extends AnyVal {
def html(args: Any*): Html = interpolate(args, HtmlFormat)

def xml(args: Any*): Xml = interpolate(args, XmlFormat)

def js(args: Any*): JavaScript = interpolate(args, JavaScriptFormat)

def interpolate[A <: Appendable[A]: ClassTag](args: Seq[Any], format: Format[A]): A = {
StringContext.checkLengths(args, sc.parts)
val array = Array.ofDim[Any](args.size + sc.parts.size)
val strings = sc.parts.iterator
val expressions = args.iterator
array(0) = format.raw(strings.next())
var i = 1
while (strings.hasNext) {
array(i) = expressions.next()
array(i + 1) = format.raw(strings.next())
i += 2
}
new BaseScalaTemplate[A, Format[A]](format)._display_(array)
}
}
}
14 changes: 7 additions & 7 deletions api/shared/src/main/scala/play/twirl/api/TemplateMagic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ object TemplateMagic {

// --- IF

implicit def iterableToBoolean(x: Iterable[_]) = x != null && !x.isEmpty
implicit def optionToBoolean(x: Option[_]) = x != null && x.isDefined
implicit def stringToBoolean(x: String) = x != null && !x.isEmpty
implicit def iterableToBoolean(x: Iterable[_]): Boolean = x != null && !x.isEmpty
implicit def optionToBoolean(x: Option[_]): Boolean = x != null && x.isDefined
implicit def stringToBoolean(x: String): Boolean = x != null && !x.isEmpty

// --- JAVA

implicit def javaCollectionToScala[T](x: java.lang.Iterable[T]) = {
implicit def javaCollectionToScala[T](x: java.lang.Iterable[T]): Iterable[T] = {
import scala.collection.JavaConverters._
x.asScala
}
Expand All @@ -42,7 +42,7 @@ object TemplateMagic {
}
}

implicit def anyToDefault(x: Any) = Default(x)
implicit def anyToDefault(x: Any): Default = Default(x)

// --- DATE

Expand All @@ -52,7 +52,7 @@ object TemplateMagic {
}
}

implicit def richDate(date: java.util.Date) = new RichDate(date)
implicit def richDate(date: java.util.Date): RichDate = new RichDate(date)

// --- STRING

Expand All @@ -65,5 +65,5 @@ object TemplateMagic {
}
}

implicit def richString(string: String) = new RichString(string)
implicit def richString(string: String): RichString = new RichString(string)
}
Loading

0 comments on commit 64097c4

Please sign in to comment.