vil1 on master
Update README.md (compare)
vil1 on v2.2.1
vil1 on master
Fix cross-building definition a… (compare)
vil1 on cross-build
Fix cross-building definition a… (compare)
vil1 on sbt-ci-release
vil1 on sbt-ci-release
Setup sbt-ci-release Fix build.sbt compilation Fix tests compilation (warning) and 1 more (compare)
vil1 on sbt-ci-release
vil1 on sbt-ci-release
Fix cross-building definition a… (compare)
Ending with scalaz
and cats
should be OK as long as they aren't at the same level as a wildcard import.
import io.kanaka.play._
import compat.scalaz._ // or FQed as import io.kanaka.play.compat.scalaz._
is OK.
import io.kanaka.play._
import scalaz._ // or FQed as import io.kanaka.play.scalaz._
is bad because the io.kanaka.play.scalaz
masks the real scalaz.
Step[A]
generalized to StepT[F[_], A]
: https://github.com/drbild/play-monadic-actions/commits/generalize-base-monad
scalaz
or cats
modules, so only the tests in core
will pass.
shims
to abstract over the scalaz
and cats
typeclasses.
Something like
trait BaseMonadicActions[F[_]] {
implicit def fOptionToStepOps[A](fOption: F[Option[A]])(implicit F: shims.Monad[F]): StepTOps[F, A] = ???
...
}
and
trait MonadicActions extends BaseMonadicActions[Future] {
implicit val futureMonad: shims.Monad[Future] = ???
}
.
[play-monadic-actions-scalaz_7.1] $ test
[error]
[error]
[error] This looks like a specs2 exception...
[error] Please report it with the preceding stacktrace at http://github.com/etorreborre/specs2/issues
[error]
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error] io.kanaka.play.ScalazStepOpsSpec
[error] (scalaz71/test:test) sbt.TestsFailedException: Tests unsuccessful
[error] Total time: 1 s, completed 20 avr. 2016 16:22:38
[error] Uncaught exception when running io.kanaka.play.ScalazStepOpsSpec: java.lang.ClassCastException: scalaz.Free$Gosub cannot be cast to scala.Tuple2
[trace] Stack trace suppressed: run last scalaz72/test:test for the full output.
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error] io.kanaka.play.ScalazStepOpsSpec
[error]
[error] java.lang.ClassNotFoundException: io.kanaka.play.ScalazStepOpsSpec
[error]
[error] STACKTRACE
[error] java.net.URLClassLoader.findClass(URLClassLoader.java:381)
...elided
[error] java.lang.Thread.run(Thread.java:745)
[error]
[error]
[error] This looks like a specs2 exception...
[error] Please report it with the preceding stacktrace at http://github.com/etorreborre/specs2/issues
[error]
[error] Error: Total 1, Failed 0, Errors 1, Passed 0
[error] Error during tests:
[error] io.kanaka.play.ScalazStepOpsSpec
BaseMonadicActions[F[_]]
and MonadicActions extends BaseMonadicActions[Future]
may be the way forward. That's the approach I've used successfully in the past.
toStepOps
defs in the compat
modules are brought into scope via imports but those in core
are brought in by extending the MonadicActions
trait. It'd be nice to standardize.
trait BaseMonadicActions[F[_]] {
implicit def optionToStepOps[A](option: Option[A]): StepTOps[F, A, Unit] = ???
...
}
trait ScalazMonadicActions[F[_]] {
implicit def disjunctionToStepOps[A, B](disjunction: A \/ B): StepTOps[F, B, A] = ???
...
}
trait MonadicActions extends BaseMonadicActions[Future] // backward compatibility
object io.kanaka.play.future extends BaseMonadicActions[Future]
object io.kanaka.play.compat.scalaz.future extends ScalazMonadicActions[Future]
class ControllerViaTraits() extends Controller with BaseMonadicActions[Future] with ScalazMonadicActions[Future] {
...
}
class ControllerViaImports() extends Controller {
import io.kanaka.play.future._
import io.kanaka.play.compat.scalaz.future._
...
}
Future[Either[Result, Result]]
(that we "merge" to finally get a suitable Future[Result]
)
MonadicActions
traitio.kanaka.monadic.dsl
(users only have to import io.kanaka.monadic.dsl._
to get the goodies)ExecutionContext
in Step
, it is now passed via an implicit parameter everywhere (meaning that users must have an implicit ExecutionContext
in scope for the DSL to work)Hi! I have a case where I am retrieving an optional user (to see whether there exists already a user with the email adress that tries to sign up). And the happy path would be the case when there is None
returned (then signup process goes on). Otherwise I want to return and tell the user that his email is already signed up. For me, the library seems not to be designed for such a use case. But maybe you can tell me some smart way to do it? Here is the code I want to transform:
def submit = silhouette.UnsecuredAction.async { implicit request: Request[AnyContent] =>
SignUpForm.form.bindFromRequest.fold(
form => Future.successful(BadRequest(views.html.signUp(form))),
data => {
val result = Redirect(routes.SignUpController.view()).flashing("info" -> Messages("sign.up.email.sent", data.email))
val loginInfo = LoginInfo(CredentialsProvider.ID, data.email)
userService.retrieve(loginInfo).flatMap {
case Some(user) =>
val url = routes.SignInController.view().absoluteURL()
mailerClient.send(Email(
subject = Messages("email.already.signed.up.subject"),
from = Messages("email.from"),
to = Seq(data.email),
bodyText = Some(views.txt.emails.alreadySignedUp(user, url).body),
bodyHtml = Some(views.html.emails.alreadySignedUp(user, url).body)
))
Future.successful(result)
case None =>
val authInfo = passwordHasherRegistry.current.hash(data.password)
val user = User(
userID = UUID.randomUUID(),
loginInfo = loginInfo,
firstName = Some(data.firstName),
lastName = Some(data.lastName),
fullName = Some(data.firstName + " " + data.lastName),
email = Some(data.email),
avatarURL = None,
activated = false
)
for {
avatar <- avatarService.retrieveURL(data.email)
user <- userService.save(user.copy(avatarURL = avatar))
authInfo <- authInfoRepository.add(loginInfo, authInfo)
authToken <- authTokenService.create(user.userID)
} yield {
val url = routes.ActivateAccountController.activate(authToken.id).absoluteURL()
mailerClient.send(Email(
subject = Messages("email.sign.up.subject"),
from = Messages("email.from"),
to = Seq(data.email),
bodyText = Some(views.txt.emails.signUp(user, url).body),
bodyHtml = Some(views.html.emails.signUp(user, url).body)
))
silhouette.env.eventBus.publish(SignUpEvent(user, request))
result
}
}
}
(from here)
Future[Option]
to a Future[Either[UserAlreadyDefined, Unit]]