I want to declare 2 routes. The first one, "/api" will provide some REST stuff, and the other one, "/static" should serve static content.
I tried to start from the quick start samples, but I don't know how to do this.
import tink.http.containers.*;
import tink.http.Response;
import tink.web.routing.*;
class Server {
static function main() {
var container = new NodeContainer(8080);
var router = new Router<Root>(new Root());
container.run(function(req) {
return router.route(Context.ofRequest(req))
.recover(OutgoingResponse.reportError);
});
}
}
class Root {
public function new() {}
#:get('/')
#:get('/$name')
public function hello(name = 'World')
return 'Hello, $name!';
}
For /static you can use Static from tink_http_middleware.
For /api you can use a #:sub route.
import tink.http.containers.*;
import tink.http.Handler;
import tink.http.Response;
import tink.http.middleware.Static;
import tink.web.routing.*;
class Server {
static function main() {
var container = new NodeContainer(8080);
var router = new Router<Api>(new Api());
var handler:Handler = req -> router.route(Context.ofRequest(req)).recover(OutgoingResponse.reportError);
container.run(handler.applyMiddleware(new Static('public_html', '/static')));
}
}
class Api {
#:sub public var api:Root = new Root();
}
class Root {
public function new() {}
#:get('/')
#:get('/$name')
public function hello(name = 'World')
return 'Hello, $name!';
}
First of all, i had to use git version of tink_http_middleware and asys
-lib tink_web
-lib hxnodejs
-lib tink_http_middleware:git:https://github.com/haxetink/tink_http_middleware.git
-lib asys:git:https://github.com/benmerckx/asys.git
-main server.Api
-js www/api/api.js
Next, #KevinResoL's answer was very helpful, i changed only one thing.
staticMiddleware.apply(handler)
package server;
import tink.http.Handler;
import tink.http.middleware.Static;
import tink.http.containers.*;
import tink.http.Response;
import tink.web.routing.*;
class Api {
public static function main() {
var container = new NodeContainer(8080);
var router = new Router<ApiRoute>(new ApiRoute());
var staticMiddleware = new Static("..","/");
var handler:Handler = req -> router.route(Context.ofRequest(req)).recover(OutgoingResponse.reportError);
container.run(staticMiddleware.apply(handler));
}
}
class ApiRoute {
public function new() { }
#:sub public var api:Root = new Root();
}
class Root {
public function new() { }
#:get('/')
#:get('/$name')
public function serve(name = 'index.html')
return 'Hello, $name!';
}
Related
I'm looking for a way to get the filename of a derived class from a base class in typescript running on node.js. An example of this would be:
Foo.ts
export abstract class Foo {
constructor() { }
name() { return (__filename); }
print() { console.log(this.name()); }
}
Bar.ts
import { Foo } from './Foo';
export class Bar extends Foo {
constructor() { super(); }
}
main.ts
import { Bar } from './Bar';
let bar = new Bar();
bar.print(); // should yield the location of Bar.ts
Due to the number of files involved and just cleanliness I'd like this to be confined to the Foo class rather than having an override of the name() function in each derived class.
I was able to sort-of solve this with the code:
private getDerivedFilePath(): string {
let errorStack: string[] = new Error().stack.split('\n');
let ret: string = __filename;
let baseClass: any = ThreadPoolThreadBase;
for (let i: number = 3; i < errorStack.length; i++) {
let filename: string = errorStack[i].slice(
errorStack[i].lastIndexOf('(') + 1,
Math.max(errorStack[i].lastIndexOf('.js'), errorStack[i].lastIndexOf('.ts')) + 3
);
let other: any = require(filename);
if (other.__proto__ === baseClass) {
ret = filename;
baseClass = other;
} else {
break;
}
}
return (ret || '');
}
Added to Foo, which will work when called from the constructor to set a private _filename property, for inheritance chains beyond the example above so long as the files are structured with a default export of the class being used. There may also be a caveat that if a base class from which a derived object is inheriting directly is initialized as a separate instance within the constructor of any member of the inheritance chain it could get confused and jump to another independent derived class - so it's a bit of a hacky work-around and I'd be interested if someone comes up with something better, but wanted to post this in case someone stumbles across this question and it works for them.
You can use require.cache to get all cached NodeModule objects and filter it to find your module.
https://nodejs.org/api/modules.html#requirecache
class ClassA {
public static getFilePath():string{
const nodeModule = this.getNodeModule();
return (nodeModule) ? nodeModule.filename : "";
}
public static getNodeModule(): NodeModule | undefined{
const nodeModule = Object.values(require.cache)
.filter((chl) => chl?.children.includes(module))
.filter((mn)=> mn?.filename.includes(this.name))
.shift();
return nodeModule;
}
}
class ClassB extends ClassA {
constructor(){}
}
const pathA = ClassA.getFilePath(); //Must return the absolute path of ClassA
const pathB = ClassB.getFilePath(); //Must return the absolute path of ClassB
What if I have classes that are different only by some constant used in code. Is it possible to have one generic implementation without runtime cost?
Here is the example (it's a little bit too long...)
#:enum abstract Param(Int) {
var foo = 0;
var bar = 1;
}
class WorkBase {
public function new() {}
private inline function work_impl(p: Param): Void {
if(p == foo) {
trace('foo');
}
else {
trace('bar');
}
}
public function work(): Void {
}
}
class WorkFoo extends WorkBase{
override public function work(): Void {
work_impl(foo);
}
}
class WorkBar extends WorkBase {
override public function work(): Void {
work_impl(bar);
}
}
class Test {
public static function main() {
var workFoo = new WorkFoo();
var workBar = new WorkBar();
workFoo.work();
workBar.work();
}
}
After compilation with -D analyzer-optimize we will see that WorkFoo.work() and WorkBar.work() functions were optimized and contain only one branch of code that matches one of the Param values. In real life there are lot of such comparisons in work_impl(), and they all are optimized out. That's good.
But what if I do not want to create WorkFoo and WorkBar by hand. Is it possible to do something like this:
#:generic
class WorkBase<PARAM> {
private inline function work_impl(p: Param): Void {
...
}
public function work(): Void {
work_impl(PARAM);
}
}
The closest thing I know is const-type-parameter. But I do not feel generic build is a good choice here.
The closest thing I know is const-type-parameter. But I do not feel generic build is a good choice here.
Const type parameters can be used without #:genericBuild - a const type parameter in combination with #:generic is enough to get the desired optimization:
#:enum abstract Param(Int) from Int {
var foo = 0;
var bar = 1;
}
#:generic class Work<#:const PARAM:Int> {
public function new() {}
public function work():Void {
if (PARAM == foo) {
trace('foo');
} else {
trace('bar');
}
}
}
class Main {
public static function main() {
var workFoo = new Work<0>();
var workBar = new Work<1>();
workFoo.work();
workBar.work();
}
}
Due to #:generic, one class is generated for each constant value, for instance on JS the output looks like this:
var Work_$0 = function() {
};
Work_$0.prototype = {
work: function() {
console.log("source/Main.hx:11:","foo");
}
};
var Work_$1 = function() {
};
Work_$1.prototype = {
work: function() {
console.log("source/Main.hx:13:","bar");
}
};
Note that this example fails with a "constraint check failure" in Haxe 3.4.7 for some reason, but works fine with Haxe 4 preview 4 and later. Another limitation is that neither new Work<Param.foo>() nor new Work<foo>() work - you need to pass the actual constant value.
Is it possible to have constraint on static fields in Haxe? For example we may have classes which have static field instance of type of corresponding class. And we may want a function that will return an instance of class passed as parameter. This is my attempt:
class Foo {
static public var instance = new Foo();
function new() {}
}
class Test {
// get instance from every class that have static field instance
static function getInstance<T, ClassT:({instance:T}, Class<T>)>(t:ClassT):T {
return t.instance;
}
static function main() {
var a = getInstance(Foo);
$type(a); //Test.hx:14: characters 14-15 : Warning : Unknown<0>
}
}
but it fails, because type parameter constraints are checked lazily. Any ideas on how do this?
Have you considered using a typedef?
Heres a quick edit of your code showing the basic idea
typedef HasInstance = {
var instance:Dynamic;
}
class Foo {
static public var instance = new Foo();
function new() {}
}
class Bar {
static public var instance = new Bar();
function new() {}
}
class Test {
// get instance from every class that have static field instance
static function getInstance<T:HasInstance>(t:T):T {
trace(t);
return t.instance;
}
static function main() {
var a = getInstance(Foo);
trace(a);
$type(a);
var b = getInstance(Bar);
trace(b);
$type(b);
}
}
example on try haxe!
You would change the instance type within the typedef to be more appropriate for your needs, and you can also constrain typedefs too, which can be very useful
If you don't mind using macro, here is a possible solution:
http://try-haxe.mrcdk.com/#7d650
Foo.hx
class Foo {
static public var instance = new Foo();
public var foo:Int;
function new() {}
}
class Test {
macro static function getInstance(e) return Macro.getInstance(e);
static function _getInstance<T, ClassT:({instance:T}, Class<T>)>(t:ClassT):T
return t.instance;
static function main() {
var a = getInstance(Foo);
$type(a);
}
}
Macro.hx
import haxe.macro.Expr;
import haxe.macro.Context.*;
using tink.MacroApi;
class Macro {
public static function getInstance(e:Expr) {
var ct = TPath(e.toString().asTypePath());
return macro (Test._getInstance($e):$ct);
}
}
Problem: I cannot seem to instantiate a new instance of a type script class which I am importing from another file.
Explanation: I am writting a node application in type script which uses the express module. These classes are in external files as to encapsulate the routing of express.
The file 'masterRoute.ts' is to forward any incoming http requests to their intended route.ts file.
The file 'indexRoute.ts' is the intended route file for the url: 'whatever.com/'.
Aim: For learning purposes, I am trying to make an instance of the external IndexRoute class in the MasterRoute class.
Example:
indexRoute.ts
import express = require('express');
export class IndexRoute{
private _ejsFilePath: string = 'views/index.ejs';
private request: express.Request;
private response: express.Response;
constructor(request: express.Request, response: express.Response){
this.request = request;
this.response = response;
}
public renderFile(): string{
try{
this.response.render(this._ejsFilePath);
return null;
}
catch(error){
return error;
}
}
}
masterRoute.ts
import express = require('express');
import * as IndexRoute from './indexRoute';
export class MasterRoute{
private Router: express.Router;
constructor(){
this.Router = new express.Router();
}
public onHttpRequest(request: express.Request, response: express.Response){
// I have tried to use all of the lines below one by one.
// None of them work.
// let test: IndexRoute = new IndexRoute(request, response);
// let test: = new IndexRoute(request, response);
// let test = IndexRoute(request, response);
this.Router.get('/');
}
}
I'm using typeorm and I want to create a generic repository:
import "reflect-metadata";
import { DBManager } from './db-manager';
import { Photo } from './entities/Photo';
import { createConnection, Connection } from "typeorm";
class GenericRepository<T> {
private connection: DBManager;
constructor(connection: DBManager) {
this.connection = connection;
}
public list(): T[] {
let result: T[] = [];
this.connection.connect().then(async connection => {
result = await <Promise<T[]>>(connection.entityManager.find(T));
});
return result;
}
}
let genericReposity = new GenericRepository<Photo>(new DBManager());
genericReposity.list();
This code of course doesn't woork and complains on find method that can not find name T
T should be my entity but I don't know how to achieve this
Generics in TypeScript are only in compile-type and all information about your generics types are removed in output javascript code. Thats why its not possible to do what you want. Its only possible if you explicitly specify a type to your generic repository, for example via constructor:
import "reflect-metadata";
import { DBManager } from './db-manager';
import { Photo } from './entities/Photo';
import { createConnection, Connection } from "typeorm";
export type ObjectType<T> = { new (): T } | Function;
class GenericRepository<T> {
private connection: DBManager;
private type: ObjectType<T>;
constructor(type: ObjectType<T>, connection: DBManager) {
this.type = type;
this.connection = connection;
}
public list(): T[] {
let result: T[] = [];
this.connection.connect().then(async connection => {
result = await <Promise<T[]>>(connection.entityManager.find(this.type));
});
return result;
}
}
let genericReposity = new GenericRepository(Photo, new DBManager());
genericReposity.list();
Also side note, you probably don't want to connect to the database each time you request list of entities. So I recommend to redesign your class.