Skip to content

Commit 612f41e

Browse files
committed
* minor code cleaning
1 parent 0e3183d commit 612f41e

File tree

6 files changed

+50
-54
lines changed

6 files changed

+50
-54
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ flexibility of the form design.
1616
Currently it is available as a sonatype snapshot, add this to your build.sbt:
1717

1818
```
19-
libraryDependencies += "com.github.torstenrudolf.scalajs-react-form-binder" %%% "core" % "0.0.7-SNAPSHOT"
19+
libraryDependencies += "com.github.torstenrudolf.scalajs-react-form-binder" %%% "core" % "0.0.9-SNAPSHOT"
2020
```
2121

2222
If you want to use the materialui form-field descriptors, you'll need this as well:
2323
```
24-
libraryDependencies += "com.github.torstenrudolf.scalajs-react-form-binder" %%% "extras" % "0.0.7-SNAPSHOT"
24+
libraryDependencies += "com.github.torstenrudolf.scalajs-react-form-binder" %%% "extras" % "0.0.9-SNAPSHOT"
2525
```
2626

2727

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ def commonSettings: Project => Project =
33
_.enablePlugins(ScalaJSPlugin)
44
.settings(
55
organization := "com.github.torstenrudolf.scalajs-react-form-binder",
6-
version := "0.0.7-SNAPSHOT",
6+
version := "0.0.9-SNAPSHOT",
77
homepage := Some(url("https://github.com/torstenrudolf/scalajs-react-form-binder")),
88
licenses += ("MIT", url("https://opensource.org/licenses/MIT")),
99
scalaVersion := "2.11.8",

core/src/main/scala/torstenrudolf/scalajs/react/formbinder/Macros.scala

Lines changed: 39 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,19 @@ import scala.util.{Failure, Success, Try}
1010
object Macros {
1111
// Thanks to https://github.com/Voltir/form.rx for the idea!
1212

13-
def getCaseClassArgumentsDefaultValues(c: scala.reflect.macros.blackbox.Context)(tpe: c.Type): c.Expr[Map[String, Any]] = {
14-
// found at http://stackoverflow.com/a/21970758
13+
def getDefaultValuesFromCompanionObject(c: scala.reflect.macros.blackbox.Context)(companion: c.Expr[Any]): c.Expr[Map[String, Any]] = {
1514
import c.universe._
16-
val classSym = tpe.typeSymbol
17-
val moduleSym = classSym.companionSymbol
18-
val apply = moduleSym.typeSignature.declaration(newTermName("apply")).asMethod
15+
val applyMethod = companion.actualType.decls.find(_.name == TermName("apply")).get.asMethod
1916
// can handle only default parameters from the first parameter list
2017
// because subsequent parameter lists might depend on previous parameters
21-
val kvps = apply.paramss.head.map(_.asTerm).zipWithIndex.flatMap { case (p, i) =>
22-
if (!p.isParamWithDefault) None
18+
val paramName2DefaultValue = applyMethod.paramLists.head.map(_.asTerm).zipWithIndex.flatMap { case (p, i) =>
19+
if (!p.isParamWithDefault) Nil
2320
else {
24-
val getterName = newTermName("apply$default$" + (i + 1))
25-
Some(q"${p.name.toString} -> $moduleSym.$getterName")
21+
val getterName = TermName("apply$default$" + (i + 1))
22+
List(q"${p.name.toString} -> $companion.$getterName")
2623
}
2724
}
28-
c.Expr[Map[String, Any]](q"Map[String, Any](..$kvps)")
25+
c.Expr[Map[String, Any]](q"Map[String, Any](..$paramName2DefaultValue)")
2926
}
3027

3128
def getCompanion(c: scala.reflect.macros.blackbox.Context)(tpe: c.Type): c.universe.RefTree = {
@@ -36,29 +33,33 @@ object Macros {
3633
}
3734

3835
def generateWithoutDefault[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)
39-
(formLayout: c.Expr[FormLayout[T]],
40-
validatorObject: c.Expr[Any]): c.Expr[Form[T]] = {
41-
generate[T](c)(formLayout, validatorObject, None)
36+
(formLayout: c.Expr[FormLayout[T]],
37+
validatorObject: c.Expr[Any]): c.Expr[Form[T]] = {
38+
generate[T](c)(formLayout, validatorObject, c.universe.reify(None))
4239
}
4340

4441
def generateWithDefault[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)
45-
(formLayout: c.Expr[FormLayout[T]],
46-
validatorObject: c.Expr[Any],
47-
defaultModelValue: c.Expr[T]): c.Expr[Form[T]] = {
48-
generate[T](c)(formLayout, validatorObject, Some(defaultModelValue))
42+
(formLayout: c.Expr[FormLayout[T]],
43+
validatorObject: c.Expr[Any],
44+
defaultModelValue: c.Expr[T]): c.Expr[Form[T]] = {
45+
generate[T](c)(formLayout, validatorObject, c.universe.reify(Some(defaultModelValue.splice)))
4946
}
5047

51-
private def generate[T: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)
52-
(formLayout: c.Expr[FormLayout[T]],
53-
validatorObject: c.Expr[Any],
54-
defaultModelValue: Option[c.Expr[T]]): c.Expr[Form[T]] = {
48+
def generate[DataModel: c.WeakTypeTag](c: scala.reflect.macros.blackbox.Context)
49+
(formLayout: c.Expr[FormLayout[DataModel]],
50+
validatorObject: c.Expr[Any],
51+
defaultModelValue: c.Expr[Option[DataModel]]): c.Expr[Form[DataModel]] =
52+
{
5553

5654
import c.universe._
57-
val targetTpe = weakTypeOf[T]
58-
val targetCompanion = getCompanion(c)(targetTpe)
55+
56+
val dataModelTpe = weakTypeOf[DataModel]
57+
val dataModelCompanion = getCompanion(c)(dataModelTpe) // had problems when using abstract classes
58+
5959

6060
//Collect all fields for the target type
61-
val targetFields = targetTpe.decls.collectFirst {
61+
// val targetFields = dataModelCompanion.actualType.decls.find(_.name == TermName("apply")).get.asMethod.paramLists.head
62+
val targetFields = dataModelTpe.decls.collectFirst {
6263
case m: MethodSymbol if m.isPrimaryConstructor => m
6364
}.get.paramLists.head
6465

@@ -68,15 +69,11 @@ object Macros {
6869

6970
val targetFieldNames = targetFields.map(_.name.toString)
7071

71-
// val targetFieldDefaultValues = getCaseClassArgumentsDefaultValues(c)(targetTpe)
72-
// println(s"case class defaults: $targetFieldDefaultValues")
73-
// println(s"defaultModelValue: ${defaultModelValue}")
74-
7572
val targetFieldDefaultValues: c.Expr[Map[String, Any]] =
76-
if (defaultModelValue.isDefined) {
77-
c.Expr[Map[String, Any]](q"""Map[String, Any](..${targetFields.map(fn => (fn.asTerm.name.toString, q"$defaultModelValue.get.${fn.asTerm.name}"))})""")
78-
}
79-
else getCaseClassArgumentsDefaultValues(c)(targetTpe)
73+
c.Expr[Map[String, Any]](
74+
q"""
75+
$defaultModelValue.map((dmv: $dataModelTpe)=> Map[String, Any](..${targetFields.map(fn => (fn.asTerm.name.toString, q"dmv.${fn.asTerm.name}"))})).getOrElse(${getDefaultValuesFromCompanionObject(c)(c.Expr[Any](q"$dataModelCompanion"))})
76+
""")
8077

8178
val formFieldDescriptors = formLayout.actualType.members.map(_.asTerm).filter(_.isAccessor)
8279
.filter(_.asMethod.returnType.<:<(typeOf[FormFieldDescriptor[_]]))
@@ -137,15 +134,15 @@ object Macros {
137134
if (!globalTargetValidator.forall(v => {
138135
v.returnType.<:<(typeOf[ValidationResult]) &&
139136
v.paramLists.size == 1 && v.paramLists.head.size == 1 &&
140-
v.paramLists.head.head.info == targetTpe
137+
v.paramLists.head.head.info == dataModelTpe
141138
})) {
142139
c.abort(c.enclosingPosition,
143140
s"Type of global validator must match the target type. (def $$global(data: ${globalTargetValidator.get.accessed.info.typeArgs}): torstenrudolf.scalajs.react.formbinder.ValidationResult = ...)")
144141
}
145142

146143
val transformedGlobalTargetValidator =
147144
globalTargetValidator.map(v => q"$v")
148-
.getOrElse(q"(d: $targetTpe) => torstenrudolf.scalajs.react.formbinder.ValidationResult.Success")
145+
.getOrElse(q"(d: $dataModelTpe) => torstenrudolf.scalajs.react.formbinder.ValidationResult.Success")
149146

150147
case class CompoundField(targetField: c.universe.Symbol,
151148
defaultValueExpr: c.universe.Tree,
@@ -188,33 +185,33 @@ object Macros {
188185

189186
val newTree =
190187
q"""
191-
new torstenrudolf.scalajs.react.formbinder.Form[$targetTpe] with torstenrudolf.scalajs.react.formbinder.FormAPI[$targetTpe] {
192-
override val formLayout: torstenrudolf.scalajs.react.formbinder.FormLayout[$targetTpe] = $formLayout
188+
new torstenrudolf.scalajs.react.formbinder.Form[$dataModelTpe] with torstenrudolf.scalajs.react.formbinder.FormAPI[$dataModelTpe] {
189+
override val formLayout: torstenrudolf.scalajs.react.formbinder.FormLayout[$dataModelTpe] = $formLayout
193190

194-
override def globalValidator(data: $targetTpe): torstenrudolf.scalajs.react.formbinder.ValidationResult =
191+
override def globalValidator(data: $dataModelTpe): torstenrudolf.scalajs.react.formbinder.ValidationResult =
195192
($transformedGlobalTargetValidator)(data)
196193

197194
override var isInitializing: Boolean = true
198195

199196
private case class FieldBindingsHolder(..${compoundFields.map(_.formFieldBindingValDef)})
200197
private val fieldBindingsHolder = new FieldBindingsHolder(..${compoundFields.map(f => q"""torstenrudolf.scalajs.react.formbinder.FormFieldBinding[${f.fieldType}]($formLayout.${f.formFieldDescriptor}, ${f.defaultValueExpr}, ${f.targetField.info =:= typeOf[String]}, ${f.transformedTargetFieldValidator}, this, ${f.name})""")})
201198

202-
val allFormFieldBindings = ${compoundFields.map(f => q"fieldBindingsHolder.${f.termName}")}
199+
val allFormFieldBindings: List[torstenrudolf.scalajs.react.formbinder.FormFieldBinding[_]] = ${compoundFields.map(f => q"fieldBindingsHolder.${f.termName}")}
203200

204201
isInitializing = false
205202
onChangeCB.runNow() // update default values
206203

207-
override def currentValueWithoutGlobalValidation: scala.util.Try[$targetTpe] = {
204+
override def currentValueWithoutGlobalValidation: scala.util.Try[$dataModelTpe] = {
208205
val fieldValues = allFormFieldBindings.map(_.currentValidatedValue)
209206
if (fieldValues.forall(_.nonEmpty)) {
210-
val d = $targetCompanion.apply(..${compoundFields.map(f => q"fieldBindingsHolder.${f.termName}.currentValidatedValue.get")})
207+
val d = $dataModelCompanion.apply(..${compoundFields.map(f => q"fieldBindingsHolder.${f.termName}.currentValidatedValue.get")})
211208
scala.util.Success(d)
212209
} else {
213210
scala.util.Failure(torstenrudolf.scalajs.react.formbinder.FormUninitialized)
214211
}
215212
}
216213

217-
override def setModelValue(newModelValue: $targetTpe): japgolly.scalajs.react.Callback = {
214+
override def setModelValue(newModelValue: $dataModelTpe): japgolly.scalajs.react.Callback = {
218215
${compoundFields.map(f =>
219216
q"""fieldBindingsHolder.${f.termName}.updateValue(newModelValue.${f.termName}) match {
220217
case scala.util.Success(cb) => cb
@@ -236,7 +233,7 @@ object Macros {
236233
}
237234
"""
238235
// println(show(newTree))
239-
c.Expr[Form[T]](newTree)
236+
c.Expr[Form[DataModel]](newTree)
240237
}
241238

242239
}

core/src/main/scala/torstenrudolf/scalajs/react/formbinder/package.scala

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,13 @@ import scala.language.experimental.macros
33

44
package object formbinder {
55

6+
// not: currently scala macros dont support default values for arguments
7+
68
def bindWithDefault[DataModel](formLayout: FormLayout[DataModel],
79
validatorObject: Any,
810
defaultModelValue: DataModel): Form[DataModel] = macro Macros.generateWithDefault[DataModel]
911

1012
def bind[DataModel](formLayout: FormLayout[DataModel],
1113
validatorObject: Any): Form[DataModel] = macro Macros.generateWithoutDefault[DataModel]
1214

13-
// def bind[DataModel](formLayout: FormLayout[DataModel],
14-
// validatorObject: Any): Form[DataModel] = bind[DataModel](formLayout, validatorObject, None)
15-
1615
}

demo/package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
"source-map-support": "v0.4.2",
1515
"webpack": "^1.9.10",
1616
"compression-webpack-plugin": "^0.2.0",
17-
"lodash": "^4.14.1"
17+
"lodash": "^4.17.2"
1818
},
1919
"dependencies": {
20-
"material-ui": "v0.15.4",
20+
"material-ui": "v0.16.5",
2121
"highlight.js": "8.9.1",
22-
"react": "^15.4.1",
23-
"react-dom": "^15.4.1",
24-
"react-tap-event-plugin": "^1.0.0"
22+
"react": "^15.4.2",
23+
"react-dom": "^15.4.2",
24+
"react-tap-event-plugin": "^2.0.1"
2525
}
2626
}

demo/src/main/scala/torstenrudolf/scalajs/react/formbinder/demo/components/SimpleMuiDemo.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ object SimpleMuiDemo {
1616
import torstenrudolf.scalajs.react.formbinder._
1717

1818
// the data model
19-
case class Data(username: String = "Joe", password: String, age: Int = 18)
19+
case class Data(username: String = "Joe", password: String, age: Int)
2020

2121
// define validation rules in separate object -- note: the method names and signatures must match to the data model's
2222
object DataValidation {

0 commit comments

Comments
 (0)