How to make a field READONLY in lift crudify - scala

I have a field that I would like to be read-only and have a default value.
here what I have till now:
object passwd extends MappedString(this, 20)
{
override def defaultValue = "XXX" + Random.alphanumeric.take(12).mkString // some default value
// readOnly = true // Or something similar???
}
I tried overriding toHtml, asHtml, toForm, displayHtml but they all change the view or the label or something else.
I just want the defaultValue to be automatically generated (which works) but the users not to be able to edit that field when create/editing the entity.
Any advice would be very much appreciated

Got it,
Just override _toForm and disable the input element:
import net.liftweb.http.S
override def _toForm =
S.fmapFunc({s: List[String] => this.setFromAny(s)}){name =>
Full(<input disabled='disabled' type='text' id={fieldId} maxlength={maxLen.toString}
name={name}
value={is match {case null => "" case s => s.toString}}/>)}

The trait MappedField defines the methods writePermission_? and readPermission_?:
/**
* Given the current execution state, can the field be written?
*/
def writePermission_? = false
/**
* Given the current execution state, can the field be read?
*/
def readPermission_? = false
So you could just override these with
object passwd extends MappedString(this, 20) {
override def defaultValue = "XXX" + Random.alphanumeric.take(12).mkString // some default value
override writePermission_? = false
override readPermission_? = true
}
Is that what you're looking for?

Related

scala store method in set with possibility of removal

Here's some test code to demonstrate the problem:
import scala.collection.mutable.HashSet
import scala.collection.mutable.Set
object Test extends App
{
val set: Set[Int => Boolean] = new HashSet()
def test(i: Int): Boolean = {
false
}
Console.println(set.add(test)) // true
Console.println(set.remove(test)) // false
Console.println(set.add(test _)) // true
Console.println(set.remove(test _)) // false
}
What options are there for making this work?
Edit: I also want this to support lambdas.
Every time you perform "add" or "remove" operation Scala compiler creates new AbstractFunction1.mcZI.sp object instance:
Console..MODULE$.println(BoxesRunTime.boxToBoolean(set().add(
new AbstractFunction1.mcZI.sp() {
public final boolean apply(int i) { return apply$mcZI$sp(i); }
public boolean apply$mcZI$sp(int i) { return Test..MODULE$.test(i); }
})));
As I understand this object instance doesn't contain information about enclosing object and 'test' method which can be used for 'hashCode' and 'equals' methods.
I think as a workaround you may consider this:
import scala.collection.mutable.HashSet
import scala.collection.mutable.Set
object Test extends App
{
val set: Set[Int => Boolean] = new HashSet()
def test(i: Int): Boolean = {
false
}
val testFunc: Function1[Int, Boolean] = test
Console.println(set.add(testFunc)) // true
Console.println(set.remove(testFunc)) // true
val testPartFunc = test _
Console.println(set.add(testPartFunc)) // true
Console.println(set.remove(testPartFunc)) // true
}
If instead of passing methods you pass function it works:
val testFun: Int => Boolean = i => i > 10
set.add(testFun) // true
set.remove(testFun) // true
it will also work for anonymous functions:
set.add { _ => false }
but then I don't know how you would like to remove such function from the set.

Is there a best practice to assign a value to a scala Option when it's not initialized/None?

I have the following class where the properties are an Option[T]
class User extends IdBaseEntity[UUID] {
var id: Option[UUID] = None
var name: Option[String] = None
var createdOn: Option[Date] = None
}
In some data access layer I need to assign these properties if they aren't set before the object is persisted to cassandra. Here are a few ways for the createdOn property. Are any of these the best approach or is there something better I should be doing?
Example 1
entity.createdOn = Some(entity.createdOn.map(identity).getOrElse(new Date()))
Example 2
entity.createdOn = entity.createdOn.orElse(Some(new Date()))
Example 3
entity.createdOn = entity.createdOn match {
case None => Some(new Date())
case _ => entity.createdOn
}
Example 4
entity.createdOn = entity.createdOn match {
case None => Some(new Date())
case Some(x) => Some(x)
}
Example 5
entity.createdOn match {
case None => entity.createdOn = Some(new Date())
case _ =>;
}
Matching on Option is not really idiomatic (IMHO). I prefer to get orElse or getOrElse. Personally I would go with example 2.
I'm not sure whether this will fit your use case, but it is more idiomatic to make User an immutable case class:
case class User(id: Option[UUID] = None, ...)
and copy it, rather than updating the fields in-place:
val updatedEntity = entity.copy(name = user.name.orElse(Some("Chris")))
I'd consider changing your design a bit - for two reasons:
It looks like the User class should be read-only once initialized, so something like a case class or val instead of var would capture that requirement:
case class User( id:UUID, name:String, createdOn:Date );
It looks like every User is required to have an id, name, and createdOn property set, so Option[] is not a good way to model that.
I often setup a Builder class along side read-only classes to
simplify and decouple the object-construction process from
what the object represents - something like
this
object User {
class Builder {
var id:UUID = UUID.randomUUID()
def id( v:UUID ):this.type = {id =v; this; }
var name:String = id.toString
def name( v:String ):this.type = { name=v; this; }
var createdOn:Date = new Date()
def createdOn( v:Date ):this.type = { createdOn = v; this; }
def build():User = {
assert( Seq(id,name,createdOn).find( _ == null ).isEmpty, "Must set all props" )
User( user, name, createdOn )
}
}
}
Anyway - that's another way to do things ...
Since the scenario is "get a property value and update it if some condition holds", I'd try to encapsulate access to properties. For example:
/**
* Read+write access to property `P` of object `R`.
*/
case class Accessor[R,P](get: R => P, set: (R, P) => Unit) {
/** Utility for updating values. */
def update(record: R, modfn: P => P) =
set(record, modfn(get(record)));
}
class User {
var id: Option[Int] = None;
}
object User {
// For each property of `User` we need to define an accessor,
// but this is one time job:
val idA: Accessor[User,Option[Int]] =
Accessor((u: User) => u.id,
(u: User, r: Option[Int]) => u.id = r);
}
object Test {
import User._;
// We can do a lot of stuff now with accessors, for example set
// default values for `Option[...]` ones:
def setDefault[A,B](record: A,
accessor: Accessor[A,Option[B]],
defPropVal: => B) =
accessor.update(record, _.orElse(Some(defPropVal)));
val user = new User();
// Set user's id, if not defined:
setDefault(user, idA, 42);
}
So instead of defining a specific method for each property for filling in default values, we define a generic accessor for each property. Then we can use them to implement all the other stuff generically.

How to override implicit Action val into Play framework 2

I need to override implicited values befor it pass to the template, but dont have idea how.
Something like this:
/* This Session User and City setup */
case class MySession(user: Option[User] = None, city: Option[City] = None) {}
/* Trait for Controllers */
trait CMySession {
implicit def mySession[A](implicit request: Request[A]) : MySession = {
val userOpt = /*... get from session user here ...*/
val cityOpt = /*... get from session city here ...*/
MySession(user = userOpt, city = cityOpt)
}
}
/* Controller */
def showCity(city_name: String) = Action { implicit request =>
// Get city
val cityOpt = { for (c <- mySession.city) yield Some(c) } getOrElse Cities.getByName(city_name)
// Check if NO City in session, but we get it from request
if (mySession.city != cityOpt) {
// NEED some how override implicited mySession value here for template?!
}
Ok(views.html.showCity())
}}
Thank you for any clues!
The great thing about implicit values in Scala is that you can override them, either by declaring your own implicit value in scope (in your case in the if block) or by passing it explicitly (in your case the template, e.g. views.html.showCity(session = myOtherSession)).

Override how a BsonRecord fills in its fields

I would like to extend the BsonRecord class to handles some of its fields when they are filled in. I'm trying to do it by extending the setFieldsFrom... methods, but it doesn't seem to work...
Here is the code I have :
trait NodeBsonRecord[MyType <: BsonRecord[MyType]] extends BsonRecord[MyType]
{
self: MyType =>
override def setFieldsFromDBObject(dbo:DBObject) =
{
super.setFieldsFromDBObject(dbo)
println("setFieldsFromDBObject")
}
override def setFieldsFromJSON(json:String) =
{
val out = super.setFieldsFromJSON(json)
println("setFieldsFromJSON")
out
}
override def setFieldsFromJsonString(json:String) =
{
val out = super.setFieldsFromJsonString(json)
println("setFieldsFromJsonString")
out
}
override def setFieldsFromJValue(jval:JValue) =
{
val out = super.setFieldsFromJValue(jval)
println("setFieldsFromJValue")
out
}
override def setFieldsFromReq(req:Req) =
{
val out = super.setFieldsFromReq(req)
println("setFieldsFromReq")
out
}
}
So when I request for a Record (using MongoRecord.find()), I expect to see a "setFieldFrom..." thing, but nothing is printed out...
Anybody can tell me how to do this ?
Mongo seems to use setFieldsFromDBObject in BsonMetaRecord as part of find, which iterates through each field and calls setFromAny.

why does MappedStringIndex not included in fieldList in Mapper

In Mapper's having String as a primary Key, why is the MappedStringIndex not shown in the list of all fields that are obtained via the Mapper's allFields method
My Mapper goes like this ...
class DummyMapper extends KeyedMapper[String,DummyMapper] {
def getSingleton = DummyMapper
def primaryKeyField = dummyCode
object dummyCode extends MappedStringIndex(this,5)
{
override def writePermission_? = true
override def dbAutogenerated_? = false
override def dbNotNull_? = true
override def dbColumnName="dummy_code"
}
.....
I even tried it including in the fieldOrder. Still the result was same, it din't showed up in DummyMapper.allFields list
primarykey fields ( of any data-type ) are not included in the list returned by allFileds method of the Mapper.
You can prepend the field separately if you want
something like
var myMapperPrimaryKey=DummyMapper.primaryKeyField
var fieldList=DummyMapper.allFields.toBuffer[BaseField]
fieldList.prepend(myMapperPrimaryKey)
// Now fieldList is having the primaryKey along with other fields.