A lot of modern web frameworks - such as Ruby on Rails1 and Yii22 provide a feature called Mass assignment. It's a convenience feature which maps out an assignment of attributes from one model to another, 'populating' it for a certain scenario.
I dislike writing a bunch of assignment statements consecutively. It feels like a bit of a code smell and I want it to go away. This is more for personal use than anything else.
A build and initialization macro with some syntax sugar to make your code puurrttyyy~.
Caution!
Using Massive assignment in a improper manner can lead to security vulnerabilities.345
Given a model ExampleModel;
classExampleModel{
publicvarpropertyA:Int;
publicvarpropertyB:Int;
publicvarpropertyC:Int;
privatevarpropertyD:Int;
publicfunctionnew(a:Int,b:Int,c:Int,d:Int){
this.propertyA=a;
this.propertyB=b;
this.propertyC=c;
this.propertyD=d;
}
}and two instances of the model;
varoldModel=newExampleModel(1, 2, 3, 4);
varnewModel=newExampleModel(3, 4, 10, 55);We can selectively copy attributes from newModel to oldModel;
functionexample(){
@:mass(propertyA, propertyB) oldModel=newModel;
}Which will map out to the following at compile time;
functionexample(){
oldModel.propertyA=newModel.propertyA;
oldModel.propertyB=newModel.propertyB;
}We can also copy all public writable attributes from newModel to oldModel that oldModel can hold;
@:massfunctionexample(){
@:massoldModel=newModel;
}Which will map out to the following at compile time;
functionexample(){
oldModel.propertyA=newModel.propertyA;
oldModel.propertyB=newModel.propertyB;
oldModel.propertyC=newModel.propertyC;
}Scenarios are an addition to massive assignment, traditionally to work with ActiveRecord instances or instances of a model from the database. They are an attempt to simultaneously 'raise' the abstraction level by defining the whitelisted attributes in the class model, so you can reuse the whitelists across multiple @:mass assignments, and to coerce the programmer to explicitly define what attributes are 'safe' in a certain context.
The scenario implementation assumes that your ExampleModel has a function public inline function scenarios():Map<String,Array<String>>. Inlining isn't mandatory but there's no reason not to.
classExampleModel{
...publicinlinefunctionscenarios() return [
"exampleScenario"=> ["propertyB", "propertyC"]
];
...
}Then, when you want to assign to a ExampleModel instance in a constrained monomorph, ie an explicit ExampleModel instance, use a @:scenario("scenario") in a parent node of the AST, ie a function;
@:scenario("exampleScenario") functionclosure(){
@:massoldModel=newModel;
}Which will then map to (roughly) the following code using Reflection;
functionclosure(){
for(pin ["propertyB", "propertyC"]){
varval=Reflect.getProperty(newModel, p);
Reflect.setProperty, oldModel, p, val);
}
}A big thankyou to back2dos for the tink libraries - they are extremely useful and this language extension was based off of the tink_await library.
- Check at compiletime if object B has all props from object A?
- Only update props from B that intersect with A, warn otherwise
- Allow assignment to anonymous structures
- Allow assignment from anonymous structures
- public readable check for getting vals from B