Skip to content

Commit 24329e6

Browse files
committed
Added a ext-monocle module with a few extensions for Monocle
Fixes #38
1 parent 45038ea commit 24329e6

File tree

9 files changed

+116
-26
lines changed

9 files changed

+116
-26
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ libraryDependencies += "com.github.japgolly.scalajs-react" %%% "test" % "0.6.1"
5151
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "ext-scalaz71" % "0.6.1" // or
5252
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "ext-scalaz70" % "0.6.1"
5353

54+
// Monocle support
55+
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "ext-monocle" % "0.6.1"
56+
5457
// Extra features
5558
libraryDependencies += "com.github.japgolly.scalajs-react" %%% "extra" % "0.6.1"
5659
```
@@ -128,6 +131,9 @@ Also included are `runStateF` methods which use a `ChangeFilter` typeclass to co
128131
See [ScalazExamples](https://github.com/japgolly/scalajs-react/tree/master/example/src/main/scala/japgolly/scalajs/react/example/ScalazExamples.scala) for a taste.
129132
Take a look at the [ScalazReact module](https://github.com/japgolly/scalajs-react/tree/master/scalaz-7.1/src/main/scala/japgolly/scalajs/react/ScalazReact.scala) for the source.
130133

134+
#### Monocle
135+
A module with a extensions for [Monocle](https://github.com/julien-truffaut/Monocle) also exists under `ext-monocle`.
136+
131137

132138
Testing
133139
=======

core/src/main/scala/japgolly/scalajs/react/package.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -298,9 +298,9 @@ package object react {
298298
() => _c.state,
299299
(a: S, cb: OpCallback) => _c.setState(a, cb))
300300

301-
@inline def focusState[B](f: S => B)(g: (S, B) => S)(implicit C: CC) = new ComponentStateFocus[B](
301+
@inline def focusState[T](f: S => T)(g: (S, T) => S)(implicit C: CC) = new ComponentStateFocus[T](
302302
() => f(_c.state),
303-
(b: B, cb: OpCallback) => _c.setState(g(_c.state, b), cb))
303+
(b: T, cb: OpCallback) => _c.setState(g(_c.state, b), cb))
304304
}
305305

306306
final class ComponentStateFocus[B] private[react](

doc/CHANGELOG-0.7.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ https://gist.github.com/japgolly/c68482dbadb0077f550c
77
#### Changes
88
* Added a router! _(See the `extra` module)_.
99
* Moved `.experiment` into a new module called `extra`.
10+
* Added a `ext-monocle` module with a few extensions for [Monocle](https://github.com/julien-truffaut/Monocle).
1011
* More supported React tags and attributes.
1112
* More ScalazReact extensions: `{state,setState,modState,modStateF}IO`.
1213
* Removed deprecated methods marked for removal in 0.7.0.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package japgolly.scalajs.react
2+
3+
import monocle._
4+
import scalaz.Functor
5+
import japgolly.scalajs.react.ScalazReact._
6+
7+
object MonocleReact {
8+
9+
@inline implicit final def toMonRExtCompStateAccessOps[C[_]: CompStateAccess, S](c: C[S]) = new MonRExt_CompStateAccessOps(c)
10+
final class MonRExt_CompStateAccessOps[C[_], S](val _c: C[S]) extends AnyVal {
11+
// CompStateAccess[C] should really be a class param but then we lose the AnyVal
12+
type CC = CompStateAccess[C]
13+
14+
@inline def focusStateL[T](l: Lens[S, T])(implicit C: CC) = new ComponentStateFocus[T](
15+
() => l get _c.state,
16+
(t: T, cb: OpCallback) => _c.modState(l set t, cb))
17+
}
18+
19+
@inline implicit final class MonRExt_ReactSTOps[M[_], S, A](val _r: ReactST[M,S,A]) extends AnyVal {
20+
21+
@inline def zoomL[T](l: Lens[T, S])(implicit M: Functor[M]): ReactST[M, T, A] =
22+
ReactS.zoom[M, S, T, A](_r, l.get, (a, b) => l.set(b)(a))
23+
}
24+
25+
// Seriously, Scala, get your shit together.
26+
@inline final implicit def moarScalaHandHoldingMon[P,S](b: BackendScope[P,S]): MonRExt_CompStateAccessOps[ComponentScope_SS, S] = (b: ComponentScope_SS[S])
27+
@inline final implicit def moarScalaHandHoldingMon[P,S,B](b: ComponentScopeU[P,S,B]): MonRExt_CompStateAccessOps[ComponentScope_SS, S] = (b: ComponentScope_SS[S])
28+
}

project/Build.scala

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ object ScalajsReact extends Build {
8585
(_: Project).settings(s: _*)
8686
}
8787

88+
def extModuleName(shortName: String): PE =
89+
_.settings(name := s"ext-$shortName")
90+
8891
// ==============================================================================================
8992
lazy val root = Project("root", file("."))
9093
.aggregate(core, test, scalaz70, scalaz71, extra, ghpages)
@@ -104,7 +107,7 @@ object ScalajsReact extends Build {
104107

105108
lazy val test = project
106109
.configure(commonSettings, publicationSettings, utestSettings)
107-
.dependsOn(core, scalaz71, extra)
110+
.dependsOn(core, scalaz71, extra, monocle)
108111
.settings(
109112
name := "test",
110113
scalacOptions += "-language:reflectiveCalls")
@@ -113,16 +116,22 @@ object ScalajsReact extends Build {
113116
def scalazModule(name: String, version: String) = {
114117
val shortName = name.replaceAll("[^a-zA-Z0-9]+", "")
115118
Project(shortName, file(name))
116-
.configure(commonSettings, publicationSettings)
119+
.configure(commonSettings, publicationSettings, extModuleName(shortName))
117120
.dependsOn(core)
118121
.settings(
119-
Keys.name := s"ext-$shortName",
120122
libraryDependencies += "com.github.japgolly.fork.scalaz" %%% "scalaz-effect" % version)
121123
}
122124

123125
lazy val scalaz70 = scalazModule("scalaz-7.0", "7.0.6")
124126
lazy val scalaz71 = scalazModule("scalaz-7.1", "7.1.0-4")
125127

128+
// ==============================================================================================
129+
lazy val monocle = project
130+
.configure(commonSettings, publicationSettings, extModuleName("monocle"))
131+
.dependsOn(core, scalaz71)
132+
.settings(
133+
libraryDependencies += "com.github.japgolly.fork.monocle" %%% "monocle-core" % "1.0.1")
134+
126135
// ==============================================================================================
127136
lazy val extra = project
128137
.configure(commonSettings, publicationSettings)
@@ -132,7 +141,7 @@ object ScalajsReact extends Build {
132141

133142
// ==============================================================================================
134143
lazy val ghpages = Project("gh-pages", file("gh-pages"))
135-
.dependsOn(core, scalaz71, extra)
144+
.dependsOn(core, scalaz71, extra, monocle)
136145
.configure(commonSettings, useReact(), preventPublication)
137146
.settings(
138147
emitSourceMaps := false,

test/src/test/scala/japgolly/scalajs/react/CoreTest.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,5 +331,15 @@ object CoreTest extends TestSuite {
331331
.componentDidMount(scope => assert(scope.refs("test").get == null))
332332
}
333333
}
334+
335+
'inference {
336+
import TestUtil.Inference._
337+
def st_get: S => T = null
338+
def st_set: (S, T) => S = null
339+
340+
"BackendScope ops" - test[BackendScope[Unit, S] ](_.focusState[T](st_get)(st_set)).expect[ComponentStateFocus[T]]
341+
"ComponentScopeM ops" - test[ComponentScopeM[U, S, U] ](_.focusState[T](st_get)(st_set)).expect[ComponentStateFocus[T]]
342+
"ReactComponentM ops" - test[ReactComponentM[U, S, U, N]](_.focusState[T](st_get)(st_set)).expect[ComponentStateFocus[T]]
343+
}
334344
}
335345
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package japgolly.scalajs.react
2+
3+
import monocle._
4+
import utest._
5+
import React._
6+
import ScalazReact._
7+
import MonocleReact._
8+
9+
object MonocleTest extends TestSuite {
10+
11+
val tests = TestSuite {
12+
13+
'inference {
14+
import TestUtil.Inference._
15+
val lensST: Lens[S, T] = null
16+
val lensTS: Lens[T, S] = null
17+
18+
"BackendScope ops" - test[BackendScope[Unit, S] ](_ focusStateL lensST).expect[ComponentStateFocus[T]]
19+
"ComponentScopeM ops" - test[ComponentScopeM[U, S, U] ](_ focusStateL lensST).expect[ComponentStateFocus[T]]
20+
"ReactComponentM ops" - test[ReactComponentM[U, S, U, N]](_ focusStateL lensST).expect[ComponentStateFocus[T]]
21+
"ReactS.zoomL" - test[ReactST[M, S, A] ](_ zoomL lensTS ).expect[ReactST[M, T, A]]
22+
}
23+
}
24+
}

test/src/test/scala/japgolly/scalajs/react/ScalazTest.scala

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package japgolly.scalajs.react
22

33
import japgolly.scalajs.react.test.ReactTestUtils
44
import utest._
5-
import scalaz.{~>, StateT, Monad}
5+
import scalaz.StateT
66
import scalaz.effect.IO
77
import ScalazReact._
88

@@ -12,29 +12,18 @@ import ScalazReact._
1212
*/
1313
object ScalazTest extends TestSuite {
1414

15-
def test[A] = new {
16-
def apply[B](f: A => B) = new {
17-
def expect[C](implicit ev: B =:= C): Unit = ()
18-
}
19-
}
20-
21-
trait M[A]
22-
implicit val mMonad = null.asInstanceOf[Monad[M] with (M ~> IO)]
23-
trait S
24-
trait A
25-
trait B
26-
val c = null.asInstanceOf[ComponentScopeM[Unit, S, Unit]]
27-
type U = Unit
28-
type N = TopNode
29-
3015
val tests = TestSuite {
16+
3117
'inference {
18+
import TestUtil.Inference._
19+
3220
"runState(s.liftS)" - test[StateT[M,S,A] ](s => c.runState(s.liftS) ).expect[IO[A]]
3321
"_runState(f.liftS)" - test[B => StateT[M,S,A] ](s => c._runState(s.liftS)).expect[B => IO[A]]
34-
"BackendScope ops" - test[BackendScope[Unit, S] ](_.modStateIO(identity) ).expect[IO[Unit]]
35-
"ComponentScopeM ops" - test[ComponentScopeM[U, S, U] ](_.modStateIO(identity) ).expect[IO[Unit]]
36-
"ReactComponentM ops" - test[ReactComponentM[U, S, U, N]](_.modStateIO(identity) ).expect[IO[Unit]]
22+
"BackendScope ops" - test[BackendScope[Unit, S] ](_ modStateIO identity ).expect[IO[Unit]]
23+
"ComponentScopeM ops" - test[ComponentScopeM[U, S, U] ](_ modStateIO identity ).expect[IO[Unit]]
24+
"ReactComponentM ops" - test[ReactComponentM[U, S, U, N]](_ modStateIO identity ).expect[IO[Unit]]
3725
}
26+
3827
'runState {
3928
val c = ReactTestUtils.renderIntoDocument(CoreTest.SI())
4029
assert(c.state == 123)

test/src/test/scala/japgolly/scalajs/react/TestUtil.scala

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,27 @@ object TestUtil {
8383

8484
def assertTypeMismatch(e: CompileError): Unit =
8585
assertContains(e.msg, "type mismatch")
86-
}
86+
87+
// ===================================================================================================================
88+
object Inference {
89+
import scalaz.{Monad, ~>}
90+
import scalaz.effect.IO
91+
92+
def test[A] = new {
93+
def apply[B](f: A => B) = new {
94+
def expect[C](implicit ev: B =:= C): Unit = ()
95+
}
96+
}
97+
98+
trait M[A]
99+
trait S
100+
trait T
101+
trait A
102+
trait B
103+
val c = null.asInstanceOf[ComponentScopeM[Unit, S, Unit]]
104+
type U = Unit
105+
type N = TopNode
106+
107+
implicit val mMonad = null.asInstanceOf[Monad[M] with (M ~> IO)]
108+
}
109+
}

0 commit comments

Comments
 (0)