Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch 'irc_wip_290' of github.com:lift/framework into irc_wip_290

  • Loading branch information...
commit 6530d0a9981c1db9605a6b5e3188868d30d65c76 2 parents 77ce071 + 95bbf60
@nafg nafg authored
Showing with 1,869 additions and 1,065 deletions.
  1. +14 −6 core/actor/src/main/scala/net/liftweb/actor/LAPinger.scala
  2. +7 −5 core/json-ext/src/test/scala/net/liftweb/json/ext/JsonBoxSerializerSpec.scala
  3. +1 −0  core/json/README.md
  4. +22 −13 core/json/src/main/scala/net/liftweb/json/Extraction.scala
  5. +95 −54 core/json/src/main/scala/net/liftweb/json/Meta.scala
  6. +104 −0 core/json/src/main/scala/net/liftweb/json/ScalaSig.scala
  7. +12 −0 core/json/src/test/scala/net/liftweb/json/ExtractionExamplesSpec.scala
  8. +10 −2 core/json/src/test/scala/net/liftweb/json/SerializationExamples.scala
  9. +2 −2 core/json/src/test/scala/net/liftweb/json/XmlBugs.scala
  10. +1 −1  core/json/src/test/scala/net/liftweb/json/XmlExamples.scala
  11. +80 −0 core/util/src/main/scala/net/liftweb/util/BasicTypesHelpers.scala
  12. +1 −57 core/util/src/main/scala/net/liftweb/util/BindHelpers.scala
  13. +1 −0  core/util/src/main/scala/net/liftweb/util/Schedule.scala
  14. +19 −15 core/util/src/main/scala/net/liftweb/util/SoftReferenceCache.scala
  15. +8 −0 core/util/src/test/scala/net/liftweb/util/BindHelpersSpec.scala
  16. +1 −1  liftsh
  17. +1 −1  liftsh.cmd
  18. +133 −96 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/CustomSerializersSpec.scala
  19. +16 −10 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala
  20. +12 −2 persistence/mongodb/src/main/scala/net/liftweb/mongodb/Mongo.scala
  21. +55 −9 persistence/mongodb/src/test/scala/net/liftweb/mongodb/MongoSpec.scala
  22. +1 −1  persistence/proto/src/main/scala/net/liftweb/proto/ProtoRules.scala
  23. +111 −13 persistence/proto/src/main/scala/net/liftweb/proto/ProtoUser.scala
  24. +25 −7 persistence/record/src/main/scala/net/liftweb/record/MetaRecord.scala
  25. +24 −19 persistence/record/src/test/scala/net/liftweb/record/FieldSpec.scala
  26. +2 −2 project/build.properties
  27. +15 −15 project/build/LiftFrameworkProject.scala
  28. +1 −1  project/plugins/Plugins.scala
  29. BIN  project/{sbt-launch-0.7.5.jar → sbt-launch-0.7.7.jar}
  30. +118 −81 web/webkit/src/main/scala/net/liftweb/http/LiftRules.scala
  31. +516 −352 web/webkit/src/main/scala/net/liftweb/http/LiftScreen.scala
  32. +203 −181 web/webkit/src/main/scala/net/liftweb/http/LiftServlet.scala
  33. +130 −47 web/webkit/src/main/scala/net/liftweb/http/Vars.scala
  34. +128 −72 web/wizard/src/main/scala/net/liftweb/wizard/Wizard.scala
View
20 core/actor/src/main/scala/net/liftweb/actor/LAPinger.scala
@@ -25,19 +25,23 @@ import java.util.concurrent._
*/
object LAPinger {
- /** The underlying <code>java.util.concurrent.ScheduledExecutor</code> */
+ /**The underlying <code>java.util.concurrent.ScheduledExecutor</code> */
private var service = Executors.newSingleThreadScheduledExecutor(TF)
/**
* Re-create the underlying <code>SingleThreadScheduledExecutor</code>
*/
- def restart: Unit = synchronized { if ((service eq null) || service.isShutdown)
- service = Executors.newSingleThreadScheduledExecutor(TF) }
+ def restart: Unit = synchronized {
+ if ((service eq null) || service.isShutdown)
+ service = Executors.newSingleThreadScheduledExecutor(TF)
+ }
/**
* Shut down the underlying <code>SingleThreadScheduledExecutor</code>
*/
- def shutdown: Unit = synchronized { service.shutdown }
+ def shutdown: Unit = synchronized {
+ service.shutdown
+ }
/**
* Schedules the sending of a message to occur after the specified delay.
@@ -47,7 +51,9 @@ object LAPinger {
*/
def schedule[T](to: SpecializedLiftActor[T], msg: T, delay: Long): ScheduledFuture[Unit] = {
val r = new Callable[Unit] {
- def call: Unit = { to ! msg }
+ def call: Unit = {
+ to ! msg
+ }
}
try {
service.schedule(r, delay, TimeUnit.MILLISECONDS)
@@ -65,10 +71,12 @@ case class PingerException(msg: String, e: Throwable) extends RuntimeException(m
private object TF extends ThreadFactory {
val threadFactory = Executors.defaultThreadFactory()
- def newThread(r: Runnable) : Thread = {
+
+ def newThread(r: Runnable): Thread = {
val d: Thread = threadFactory.newThread(r)
d setName "ActorPinger"
d setDaemon true
+ d setContextClassLoader null
d
}
}
View
12 core/json-ext/src/test/scala/net/liftweb/json/ext/JsonBoxSerializerSpec.scala
@@ -35,19 +35,21 @@ object JsonBoxSerializerSpec extends Specification("JsonBoxSerializer Specificat
parse("""{"name":"joe"}""").extract[Person] mustEqual Person("joe", Empty, Empty)
}
- "Extract boxed age" in {
- parse("""{"name":"joe", "age":12}""").extract[Person] mustEqual Person("joe", Full(12), Empty)
+ "Extract boxed thing" in {
+ parse("""{"name":"joe", "thing": "rog", "age":12}""").extract[Person] mustEqual Person("joe", Full(12), Empty, Full("rog"))
}
+
+
"Extract boxed mother" in {
val json = """{"name":"joe", "age":12, "mother": {"name":"ann", "age":53}}"""
val p = parse(json).extract[Person]
p mustEqual Person("joe", Full(12), Full(Person("ann", Full(53), Empty)))
- (for { a1 <- p.age; m <-p.mother; a2 <- m.age } yield a1+a2) mustEqual Full(65)
+ (for { a1 <- p.age; m <-p.mother; a2 <- m.age } yield a1+a2) mustEqual Some(65)
}
"Render with age" in {
- swrite(Person("joe", Full(12), Empty)) mustEqual """{"name":"joe","age":12,"mother":null}"""
+ swrite(Person("joe", Full(12), Empty)) mustEqual """{"name":"joe","age":12,"mother":null,"thing":null}"""
}
"Serialize failure" in {
@@ -68,5 +70,5 @@ object JsonBoxSerializerSpec extends Specification("JsonBoxSerializer Specificat
case class SomeException(msg: String) extends Exception
-case class Person(name: String, age: Box[Int], mother: Box[Person])
+case class Person(name: String, age: Option[Int], mother: Box[Person], thing: Box[String] = Empty)
View
1  core/json/README.md
@@ -61,6 +61,7 @@ Download following jars:
* http://scala-tools.org/repo-releases/net/liftweb/lift-json/XXX/lift-json-XXX.jar
* http://mirrors.ibiblio.org/pub/mirrors/maven2/com/thoughtworks/paranamer/paranamer/2.1/paranamer-2.1.jar
+* scalap (Only for Scala-2.9 compatible versions)
Extras
------
View
35 core/json/src/main/scala/net/liftweb/json/Extraction.scala
@@ -34,13 +34,17 @@ object Extraction {
* @see net.liftweb.json.JsonAST.JValue#extract
* @throws MappingException is thrown if extraction fails
*/
- def extract[A](json: JValue)(implicit formats: Formats, mf: Manifest[A]): A =
+ def extract[A](json: JValue)(implicit formats: Formats, mf: Manifest[A]): A = {
+ def allTypes(mf: Manifest[_]): List[Class[_]] = mf.erasure :: (mf.typeArguments flatMap allTypes)
+
try {
- extract0(json, mf.erasure, mf.typeArguments.map(_.erasure)).asInstanceOf[A]
+ val types = allTypes(mf)
+ extract0(json, types.head, types.tail).asInstanceOf[A]
} catch {
case e: MappingException => throw e
case e: Exception => throw new MappingException("unknown error", e)
}
+ }
/** Extract a case class from JSON.
* @see net.liftweb.json.JsonAST.JValue#extract
@@ -79,7 +83,7 @@ object Extraction {
case x: Option[_] => x.flatMap[JValue] { y => Some(decompose(y)) }.getOrElse(JNothing)
case x =>
val constructorArgs = primaryConstructorArgs(x.getClass)
- constructorArgs.collect { case (name, _, _) if Reflection.hasDeclaredField(x.getClass, name) =>
+ constructorArgs.collect { case (name, _) if Reflection.hasDeclaredField(x.getClass, name) =>
val f = x.getClass.getDeclaredField(name)
f.setAccessible(true)
JField(unmangleName(name), decompose(f get x))
@@ -179,13 +183,14 @@ object Extraction {
private def extract0(json: JValue, clazz: Class[_], typeArgs: Seq[Class[_]])
(implicit formats: Formats): Any = {
- val mapping =
- if (clazz == classOf[List[_]] || clazz == classOf[Set[_]] || clazz.isArray)
- Col(clazz, mappingOf(typeArgs(0)))
- else if (clazz == classOf[Map[_, _]])
- Dict(mappingOf(typeArgs(1)))
+ def mkMapping(clazz: Class[_], typeArgs: Seq[Class[_]])(implicit formats: Formats): Meta.Mapping = {
+ if (clazz == classOf[List[_]] || clazz == classOf[Set[_]] || clazz.isArray)
+ Col(clazz, mkMapping(typeArgs.head, typeArgs.tail))
+ else if (clazz == classOf[Map[_, _]])
+ Dict(mkMapping(typeArgs.tail.head, typeArgs.tail.tail))
else mappingOf(clazz, typeArgs)
- extract0(json, mapping)
+ }
+ extract0(json, mkMapping(clazz, typeArgs))
}
def extract(json: JValue, target: TypeInfo)(implicit formats: Formats): Any =
@@ -210,7 +215,7 @@ object Extraction {
case o: JObject =>
formats.fieldSerializer(a.getClass).map { serializer =>
val constructorArgNames =
- Reflection.constructorArgs(constructor, formats.parameterNameReader).map(_._1).toSet
+ Reflection.constructorArgs(a.getClass, constructor, formats.parameterNameReader, None).map(_._1).toSet
val jsonFields = o.obj.map { f =>
val JField(n, v) = (serializer.deserializer orElse Map(f -> f))(f)
(n, (n, v))
@@ -222,7 +227,11 @@ object Extraction {
fieldsToSet.foreach { case (name, typeInfo) =>
jsonFields.get(name).foreach { case (n, v) =>
val typeArgs = typeInfo.parameterizedType
- .map(_.getActualTypeArguments.map(_.asInstanceOf[Class[_]]).toList)
+ .map(_.getActualTypeArguments.map(_.asInstanceOf[Class[_]]).toList.zipWithIndex
+ .map { case (t, idx) =>
+ if (t == classOf[java.lang.Object]) ScalaSigReader.readField(name, a.getClass, idx)
+ else t
+ })
val value = extract0(v, typeInfo.clazz, typeArgs.getOrElse(Nil))
Reflection.setField(a, n, value)
}
@@ -322,8 +331,8 @@ object Extraction {
if (x == null) None else Some(x)
} else x
} catch {
- case MappingException(msg, _) =>
- if (optional) None else fail("No usable value for " + path + "\n" + msg)
+ case e @ MappingException(msg, _) =>
+ if (optional) None else fail("No usable value for " + path + "\n" + msg, e)
}
}
View
149 core/json/src/main/scala/net/liftweb/json/Meta.scala
@@ -17,7 +17,7 @@
package net.liftweb
package json
-import java.lang.reflect.{Constructor => JConstructor, Field, Type, ParameterizedType}
+import java.lang.reflect.{Constructor => JConstructor, Field, Type, ParameterizedType, GenericArrayType}
import java.util.Date
case class TypeInfo(clazz: Class[_], parameterizedType: Option[ParameterizedType])
@@ -49,7 +49,7 @@ private[json] object Meta {
sealed abstract class Mapping
case class Arg(path: String, mapping: Mapping, optional: Boolean) extends Mapping
case class Value(targetType: Class[_]) extends Mapping
- case class Cycle(targetType: Class[_]) extends Mapping
+ case class Cycle(targetType: Type) extends Mapping
case class Dict(mapping: Mapping) extends Mapping
case class Col(targetType: Class[_], mapping: Mapping) extends Mapping
case class Constructor(targetType: TypeInfo, choices: List[DeclaredConstructor]) extends Mapping {
@@ -76,7 +76,10 @@ private[json] object Meta {
case class DeclaredConstructor(constructor: JConstructor[_], args: List[Arg])
- private val mappings = new Memo[Class[_], Mapping]
+ // Current constructor parsing context. (containingClass + allArgs could be replaced with Constructor)
+ case class Context(argName: String, containingClass: Class[_], allArgs: List[(String, Type)])
+
+ private val mappings = new Memo[Type, Mapping]
private val unmangledNames = new Memo[String, String]
private val paranamer = new CachingParanamer(new BytecodeReadingParanamer)
@@ -85,62 +88,83 @@ private[json] object Meta {
paranamer.lookupParameterNames(constructor)
}
- private[json] def mappingOf(clazz: Class[_], typeArgs: Seq[Class[_]] = Seq())
+ private[json] def mappingOf(clazz: Type, typeArgs: Seq[Class[_]] = Seq())
(implicit formats: Formats): Mapping = {
import Reflection._
- def constructors(clazz: Class[_], visited: Set[Class[_]]) =
- Reflection.constructors(clazz, formats.parameterNameReader).map { case (c, args) =>
- DeclaredConstructor(c, args.map { case (name, atype, genericType) =>
- toArg(unmangleName(name), atype, genericType, visited) })
+ def constructors(t: Type, visited: Set[Type], context: Option[Context]) = {
+ Reflection.constructors(t, formats.parameterNameReader, context).map { case (c, args) =>
+ DeclaredConstructor(c, args.map { case (name, t) =>
+ toArg(unmangleName(name), t, visited, Context(name, c.getDeclaringClass, args)) })
}
+ }
- def toArg(name: String, fieldType: Class[_], genericType: Type, visited: Set[Class[_]]): Arg = {
+ def toArg(name: String, genericType: Type, visited: Set[Type], context: Context): Arg = {
def mkContainer(t: Type, k: Kind, valueTypeIndex: Int, factory: Mapping => Mapping) =
if (typeConstructor_?(t)) {
- val types = typeConstructors(t, k)(valueTypeIndex)
- factory(fieldMapping(types._1, types._2)._1)
- } else factory(fieldMapping(typeParameters(t, k)(valueTypeIndex), null)._1)
+ val typeArgs = typeConstructors(t, k)(valueTypeIndex)
+ factory(fieldMapping(typeArgs)._1)
+ } else factory(fieldMapping(typeParameters(t, k, context)(valueTypeIndex))._1)
def parameterizedTypeOpt(t: Type) = t match {
case x: ParameterizedType => Some(x)
case _ => None
}
- def fieldMapping(fType: Class[_], genType: Type): (Mapping, Boolean) = {
- if (primitive_?(fType)) (Value(fType), false)
- else if (classOf[Set[_]].isAssignableFrom(fType))
- (mkContainer(genType, `* -> *`, 0, Col.apply(classOf[Set[_]], _)), false)
- else if (fType.isArray)
- (mkContainer(genType, `* -> *`, 0, Col.apply(fType, _)), false)
- else if (classOf[Option[_]].isAssignableFrom(fType))
- (mkContainer(genType, `* -> *`, 0, identity _), true)
- else if (classOf[Map[_, _]].isAssignableFrom(fType))
- (mkContainer(genType, `(*,*) -> *`, 1, Dict.apply _), false)
- else if (classOf[Seq[_]].isAssignableFrom(fType))
- (mkContainer(genType, `* -> *`, 0, Col.apply(classOf[List[_]], _)), false)
- else {
- if (visited.contains(fType)) (Cycle(fType), false)
- else (Constructor(TypeInfo(fType, parameterizedTypeOpt(genType)),
- constructors(fType, visited + fType)), false)
- }
+ def mkConstructor(t: Type) =
+ if (visited.contains(t)) (Cycle(t), false)
+ else (Constructor(TypeInfo(rawClassOf(t), parameterizedTypeOpt(t)), constructors(t, visited + t, Some(context))), false)
+
+ def fieldMapping(t: Type): (Mapping, Boolean) = t match {
+ case pType: ParameterizedType =>
+ val raw = rawClassOf(pType)
+ if (classOf[Set[_]].isAssignableFrom(raw))
+ (mkContainer(t, `* -> *`, 0, Col.apply(classOf[Set[_]], _)), false)
+ else if (raw.isArray)
+ (mkContainer(t, `* -> *`, 0, Col.apply(raw, _)), false)
+ else if (classOf[Option[_]].isAssignableFrom(raw))
+ (mkContainer(t, `* -> *`, 0, identity _), true)
+ else if (classOf[Map[_, _]].isAssignableFrom(raw))
+ (mkContainer(t, `(*,*) -> *`, 1, Dict.apply _), false)
+ else if (classOf[Seq[_]].isAssignableFrom(raw))
+ (mkContainer(t, `* -> *`, 0, Col.apply(classOf[List[_]], _)), false)
+ else
+ mkConstructor(t)
+ case aType: GenericArrayType =>
+ // Couldn't find better way to reconstruct proper array type:
+ val raw = java.lang.reflect.Array.newInstance(rawClassOf(aType.getGenericComponentType), 0: Int).getClass
+ (Col(raw, fieldMapping(aType.getGenericComponentType)._1), false)
+ case raw: Class[_] =>
+ if (primitive_?(raw)) (Value(raw), false)
+ else if (raw.isArray)
+ (mkContainer(t, `* -> *`, 0, Col.apply(raw, _)), false)
+ else
+ mkConstructor(t)
+ case x => (Constructor(TypeInfo(classOf[AnyRef], None), Nil), false)
}
- val (mapping, optional) = fieldMapping(fieldType, genericType)
+ val (mapping, optional) = fieldMapping(genericType)
Arg(name, mapping, optional)
}
- if (primitive_?(clazz)) Value(clazz)
+ if (primitive_?(clazz)) Value(rawClassOf(clazz))
else {
- mappings.memoize(clazz, c => {
+ mappings.memoize(clazz, t => {
+ val c = rawClassOf(t)
val typeInfo =
if (typeArgs.isEmpty) TypeInfo(c, None)
else TypeInfo(c, Some(mkParameterizedType(c, typeArgs)))
- Constructor(typeInfo, constructors(c, Set()))
+ Constructor(typeInfo, constructors(t, Set(), None))
})
}
}
+ private[json] def rawClassOf(t: Type): Class[_] = t match {
+ case c: Class[_] => c
+ case p: ParameterizedType => rawClassOf(p.getRawType)
+ case x => fail("Raw type of " + x + " not known")
+ }
+
private[json] def mkParameterizedType(owner: Class[_], typeArgs: Seq[Class[_]]) =
new ParameterizedType {
def getActualTypeArguments = typeArgs.toArray
@@ -151,7 +175,7 @@ private[json] object Meta {
private[json] def unmangleName(name: String) =
unmangledNames.memoize(name, operators.foldLeft(_)((n, o) => n.replace(o._1, o._2)))
- private[json] def fail(msg: String) = throw new MappingException(msg)
+ private[json] def fail(msg: String, cause: Exception = null) = throw new MappingException(msg, cause)
private val operators = Map(
"$eq" -> "=", "$greater" -> ">", "$less" -> "<", "$plus" -> "+", "$minus" -> "-",
@@ -175,8 +199,6 @@ private[json] object Meta {
import java.lang.reflect._
import scala.collection.JavaConversions._
- private val cachedConstructorArgs = new Memo[JConstructor[_], List[(String, Class[_], Type)]]
-
sealed abstract class Kind
case object `* -> *` extends Kind
case object `(*,*) -> *` extends Kind
@@ -190,40 +212,55 @@ private[json] object Meta {
classOf[java.lang.Short], classOf[Date], classOf[Symbol], classOf[JValue],
classOf[JObject], classOf[JArray]).map((_, ())))
- def constructors(clazz: Class[_],
- nameReader: ParameterNameReader): List[(JConstructor[_], List[(String, Class[_], Type)])] =
- clazz.getDeclaredConstructors.map(c => (c, constructorArgs(c, nameReader))).toList
+ def constructors(t: Type, names: ParameterNameReader, context: Option[Context]): List[(JConstructor[_], List[(String, Type)])] =
+ rawClassOf(t).getDeclaredConstructors.map(c => (c, constructorArgs(t, c, names, context))).toList
- def constructorArgs(constructor: JConstructor[_],
- nameReader: ParameterNameReader): List[(String, Class[_], Type)] = {
- def argsInfo(c: JConstructor[_]) = {
+ def constructorArgs(t: Type, constructor: JConstructor[_],
+ nameReader: ParameterNameReader, context: Option[Context]): List[(String, Type)] = {
+ def argsInfo(c: JConstructor[_], typeArgs: Map[TypeVariable[_], Type]) = {
val Name = """^((?:[^$]|[$][^0-9]+)+)([$][0-9]+)?$"""r
def clean(name: String) = name match {
case Name(text, junk) => text
}
try {
val names = nameReader.lookupParameterNames(c).map(clean)
- val types = c.getParameterTypes
- val ptypes = c.getGenericParameterTypes
- (names.toList, types.toList, ptypes.toList).zip
+ val types = c.getGenericParameterTypes.toList map {
+ case v: TypeVariable[_] =>
+ val arg = typeArgs.getOrElse(v, v)
+ if (arg == classOf[java.lang.Object])
+ context.map(ctx => ScalaSigReader.readConstructor(ctx.argName, ctx.containingClass, ctx.allArgs.map(_._1))).getOrElse(arg)
+ else arg
+ case x => x
+ }
+ names.toList.zip(types)
} catch {
case e: ParameterNamesNotFoundException => Nil
}
}
- cachedConstructorArgs.memoize(constructor, argsInfo(_))
+ t match {
+ case c: Class[_] => argsInfo(constructor, Map())
+ case p: ParameterizedType =>
+ val vars =
+ Map() ++ rawClassOf(p).getTypeParameters.toList.map(_.asInstanceOf[TypeVariable[_]]).zip(p.getActualTypeArguments.toList) // FIXME this cast should not be needed
+ argsInfo(constructor, vars)
+ case x => fail("Do not know how query constructor info for " + x)
+ }
}
def primaryConstructorArgs(c: Class[_])(implicit formats: Formats) = {
val ord = Ordering[Int].on[JConstructor[_]](_.getParameterTypes.size)
val primary = c.getDeclaredConstructors.max(ord)
- constructorArgs(primary, formats.parameterNameReader)
+ constructorArgs(c, primary, formats.parameterNameReader, None)
}
- def typeParameters(t: Type, k: Kind): List[Class[_]] = {
+ def typeParameters(t: Type, k: Kind, context: Context): List[Class[_]] = {
def term(i: Int) = t match {
case ptype: ParameterizedType => ptype.getActualTypeArguments()(i) match {
- case c: Class[_] => c
+ case c: Class[_] =>
+ if (c == classOf[java.lang.Object])
+ ScalaSigReader.readConstructor(context.argName, context.containingClass, context.allArgs.map(_._1))
+ else c
case p: ParameterizedType => p.getRawType.asInstanceOf[Class[_]]
case x => fail("do not know how to get type parameter from " + x)
}
@@ -244,12 +281,12 @@ private[json] object Meta {
}
}
- def typeConstructors(t: Type, k: Kind): List[(Class[_], Type)] = {
- def types(i: Int): (Class[_], Type) = {
+ def typeConstructors(t: Type, k: Kind): List[Type] = {
+ def types(i: Int): Type = {
val ptype = t.asInstanceOf[ParameterizedType]
ptype.getActualTypeArguments()(i) match {
- case p: ParameterizedType => (p.getRawType.asInstanceOf[Class[_]], p)
- case c: Class[_] => (c, c)
+ case p: ParameterizedType => p
+ case c: Class[_] => c
}
}
@@ -259,7 +296,11 @@ private[json] object Meta {
}
}
- def primitive_?(clazz: Class[_]) = primitives contains clazz
+ def primitive_?(t: Type) = t match {
+ case clazz: Class[_] => primitives contains clazz
+ case _ => false
+ }
+
def static_?(f: Field) = Modifier.isStatic(f.getModifiers)
def typeConstructor_?(t: Type) = t match {
case p: ParameterizedType =>
View
104 core/json/src/main/scala/net/liftweb/json/ScalaSig.scala
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2009-2010 WorldWide Conferencing, LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.liftweb
+package json
+
+import scala.tools.scalap.scalax.rules.scalasig._
+
+private[json] object ScalaSigReader {
+ def readConstructor(argName: String, clazz: Class[_], argNames: List[String]): Class[_] = {
+ val cl = findClass(clazz)
+ val cstr = findConstructor(cl, argNames).getOrElse(Meta.fail("Can't find constructor " + clazz))
+ findArgType(cstr, argNames.indexOf(argName))
+ }
+
+ def readField(name: String, clazz: Class[_], typeArgIndex: Int): Class[_] = {
+ def read(current: Class[_]): MethodSymbol = {
+ if (current == null)
+ Meta.fail("Can't find field " + name + " from " + clazz)
+ else
+ findField(findClass(current), name).getOrElse(read(current.getSuperclass))
+ }
+ findArgTypeF(read(clazz), typeArgIndex)
+ }
+
+ private def findClass(clazz: Class[_]): ClassSymbol = {
+ val sig = findScalaSig(clazz).getOrElse(Meta.fail("Can't find ScalaSig for " + clazz))
+ findClass(sig, clazz).getOrElse(Meta.fail("Can't find " + clazz + " from parsed ScalaSig"))
+ }
+
+ private def findClass(sig: ScalaSig, clazz: Class[_]): Option[ClassSymbol] = {
+ sig.symbols.collect { case c: ClassSymbol => c }.find(_.name == clazz.getSimpleName).orElse {
+ sig.topLevelClasses.find(_.symbolInfo.name == clazz.getSimpleName).orElse {
+ sig.topLevelObjects.map { obj =>
+ val t = obj.infoType.asInstanceOf[TypeRefType]
+ t.symbol.children collect { case c: ClassSymbol => c } find(_.symbolInfo.name == clazz.getSimpleName)
+ }.head
+ }
+ }
+ }
+
+ private def findConstructor(c: ClassSymbol, argNames: List[String]): Option[MethodSymbol] = {
+ val ms = c.children collect { case m: MethodSymbol if m.name == "<init>" => m }
+ ms.find(m => m.children.map(_.name) == argNames)
+ }
+
+ private def findField(c: ClassSymbol, name: String): Option[MethodSymbol] =
+ (c.children collect { case m: MethodSymbol if m.name == name => m }).headOption
+
+ private def findArgType(s: MethodSymbol, argIdx: Int): Class[_] = {
+ def findPrimitive(t: Type): Symbol = t match {
+ case TypeRefType(_, _, TypeRefType(ThisType(_), symbol, _) :: xs) => symbol
+ case TypeRefType(_, _, (ref @ TypeRefType(_, _, _)) :: xs) => findPrimitive(ref)
+ case x => Meta.fail("Unexpected type info " + x)
+ }
+ toClass(findPrimitive(s.children(argIdx).asInstanceOf[SymbolInfoSymbol].infoType))
+ }
+
+ private def findArgTypeF(s: MethodSymbol, typeArgIdx: Int): Class[_] = {
+ // FIXME can be removed when 2.8 no loner needs to supported.
+ // 2.8 does not have NullaryMethodType, work around that.
+ /*
+ val t = s.infoType match {
+ case NullaryMethodType(TypeRefType(_, _, args)) => args(typeArgIdx)
+ }
+ */
+ val t = s.infoType.asInstanceOf[{ def resultType: Type }].resultType match {
+ case TypeRefType(_, _, args) => args(typeArgIdx)
+ }
+
+ def findPrimitive(t: Type): Symbol = t match {
+ case TypeRefType(ThisType(_), symbol, _) => symbol
+ case ref @ TypeRefType(_, _, _) => findPrimitive(ref)
+ case x => Meta.fail("Unexpected type info " + x)
+ }
+ toClass(findPrimitive(t))
+ }
+
+ private def toClass(s: Symbol) = s.path match {
+ case "scala.Short" => classOf[Short]
+ case "scala.Int" => classOf[Int]
+ case "scala.Long" => classOf[Long]
+ case "scala.Boolean" => classOf[Boolean]
+ case "scala.Float" => classOf[Float]
+ case "scala.Double" => classOf[Double]
+ case _ => classOf[AnyRef]
+ }
+
+ private def findScalaSig(clazz: Class[_]): Option[ScalaSig] =
+ ScalaSigParser.parse(clazz).orElse(findScalaSig(clazz.getDeclaringClass))
+}
View
12 core/json/src/test/scala/net/liftweb/json/ExtractionExamplesSpec.scala
@@ -203,6 +203,18 @@ object ExtractionExamples extends Specification("Extraction Examples Specificati
JDouble(2.1).extract[Long] mustEqual 2L
}
+ "Map with nested non-polymorphic list extraction example" in {
+ parse("""{"a":["b"]}""").extract[Map[String, List[String]]] mustEqual Map("a" -> List("b"))
+ }
+
+ "List with nested non-polymorphic list extraction example" in {
+ parse("""[["a"]]""").extract[List[List[String]]] mustEqual List(List("a"))
+ }
+
+ "Complex nested non-polymorphic collections extraction example" in {
+ parse("""{"a":[{"b":"c"}]}""").extract[Map[String, List[Map[String, String]]]] mustEqual Map("a" -> List(Map("b" -> "c")))
+ }
+
val testJson =
"""
{ "name": "joe",
View
12 core/json/src/test/scala/net/liftweb/json/SerializationExamples.scala
@@ -20,7 +20,6 @@ package json
import java.util.Date
import org.specs.Specification
-
object SerializationExamples extends Specification {
import Serialization.{read, write => swrite}
@@ -115,6 +114,12 @@ object SerializationExamples extends Specification {
read[Members](ser) mustEqual m
}
+ "Case class from type constructors example" in {
+ val p = ProperType(TypeConstructor(Chicken(10)), (25, Player("joe")))
+ val ser = swrite(p)
+ read[ProperType](ser) mustEqual p
+ }
+
case class Ints(x: List[List[Int]])
case class Rec(n: Int, xs: List[Rec])
@@ -329,7 +334,6 @@ class DateTime(val time: Long)
case class Times(times: List[DateTime])
-
sealed abstract class Bool
case class True() extends Bool
case class False() extends Bool
@@ -352,3 +356,7 @@ case class ArrayContainer(array: Array[String])
case class SeqContainer(seq: Seq[String])
case class OptionOfTupleOfDouble(position: Option[Tuple2[Double, Double]])
+
+case class Player(name: String)
+case class TypeConstructor[A](x: A)
+case class ProperType(x: TypeConstructor[Chicken], t: (Int, Player))
View
4 core/json/src/test/scala/net/liftweb/json/XmlBugs.scala
@@ -42,8 +42,8 @@ object XmlBugs extends Specification {
val example2 = <word term="example" self="http://localhost:8080/word/example" available="true"></word>
val expected2 = """{"self":"http://localhost:8080/word/example","term":"example","available":"true"}"""
- Printer.compact(render(toJson(example1))) mustEqual expected1
- Printer.compact(render(toJson(example2))) mustEqual expected2
+ (toJson(example1) diff parse(expected1)) mustEqual Diff(JNothing, JNothing, JNothing)
+ (toJson(example2) diff parse(expected2)) mustEqual Diff(JNothing, JNothing, JNothing)
}
"Nodes with attributes converted to correct JSON" in {
View
2  core/json/src/test/scala/net/liftweb/json/XmlExamples.scala
@@ -163,7 +163,7 @@ object XmlExamples extends Specification("XML Examples") {
val a1 = attrToObject("stats", "count", s => JInt(s.s.toInt)) _
val a2 = attrToObject("messages", "href", identity) _
val json = a1(a2(toJson(messageXml1)))
- compact(render(json)) mustEqual expected1
+ (json diff parse(expected1)) mustEqual Diff(JNothing, JNothing, JNothing)
}
"Example with one attribute, one nested element " in {
View
80 core/util/src/main/scala/net/liftweb/util/BasicTypesHelpers.scala
@@ -78,6 +78,86 @@ trait BasicTypesHelpers { self: StringHelpers with ControlHelpers =>
}
/**
+ * Compare two NodeSeq and return true if they are equal, even if
+ * attribute order of Elems is different
+ */
+ def compareXml(left: NodeSeq, right: NodeSeq): Boolean = {
+ val ls: Seq[Node] = left.toSeq
+ val rs: Seq[Node] = right.toSeq
+ if (ls.length == rs.length) {
+ ls.zip(rs).foldLeft(true){case (b, (l, r)) => b && compareNode(l, r)}
+ } else {
+ false
+ }
+ }
+
+ /**
+ * Compare two Elems
+ */
+ def compareElem(left: Elem, right: Elem): Boolean =
+ compareXml(left.child, right.child) &&
+ left.label == right.label &&
+ (((null eq left.prefix) && (null eq right.prefix)) || left.prefix == right.prefix) &&
+ left.scope == right.scope &&
+ compareMetaData(left.attributes.toList, right.attributes.toList)
+
+ private def findFilter(m: MetaData, lst: List[MetaData]): Box[List[MetaData]] = {
+ var found = false
+ val ret = lst.filter {
+ case PrefixedAttribute(pre, label, value, _) if !found =>
+ m match {
+ case PrefixedAttribute(p2, l2, v2, _) if p2 == pre && l2 == label && v2.text == value.text =>
+ found = true
+ false
+ case _ => true
+ }
+ case UnprefixedAttribute(label, value, _) if !found =>
+ m match {
+ case UnprefixedAttribute(l2, v2, _) if l2 == label && v2.text == value.text =>
+ found = true
+ false
+ case _ => true
+ }
+ case _ => true
+ }
+ if (found) Full(ret) else Empty
+ }
+
+ /**
+ * Compare the metadata of two attributes
+ */
+ def compareMetaData(left: List[MetaData], right: List[MetaData]): Boolean =
+ (left, right) match {
+ case (Nil, Nil) => true
+ case (_, Nil) => false
+ case (Nil, _) => false
+ case (attr :: rl, right) => findFilter(attr, right) match {
+ case Full(rr) => compareMetaData(rl, rr)
+ case _ => false
+ }
+ case _ => false
+ }
+
+ /**
+ * Comparse two XML nodes
+ */
+ def compareNode(left: Node, right: Node): Boolean = {
+ (left, right) match {
+ case (Group(gl), Group(gr)) => compareXml(gl, gr)
+ case (el: Elem, er: Elem) => compareElem(el, er)
+ case (Unparsed(tl), Unparsed(tr)) => tl == tr
+ case (Text(tl), Text(tr)) => tl == tr
+
+ case (el: EntityRef, er: EntityRef) => el === er
+ case (Comment(cl), Comment(cr)) => cl == cr
+ case (PCData(dl), PCData(dr)) => dl == dr
+ case (pl: ProcInstr, pr: ProcInstr) => pl === pr
+ case (a, b) => a.toString == b.toString
+ }
+
+ }
+
+ /**
* Implicit transformation from a Boolean expression to an OptionalCons object so
* that an element can be added to a list if the expression is true
*/
View
58 core/util/src/main/scala/net/liftweb/util/BindHelpers.scala
@@ -2011,7 +2011,7 @@ private class SelectorMap(binds: List[CssBind]) extends Function1[NodeSeq, NodeS
}
-
+ // This is where the rules are applied -- DPP HERE
final def applyRule(bind: CssBind, realE: Elem): NodeSeq = {
def uniqueClasses(cv: String*): String = {
import Helpers._
@@ -2120,62 +2120,6 @@ private class SelectorMap(binds: List[CssBind]) extends Function1[NodeSeq, NodeS
}
}
}
-
- /*
- case Full(AttrSubNode(attr)) => {
- val calced = bind.calculate(realE)
- val filtered = realE.attributes.filter{
- case up: UnprefixedAttribute => up.key != attr
- case _ => true
- }
-
- val newAttr = if (calced.isEmpty) {
- filtered
- } else {
- val flat: NodeSeq = calced.flatMap(a => a)
- new UnprefixedAttribute(attr, flat, filtered)
- }
-
- new Elem(realE.prefix,
- realE.label, newAttr,
- realE.scope, SelectorMap.this.apply(realE.child) :_*)
- }
-
- case Full(AttrAppendSubNode(attr)) => {
- val org: NodeSeq = realE.attribute(attr).getOrElse(NodeSeq.Empty)
- val calced = bind.calculate(realE).toList
-
-
- if (calced.isEmpty) {
- realE
- } else {
- val filtered = realE.attributes.filter{
- case up: UnprefixedAttribute => up.key != attr
- case _ => true
- }
-
- val flat: NodeSeq = if (attr == "class") {
- if (org.isEmpty) {
- calced.dropRight(1).flatMap(a => a ++ Text(" ")) ++
- calced.takeRight(1).head
- } else {
- org ++ Text(" ") ++
- calced.dropRight(1).flatMap(a => a ++ Text(" ")) ++
- calced.takeRight(1).head
- }
- } else {
- org ++ (calced.flatMap(a => a): NodeSeq)
- }
-
- val newAttr = new UnprefixedAttribute(attr, flat, filtered)
-
- new Elem(realE.prefix,
- realE.label, newAttr,
- realE.scope, SelectorMap.this.apply(realE.child) :_*)
-
- }
- }
- */
case x: EmptyBox => {
val calced = bind.calculate(realE)
View
1  core/util/src/main/scala/net/liftweb/util/Schedule.scala
@@ -196,6 +196,7 @@ private object TF extends ThreadFactory {
val d: Thread = threadFactory.newThread(r)
d setName "Lift Scheduler"
d setDaemon true
+ d setContextClassLoader null
d
}
}
View
34 core/util/src/main/scala/net/liftweb/util/SoftReferenceCache.scala
@@ -17,7 +17,7 @@
package net.liftweb
package util
-import java.lang.ref.{ReferenceQueue,SoftReference};
+import java.lang.ref.{ReferenceQueue, SoftReference};
import java.util._
import Map._
import concurrent.locks._
@@ -26,6 +26,7 @@ import common._
import util._
import Helpers._
import Schedule._
+import java.lang.Thread._
/**
@@ -44,7 +45,7 @@ object SoftReferenceCache {
/**
* Create a new SoftReferenceCache instance
*/
- def apply[K, V](size: Int) = new SoftReferenceCache[K,V](size)
+ def apply[K, V](size: Int) = new SoftReferenceCache[K, V](size)
/**
* Initialize the orphan keys monitor
@@ -52,12 +53,13 @@ object SoftReferenceCache {
def initialize = {
// A daemon thread is more approapriate here then an Actor as
// we'll do blocking reads from the reference queue
- val thread = new Thread(new Runnable(){
- def run(){
- processQueue
- }
- })
+ val thread = new Thread(new Runnable() {
+ def run() {
+ processQueue
+ }
+ })
thread.setDaemon(true)
+ thread setContextClassLoader null
thread.start
}
@@ -72,7 +74,7 @@ object SoftReferenceCache {
while (!terminated) {
tryo {
// Wait 30 seconds for something to appear in the queue.
- val sftVal = refQueue.remove(30000).asInstanceOf[SoftValue[_,_]];
+ val sftVal = refQueue.remove(30000).asInstanceOf[SoftValue[_, _]];
if (sftVal != null) {
sftVal.cache.remove(sftVal.key);
}
@@ -82,6 +84,7 @@ object SoftReferenceCache {
}
case object ProcessQueue
+
case object Done
/**
@@ -118,10 +121,10 @@ class SoftReferenceCache[K, V](cacheSize: Int) {
def apply(key: K): Box[V] = lock(readLock) {
Box.!!(cache.get(key)) match {
case Full(value) =>
- Box.!!(value.get) or {
- remove(key);
- Empty
- }
+ Box.!!(value.get) or {
+ remove(key);
+ Empty
+ }
case _ => Empty
}
}
@@ -131,7 +134,7 @@ class SoftReferenceCache[K, V](cacheSize: Int) {
* @param tuple: (K, V)*
* @return this
*/
- def += (tuple: (K, V)*) = {
+ def +=(tuple: (K, V)*) = {
lock(writeLock) {
for (t <- tuple) yield {
cache.put(t._1, new SoftValue(t._1, t._2, this, SoftReferenceCache.refQueue));
@@ -162,7 +165,8 @@ class SoftValue[K, V](k: K,
v: V,
lruCache: SoftReferenceCache[K, V],
queue: ReferenceQueue[Any]) extends SoftReference[V](v, queue) {
- def key: K = k
- def cache: SoftReferenceCache[K, V] = lruCache
+ def key: K = k
+
+ def cache: SoftReferenceCache[K, V] = lruCache
}
View
8 core/util/src/test/scala/net/liftweb/util/BindHelpersSpec.scala
@@ -421,6 +421,14 @@ object CssBindHelpersSpec extends Specification {
xf(<div/>)
}
+ "support modifying attributes along with body" in {
+ val org = <a>foo</a>
+ val func = "a [href]" #> "dog" & "a *" #> "bar"
+ val res = func(org)
+
+ res.toString must_== "<a href=\"dog\">bar</a>"
+ }
+
"substitute a String by id" in {
("#foo" replaceWith "hello")(<b><span id="foo"/></b>) must ==/ (<b>hello</b>)
}
View
2  liftsh
@@ -12,4 +12,4 @@ INTERNAL_OPTS="-Dfile.encoding=UTF-8 -Xss8M -Xmx1G -noverify -XX:+CMSClassUnload
DEFAULT_OPTS="-Dsbt.intransitive=true"
# Call with INTERNAL_OPTS followed by LIFTSH_OPTS (or DEFAULT_OPTS). java aways takes the last option when duplicate.
-exec java ${INTERNAL_OPTS} ${LIFTSH_OPTS:-${DEFAULT_OPTS}} -jar `dirname $0`/project/sbt-launch-0.7.5.jar "$@"
+exec java ${INTERNAL_OPTS} ${LIFTSH_OPTS:-${DEFAULT_OPTS}} -jar `dirname $0`/project/sbt-launch-0.7.7.jar "$@"
View
2  liftsh.cmd
@@ -11,4 +11,4 @@ if "%LIFTSH_OPTS%"=="" (
)
@REM Call with INTERNAL_OPTS followed by LIFTSH_OPTS (or DEFAULT_OPTS). java aways takes the last option when duplicate.
-java %INTERNAL_OPTS% %LIFTSH_OPTS% -jar "%~dp0\project\sbt-launch-0.7.5.jar" %*
+java %INTERNAL_OPTS% %LIFTSH_OPTS% -jar "%~dp0\project\sbt-launch-0.7.7.jar" %*
View
229 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/CustomSerializersSpec.scala
@@ -31,102 +31,121 @@ import org.bson.types.ObjectId
import org.specs.Specification
import net.liftweb.record.field._
-
+import xml.{Elem, NodeSeq}
+import util.Helpers
package customserializersspecs {
- case class Child(name: String, birthdate: Date) extends JsonObject[Child] {
- def meta = Child
- }
- object Child extends JsonObjectMeta[Child]
+case class Child(name: String, birthdate: Date) extends JsonObject[Child] {
+ def meta = Child
+}
- /*
- * Date as String
- */
- class Person extends MongoRecord[Person] with MongoId[Person] {
- def meta = Person
+object Child extends JsonObjectMeta[Child]
- object children extends MongoJsonObjectListField(this, Child)
- object firstBorn extends JsonObjectField(this, Child) {
- def defaultValue = Child("", now)
- }
- }
- object Person extends Person with MongoMetaRecord[Person]
+/*
+* Date as String
+*/
+class Person extends MongoRecord[Person] with MongoId[Person] {
+ def meta = Person
- /*
- * Date as Date
- */
- class Person2 extends MongoRecord[Person2] with MongoId[Person2] {
- def meta = Person2
+ object children extends MongoJsonObjectListField(this, Child)
- object children extends MongoJsonObjectListField(this, Child)
- object firstBorn extends JsonObjectField(this, Child) {
- def defaultValue = Child("", now)
- }
- }
- object Person2 extends Person2 with MongoMetaRecord[Person2] {
- override def formats = allFormats
+ object firstBorn extends JsonObjectField(this, Child) {
+ def defaultValue = Child("", now)
}
- class Player extends MongoRecord[Player] with MongoId[Player] {
- def meta = Player
+}
- object name extends StringField(this, 256)
- }
+object Person extends Person with MongoMetaRecord[Person]
- object Player extends Player with MongoMetaRecord[Player]
+/*
+* Date as Date
+*/
+class Person2 extends MongoRecord[Person2] with MongoId[Person2] {
+ def meta = Person2
+
+ object children extends MongoJsonObjectListField(this, Child)
- /*
- * ObjectId as String
- */
- case class Team(id: String, name: String, qb: String) extends JsonObject[Team] {
- def meta = Team
+ object firstBorn extends JsonObjectField(this, Child) {
+ def defaultValue = Child("", now)
}
- object Team extends JsonObjectMeta[Team]
- class League extends MongoRecord[League] with MongoId[League] {
- def meta = League
+}
+
+object Person2 extends Person2 with MongoMetaRecord[Person2] {
+ override def formats = allFormats
+}
- object teams extends MongoJsonObjectListField(this, Team)
- object champion extends JsonObjectField(this, Team) {
- def defaultValue = Team("", "", "")
- }
- }
- object League extends League with MongoMetaRecord[League]
+class Player extends MongoRecord[Player] with MongoId[Player] {
+ def meta = Player
- /*
- * ObjectId as ObjectId
- */
- case class Team2(id: ObjectId, name: String, qb: ObjectId) extends JsonObject[Team2] {
- def meta = Team2
- }
- object Team2 extends JsonObjectMeta[Team2]
+ object name extends StringField(this, 256)
- class League2 extends MongoRecord[League2] with MongoId[League2] {
- def meta = League2
+}
- object teams extends MongoJsonObjectListField(this, Team2)
- object champion extends JsonObjectField(this, Team2) {
- def defaultValue = Team2(ObjectId.get, "", ObjectId.get)
- }
- }
+object Player extends Player with MongoMetaRecord[Player]
- object League2 extends League2 with MongoMetaRecord[League2] {
- override def formats = super.formats + new ObjectIdSerializer
- }
+/*
+* ObjectId as String
+*/
+case class Team(id: String, name: String, qb: String) extends JsonObject[Team] {
+ def meta = Team
+}
- object WeekDay extends Enumeration {
- type WeekDay = Value
- val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
- }
+object Team extends JsonObjectMeta[Team]
+
+class League extends MongoRecord[League] with MongoId[League] {
+ def meta = League
- class EnumRec extends MongoRecord[EnumRec] with MongoId[EnumRec] {
- def meta = EnumRec
+ object teams extends MongoJsonObjectListField(this, Team)
- object dow extends EnumField(this, WeekDay)
+ object champion extends JsonObjectField(this, Team) {
+ def defaultValue = Team("", "", "")
}
- object EnumRec extends EnumRec with MongoMetaRecord[EnumRec] {
- override def collectionName = "enumrecs"
+
+}
+
+object League extends League with MongoMetaRecord[League]
+
+/*
+* ObjectId as ObjectId
+*/
+case class Team2(id: ObjectId, name: String, qb: ObjectId) extends JsonObject[Team2] {
+ def meta = Team2
+}
+
+object Team2 extends JsonObjectMeta[Team2]
+
+class League2 extends MongoRecord[League2] with MongoId[League2] {
+ def meta = League2
+
+ object teams extends MongoJsonObjectListField(this, Team2)
+
+ object champion extends JsonObjectField(this, Team2) {
+ def defaultValue = Team2(ObjectId.get, "", ObjectId.get)
}
+
+}
+
+object League2 extends League2 with MongoMetaRecord[League2] {
+ override def formats = super.formats + new ObjectIdSerializer
+}
+
+object WeekDay extends Enumeration {
+ type WeekDay = Value
+ val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
+}
+
+class EnumRec extends MongoRecord[EnumRec] with MongoId[EnumRec] {
+ def meta = EnumRec
+
+ object dow extends EnumField(this, WeekDay)
+
+}
+
+object EnumRec extends EnumRec with MongoMetaRecord[EnumRec] {
+ override def collectionName = "enumrecs"
+}
+
}
@@ -158,9 +177,10 @@ object CustomSerializersSpec extends Specification("CustomSerializers Specificat
// retrieve it and compare
val mother2 = Person.find(mother.id)
mother2 must notBeEmpty
- mother2 foreach { m =>
- m.children.value mustEqual mother.children.value
- m.firstBorn.value mustEqual mother.firstBorn.value
+ mother2 foreach {
+ m =>
+ m.children.value mustEqual mother.children.value
+ m.firstBorn.value mustEqual mother.firstBorn.value
}
// check the conversion functions
@@ -178,7 +198,7 @@ object CustomSerializersSpec extends Specification("CustomSerializers Specificat
JObject(List(
JField("name", JString("Jill")),
JField("birthdate", JString("2010-11-03T00:08:00.000Z"))))
- ))
+ ))
mother.children.toForm must beEmpty
/*
mother.firstBorn.asJs mustEqual
@@ -212,9 +232,10 @@ object CustomSerializersSpec extends Specification("CustomSerializers Specificat
// retrieve it and compare
val mother2 = Person2.find(mother.id)
mother2 must notBeEmpty
- mother2 foreach { m =>
- m.children.value mustEqual mother.children.value
- m.firstBorn.value mustEqual mother.firstBorn.value
+ mother2 foreach {
+ m =>
+ m.children.value mustEqual mother.children.value
+ m.firstBorn.value mustEqual mother.firstBorn.value
}
// check the conversion functions
@@ -268,28 +289,36 @@ object CustomSerializersSpec extends Specification("CustomSerializers Specificat
// retrieve it and compare
val nfl2 = League.find(nfl.id)
nfl2 must notBeEmpty
- nfl2 foreach { l =>
- l.teams.value mustEqual nfl.teams.value
- l.champion.value mustEqual nfl.champion.value
+ nfl2 foreach {
+ l =>
+ l.teams.value mustEqual nfl.teams.value
+ l.champion.value mustEqual nfl.champion.value
}
// find a player
val vqb = Player.find(vikes.qb)
vqb must notBeEmpty
- vqb foreach { p =>
- p.name.value mustEqual "Brett Favre"
+ vqb foreach {
+ p =>
+ p.name.value mustEqual "Brett Favre"
}
// check the conversion functions
// nfl._id.asJs mustEqual Str(nfl._id.value.toString)
nfl._id.asJValue mustEqual JString(nfl._id.value.toString)
val session = new LiftSession("", randomString(20), Empty)
- val formPattern = "<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+nfl._id.value.toString+"\" id=\"_id_id\"></input>"
+ val formPattern = <input name=".*" type="text" tabindex="1" value={nfl._id.value.toString} id="_id_id"></input>
S.initIfUninitted(session) {
val form = nfl._id.toForm
form must notBeEmpty
- form foreach { f =>
- f.toString must beMatching(formPattern)
+ form foreach {
+ fprime =>
+ val f = ("* [name]" #> ".*" & "select *" #> (((ns: NodeSeq) => ns.filter {
+ case e: Elem => e.attribute("selected").map(_.text) == Some("selected")
+ case _ => false
+ }) andThen "* [value]" #> ".*"))(fprime)
+ val ret: Boolean = Helpers.compareXml(f, formPattern)
+ ret must_== true
}
}
@@ -331,28 +360,36 @@ object CustomSerializersSpec extends Specification("CustomSerializers Specificat
// retrieve it and compare
val nfl2 = League2.find(nfl.id.toString)
nfl2 must notBeEmpty
- nfl2 foreach { l =>
- l.teams.value mustEqual nfl.teams.value
- l.champion.value mustEqual nfl.champion.value
+ nfl2 foreach {
+ l =>
+ l.teams.value mustEqual nfl.teams.value
+ l.champion.value mustEqual nfl.champion.value
}
// find a player
val vqb = Player.find(vikes.qb)
vqb must notBeEmpty
- vqb foreach { p =>
- p.name.value mustEqual "Brett Favre"
+ vqb foreach {
+ p =>
+ p.name.value mustEqual "Brett Favre"
}
// check the conversion functions
// nfl._id.asJs mustEqual JsObj(("$oid", Str(nfl._id.value.toString)))
nfl._id.asJValue mustEqual JObject(List(JField("$oid", JString(nfl._id.value.toString))))
val session = new LiftSession("", randomString(20), Empty)
- val formPattern = "<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+nfl._id.value.toString+"\" id=\"_id_id\"></input>"
+ val formPattern = <input name=".*" type="text" tabindex="1" value={nfl._id.value.toString} id="_id_id"></input>
S.initIfUninitted(session) {
val form = nfl._id.toForm
form must notBeEmpty
- form foreach { f =>
- f.toString must beMatching(formPattern)
+ form foreach {
+ fprime =>
+ val f = ("* [name]" #> ".*" & "select *" #> (((ns: NodeSeq) => ns.filter {
+ case e: Elem => e.attribute("selected").map(_.text) == Some("selected")
+ case _ => false
+ }) andThen "* [value]" #> ".*"))(fprime)
+ val ret: Boolean = Helpers.compareXml(f, formPattern)
+ ret must_== true
}
}
View
26 persistence/mongodb-record/src/test/scala/net/liftweb/mongodb/record/MongoFieldSpec.scala
@@ -21,8 +21,6 @@ package record
import java.util.{Date, UUID}
import java.util.regex.Pattern
-import xml.Text
-
import com.mongodb.DBRef
import org.bson.types.ObjectId
import org.specs.Specification
@@ -30,13 +28,15 @@ import org.specs.Specification
import common._
import json.{Num => JsonNum, _}
import JsonDSL._
-import util.FieldError
import util.Helpers.randomString
import http.{LiftSession, S}
import http.js.JE._
import http.js.JsExp
import net.liftweb.record._
-
+import common.Box._
+import xml.{Elem, NodeSeq, Text}
+import util.{Helpers, FieldError}
+import Helpers._
/**
* Systems under specification for MongoField.
@@ -139,7 +139,7 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon
}
}
- def passConversionTests[A](example: A, mandatory: MandatoryTypedField[A], jsexp: JsExp, jvalue: JValue, formPattern: Box[String]): Unit = {
+ def passConversionTests[A](example: A, mandatory: MandatoryTypedField[A], jsexp: JsExp, jvalue: JValue, formPattern: Box[NodeSeq]): Unit = {
/*
"convert to JsExp" in {
@@ -167,8 +167,14 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon
S.initIfUninitted(session) {
val formXml = mandatory.toForm
formXml must notBeEmpty
- formXml foreach { f =>
- f.toString must beMatching(fp)
+ formXml foreach { fprime =>
+ val f = ("* [name]" #> ".*" & "select *" #> (((ns: NodeSeq) => ns.filter {
+ case e: Elem => e.attribute("selected").map(_.text) == Some("selected")
+ case _ => false
+ }) andThen "* [value]" #> ".*"))(fprime)
+ val ret: Boolean = Helpers.compareXml(f, fp)
+
+ ret must_== true
}
}
}
@@ -185,7 +191,7 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon
rec.mandatoryDateField,
JsObj(("$dt", Str(nowStr))),
JObject(List(JField("$dt", JString(nowStr)))),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+nowStr+"\" id=\"mandatoryDateField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={nowStr} id="mandatoryDateField_id"></input>)
)
}
@@ -214,7 +220,7 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon
rec.mandatoryObjectIdField,
JsObj(("$oid", oid.toString)),
JObject(List(JField("$oid", JString(oid.toString)))),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+oid.toString+"\" id=\"mandatoryObjectIdField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={oid.toString} id="mandatoryObjectIdField_id"></input>)
)
}
@@ -240,7 +246,7 @@ object MongoFieldSpec extends Specification("MongoField Specification") with Mon
rec.mandatoryUUIDField,
JsObj(("$uuid", Str(uuid.toString))),
JObject(List(JField("$uuid", JString(uuid.toString)))),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+uuid.toString+"\" id=\"mandatoryUUIDField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={uuid.toString} id="mandatoryUUIDField_id"></input>)
)
}
View
14 persistence/mongodb/src/main/scala/net/liftweb/mongodb/Mongo.scala
@@ -99,8 +99,8 @@ object MongoDB {
/*
* Define a Mongo db using a standard Mongo instance.
*/
- def defineDb(name: MongoIdentifier, mongo: Mongo, dbName: String) {
- dbs.put(name, MongoAddress(new MongoHostBase { def mongo = mongo }, dbName))
+ def defineDb(name: MongoIdentifier, mngo: Mongo, dbName: String) {
+ dbs.put(name, MongoAddress(new MongoHostBase { def mongo = mngo }, dbName))
}
/*
@@ -114,6 +114,16 @@ object MongoDB {
}
/*
+ * Define and authenticate a Mongo db using a standard Mongo instance.
+ */
+ def defineDbAuth(name: MongoIdentifier, mngo: Mongo, dbName: String, username: String, password: String) {
+ if (!mngo.getDB(dbName).authenticate(username, password.toCharArray))
+ throw new MongoException("Authorization failed: "+mngo.toString)
+
+ dbs.put(name, MongoAddress(new MongoHostBase { def mongo = mngo }, dbName))
+ }
+
+ /*
* Get a DB reference
*/
def getDb(name: MongoIdentifier): Option[DB] = dbs.get(name) match {
View
64 persistence/mongodb/src/test/scala/net/liftweb/mongodb/MongoSpec.scala
@@ -28,6 +28,10 @@ object MongoSpec extends Specification("Mongo Specification") {
}
def passDefinitionTests(id: MongoIdentifier, ma: MongoAddress): Unit = {
+ // define the db
+ MongoDB.close
+ MongoDB.defineDb(id, ma)
+
// make sure mongo is running
try {
MongoDB.use(id) { db =>
@@ -38,10 +42,6 @@ object MongoSpec extends Specification("Mongo Specification") {
case e: Exception => skip("MongoDB is not running")
}
- // define the db
- MongoDB.close
- MongoDB.defineDb(id, ma)
-
// using an undefined identifier throws an exception
MongoDB.use(DefaultMongoIdentifier) { db =>
db.getLastError.ok must beEqualTo(true)
@@ -60,11 +60,6 @@ object MongoSpec extends Specification("Mongo Specification") {
"Define DB with specified host and port" in {
passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoHost("127.0.0.1", 27017), "test_default"))
}
- "Define DB pair with DBAddress" in {
- val dba = new DBAddress("127.0.0.1", 27017, "test_a")
- val dbb = new DBAddress("127.0.0.1", 27018, "test_b")
- passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoPair(dba, dbb), "test_default"))
- }
"Define DB with ServerAddress and MongoOptions" in {
passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoHost(new ServerAddress, new MongoOptions), "test_default"))
}
@@ -76,6 +71,12 @@ object MongoSpec extends Specification("Mongo Specification") {
mo.connectionsPerHost = 12
passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoHost(options=mo), "test_default"))
}
+ /* These need all of the Mongo instances to be running to be useful.
+ "Define DB pair with DBAddress" in {
+ val dba = new DBAddress("127.0.0.1", 27017, "test_a")
+ val dbb = new DBAddress("127.0.0.1", 27018, "test_b")
+ passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoPair(dba, dbb), "test_default"))
+ }
"Define DB pair with ServerAddress" in {
val dba = new ServerAddress("127.0.0.1", 27017)
val dbb = new ServerAddress("127.0.0.1", 27018)
@@ -87,5 +88,50 @@ object MongoSpec extends Specification("Mongo Specification") {
val dbc = new ServerAddress("127.0.0.1", 27019)
passDefinitionTests(TestMongoIdentifier, MongoAddress(MongoSet(List(dba, dbb, dbc)), "test_default"))
}
+ */
+ "Define DB with Mongo instance" in {
+ // define the db
+ MongoDB.close
+ MongoDB.defineDb(TestMongoIdentifier, new Mongo, "test_default")
+
+ // make sure mongo is running
+ try {
+ MongoDB.use(TestMongoIdentifier) { db =>
+ db.getLastError.ok must beEqualTo(true)
+ }
+ }
+ catch {
+ case e: Exception => skip("MongoDB is not running")
+ }
+
+ // using an undefined identifier throws an exception
+ MongoDB.use(DefaultMongoIdentifier) { db =>
+ db.getLastError.ok must beEqualTo(true)
+ } must throwA(new MongoException("Mongo not found: MongoIdentifier(test)"))
+ // remove defined db
+ MongoDB.close
+ }
+ /* Requires a server other than localhost with auth setup.
+ "Define and authenticate DB with Mongo instance" in {
+ MongoDB.close
+
+ // make sure mongo is running
+ try {
+ val pwd = "lift_pwd"
+ val dbUri = new MongoURI("mongodb://")
+ // define the db
+ MongoDB.defineDbAuth(TestMongoIdentifier, new Mongo(dbUri), "lift_auth_test", "lift_user", pwd)
+ // try to use it
+ MongoDB.use(TestMongoIdentifier) { db =>
+ db.getLastError.ok must beEqualTo(true)
+ }
+ }
+ catch {
+ case e: Exception => skip("MongoDB is not running")
+ }
+ // remove defined db
+ MongoDB.close
+ }
+ */
}
}
View
2  persistence/proto/src/main/scala/net/liftweb/proto/ProtoRules.scala
@@ -32,7 +32,7 @@ object ProtoRules extends Factory with LazyLoggable {
* The regular expression pattern for matching email addresses. This
* assumes that the email address has been converted to lower case.
*/
- var emailRegexPattern = new FactoryMaker(Pattern.compile("^[a-z0-9._%\\-+]+@(?:[a-z0-9\\-]+\\.)+[a-z]{2,4}$")) {}
+ val emailRegexPattern = new FactoryMaker(Pattern.compile("^[a-z0-9._%\\-+]+@(?:[a-z0-9\\-]+\\.)+[a-z]{2,4}$")) {}
}
View
124 persistence/proto/src/main/scala/net/liftweb/proto/ProtoUser.scala
@@ -196,32 +196,108 @@ trait ProtoUser {
*/
def screenWrap: Box[Node] = Empty
- val basePath: List[String] = "user_mgt" :: Nil
- def signUpSuffix = "sign_up"
+ /**
+ * The base path for the user related URLs. Override this
+ * method to change the base path
+ */
+ def basePath: List[String] = "user_mgt" :: Nil
+
+ /**
+ * The path suffix for the sign up screen
+ */
+ def signUpSuffix: String = "sign_up"
+
+ /**
+ * The computed path for the sign up screen
+ */
lazy val signUpPath = thePath(signUpSuffix)
+
+ /**
+ * The path suffix for the login screen
+ */
def loginSuffix = "login"
+
+ /**
+ * The computed path for the login screen
+ */
lazy val loginPath = thePath(loginSuffix)
+
+ /**
+ * The path suffix for the lost password screen
+ */
def lostPasswordSuffix = "lost_password"
+
+ /**
+ * The computed path for the lost password screen
+ */
lazy val lostPasswordPath = thePath(lostPasswordSuffix)
+
+ /**
+ * The path suffix for the reset password screen
+ */
def passwordResetSuffix = "reset_password"
+
+ /**
+ * The computed path for the reset password screen
+ */
lazy val passwordResetPath = thePath(passwordResetSuffix)
+
+ /**
+ * The path suffix for the change password screen
+ */
def changePasswordSuffix = "change_password"
+
+ /**
+ * The computed path for change password screen
+ */
lazy val changePasswordPath = thePath(changePasswordSuffix)
+
+ /**
+ * The path suffix for the logout screen
+ */
def logoutSuffix = "logout"
+
+ /**
+ * The computed pat for logout
+ */
lazy val logoutPath = thePath(logoutSuffix)
+
+ /**
+ * The path suffix for the edit screen
+ */
def editSuffix = "edit"
+
+ /**
+ * The computed path for the edit screen
+ */
lazy val editPath = thePath(editSuffix)
+
+ /**
+ * The path suffix for the validate user screen
+ */
def validateUserSuffix = "validate_user"
+
+ /**
+ * The calculated path to the user validation screen
+ */
lazy val validateUserPath = thePath(validateUserSuffix)
+ /**
+ * The application's home page
+ */
def homePage = "/"
+ /**
+ * If you want to redirect a user to a different page after login,
+ * put the page here
+ */
object loginRedirect extends SessionVar[Box[String]](Empty) {
override lazy val __nameSalt = Helpers.nextFuncName
}
-
-
+ /**
+ * A helper class that holds menu items for the path
+ */
case class MenuItem(name: String, path: List[String],
loggedIn: Boolean) {
lazy val endOfPath = path.last
@@ -232,19 +308,35 @@ trait ProtoUser {
}
}
- def thePath(end: String): List[String] = basePath ::: List(end)
+ /**
+ * Calculate the path given a suffix by prepending the basePath to the suffix
+ */
+ protected def thePath(end: String): List[String] = basePath ::: List(end)
/**
* Return the URL of the "login" page
*/
def loginPageURL = loginPath.mkString("/","/", "")
+ /**
+ * Inverted loggedIn_?
+ */
def notLoggedIn_? = !loggedIn_?
+ /**
+ * A Menu.LocParam to test if the user is logged in
+ */
lazy val testLogginIn = If(loggedIn_? _, S.??("must.be.logged.in")) ;
+ /**
+ * A Menu.LocParam to test if the user is a super user
+ */
lazy val testSuperUser = If(superUser_? _, S.??("must.be.super.user"))
+ /**
+ * A Menu.LocParam for testing if the user is logged in and if they're not,
+ * redirect them to the login page
+ */
def loginFirst = If(
loggedIn_? _,
() => {
@@ -266,7 +358,7 @@ trait ProtoUser {
* The menu item for login (make this "Empty" to disable)
*/
def loginMenuLoc: Box[Menu] =
- Full(Menu(Loc("Login", loginPath, S.??("login"), loginMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("Login" + menuNameSuffix, loginPath, S.??("login"), loginMenuLocParams ::: globalUserLocParams)))
/**
@@ -285,10 +377,16 @@ trait ProtoUser {
Nil
/**
+ * If you have more than 1 ProtoUser in your application, you'll need to distinguish the menu names.
+ * Do so by changing the menu name suffix so that there are no name clashes
+ */
+ protected def menuNameSuffix: String = ""
+
+ /**
* The menu item for logout (make this "Empty" to disable)
*/
def logoutMenuLoc: Box[Menu] =
- Full(Menu(Loc("Logout", logoutPath, S.??("logout"), logoutMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("Logout" + menuNameSuffix, logoutPath, S.??("logout"), logoutMenuLocParams ::: globalUserLocParams)))
/**
* The LocParams for the menu item for logout.
@@ -303,7 +401,7 @@ trait ProtoUser {
* The menu item for creating the user/sign up (make this "Empty" to disable)
*/
def createUserMenuLoc: Box[Menu] =
- Full(Menu(Loc("CreateUser", signUpPath, S.??("sign.up"), createUserMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("CreateUser" + menuNameSuffix, signUpPath, S.??("sign.up"), createUserMenuLocParams ::: globalUserLocParams)))
/**
* The LocParams for the menu item for creating the user/sign up.
@@ -318,7 +416,7 @@ trait ProtoUser {
* The menu item for lost password (make this "Empty" to disable)
*/
def lostPasswordMenuLoc: Box[Menu] =
- Full(Menu(Loc("LostPassword", lostPasswordPath, S.??("lost.password"), lostPasswordMenuLocParams ::: globalUserLocParams))) // not logged in
+ Full(Menu(Loc("LostPassword" + menuNameSuffix, lostPasswordPath, S.??("lost.password"), lostPasswordMenuLocParams ::: globalUserLocParams))) // not logged in
/**
* The LocParams for the menu item for lost password.
@@ -333,7 +431,7 @@ trait ProtoUser {
* The menu item for resetting the password (make this "Empty" to disable)
*/
def resetPasswordMenuLoc: Box[Menu] =
- Full(Menu(Loc("ResetPassword", (passwordResetPath, true), S.??("reset.password"), resetPasswordMenuLocParams ::: globalUserLocParams))) //not Logged in
+ Full(Menu(Loc("ResetPassword" + menuNameSuffix, (passwordResetPath, true), S.??("reset.password"), resetPasswordMenuLocParams ::: globalUserLocParams))) //not Logged in
/**
* The LocParams for the menu item for resetting the password.
@@ -349,7 +447,7 @@ trait ProtoUser {
* The menu item for editing the user (make this "Empty" to disable)
*/
def editUserMenuLoc: Box[Menu] =
- Full(Menu(Loc("EditUser", editPath, S.??("edit.user"), editUserMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("EditUser" + menuNameSuffix, editPath, S.??("edit.user"), editUserMenuLocParams ::: globalUserLocParams)))
/**
* The LocParams for the menu item for editing the user.
@@ -364,7 +462,7 @@ trait ProtoUser {
* The menu item for changing password (make this "Empty" to disable)
*/
def changePasswordMenuLoc: Box[Menu] =
- Full(Menu(Loc("ChangePassword", changePasswordPath, S.??("change.password"), changePasswordMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("ChangePassword" + menuNameSuffix, changePasswordPath, S.??("change.password"), changePasswordMenuLocParams ::: globalUserLocParams)))
/**
* The LocParams for the menu item for changing password.
@@ -379,7 +477,7 @@ trait ProtoUser {
* The menu item for validating a user (make this "Empty" to disable)
*/
def validateUserMenuLoc: Box[Menu] =
- Full(Menu(Loc("ValidateUser", (validateUserPath, true), S.??("validate.user"), validateUserMenuLocParams ::: globalUserLocParams)))
+ Full(Menu(Loc("ValidateUser" + menuNameSuffix, (validateUserPath, true), S.??("validate.user"), validateUserMenuLocParams ::: globalUserLocParams)))
/**
* The LocParams for the menu item for validating a user.
View
32 persistence/record/src/main/scala/net/liftweb/record/MetaRecord.scala
@@ -99,10 +99,28 @@ trait MetaRecord[BaseRecord <: Record[BaseRecord]] {
private def isLifecycle(m: Method) = classOf[LifecycleCallbacks].isAssignableFrom(m.getReturnType)
- private def isField(m: Method) = !m.isSynthetic && classOf[Field[_, _]].isAssignableFrom(m.getReturnType)
+ private def isField(m: Method) = {
+ val ret = !m.isSynthetic && classOf[Field[_, _]].isAssignableFrom(m.getReturnType)
+ ret
+ }
+
+ def introspect(rec: BaseRecord, methods: Array[Method])(f: (Method, Field[_, BaseRecord]) => Any): Unit = {
+
+ // find all the potential fields
+ val potentialFields = methods.toList.filter(isField)
+
+ // any fields with duplicate names get put into a List
+ val map: Map[String, List[Method]] = potentialFields.foldLeft[Map[String, List[Method]]](Map()){
+ case (map, method) => val name = method.getName
+ map + (name -> (method :: map.getOrElse(name, Nil)))
+ }
- def introspect(rec: BaseRecord, methods: Array[Method])(f: (Method, Field[_, BaseRecord]) => Any) = {
- for (v <- methods if isField(v)) {
+ // sort each list based on having the most specific type and use that method
+ val realMeth = map.values.map(_.sortWith {
+ case (a, b) => !a.getReturnType.isAssignableFrom(b.getReturnType)
+ }).map(_.head)
+
+ for (v <- realMeth) {
v.invoke(rec) match {
case mf: Field[_, BaseRecord] if !mf.ignoreField_? =>
mf.setName_!(v.getName)
@@ -124,12 +142,12 @@ trait MetaRecord[BaseRecord <: Record[BaseRecord]] {
introspect(this, methods) {
case (v, mf) => tArray += FieldHolder(mf.name, v, mf)
}
-
+
fieldList = {
val ordered = fieldOrder.flatMap(f => tArray.find(_.metaField == f))
ordered ++ (tArray -- ordered)
}
-
+
fieldMap = Map() ++ fieldList.map(i => (i.name, i))
}
@@ -213,7 +231,7 @@ trait MetaRecord[BaseRecord <: Record[BaseRecord]] {
}
JsObj(tups:_*)
}
-
+
/**
* Retuns the JSON representation of <i>inst</i> record, converts asJValue to JsObj
*
@@ -296,7 +314,7 @@ trait MetaRecord[BaseRecord <: Record[BaseRecord]] {
def setFieldsFromJsonString(inst: BaseRecord, json: String): Box[Unit] =
setFieldsFromJValue(inst, JsonParser.parse(json))
- protected def foreachCallback(inst: BaseRecord, f: LifecycleCallbacks => Any) {
+ def foreachCallback(inst: BaseRecord, f: LifecycleCallbacks => Any) {
lifecycleCallbacks.foreach(m => f(m._2.invoke(inst).asInstanceOf[LifecycleCallbacks]))
}
View
43 persistence/record/src/test/scala/net/liftweb/record/FieldSpec.scala
@@ -24,16 +24,15 @@ import http.{LiftSession, S}
import http.js.JE._
import http.js.JsExp
import json.JsonAST._
-import util.FieldError
import util.Helpers._
import java.util.Calendar
-import scala.xml.{Node, Text}
-
import org.specs._
import org.specs.runner.{ConsoleRunner, JUnit3}
import fixtures._
+import net.liftweb.util.{Helpers, FieldError}
+import scala.xml.{NodeSeq, Elem, Node, Text}
/**
@@ -156,7 +155,7 @@ object FieldSpec extends Specification("Record Field Specification") {
}
}
- def passConversionTests[A](example: A, mandatory: MandatoryTypedField[A], jsexp: JsExp, jvalue: JValue, formPattern: Box[String]): Unit = {
+ def passConversionTests[A](example: A, mandatory: MandatoryTypedField[A], jsexp: JsExp, jvalue: JValue, formPattern: Box[NodeSeq]): Unit = {
"convert to JsExp" in {
mandatory.set(example)
@@ -184,8 +183,14 @@ object FieldSpec extends Specification("Record Field Specification") {
S.initIfUninitted(session) {
val formXml = mandatory.toForm
formXml must notBeEmpty
- formXml foreach { f =>
- f.toString must beMatching(fp)
+ formXml foreach { fprime =>
+ val f = ("* [name]" #> ".*" & "select *" #> (((ns: NodeSeq) => ns.filter {
+ case e: Elem => e.attribute("selected").map(_.text) == Some("selected")
+ case _ => false
+ }) andThen "* [value]" #> ".*"))(fprime)
+ val ret: Boolean = Helpers.compareXml(f, fp)
+
+ ret must_== true
}
}
}
@@ -212,7 +217,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryBooleanField,
JsTrue,
JBool(bool),
- Full("<input checked=\"checked\" tabIndex=\"1\" value=\"true\" type=\"checkbox\" name=\".*\" id=\"mandatoryBooleanField_id\"></input><input value=\"false\" type=\"hidden\" name=\".*\"></input>")
+ Full(<input checked="checked" tabIndex="1" value="true" type="checkbox" name=".*" id="mandatoryBooleanField_id"></input><input value="false" type="hidden" name=".*"></input>)
)
"support java.lang.Boolean" in {
rec.mandatoryBooleanField.setFromAny(java.lang.Boolean.TRUE)
@@ -240,7 +245,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryCountryField,
Str(country.toString),
JInt(country.id),
- Full("<select tabindex=\"1\" name=\".*\" id=\"mandatoryCountryField_id\">.*<option value=\".*\" selected=\"selected\">"+country.toString+"</option>.*</select>")
+ Full(<select tabindex="1" name=".*" id="mandatoryCountryField_id"><option value=".*" selected="selected">{country.toString}</option></select>)
)
}
@@ -254,7 +259,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryDateTimeField,
Str(dtStr),
JString(dtStr),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+dtStr+"\" id=\"mandatoryDateTimeField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={dtStr} id="mandatoryDateTimeField_id"></input>)
)
}
@@ -267,7 +272,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryDecimalField,
JsRaw(bd.toString),
JString(bd.toString),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+bd.toString+"\" id=\"mandatoryDecimalField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={bd.toString} id="mandatoryDecimalField_id"></input>)
)
}
@@ -280,7 +285,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryDoubleField,
JsRaw(d.toString),
JDouble(d),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+d.toString+"\" id=\"mandatoryDoubleField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={d.toString} id="mandatoryDoubleField_id"></input>)
)
}
@@ -293,7 +298,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryEmailField,
Str(email),
JString(email),
- Full("<input name=\".*\" type=\"text\" maxlength=\"100\" tabindex=\"1\" value=\""+email+"\" id=\"mandatoryEmailField_id\"></input>")
+ Full(<input name=".*" type="text" maxlength="100" tabindex="1" value={email} id="mandatoryEmailField_id"></input>)
)
}
@@ -306,7 +311,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryEnumField,
Str(ev.toString),
JInt(ev.id),
- Full("<select tabindex=\"1\" name=\".*\" id=\"mandatoryEnumField_id\">.*<option value=\".*\" selected=\"selected\">"+ev.toString+"</option>.*</select>")
+ Full(<select tabindex="1" name=".*" id="mandatoryEnumField_id"><option value=".*" selected="selected">{ev.toString}</option></select>)
)
}
@@ -319,7 +324,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryIntField,
JsRaw(num.toString),
JInt(num),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+num.toString+"\" id=\"mandatoryIntField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={num.toString} id="mandatoryIntField_id"></input>)
)
}
@@ -341,7 +346,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryLongField,
JsRaw(lng.toString),
JInt(lng),
- Full("<input name=\".*\" type=\"text\" tabindex=\"1\" value=\""+lng.toString+"\" id=\"mandatoryLongField_id\"></input>")
+ Full(<input name=".*" type="text" tabindex="1" value={lng.toString} id="mandatoryLongField_id"></input>)
)
}
@@ -375,7 +380,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryPostalCodeField,
Str(zip),
JString(zip),
- Full("<input name=\".*\" type=\"text\" maxlength=\"32\" tabindex=\"1\" value=\""+zip+"\" id=\"mandatoryPostalCodeField_id\"></input>")
+ Full(<input name=".*" type="text" maxlength="32" tabindex="1" value={zip} id="mandatoryPostalCodeField_id"></input>)
)
}
@@ -389,7 +394,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryStringField,
Str(str),
JString(str),
- Full("<input name=\".*\" type=\"text\" maxlength=\"100\" tabindex=\"1\" value=\""+str+"\" id=\"mandatoryStringField_id\"></input>")
+ Full(<input name=".*" type="text" maxlength="100" tabindex="1" value={str} id="mandatoryStringField_id"></input>)
)
}
@@ -475,7 +480,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryTextareaField,
Str(txt),
JString(txt),
- Full("<textarea name=\".*\" rows=\"8\" tabindex=\"1\" cols=\"20\" id=\"mandatoryTextareaField_id\">"+txt+"</textarea>")
+ Full(<textarea name=".*" rows="8" tabindex="1" cols="20" id="mandatoryTextareaField_id">{txt}</textarea>)
)
}
@@ -491,7 +496,7 @@ object FieldSpec extends Specification("Record Field Specification") {
rec.mandatoryTimeZoneField,
Str(example),
JString(example),
- Full("<select tabindex=\"1\" name=\".*\" id=\"mandatoryTimeZoneField_id\">.*<option value=\""+example+"\" selected=\"selected\">"+example+"</option>.*</select>")
+ Full(<select tabindex="1" name=".*" id="mandatoryTimeZoneField_id"><option value=".*" selected="selected">{example}</option></select>)
)
}
}
View
4 project/build.properties
@@ -1,7 +1,7 @@
#Project properties
project.organization=net.liftweb
project.name=lift-framework
-sbt.version=0.7.5
+sbt.version=0.7.7
project.version=2.4-SNAPSHOT
-build.scala.versions=2.9.0.RC3 2.8.1 2.8.0
+build.scala.versions=2.9.0 2.8.1 2.8.0
project.initialize=false
View
30 project/build/LiftFrameworkProject.scala
@@ -14,11 +14,11 @@
* limitations under the License.
*/
-import com.weiglewilczek.bnd4sbt.BNDPlugin
import java.util.Calendar
import java.util.jar.Attributes.Name
-import sbt._
+import com.weiglewilczek.bnd4sbt.BNDPlugin
import net.liftweb.sbt._
+import sbt._
class LiftFrameworkProject(info: ProjectInfo) extends ParentProject(info) with LiftParentProject {
@@ -31,9 +31,9 @@ class LiftFrameworkProject(info: ProjectInfo) extends ParentProject(info) with L
// -------------
lazy val common = coreProject("common", slf4j_api, logback, log4j)()
lazy val actor = coreProject("actor")(common)
- lazy val json = coreProject("json", paranamer)()
-// FIXME: Scala 2.9.0.RC3
-// lazy val json_scalaz = coreProject("json-scalaz", scalaz)(json)
+ lazy val json = coreProject("json", paranamer, scalap)()