How do I extend/implement Mongo.Collection in Meteor with Typescript? - meteor

It appears that meteor/mongo.Mongo.Collection is an interface (according to these type declarations), and most of the sample code on the web shows how to extend that interface and then write code for methods in a separate block:
export interface List {
_id?: string;
name?: string;
incompleteCount?: number;
}
export interface CollectionLists extends Mongo.Collection<List> {
defaultName(): string;
}
export var Lists = <CollectionLists>(new Mongo.Collection<List>('lists'));
// Calculate a default name for a list in the form of 'List A'
Lists.defaultName = function(): string {
var nextLetter = 'A', nextName = 'List ' + nextLetter;
while (Lists.findOne({name: nextName})) {
// not going to be too smart here, can go past Z
nextLetter = String.fromCharCode(nextLetter.charCodeAt(0) + 1);
nextName = 'List ' + nextLetter;
}
return nextName;
};
Is there a way to rewrite this example with classes instead of this interface with separate code blocks for all the added methods?

Related

Presetting values for a type using Generics - Typescript

I know I'm way off track here but I'm trying to use generics to "preset" particular values of an "Event" type.
What I'm doing is as follows and what I'm expecting is "my-source" to be the console output.
I can see why this code doesn't work, but I'm putting it here to try and illustrate what I want to try and achieve.
type EventDetail = {
shopId: string;
};
export type Event<
TEventDetailType extends EventDetail,
TType extends string,
TSource extends string
> = {
source?: TSource;
body: TEventDetailType;
type?: TType;
};
export type UpdateShopCustomDomainEventDetail = EventDetail & {
domainName: string;
};
export type UpdateShopCustomDomainEvent = Event<
UpdateShopCustomDomainEventDetail,
"update-shop-custom-domain",
"my-source"
>;
const test:UpdateShopCustomDomainEvent = {body:{shopId:"123123", domainName:"asdf"}}
console.log(test.source);
// undefined
Essentially what I am wanting to do is something like the following. Where I send an event, but I define the type of event that I'm sending with a generic. This UpdateShopCustomDomainEvent will force the user to enter a value for shopId and domainName, and it will also set the type value to "update-shop-custom-domain" and the source to "my-source".
putEvent<UpdateShopCustomDomainEvent>({
body: {
shopId:"1234",
domainName:"mydomain.com"
}
});

Switching feature in Handlebars Templates

So, we have created many templates using handlebars. Out of the many, we have one handlebar where we would like to make some changes that should only go live after a certain date. To do so, we wanted to create sort of a toggle switch, something like:
{{if switch on}}
display new content
{{else}}
display old content
Below is the generic template parser where I am trying to create a switch that I can inject in the if part of my template. Any suggestions?
/**
* Creates HTML output of the specified context for the given templateId and templateVersion combination
* we implicitly assume certain json fields (template specific) to be present in the content
*/
#Timed("handlebarsService.parseTemplateToHtml")
fun parseTemplateToHtml(htmlTemplateLocation: String, model: Map<String, Any>, locale: Locale): String {
val modelWithLanguage = model.toMutableMap()
modelWithLanguage[languageTag] = locale.language
//modelWithLanguage[switch] = "off"
val context = Context.newBuilder(modelWithLanguage)
.resolver(MapValueResolver.INSTANCE)
.build()
val template = try {
handlebars.compile(htmlTemplateLocation)
} catch (e: IOException) {
throw PdfGenerationException(e, "Internal error while compiling template")
}
return try {
template.apply(context)
} catch (e: IOException) {
throw PdfGenerationException(e, "Internal error while applying template")
}
}
}
private const val languageTag = "languageTag"
//private const val switch ="off"

TypeScript - passing a class as an argument, and reflection

I am writing a generic unmarshaller. It converts graph DB data to generated TypeScript (1.8.7) model classes. The input is JSON. The output should be an instance of a model class.
My ultimate goal is to create something like Hibernate OGM, only for Tinkerpop Frames and TypeScript, with REST endpoint in the middle.
What's the right way to pass a class as a parameter and reach it's static members? I want to have something like this:
SomeModel some = <SomeModel> unmarshaller.fromJSON({/*Object from JSON*/}, SomeModel);
I've tried to write a method.
Not sure if I am heading in the right direction, feel free to suggest different approaches.
public fromJSON(input: Object, clazz: typeof FrameModel): FrameModel
{
// This only demonstrates access to Framemodel's metadata
// about original Java model classes.
clazz.graphPropertyMapping;
clazz.graphRelationMapping;
let result = {};
...
return result;
}
...
But when I tried to execute this on Plunker, I got execution errors with unuseful stacktrace.
The model superclass looks like this:
/**
* Things common to all Frames models on the Typescript side.
*/
export class FrameModel
{
// Model metadata
static discriminator: string;
static graphPropertyMapping: { [key:string]:string; };
static graphRelationMapping: { [key:string]:string; };
// Each instance needs a vertex ID
private vertexId: number;
public getVertexId(): number {
return this.vertexId;
}
}
Sample model class:
import {TestPlanetModel} from './TestPlanetModel';
import {TestShipModel} from './TestShipModel';
export class TestGeneratorModel extends FrameModel
{
static discriminator: string = 'TestGenerator';
static graphPropertyMapping: { [key:string]:string; } = {
bar: 'boo',
name: 'name',
rank: 'rank',
};
static graphRelationMapping: { [key:string]:string; } = {
colonizes: 'colonizedPlanet',
commands: 'ship',
};
boo: string;
name: string;
rank: string;
public colonizedPlanet: TestPlanetModel[]; // edge label 'colonizedPlanet'
public ship: TestShipModel; // edge label 'ship'
}
I haven't found much material on reflection and class handling in TypeScript.
I know how I would do this in Java.
I know how I would do this in JavaScript.
I understand that I might achieve similar results with decorators, but having fields or static fields seemed a bit simpler, for generated models.
You've maybe already noticed that class members cannot have const keyword. But you could go with static instead. Also member should be public if you want it to be accessible from outside world.
public static graphPropertyMapping: { [key:string]:string; } = {
bar: 'boo',
name: 'name',
rank: 'rank',
};
As for creating result instance:
let result = new clazz();
//copy properties
return result;
If I understand you correctly then here's something to help you get started:
interface Model {}
interface ModelData {}
interface MyModelConstructor<M extends Model, D extends ModelData> {
new(data: D): M;
// static members
graphPropertyMapping: any;
graphRelationMapping: any;
}
class Unmarshaller {
public fromJSON<T>(input: string | ModelData, ctor: MyModelConstructor<T, ModelData>): T {
let data: ModelData = (typeof input === "string") ? JSON.parse(input) : input;
let propertyMapping = ctor.graphPropertyMapping;
let relationMapping = ctor.graphRelationMapping;
// do whatever with the mappings
return new ctor(input);
}
}
(code in playground)
I don't know how your models look like, so I hope this is close enough.
I recently released an enhanced version of the TypeScript compiler that allows exactly what you are expecting: read all (static or not) fields metadata from a class. For example you can write:
interface MyInterface {
active:boolean;
description: string;
}
class MyClass {
id: number;
name: string;
myComplexField: MyInterface;
}
function printMembers(clazz: Class) {
let fields = clazz.members.filter(m => m.type.kind !== 'function'); //exclude methods.
for(let field of fields) {
let typeName = field.type.kind;
if(typeName === 'class' || typeName === 'interface') {
typeName = (<Class | Interface>field.type).name;
}
console.log(`Field ${field.name} of ${clazz.name} has type: ${typeName}`);
}
}
printMembers(MyClass.getClass());
this is the output:
$ node main.js
Field id of MyClass has type: number
Field name of MyClass has type: string
Field myComplexField of MyClass has type: MyInterface
Of course, if you change the members property access of clazz to statics you will retrieve all static members. These information can be accessed at coding time too, so you can use autocompletion.
You can do the same with Interfaces metadata. Simply write MyInterface for example, and access its members.
You can find the project here.

Accessing index in a TypeScript indexible interface

I’ve created a “Dictionary” in typescript using index types and are trying to use it as an array in a for in statement. I am able to get the value without problem, but can't figure out how I can access the index as well (see the ?? in the for loop).
export interface Settings {
dictionary : IDictionary[];
}
export interface IDictionary{
[index: number]: string;
}
// To be used like this
for (var setting in Settings.dictionary)
{
console.log(“Setting is: “ + setting + “ for id: “ + ??);
}
Here is a full working example of your code... essentially all your interfaces are spot on and you just need to adjust that bit in your loop as you indicated with the ???.
The key is setting
To get the value, use settings.dictionary[setting]
Here is the full code:
export interface Settings {
dictionary : IDictionary[];
}
export interface IDictionary{
[index: number]: string;
}
function logSettings(settings: Settings) {
for (var setting in settings.dictionary) {
console.log('Setting is: ' + settings.dictionary[setting] +
' for id: ' + setting);
}
}
var settings: Settings = {
dictionary: []
};
settings.dictionary[0] = 'A';
settings.dictionary[1] = 'B';
logSettings(settings);
Actually, setting is the ID. The value you need to index which you can do using [id]
Learn more about for/in : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in

Adding observer for KVO without pointers using Swift

In Objective-C, I would normally use something like this:
static NSString *kViewTransformChanged = #"view transform changed";
// or
static const void *kViewTransformChanged = &kViewTransformChanged;
[clearContentView addObserver:self
forKeyPath:#"transform"
options:NSKeyValueObservingOptionNew
context:&kViewTransformChanged];
I have two overloaded methods to choose from to add an observer for KVO with the only difference being the context argument:
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, context: CMutableVoidPointer)
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, kvoContext: KVOContext)
With Swift not using pointers, I'm not sure how to dereference a pointer to use the first method.
If I create my own KVOContext constant for use with the second method, I wind up with it asking for this:
let test:KVOContext = KVOContext.fromVoidContext(context: CMutableVoidPointer)
EDIT: What is the difference between CMutableVoidPointer and KVOContext? Can someone give me an example how how to use them both and when I would use one over the other?
EDIT #2: A dev at Apple just posted this to the forums: KVOContext is going away; using a global reference as your context is the way to go right now.
There is now a technique officially recommended in the documentation, which is to create a private mutable variable and use its address as the context.
(Updated for Swift 3 on 2017-01-09)
// Set up non-zero-sized storage. We don't intend to mutate this variable,
// but it needs to be `var` so we can pass its address in as UnsafeMutablePointer.
private static var myContext = 0
// NOTE: `static` is not necessary if you want it to be a global variable
observee.addObserver(self, forKeyPath: …, options: [], context: &MyClass.myContext)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if context == &myContext {
…
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
Now that KVOContext is gone in Xcode 6 beta 3, you can do the following. Define a global (i.e. not a class property) like so:
let myContext = UnsafePointer<()>()
Add an observer:
observee.addObserver(observer, forKeyPath: …, options: nil, context: myContext)
In the observer:
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafePointer<()>) {
if context == myContext {
…
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
Swift 4 - observing contentSize change on UITableViewController popover to fix incorrect size
I had been searching for an answer to change to a block based KVO because I was getting a swiftlint warning and it took me piecing quite a few different answers together to get to the right solution. Swiftlint warning:
Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo).
My use case was to present a popover controller attached to a button in a Nav bar in a view controller and then resize the popover once it's showing - otherwise it would be too big and not fitting the contents of the popover. The popover itself was a UITableViewController that contained static cells, and it was displayed via a Storyboard segue with style popover.
To setup the block based observer, you need the following code inside your popover UITableViewController:
// class level variable to store the statusObserver
private var statusObserver: NSKeyValueObservation?
// Create the observer inside viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusObserver = tableView.observe(\UITableView.contentSize,
changeHandler: { [ weak self ] (theTableView, _) in self?.popoverPresentationController?.presentedViewController.preferredContentSize = theTableView.contentSize
})
}
// Don't forget to remove the observer when the popover is dismissed.
override func viewDidDisappear(_ animated: Bool) {
if let observer = statusObserver {
observer.invalidate()
statusObserver = nil
}
super.viewDidDisappear(animated)
}
I didn't need the previous value when the observer was triggered, so left out the options: [.new, .old] when creating the observer.
Update for Swift 4
Context is not required for block-based observer function and existing #keyPath() syntax is replaced with smart keypath to achieve swift type safety.
class EventOvserverDemo {
var statusObserver:NSKeyValueObservation?
var objectToObserve:UIView?
func registerAddObserver() -> Void {
statusObserver = objectToObserve?.observe(\UIView.tag, options: [.new, .old], changeHandler: {[weak self] (player, change) in
if let tag = change.newValue {
// observed changed value and do the task here on change.
}
})
}
func unregisterObserver() -> Void {
if let sObserver = statusObserver {
sObserver.invalidate()
statusObserver = nil
}
}
}
Complete example using Swift:
//
// AppDelegate.swift
// Photos-MediaFramework-swift
//
// Created by Phurg on 11/11/16.
//
// Displays URLs for all photos in Photos Library
//
// #see http://stackoverflow.com/questions/30144547/programmatic-access-to-the-photos-library-on-mac-os-x-photokit-photos-framewo
//
import Cocoa
import MediaLibrary
// For KVO: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12
private var mediaLibraryLoaded = 1
private var rootMediaGroupLoaded = 2
private var mediaObjectsLoaded = 3
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var mediaLibrary : MLMediaLibrary!
var allPhotosAlbum : MLMediaGroup!
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSLog("applicationDidFinishLaunching:");
let options:[String:Any] = [
MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue, // Can't be Swift enum
MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier], // Array
]
self.mediaLibrary = MLMediaLibrary(options:options)
NSLog("applicationDidFinishLaunching: mediaLibrary=%#", self.mediaLibrary);
self.mediaLibrary.addObserver(self, forKeyPath:"mediaSources", options:[], context:&mediaLibraryLoaded)
NSLog("applicationDidFinishLaunching: added mediaSources observer");
// Force load
self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier]
NSLog("applicationDidFinishLaunching: done");
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
NSLog("observeValue: keyPath=%#", keyPath!)
let mediaSource:MLMediaSource = self.mediaLibrary.mediaSources![MLMediaSourcePhotosIdentifier]!
if (context == &mediaLibraryLoaded) {
NSLog("observeValue: mediaLibraryLoaded")
mediaSource.addObserver(self, forKeyPath:"rootMediaGroup", options:[], context:&rootMediaGroupLoaded)
// Force load
mediaSource.rootMediaGroup
} else if (context == &rootMediaGroupLoaded) {
NSLog("observeValue: rootMediaGroupLoaded")
let albums:MLMediaGroup = mediaSource.mediaGroup(forIdentifier:"TopLevelAlbums")!
for album in albums.childGroups! {
let albumIdentifier:String = album.attributes["identifier"] as! String
if (albumIdentifier == "allPhotosAlbum") {
self.allPhotosAlbum = album
album.addObserver(self, forKeyPath:"mediaObjects", options:[], context:&mediaObjectsLoaded)
// Force load
album.mediaObjects
}
}
} else if (context == &mediaObjectsLoaded) {
NSLog("observeValue: mediaObjectsLoaded")
let mediaObjects:[MLMediaObject] = self.allPhotosAlbum.mediaObjects!
for mediaObject in mediaObjects {
let url:URL? = mediaObject.url
// URL does not extend NSObject, so can't be passed to NSLog; use string interpolation
NSLog("%#", "\(url)")
}
}
}
}

Resources