On 27/04/2015 12:10 a.m., Laeeth Isharc wrote:
On Sunday, 26 April 2015 at 01:03:12 UTC, Rikki Cattermole wrote:

I'm personally moving towards a DSL.

unittest {
    auto myQuery = """
using webdev.base.orm.query.parser.defs # allow for D class name
instead of table name
; # end of \"sentence\"

from MyModel
where key == $0
as simple
# as index # but only one can be defined, two in total internal
; # end of \"sentence\"

from MyModel
where value contains $0
as complex
# as index # but only one can be defined, two in total internal
; # end of \"sentence\"

""".query;

    MyModel[] gotValues = myQuery.simple("test");
    gotValues = myQuery.perform("complex", "another");
}

Pros:
- Runtime reloadability
- Runtime composability
- More flexible
Cons:
- It's a string

Can Pegged be usefully applied to implement this?

Incidentally, it seems that although Hibernated is very nice, there is
still work to be done.  Eg I would like to insert 10 million rows, and
it seems like a transaction would be the best way of doing so, but it's
not yet supported.  No big deal since the schema is very simple and I
can do it by hand, but it would be nice to have at some point.  (I
looked at your own ORM, but keeping it in memory won't work for me).

Dvorm like Cmsed is going bye byes.
My next web service framework is going to have a very different approach to ORM's and routing!

The given code example, is an actual unittest for the parser. It'll be using a reflection API to wrap ORM models up. So the ORM will no longer do serialization. Instead the backend will to the appropriate types.

Just so you get a small taste of the reflection capabilities. I know it may not seem like much, but imagine e.g. lua to have wrapper code around this and to act as if the data models were written natively in it or to pass a template (lets say lua based) a bunch of data models and have it call D methods on it! The only problem is that the web server that it is meant to compliment isn't ready yet.
https://github.com/DNetDev/webserver

Until I or somebody else gets round to implementing this https://github.com/rejectedsoftware/vibe.d/issues/1074 mindset wise, I can't continue on.

Also you are welcome to join https://gitter.im/DNetDev/Public if you want to talk more.

module webdev.base.reflection.model;
import webdev.base.traits.are : isADataModel, isADataModelProperty, isDataModelMemberId, isADataModelQueryMethod, isADataModelQueryStaticMethod; import webdev.base.traits.have : getDataModelName, getDataModelDescription, getDataModelPropertyDescription, getDataModelPropertyHints, getDataModelPropertyName;
import webdev.base.udas : OrmPropertyTypes;

private __gshared {
        import std.variant : Algebraic;
import std.traits : fullyQualifiedName, isArray, ReturnType, ParameterTypeTuple;

        AReflectedModel*[string] models;
        AReflectedModel*[string] modelsByTableName;
}

/*
 * Basic interactions of the different kinds of models
 */

/**
 * Gets all names of data models registered
 * Uses the fully qualified name (package + module + class/struct name)
 *
 * Returns:
 *              The names to all data models
 */
string[] reflectedModelNames() {
        return models.keys;
}

/**
 * Lazily registers and gets a reflected model given the data model type
 *
 * Returns:
 *              The reflected model
 */
AReflectedModel* getReflectModel(T)() if(isADataModel!T) {
        return getReflectedModel(fullyQualifiedName!T);
}


/**
 * Gets a reflected model based upon its fully qualified name
 *
 * Params:
 *              name    =       Name of the model
 *
 * Returns:
 *              The reflected model
 */
AReflectedModel* getReflectedModel(string name) {
        if (name !in models) return null;
        return models[name].dup();
}

/**
 * Gets a reflected model based upon its table name
 *
 * Params:
 *              name    =       Name of the model
 *
 * Returns:
 *              The reflected model
 */
AReflectedModel* getReflectedModelByTableName(string name) {
        if (name !in modelsByTableName) return null;
        return modelsByTableName[name].dup();
}

/*
 * General reflection based types
 */

///
enum OrmActualPropertyTypes {
        Unknown,

        UByte,
        Byte,
        UShort,
        Short,
        UInt,
        Int,
        ULong,
        Long,
        Float,
        Double,
        String,
        WString,
        DString,
        
        Array,
        DataModel
}

///
alias ModelValidTypes = Algebraic!(AReflectedModelInstance*, ubyte, byte, ushort, short, uint, int, ulong, long, float, double, string, wstring, dstring);

///
struct PropertyHint {
        ///
        string name;

        ///
        OrmActualPropertyTypes actualType;

        ///
        OrmActualPropertyTypes arrayActualType;

        ///
        AReflectedModel* objectActualType;

        ///
        OrmPropertyTypes hintType;

        ///
        size_t size;

        ///
        string description;

        ///
        bool isId;
}

///
struct QueryDescriptor {
        ///
        QueryTypeDescriptor[] arguments;

        ///
        QueryTypeDescriptor returnType;

        struct QueryTypeDescriptor {
                ///
                OrmActualPropertyTypes actualType;
                
                ///
                OrmActualPropertyTypes arrayActualType;
                
                ///
                AReflectedModel* objectActualType;
        }
}

/*
 * The actual reflection mechanism
 */

struct AReflectedModel {
        static AReflectedModel* reflect(T)() if(isADataModel!T) {
                AReflectedModel* ret = new AReflectedModel;

                ret.dup = () { return AReflectedModel.reflect!T(); };

                /// constructs function delegates for a model instance aware of 
the type
                void funcCalls(AReflectedModelInstance* retm) {
                        static if (__traits(hasMember, T, "isValid")) {
                                retm.isValid = () { return 
(cast(T*)retm.instance_).isValid(); };
                        } else {
                                retm.isValid = () { return true; };
                        }

                        retm.get = (string name) {
                                foreach(member; __traits(allMembers, T)) {
                                        static if (isADataModelProperty!(T, 
member)) {
                                                if (member == name) {
return new ModelValidTypes(mixin("(cast(T*)retm.instance_)." ~ member));
                                                }
                                        }
                                }

                                return null;
                        };

                        retm.set = (string name, ModelValidTypes* value) {
                                foreach(member; __traits(allMembers, T)) {
                                        mixin("alias MTYPE = typeof(T." ~ member ~ 
");");

                                        static if (isADataModelProperty!(T, 
member)) {
                                                if (member == name) {
                                                        static if 
(__traits(compiles, {MTYPE t = null;})) {
                                                                if (value is 
null) {
                                                                        
mixin("(cast(T*)retm.instance_)." ~ member ~ " = null;");
                                                                } else if 
(value.convertsTo!MTYPE) {
mixin("(cast(T*)retm.instance_)." ~ member ~ " = value.get!MTYPE;");
                                                                } else {
assert(0, value.type.toString ~ " is not convertable to " ~ MTYPE.stringof);
                                                                }
                                                        } else {
                                                                if (value !is null 
&& value.convertsTo!MTYPE) {
mixin("(cast(T*)retm.instance_)." ~ member ~ " = value.get!MTYPE;");
                                                                } else {
assert(0, value.type.toString ~ " is not convertable to " ~ MTYPE.stringof);
                                                                }
                                                        }
                                                }
                                        }
                                }
                        };

                        retm.query = (string name, ModelValidTypes[] values...) 
{
                                foreach(member; __traits(allMembers, T)) {
                                        mixin("alias MTYPE = typeof(T." ~ member ~ 
");");

                                        static if (isADataModelQueryMethod!(T, 
member)) {
                                                alias MRET = ReturnType!MTYPE;

                                                if (member == name) {
                                                        alias ARGU = 
ParameterTypeTuple!MTYPE;

                                                        if (values.length != 
ARGU.length)
                                                                assert(0, "Not 
enough arguments");
                                                                
                                                        foreach(i, ARG; ARGU) {
                                                                if 
(!values[i].convertsTo!ARG)
                                                                        assert(0, 
"Wrong types for arguments");
                                                        }

                                                        static if (is(MRET == 
void)) {
                                                                // return call
mixin("(cast(T*)retm.instance)." ~ member ~ "(" ~ getCallToMethodSyntaxVarient!("values", "ARGU", ARGU) ~ ");");
                                                                return 
cast(ModelValidTypes[])null;
                                                        } else {
                                                                // call
mixin("auto ret = (cast(T*)retm.instance_)." ~ member ~ "(" ~ getCallToMethodSyntaxVarient!("values", "ARGU", ARGU) ~ ");");

                                                                static if 
(isArray!MRET) {
                                                                        
ModelValidTypes[] ret2;

                                                                        
foreach(v; ret) {
                                                                                
ret2 ~= ModelValidTypes(v);
                                                                        }

                                                                        return 
ret2;
                                                                } else {
                                                                        return 
cast(ModelValidTypes[])[ModelValidTypes(v)];
                                                                }
                                                        }
                                                }
                                        }
                                }

                                assert(0);
                        };
                }

                ret.create = () {
                        AReflectedModelInstance* retm = new 
AReflectedModelInstance;
                        retm.model_ = ret;

                        static if (is(T == class))
                                retm.instance_ = &(new T);
                        else static if (is(T == struct))
                                retm.instance_ = new T;
                        else static assert(0);
                        
                        funcCalls(retm);
                        return retm;
                };

                ret.fromInstance = (void* value) {
                        /// in
        
                        assert(value !is null);

                        if (T* ttv = cast(T*)value){}
                        else assert(0, "Argument is not of type " ~ T.stringof);

                        /// body

                        AReflectedModelInstance* retm = new 
AReflectedModelInstance;
                        retm.model_ = ret;

                        retm.instance_ = value;

                        funcCalls(retm);
                        return retm;
                };

                ret.tableName = () { return getDataModelName!T; };
                ret.fullName = () { return fullyQualifiedName!T; };
                ret.description = () { return getDataModelDescription!T; };

                ret.propertyNames = () {
                        string[] retm;

                        foreach(member; __traits(allMembers, T)) {
                                static if (isADataModelProperty!(T, member)) {
                                        retm ~= member;
                                }
                        }

                        return retm;
                };

                ret.propertyHints = (string name) {
                        PropertyHint retm;

                        foreach(member; __traits(allMembers, T)) {
                                static if (isADataModelProperty!(T, member)) {
                                        if (member == name) {
                                                mixin("alias MTYPE = typeof(T." ~ member 
~ ");");

                                                retm.name = 
getDataModelPropertyName!(T, member);

                                                // actualType
                                                enum MTYPEA = 
actualTypeFromType!MTYPE;
                                                retm.actualType = MTYPEA;

                                                // arrayActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.Array)
                                                        retm.arrayActualType = 
actualTypeFromType!(typeof(MTYPE.init)[0]);

                                                // objectActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.DataModel)
                                                        retm.objectActualType = 
getReflectModel!MTYPE;

                                                auto hint = 
getDataModelPropertyHints!(T, member);

                                                // hintType
                                                retm.hintType = hint.type;

                                                // size
                                                if (hint.size == 0) {
                                                        static if 
(isArray!MTYPE) {
                                                        } else
                                                                hint.size = 
MTYPE.sizeof;
                                                } else
                                                        retm.size = hint.size;

                                                // description
                                                retm.description = 
getDataModelPropertyDescription!(T, member);

                                                // isId
                                                retm.isId = 
isDataModelMemberId!(T, member);
                                        }
                                }
                        }

                        return retm;
                };

                ret.query = (string name, ModelValidTypes[] values...) {
                        foreach(member; __traits(allMembers, T)) {
                                mixin("alias MTYPE = typeof(T." ~ member ~ 
");");
                                
                                static if (isADataModelQueryStaticMethod!(T, 
member)) {
                                        alias MRET = ReturnType!MTYPE;
                                        
                                        if (member == name) {
                                                alias ARGU = 
ParameterTypeTuple!MTYPE;
                                                
                                                if (values.length != 
ARGU.length)
                                                        assert(0, "Not enough 
arguments");
                                                
                                                foreach(i, ARG; ARGU) {
                                                        if 
(!values[i].convertsTo!ARG)
                                                                assert(0, "Wrong 
types for arguments");
                                                }
                                                
                                                static if (is(MRET == void)) {
                                                        // return call
mixin("T." ~ member ~ "(" ~ getCallToMethodSyntaxVarient!("values", "ARGU", ARGU) ~ ");");
                                                        return 
cast(ModelValidTypes[])null;
                                                } else {
                                                        // call
mixin("auto ret = T." ~ member ~ "(" ~ getCallToMethodSyntaxVarient!("values", "ARGU", ARGU) ~ ");");
                                                        
                                                        ModelValidTypes[] ret2;

                                                        static if 
(isArray!MRET) {
                                                                foreach(v; ret) 
{
                                                                        static 
if (is(typeof(v) == class) || is(typeof(v) == struct)) {
ret2 ~= ModelValidTypes(getReflectModel!(typeof(v))().fromInstance(&v));
                                                                        } else {
                                                                                
ret2 ~= ModelValidTypes(v);
                                                                        }
                                                                }
                                                        } else {
                                                                static if 
(is(MRET == class) || is(MRET == struct)) {
ret2 ~= ModelValidTypes(getReflectModel!(MRET)().fromInstance(&ret));
                                                                } else {
                                                                        ret2 ~= 
ModelValidTypes(ret);
                                                                }
                                                        }

                                                        return ret2;
                                                }
                                        }
                                }
                        }
                        
                        assert(0);
                };

                // queryNames
                ret.queryNames = () {
                        string[] ret;

                        foreach(member; __traits(allMembers, T)) {
                                mixin("alias MTYPE = typeof(T." ~ member ~ 
");");
                                
                                static if (isADataModelQueryStaticMethod!(T, 
member)) {
                                        ret ~= member;
                                }
                        }

                        return ret;
                };

                // queryParameters
                ret.queryParameters = (string name) {
                        QueryDescriptor ret;
                        
                        foreach(member; __traits(allMembers, T)) {
                                static if (isADataModelQueryStaticMethod!(T, 
member)) {
                                        if (member == name) {

                                                // arguments
                                                foreach(ARG; 
ParameterTypeTuple!(mixin("T." ~ name))) {
                                                        
QueryDescriptor.QueryTypeDescriptor rett;

                                                        // actualType
                                                        enum MTYPEA = 
actualTypeFromType!ARG;
                                                        rett.actualType = 
MTYPEA;

                                                        // arrayActualType
                                                        static if (MTYPEA == 
OrmActualPropertyTypes.Array)
                                                                
rett.arrayActualType = actualTypeFromType!(typeof(ARG.init)[0]);

                                                        // objectActualType
                                                        static if (MTYPEA == 
OrmActualPropertyTypes.DataModel)
                                                                
rett.objectActualType = getReflectModel!ARG;

                                                        ret.arguments ~= rett;
                                                }

                                                // return type

                                                alias RETM = 
ReturnType!(mixin("T." ~ m));

                                                // actualType
                                                enum MTYPEA = 
actualTypeFromType!RETM;
                                                ret.returnType.actualType = 
MTYPEA;
                                                
                                                // arrayActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.Array)
ret.returnType.arrayActualType = actualTypeFromType!(typeof(ARG.init)[0]);
                                                
                                                // objectActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.DataModel)
                                                        
ret.returnType.objectActualType = getReflectModel!ARG;
                                        }
                                }
                        }
                        
                        return ret;
                };

                // queryInstanceNames
                ret.queryInstanceNames = () {
                        string[] ret;
                        
                        foreach(member; __traits(allMembers, T)) {
                                mixin("alias MTYPE = typeof(T." ~ member ~ 
");");
                                
                                static if (isADataModelQueryMethod!(T, member)) 
{
                                        ret ~= member;
                                }
                        }
                        
                        return ret;
                };

                // queryInstanceParameters
                ret.queryInstanceParameters = (string name) {
                        QueryDescriptor ret;
                        
                        foreach(member; __traits(allMembers, T)) {
                                static if (isADataModelQueryMethod!(T, member)) 
{
                                        if (member == name) {
                                                
                                                // arguments
                                                foreach(ARG; 
ParameterTypeTuple!(mixin("T." ~ name))) {
                                                        
QueryDescriptor.QueryTypeDescriptor rett;
                                                        
                                                        // actualType
                                                        enum MTYPEA = 
actualTypeFromType!ARG;
                                                        rett.actualType = 
MTYPEA;
                                                        
                                                        // arrayActualType
                                                        static if (MTYPEA == 
OrmActualPropertyTypes.Array)
                                                                
rett.arrayActualType = actualTypeFromType!(typeof(ARG.init)[0]);
                                                        
                                                        // objectActualType
                                                        static if (MTYPEA == 
OrmActualPropertyTypes.DataModel)
                                                                
rett.objectActualType = getReflectModel!ARG;
                                                        
                                                        ret.arguments ~= rett;
                                                }
                                                
                                                // return type
                                                
                                                alias RETM = 
ReturnType!(mixin("T." ~ m));
                                                
                                                // actualType
                                                enum MTYPEA = 
actualTypeFromType!RETM;
                                                ret.returnType.actualType = 
MTYPEA;
                                                
                                                // arrayActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.Array)
ret.returnType.arrayActualType = actualTypeFromType!(typeof(ARG.init)[0]);
                                                
                                                // objectActualType
                                                static if (MTYPEA == 
OrmActualPropertyTypes.DataModel)
                                                        
ret.returnType.objectActualType = getReflectModel!ARG;
                                        }
                                }
                        }
                        
                        return ret;
                };

                models[fullyQualifiedName!T] = ret;
                models[ret.tableName()] = ret;
                return ret;
        }

        AReflectedModel* delegate() dup;
        AReflectedModelInstance* delegate() create;
        AReflectedModelInstance* delegate(void*) fromInstance;

        string delegate() tableName;
        string delegate() fullName;
        string delegate() description;

        const(string[]) delegate() propertyNames;
        PropertyHint delegate(string name) propertyHints;

        ModelValidTypes[] delegate(string func, ModelValidTypes[] values...) 
query;
        const(string[]) delegate() queryNames;
QueryDescriptor delegate(string name) queryParameters; //FIXME: should not be void*

        const(string[]) delegate() queryInstanceNames;
QueryDescriptor delegate(string name) queryInstanceParameters; //FIXME: should not be void*
}

struct AReflectedModelInstance {
        private {
                AReflectedModel* model_;

                void* instance_;
        }

        AReflectedModel* model() { return model; }
        void* instance() { return instance; }

        /*
         *
* Shouldn't everything below this, stored in the model instead of the state?
         *
         */

        bool delegate() isValid;

        /*
* Get/Set for all approved properties given a name and the value for a model instance
         */
        ModelValidTypes* delegate(string name) get;
        void delegate(string name, ModelValidTypes* value) set;

        ModelValidTypes[] delegate(string func, ModelValidTypes[] values...) 
query;
}

private {
        OrmActualPropertyTypes actualTypeFromType(T)() {
                static if (is(T == ubyte))
                        return OrmActualPropertyTypes.UByte;
                else static if (is(T == byte))
                        return OrmActualPropertyTypes.Byte;
                else static if (is(T == ushort))
                        return OrmActualPropertyTypes.UShort;
                else static if (is(T == short))
                        return OrmActualPropertyTypes.Short;
                else static if (is(T == uint))
                        return OrmActualPropertyTypes.UInt;
                else static if (is(T == int))
                        return OrmActualPropertyTypes.Int;
                else static if (is(T == ulong))
                        return OrmActualPropertyTypes.ULong;
                else static if (is(T == long))
                        return OrmActualPropertyTypes.Long;
                else static if (is(T == float))
                        return OrmActualPropertyTypes.Float;
                else static if (is(T == double))
                        return OrmActualPropertyTypes.Double;
                else static if (is(T == string))
                        return OrmActualPropertyTypes.String;
                else static if (is(T == wstring))
                        return OrmActualPropertyTypes.WString;
                else static if (is(T == dstring))
                        return OrmActualPropertyTypes.DString;
                else static if (is(T == class) || is(T == struct))
                        return OrmActualPropertyTypes.DataModel;
                else static if (isArray!T)
                        return OrmActualPropertyTypes.Array;
                else
                        return OrmActualPropertyTypes.Unknown;
        }

string getCallToMethodSyntaxVarient(string valuesName, string argName, ARGS...)() pure {
                import std.conv : text;
                string ret;

                foreach(i, ARG; ARGS) {
                        string TI = text(i);
                        ret ~= valuesName ~ "[" ~ TI ~ "].get!(" ~ argName ~ "[" ~ TI ~ 
"]), ";
                }

                if (ARGS.length > 0)
                        ret.length -= 2;

                return ret;
        }
}

Reply via email to