WIP Continuing macro-based ORM layer.
This commit is contained in:
parent
61849d5e35
commit
2687c25506
@ -28,20 +28,34 @@ proc dbFormat[T](list: seq[T]): string =
|
|||||||
|
|
||||||
proc dbFormat[T](item: T): string = return $item
|
proc dbFormat[T](item: T): string = return $item
|
||||||
|
|
||||||
proc createParseStmts(t: NimNode, value: string): NimNode =
|
proc createParseStmt(t, value: NimNode): NimNode =
|
||||||
result = newStmtList()
|
result = newStmtList()
|
||||||
|
|
||||||
if t.typeKind == ntyObject:
|
if t.typeKind == ntyObject:
|
||||||
if t.getType == UUID.getType:
|
if t.getType == UUID.getType:
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
discard parseUUID(`value`)
|
parseUUID(`value`)
|
||||||
elif t.getType == DateTime.getType:
|
elif t.getType == DateTime.getType:
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
discard `value`.parseIso8601
|
`value`.parseIso8601
|
||||||
|
elif t.getTypeInst == Option.getType:
|
||||||
|
let innerType = t.getTypeImpl[2][0][0][1]
|
||||||
|
let parseStmt = createParseStmt(innerType, value)
|
||||||
|
result.add quote do:
|
||||||
|
if `value`.len == 0:
|
||||||
|
none[`innerType`]()
|
||||||
|
else:
|
||||||
|
some(`parseStmt`)
|
||||||
|
else:
|
||||||
|
error "Unknown value object type: " & $t.getTypeInst
|
||||||
elif t.typeKind == ntyString:
|
elif t.typeKind == ntyString:
|
||||||
result.add quote do:
|
result.add quote do:
|
||||||
discard `value`
|
`value`
|
||||||
|
elif t.typeKind == ntyInt:
|
||||||
|
result.add quote do:
|
||||||
|
parseInt(`value`)
|
||||||
|
else:
|
||||||
|
error "Unknown value type: " & $t.getTypeInst
|
||||||
|
|
||||||
template walkFieldDefs(t: NimNode, body: untyped) =
|
template walkFieldDefs(t: NimNode, body: untyped) =
|
||||||
let tTypeImpl = t.getTypeImpl
|
let tTypeImpl = t.getTypeImpl
|
||||||
@ -104,23 +118,28 @@ macro populateMutateClauses(t: typed, newRecord: bool, mc: var MutateClauses): u
|
|||||||
|
|
||||||
#echo result.repr
|
#echo result.repr
|
||||||
|
|
||||||
# TODO
|
macro rowToModel(modelType: typed, row: seq[string]): untyped =
|
||||||
macro rowToModel(modelType: typed): untyped =
|
|
||||||
result = newStmtList()
|
|
||||||
|
|
||||||
#echo modelType.getType[1].getType.treeRepr
|
# Create the object constructor AST node
|
||||||
|
result = newNimNode(nnkObjConstr).add(modelType)
|
||||||
|
|
||||||
|
# Create new colon expressions for each of the property initializations
|
||||||
|
var idx = 0
|
||||||
|
echo modelType.getTypeImpl.treeRepr
|
||||||
|
# TODO: how to guarantee order?
|
||||||
modelType.walkFieldDefs:
|
modelType.walkFieldDefs:
|
||||||
result.add createParseStmts(fieldType, "")
|
let itemLookup = quote do: `row`[`idx`]
|
||||||
#[
|
result.add(newColonExpr(
|
||||||
result.add quote do:
|
fieldIdent,
|
||||||
User(
|
createParseStmt(fieldType, itemLookup)))
|
||||||
id: genUUID
|
idx += 1
|
||||||
#modelType.walkFieldDefs:
|
|
||||||
]#
|
|
||||||
|
|
||||||
macro modelName(model: typed): string =
|
macro modelName(model: object): string =
|
||||||
return $model.getTypeInst
|
return $model.getTypeInst
|
||||||
|
|
||||||
|
macro modelName(modelType: type): string =
|
||||||
|
return $modelType.getType[1]
|
||||||
|
|
||||||
proc identNameToDb(name: string): string =
|
proc identNameToDb(name: string): string =
|
||||||
return name.replace(UPPERCASE_PATTERN, "$1_$2").toLower()
|
return name.replace(UPPERCASE_PATTERN, "$1_$2").toLower()
|
||||||
|
|
||||||
@ -128,6 +147,9 @@ proc dbNameToIdent(name: string): string =
|
|||||||
let parts = name.split("_")
|
let parts = name.split("_")
|
||||||
return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("")
|
return @[parts[0]].concat(parts[1..^1].mapIt(capitalize(it))).join("")
|
||||||
|
|
||||||
|
proc tableName(modelType: type): string =
|
||||||
|
return modelName(modelType).identNameToDb & "s"
|
||||||
|
|
||||||
proc tableName[T](rec: T): string =
|
proc tableName[T](rec: T): string =
|
||||||
return modelName(rec).identNameToDb & "s"
|
return modelName(rec).identNameToDb & "s"
|
||||||
|
|
||||||
@ -158,8 +180,9 @@ proc updateRecord[T](db: DbConn, rec: T): bool =
|
|||||||
|
|
||||||
return numRowsUpdated > 0;
|
return numRowsUpdated > 0;
|
||||||
|
|
||||||
# TODO
|
template getRecord(db: DbConn, modelType: type, id: UUID): untyped =
|
||||||
#proc getRecord[T](db: DbConn, UUID id): T =
|
let row = db.getRow(sql("SELECT * FROM " & tableName(modelType) & " WHERE id = ?"), @[$id])
|
||||||
|
rowToModel(modelType, row)
|
||||||
|
|
||||||
macro listFields(t: typed): untyped =
|
macro listFields(t: typed): untyped =
|
||||||
var fields: seq[tuple[n: string, t: string]] = @[]
|
var fields: seq[tuple[n: string, t: string]] = @[]
|
||||||
@ -169,30 +192,34 @@ macro listFields(t: typed): untyped =
|
|||||||
|
|
||||||
result = newLit(fields)
|
result = newLit(fields)
|
||||||
|
|
||||||
|
|
||||||
# proc create: Typed create methods for specific records
|
# proc create: Typed create methods for specific records
|
||||||
proc createUser*(db: DbConn, user: User): User = db.createRecord(user)
|
proc createUser*(db: DbConn, user: User): User = return db.createRecord(user)
|
||||||
proc createApiToken*(db: DbConn, token: ApiToken): ApiToken = db.createRecord(token)
|
proc createApiToken*(db: DbConn, token: ApiToken): ApiToken = return db.createRecord(token)
|
||||||
proc createMeasure*(db: DbConn, measure: Measure): Measure = db.createRecord(measure)
|
proc createMeasure*(db: DbConn, measure: Measure): Measure = return db.createRecord(measure)
|
||||||
proc createValue*(db: DbConn, value: Value): Value = db.createRecord(value)
|
proc createValue*(db: DbConn, value: Value): Value = return db.createRecord(value)
|
||||||
|
|
||||||
|
proc getUser*(db: DbConn, id: UUID): User = return db.getRecord(User, id)
|
||||||
|
proc getApiToken*(db: DbConn, id: UUID): ApiToken = return db.getRecord(ApiToken, id)
|
||||||
|
|
||||||
when isMainModule:
|
when isMainModule:
|
||||||
|
|
||||||
rowToModel(User)
|
let db = open("", "", "", "host=192.168.99.100 port=5500 dbname=personal_measure user=postgres password=password")
|
||||||
|
|
||||||
|
for row in db.fastRows(sql"SELECT id FROM users"):
|
||||||
|
echo $db.getUser(parseUUID(row[0]))
|
||||||
|
|
||||||
|
for row in db.fastRows(sql"SELECT id FROM api_tokens"):
|
||||||
|
echo $db.getApiToken(parseUUID(row[0]))
|
||||||
|
|
||||||
|
#echo tableName(ApiToken)
|
||||||
|
echo $rowToModel(ApiToken, @["47400441-5c3a-4119-8acf-f616ae25c16c", "9e5460dd-b580-4071-af97-c1cbdedaae12", "Test Token", "5678", ""])
|
||||||
|
#[
|
||||||
|
for row in db.fastRows(sql"SELECT * FROM api_tokens"):
|
||||||
|
echo $rowToModel(ApiToken, row);
|
||||||
|
|
||||||
let u = User(
|
let u = User(
|
||||||
displayName: "Bob",
|
displayName: "Bob",
|
||||||
email: "bob@bobsco.com",
|
email: "bob@bobsco.com",
|
||||||
hashedPwd: "test")
|
hashedPwd: "test")
|
||||||
|
|
||||||
#[
|
|
||||||
let db = open("", "", "", "host=localhost port=5500 dbname=personal_measure user=postgres password=password")
|
|
||||||
for row in db.fastRows(sql"SELECT * FROM users"):
|
|
||||||
echo $row
|
|
||||||
echo "----"
|
|
||||||
rowToModel(User)
|
|
||||||
|
|
||||||
echo "New user:\n\t" & $db.createUser(u)
|
|
||||||
for row in db.fastRows(sql"SELECT * FROM users"):
|
|
||||||
echo $row
|
|
||||||
]#
|
]#
|
||||||
|
@ -1,23 +1,34 @@
|
|||||||
import options, times, uuids
|
import options, times, timeutils, uuids
|
||||||
|
|
||||||
type
|
type
|
||||||
User* = object
|
User* = object
|
||||||
id*: UUID
|
id*: UUID
|
||||||
displayName*, email*, hashedPwd*: string
|
displayName*: string
|
||||||
|
email*: string
|
||||||
|
hashedPwd*: string
|
||||||
|
|
||||||
ApiToken* = object
|
ApiToken* = object
|
||||||
id*, userId*: UUID
|
id*: UUID
|
||||||
name*, hashedToken*: string
|
userId*: UUID
|
||||||
|
name*:string
|
||||||
expires*: Option[DateTime]
|
expires*: Option[DateTime]
|
||||||
|
hashedToken*: string
|
||||||
|
|
||||||
Measure* = object
|
Measure* = object
|
||||||
id*, userId*: UUID
|
id*: UUID
|
||||||
slug*, name*, description*, domainUnits*, rangeUnits*: string
|
userId*: UUID
|
||||||
domainSource*, rangeSource*: Option[string]
|
slug*: string
|
||||||
|
name*: string
|
||||||
|
description*: string
|
||||||
|
domainSource*: Option[string]
|
||||||
|
domainUnits*: string
|
||||||
|
rangeSource*: Option[string]
|
||||||
|
rangeUnits*: string
|
||||||
analysis*: seq[string]
|
analysis*: seq[string]
|
||||||
|
|
||||||
Value* = object
|
Value* = object
|
||||||
id*, measureId*: UUID
|
id*: UUID
|
||||||
|
measureId*: UUID
|
||||||
value*: int
|
value*: int
|
||||||
timestamp*: DateTime
|
timestamp*: DateTime
|
||||||
extData*: string
|
extData*: string
|
||||||
@ -26,7 +37,9 @@ proc `$`*(u: User): string =
|
|||||||
return "User " & ($u.id)[0..6] & " - " & u.displayName & " <" & u.email & ">"
|
return "User " & ($u.id)[0..6] & " - " & u.displayName & " <" & u.email & ">"
|
||||||
|
|
||||||
proc `$`*(tok: ApiToken): string =
|
proc `$`*(tok: ApiToken): string =
|
||||||
return "ApiToken " & ($tok.id)[0..6] & " - " & tok.name
|
result = "ApiToken " & ($tok.id)[0..6] & " - " & tok.name
|
||||||
|
if tok.expires.isSome: result &= " (expires " & tok.expires.get.formatIso8601 & ")"
|
||||||
|
else: result &= " (no expiry)"
|
||||||
|
|
||||||
proc `$`*(m: Measure): string =
|
proc `$`*(m: Measure): string =
|
||||||
return "Measure " & ($m.id)[0..6] & " - " & m.slug
|
return "Measure " & ($m.id)[0..6] & " - " & m.slug
|
||||||
|
@ -13,7 +13,7 @@ create table "api_tokens" (
|
|||||||
user_id uuid not null references users (id) on delete cascade on update cascade,
|
user_id uuid not null references users (id) on delete cascade on update cascade,
|
||||||
name varchar not null,
|
name varchar not null,
|
||||||
expires timestamp with time zone default null,
|
expires timestamp with time zone default null,
|
||||||
hashedToken varchar not null
|
hashed_token varchar not null
|
||||||
);
|
);
|
||||||
|
|
||||||
create table "measures" (
|
create table "measures" (
|
||||||
|
Loading…
x
Reference in New Issue
Block a user