@@ -10,22 +10,19 @@ import scala.util.{Failure, Success, Try}
10
10
object Macros {
11
11
// Thanks to https://github.com/Voltir/form.rx for the idea!
12
12
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 ]] = {
15
14
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
19
16
// can handle only default parameters from the first parameter list
20
17
// 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
23
20
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" )
26
23
}
27
24
}
28
- c.Expr [Map [String , Any ]](q " Map[String, Any](.. $kvps ) " )
25
+ c.Expr [Map [String , Any ]](q " Map[String, Any](.. $paramName2DefaultValue ) " )
29
26
}
30
27
31
28
def getCompanion (c : scala.reflect.macros.blackbox.Context )(tpe : c.Type ): c.universe.RefTree = {
@@ -36,29 +33,33 @@ object Macros {
36
33
}
37
34
38
35
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 ) )
42
39
}
43
40
44
41
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) ))
49
46
}
50
47
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
+ {
55
53
56
54
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
+
59
59
60
60
// 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 {
62
63
case m : MethodSymbol if m.isPrimaryConstructor => m
63
64
}.get.paramLists.head
64
65
@@ -68,15 +69,11 @@ object Macros {
68
69
69
70
val targetFieldNames = targetFields.map(_.name.toString)
70
71
71
- // val targetFieldDefaultValues = getCaseClassArgumentsDefaultValues(c)(targetTpe)
72
- // println(s"case class defaults: $targetFieldDefaultValues")
73
- // println(s"defaultModelValue: ${defaultModelValue}")
74
-
75
72
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
+ """ )
80
77
81
78
val formFieldDescriptors = formLayout.actualType.members.map(_.asTerm).filter(_.isAccessor)
82
79
.filter(_.asMethod.returnType.<:< (typeOf[FormFieldDescriptor [_]]))
@@ -137,15 +134,15 @@ object Macros {
137
134
if (! globalTargetValidator.forall(v => {
138
135
v.returnType.<:< (typeOf[ValidationResult ]) &&
139
136
v.paramLists.size == 1 && v.paramLists.head.size == 1 &&
140
- v.paramLists.head.head.info == targetTpe
137
+ v.paramLists.head.head.info == dataModelTpe
141
138
})) {
142
139
c.abort(c.enclosingPosition,
143
140
s " Type of global validator must match the target type. (def $$ global(data: ${globalTargetValidator.get.accessed.info.typeArgs}): torstenrudolf.scalajs.react.formbinder.ValidationResult = ...) " )
144
141
}
145
142
146
143
val transformedGlobalTargetValidator =
147
144
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 " )
149
146
150
147
case class CompoundField (targetField : c.universe.Symbol ,
151
148
defaultValueExpr : c.universe.Tree ,
@@ -188,33 +185,33 @@ object Macros {
188
185
189
186
val newTree =
190
187
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
193
190
194
- override def globalValidator(data: $targetTpe ): torstenrudolf.scalajs.react.formbinder.ValidationResult =
191
+ override def globalValidator(data: $dataModelTpe ): torstenrudolf.scalajs.react.formbinder.ValidationResult =
195
192
( $transformedGlobalTargetValidator)(data)
196
193
197
194
override var isInitializing: Boolean = true
198
195
199
196
private case class FieldBindingsHolder(.. ${compoundFields.map(_.formFieldBindingValDef)})
200
197
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}) """ )})
201
198
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}" )}
203
200
204
201
isInitializing = false
205
202
onChangeCB.runNow() // update default values
206
203
207
- override def currentValueWithoutGlobalValidation: scala.util.Try[ $targetTpe ] = {
204
+ override def currentValueWithoutGlobalValidation: scala.util.Try[ $dataModelTpe ] = {
208
205
val fieldValues = allFormFieldBindings.map(_.currentValidatedValue)
209
206
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 " )})
211
208
scala.util.Success(d)
212
209
} else {
213
210
scala.util.Failure(torstenrudolf.scalajs.react.formbinder.FormUninitialized)
214
211
}
215
212
}
216
213
217
- override def setModelValue(newModelValue: $targetTpe ): japgolly.scalajs.react.Callback = {
214
+ override def setModelValue(newModelValue: $dataModelTpe ): japgolly.scalajs.react.Callback = {
218
215
${compoundFields.map(f =>
219
216
q """ fieldBindingsHolder. ${f.termName}.updateValue(newModelValue. ${f.termName}) match {
220
217
case scala.util.Success(cb) => cb
@@ -236,7 +233,7 @@ object Macros {
236
233
}
237
234
"""
238
235
// println(show(newTree))
239
- c.Expr [Form [T ]](newTree)
236
+ c.Expr [Form [DataModel ]](newTree)
240
237
}
241
238
242
239
}
0 commit comments