Where communities thrive


  • Join over 1.5M+ people
  • Join over 100K+ communities
  • Free without limits
  • Create your own community
People
Repo info
Activity
  • Sep 25 07:21
    coveralls commented #41
  • Sep 25 07:17
    solicode synchronize #41
  • Sep 25 07:15
    solicode commented #41
  • Sep 20 05:41
    solicode commented #41
  • Sep 20 05:41
    coveralls commented #41
  • Sep 20 05:38
    solicode synchronize #41
  • Sep 20 05:35
  • Sep 20 05:33
    solicode synchronize #41
  • Sep 20 05:32
    solicode commented #41
  • Sep 19 20:26
    vil1 commented #41
  • Sep 19 18:52
    solicode commented #41
  • Sep 19 17:40
    vil1 commented #41
  • Sep 19 16:41
    solicode commented #41
  • Sep 19 14:38
  • Sep 19 14:34
    solicode synchronize #41
  • Sep 19 14:31
  • Sep 19 14:29
    solicode opened #41
  • May 05 23:19
    vil1 commented #40
  • May 05 23:16

    vil1 on master

    bump cats version from 0.9.0 t… (compare)

  • May 05 23:16
    vil1 closed #40
David R. Bild
@drbild

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.

For the carriers, I have one project with Task and one with Clump (http://getclump.io/)
David R. Bild
@drbild
withFilter on Step looks wrong. Won't that throw an exception if the predicate doesn't hold? Version 1.x treats InternalServerError as the zero for Result, returning that inside a Left.
David R. Bild
@drbild
Here's a variant of version 2 with Step[A] generalized to StepT[F[_], A]: https://github.com/drbild/play-monadic-actions/commits/generalize-base-monad
I didn't update the scalaz or cats modules, so only the tests in core will pass.
This uses Daniel Spiewak's shims to abstract over the scalaz and cats typeclasses.
I could flesh this out, if you want to further consider it. But if you're set against the additional complexity I won't spend the time.
Valentin Kasas
@vil1
well, that's interesting !
Valentin Kasas
@vil1
David R. Bild
@drbild
Sure thing.
David R. Bild
@drbild
If this approach goes anywhere, we might experiment with making F[_] a parameter on MonadicActions, rather than having each implicit conversion polymorphic on F. Doing so might reduce the chance of inference issues and yield better error messages if any do crop up.

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] = ???
}

.

Valentin Kasas
@vil1
hey, someone has been busy lately XD
Valentin Kasas
@vil1
I've rejected your last PR (#21) but just pushed the same mechanism using no symlink
(snapshots are published on central)
Valentin Kasas
@vil1
hmm, there seems to be a specs2 exception with that setup :/
[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
this is not really helpful
David R. Bild
@drbild
Dropping to an earlier specs 2 version (3.1, I think) leads to the more useful:
[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
Looks like it's still a scalaz compatibility issue
David R. Bild
@drbild
Oh, target needs to be an absolute path. file("scalaz71/target") is relative.
David R. Bild
@drbild
Submitted a PR for this, but you might have a cleaner way of fixing it that it doesn't require so much manual path wrangling.
David R. Bild
@drbild
Generalizing the base monad with my initial approach leads to ambiguous implicit issues. The alternative I mentioned above (BaseMonadicActions[F[_]] and MonadicActions extends BaseMonadicActions[Future] may be the way forward. That's the approach I've used successfully in the past.
First though, a different concern. Right now the implicit 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.
David R. Bild
@drbild
Assuming we'll be generalizing the base monad at some point, perhaps something like this would allow both:
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._

   ...
}
Any initial thoughts here? I can flesh this out into a PR (without the base monad generalization initially) if you want to see more.
Valentin Kasas
@vil1
Hi
I'm a little more busy than usual right now, and it will be difficult to think more deeply about this during the next days
Valentin Kasas
@vil1
this StepT will look a lot like EitherT ^^
Valentin Kasas
@vil1
(but that more or less a naming question)
The layout you propose looks cleaner, I was thinking about something similar (without the carrier monad parametrization)
A thing that's worth keeping at mind is that at the end, even with another carrier monad, we need to end up with a Future[Either[Result, Result]] (that we "merge" to finally get a suitable Future[Result])
Daniel Spasojevic
@dspasojevic
@vil1 hi - I had a pebkac moment fooling around in our fork or play-monadic-actions - please ignore the PR.
Valentin Kasas
@vil1
ok then
Daniel Spasojevic
@dspasojevic
Thanks @vil1. We are looking forwards to 2.0
Valentin Kasas
@vil1
Great, this should come quite soon (there's already a 2.0.0-SNAPSHOT available on central, but it's still subject to important changes before the release)
David R. Bild
@drbild
Sorry I've been MIA; was on vacation for a long weekend. I'll get back to this later this week.
Valentin Kasas
@vil1
No worries, I've been quite busy too, should be a little better next week
Valentin Kasas
@vil1
Hi. I've just pushed some commits on 2.0 and published a fresh 2.0.0-SNAPSHOT, with some changes
  • upgraded to play 2.5.3
  • removed the MonadicActions trait
  • moved everything under io.kanaka.monadic.dsl (users only have to import io.kanaka.monadic.dsl._ to get the goodies)
  • got rid of the 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)
I think we're ready for a first release candidate (I'm not yet settled on the idea of generalizing the base monad), we need to update the README though
Valentin Kasas
@vil1
I'm in the process of releasing a 2.0.0-RC1 on Central
2.0 has been merged into master
Valentin Kasas
@vil1
@drbild are you around ?
Jannes Stubbemann
@stubbi42

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)

Valentin Kasas
@vil1
Oh dear, I'm so sorry I missed your message for 3 months+
You're right, what you want isn't achievable out of the box, but you can map your Future[Option] to a Future[Either[UserAlreadyDefined, Unit]]