Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Caching and TTL extension #17

Open
wisepotato opened this issue Mar 1, 2019 · 2 comments
Open

Caching and TTL extension #17

wisepotato opened this issue Mar 1, 2019 · 2 comments

Comments

@wisepotato
Copy link
Contributor

wisepotato commented Mar 1, 2019

A few feature requests

  • An internal cache
  • A TTL per resource (even per request?)
  • updating, should check if resource has been updated

This can all be condensed into using a repository as an intermediary between the model and any application logic. By using predefined Repository<TModel> we can seperate the model (DBAL) and the Caching functinoality from each other (seperation of concerns).

@Cache({TimeToLive: 500})
class TasksRepository extends Repository<Task> { }

With an implementation in a component being

class class ExampleComponent implements OnInit  {
  public tasks: Task[];
  constructor(private taskRepository: TaskRepository){
    this.tasks = this.taskRepository.all();
  }
  public async getSpecificTaskOnHover(id: number) : Task{
    return this.taskRepository.find(t => t.id == id);
  }
  public async refreshTasksOnClick(){
    this.tasks = this.taskRepository.forceRefresh();
  }
  public async updateSpecifiTaskTitle(id: number, newTitle: string){
    let task = this.taskRepository.findById(id); // alternative to find(expression)
    task.title = newTitle;
    this.taskRepository.update(task);
  }
  public async forceRefresh(){
    this.tasks = this.taskRepository.forceRefresh().all();
    /// or: this.tasks = this.taskRepository.all({forceRefresh: true});
  }
}

This should make apps more snappy using this package. Maybe related to #13

This usage of the repository pattern would allow for a few things

  • Seperation of concerns with regard to saving, updating etc
  • the ability to allow for caching and a TTL per model even per part of the application (repository instance specific config)
  • Unit Of Work
  • Allow syncing with the database every x seconds with a full refresh force available per repository

and a general sense of wellbeing :)

@wisepotato
Copy link
Contributor Author

@maurei thoughts?

@wisepotato
Copy link
Contributor Author

import { Resource, ResourceType, Observables } from '@ngx-api-orm/core';
import { Observable, BehaviorSubject } from 'rxjs';
import { Injectable } from '@angular/core';


/**
 * Basic implementation of a repository
 */
export abstract class Repository<T extends Resource<Observables>> {

    // The time to live for the cache
    private ttl: number;
    public cachedObjects: T[] = [];

    /**
     * Internal observable to set when updating the outward cache
     */
    private cacheSource = new BehaviorSubject(null);
    /**
     * subscribe to this if you want to have the latest cached objects from the database
     */
    public cache: Observable<T[]> = this.cacheSource.asObservable();

    private firstFetch = true;
    /**
     * The updated objects, to still be synced with the database
     */
    private updatedObjects: T[] = [];
    private addedObjects: T[] = [];
    private deletedObjects: T[] = [];
    constructor(private modelType: ResourceType<T>) { }


    /**
     * Set the time to live for the cache
     * @param ttl The time in second 
     */
    public setTtl(ttl: number) {
        this.ttl = ttl;
    }
    public add(item: T) {
        this.addedObjects.push(item);
    }
    /**
     * Get all the items in the cache
     */
    public all(): T[] {
        if (this.firstFetch) {
            this.modelType.fetch().subscribe(e => {
                this.cachedObjects = e;
                this.emit(this.cachedObjects);
            });
        }
        return this.cachedObjects;
    }
    /**
     * 
     * @param item item to update
     */
    public update(item: T): void {
        // see if we actually have this in cache
        const index = this.cachedObjects.indexOf(this.cachedObjects.find(t => t.id === item.id));
        if (index !== -1) {
            this.updatedObjects.push(item);
            this.cachedObjects[index] = item;
        }
    }
    /**
     * Syncs any changes with the database
     */
    public sync(): void {
        for (const item of this.addedObjects) {
            item.save().subscribe(val => {
                console.log(val);
                console.log(item.id);
                this.addedObjects = this.addedObjects.filter(t => t.id !== item.id);
                this.cachedObjects.push(item);
                this.emit(this.cachedObjects);
            });
        }
        for (const item of this.updatedObjects) {
            item.update().subscribe(() => {
                this.updatedObjects.filter(t => t.id !== item.id);
                const index = this.cachedObjects.indexOf(this.cachedObjects.find(t => t.id === item.id));
                this.cachedObjects[index] = item;
                this.addedObjects.filter(t => t.id !== item.id);
                this.emit(this.cachedObjects);
            });
        }
        for (const item of this.deletedObjects) {
            this.deletedObjects = this.deletedObjects.filter(t => t.id !== item.id);
            this.cachedObjects = this.cachedObjects.filter(t => t.id !== item.id);
            item.delete().subscribe(() => {
                this.emit(this.cachedObjects);
            });
        }

    }

    /**
     * 
     * @param callbackfn function to execute
     */
    public filter(callbackfn: any): T[] {
        return this.cachedObjects.filter(callbackfn);
    }
    public remove(source: T) {
        const index = this.cachedObjects.indexOf(source);
        this.deletedObjects.push(source);
        this.cachedObjects.splice(index, 1);
    }
    public removeRange(source: T[]) {
        for (let obj of source) {
            const index = this.cachedObjects.indexOf(obj);
            this.cachedObjects.splice(index, 1);
        }
        this.deletedObjects.push(...source);
    }
    public emit(source: T[]) {
        this.cacheSource.next(source);
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant