Ver código fonte

wip filebrowser model refactor and test

Steven Silvester 8 anos atrás
pai
commit
a2356d039d

+ 1 - 1
src/filebrowser/crumbs.ts

@@ -240,7 +240,7 @@ class BreadCrumbs extends Widget {
           };
           return showDialog(options).then(button => {
             if (button.text === 'OVERWRITE') {
-              return this._model.delete(newPath).then(() => {
+              return this._model.deleteFile(newPath).then(() => {
                 return this._model.rename(name, newPath).then(() => {
                   return this._model.refresh();
                 });

+ 10 - 10
src/filebrowser/listing.ts

@@ -418,12 +418,12 @@ class DirListing extends Widget {
     let promises: Promise<void>[] = [];
     let items = this.sortedItems;
     let paths = items.map(item => item.path);
-    for (let sessionId of this._model.sessionIds) {
-      let index = paths.indexOf(sessionId.notebook.path);
+    for (let session of this._model.sessions) {
+      let index = paths.indexOf(session.notebook.path);
       if (!this._softSelection && this._selection[items[index].name]) {
-        promises.push(this._model.shutdown(sessionId));
+        promises.push(this._model.shutdown(session.id));
       } else if (this._softSelection === items[index].name) {
-        promises.push(this._model.shutdown(sessionId));
+        promises.push(this._model.shutdown(session.id));
       }
     }
     return Promise.all(promises).then(
@@ -656,11 +656,11 @@ class DirListing extends Widget {
     // Handle notebook session statuses.
     let paths = items.map(item => item.path);
     let specs = this._model.kernelspecs;
-    for (let sessionId of this._model.sessionIds) {
-      let index = paths.indexOf(sessionId.notebook.path);
+    for (let session of this._model.sessions) {
+      let index = paths.indexOf(session.notebook.path);
       let node = this._items[index];
       node.classList.add(RUNNING_CLASS);
-      node.title = specs.kernelspecs[sessionId.kernel.name].spec.display_name;
+      node.title = specs.kernelspecs[session.kernel.name].spec.display_name;
     }
 
     this._prevPath = this._model.path;
@@ -948,7 +948,7 @@ class DirListing extends Widget {
           };
           return showDialog(options).then(button => {
             if (button.text === 'OVERWRITE') {
-              return this._model.delete(newPath).then(() => {
+              return this._model.deleteFile(newPath).then(() => {
                 return this._model.rename(name, newPath);
               });
             }
@@ -1128,7 +1128,7 @@ class DirListing extends Widget {
   private _delete(names: string[]): Promise<void> {
     let promises: Promise<void>[] = [];
     for (let name of names) {
-      promises.push(this._model.delete(name));
+      promises.push(this._model.deleteFile(name));
     }
     return Promise.all(promises).then(
       () => this._model.refresh(),
@@ -1166,7 +1166,7 @@ class DirListing extends Widget {
           };
           return showDialog(options).then(button => {
             if (button.text === 'OVERWRITE') {
-              return this._model.delete(newName).then(() => {
+              return this._model.deleteFile(newName).then(() => {
                 return this._model.rename(original, newName).then(() => {
                   this._model.refresh();
                 });

+ 31 - 49
src/filebrowser/model.ts

@@ -2,7 +2,7 @@
 // Distributed under the terms of the Modified BSD License.
 
 import {
-  IContents, IKernel, ISession
+  IContents, IKernel, IServiceManager, ISession
 } from 'jupyter-js-services';
 
 import {
@@ -19,7 +19,7 @@ import {
 
 
 /**
- * An implementation of a file browser view model.
+ * An implementation of a file browser model.
  *
  * #### Notes
  * All paths parameters without a leading `'/'` are interpreted as relative to
@@ -28,12 +28,10 @@ import {
 export
 class FileBrowserModel implements IDisposable {
   /**
-   * Construct a new file browser view model.
+   * Construct a new file browser model.
    */
   constructor(options: FileBrowserModel.IOptions) {
-    this._contentsManager = options.contentsManager;
-    this._sessionManager = options.sessionManager;
-    this._specs = options.kernelspecs;
+    this._manager = options.manager;
     this._model = { path: '', name: '/', type: 'directory', content: [] };
     this.cd();
   }
@@ -84,12 +82,12 @@ class FileBrowserModel implements IDisposable {
   }
 
   /**
-   * Get the session ids for active notebooks.
+   * Get the session models for active notebooks.
    *
    * #### Notes
    * This is a read-only property.
    */
-  get sessionIds(): ISession.IModel[] {
+  get sessions(): ISession.IModel[] {
     return this._sessions.slice();
   }
 
@@ -97,7 +95,7 @@ class FileBrowserModel implements IDisposable {
    * Get the kernel specs.
    */
   get kernelspecs(): IKernel.ISpecModels {
-    return this._specs;
+    return this._manager.kernelspecs;
   }
 
   /**
@@ -105,7 +103,7 @@ class FileBrowserModel implements IDisposable {
    */
   dispose(): void {
     this._model = null;
-    this._contentsManager = null;
+    this._manager = null;
     clearSignalData(this);
   }
 
@@ -120,9 +118,13 @@ class FileBrowserModel implements IDisposable {
     if (newValue !== '') {
       newValue = Private.normalizePath(this._model.path, newValue);
     }
+    // Collapse requests to the same directory.
+    if (newValue === this._pendingPath) {
+      return this._pending;
+    }
     let oldValue = this.path;
     let options = { content: true };
-    return this._contentsManager.get(newValue, options).then(contents => {
+    this._pending = this._manager.contents.get(newValue, options).then(contents => {
       this._model = contents;
       return this._findSessions();
     }).then(() => {
@@ -135,6 +137,8 @@ class FileBrowserModel implements IDisposable {
       }
       this.refreshed.emit(void 0);
     });
+    this._pendingPath = newValue;
+    return this._pending;
   }
 
   /**
@@ -163,7 +167,7 @@ class FileBrowserModel implements IDisposable {
     let normalizePath = Private.normalizePath;
     fromFile = normalizePath(this._model.path, fromFile);
     toDir = normalizePath(this._model.path, toDir);
-    return this._contentsManager.copy(fromFile, toDir).then(contents => {
+    return this._manager.contents.copy(fromFile, toDir).then(contents => {
       this.fileChanged.emit({
         name: 'file',
         oldValue: void 0,
@@ -180,10 +184,10 @@ class FileBrowserModel implements IDisposable {
    *
    * @returns A promise which resolves when the file is deleted.
    */
-  delete(path: string): Promise<void> {
+  deleteFile(path: string): Promise<void> {
     let normalizePath = Private.normalizePath;
     path = normalizePath(this._model.path, path);
-    return this._contentsManager.delete(path).then(() => {
+    return this._manager.contents.delete(path).then(() => {
       this.fileChanged.emit({
         name: 'file',
         oldValue: path,
@@ -202,7 +206,7 @@ class FileBrowserModel implements IDisposable {
   download(path: string): Promise<IContents.IModel> {
     let normalizePath = Private.normalizePath;
     path = normalizePath(this._model.path, path);
-    return this._contentsManager.get(path, {}).then(contents => {
+    return this._manager.contents.get(path, {}).then(contents => {
       let element = document.createElement('a');
       element.setAttribute('href', 'data:text/text;charset=utf-8,' +      encodeURI(contents.content));
       element.setAttribute('download', contents.name);
@@ -226,7 +230,7 @@ class FileBrowserModel implements IDisposable {
       options.ext = options.ext || '.txt';
     }
     options.path = options.path || this._model.path;
-    return this._contentsManager.newUntitled(options).then(contents => {
+    return this._manager.contents.newUntitled(options).then(contents => {
       this.fileChanged.emit({
         name: 'file',
         oldValue: void 0,
@@ -250,7 +254,7 @@ class FileBrowserModel implements IDisposable {
     let normalizePath = Private.normalizePath;
     path = normalizePath(this._model.path, path);
     newPath = normalizePath(this._model.path, newPath);
-    return this._contentsManager.rename(path, newPath).then(contents => {
+    return this._manager.contents.rename(path, newPath).then(contents => {
       this.fileChanged.emit({
         name: 'file',
         oldValue: path,
@@ -286,7 +290,7 @@ class FileBrowserModel implements IDisposable {
       return this._upload(file);
     }
 
-    return this._contentsManager.get(file.name, {}).then(() => {
+    return this._manager.contents.get(file.name, {}).then(() => {
       return Private.typedThrow<IContents.IModel>(`"${file.name}" already exists`);
     }, () => {
       return this._upload(file);
@@ -296,20 +300,8 @@ class FileBrowserModel implements IDisposable {
   /**
    * Shut down a session by session id.
    */
-  shutdown(sessionId: ISession.IModel): Promise<void> {
-    return this._sessionManager.connectTo(sessionId.id).then(session => {
-      return session.shutdown();
-    });
-  }
-
-  /**
-   * Start a new session for a path.
-   */
-  startSession(path: string, kernel: string): Promise<ISession> {
-    return this._sessionManager.startNew({
-      path,
-      kernelName: kernel
-    });
+  shutdown(id: string): Promise<void> {
+    return this._manager.sessions.shutdown(id);
   }
 
   /**
@@ -340,7 +332,7 @@ class FileBrowserModel implements IDisposable {
           name,
           content: Private.getContent(reader)
         };
-        this._contentsManager.save(path, model).then(contents => {
+        this._manager.contents.save(path, model).then(contents => {
           this.fileChanged.emit({
             name: 'file',
             oldValue: void 0,
@@ -363,7 +355,7 @@ class FileBrowserModel implements IDisposable {
   private _findSessions(): Promise<void> {
     this._sessions = [];
 
-    return this._sessionManager.listRunning().then(models => {
+    return this._manager.sessions.listRunning().then(models => {
       if (!models.length) {
         return;
       }
@@ -380,11 +372,11 @@ class FileBrowserModel implements IDisposable {
   }
 
   private _maxUploadSizeMb = 15;
-  private _contentsManager: IContents.IManager = null;
+  private _manager: IServiceManager = null;
   private _sessions: ISession.IModel[] = [];
-  private _sessionManager: ISession.IManager = null;
   private _model: IContents.IModel;
-  private _specs: IKernel.ISpecModels = null;
+  private _pendingPath = '';
+  private _pending: Promise<void> = null;
 }
 
 
@@ -399,19 +391,9 @@ namespace FileBrowserModel {
   export
   interface IOptions {
     /**
-     * A contents manager instance.
-     */
-    contentsManager: IContents.IManager;
-
-    /**
-     * A session manager instance.
-     */
-    sessionManager: ISession.IManager;
-
-    /**
-     * The kernelspec models.
+     * A service manager instance.
      */
-    kernelspecs: IKernel.ISpecModels;
+    manager: IServiceManager;
   }
 }
 

+ 2 - 5
src/filebrowser/plugin.ts

@@ -97,11 +97,8 @@ function activateFileBrowser(app: Application, provider: ServiceManager, registr
     kernelspecs: provider.kernelspecs,
     opener
   });
-  let fbModel = new FileBrowserModel({
-    contentsManager: contents,
-    sessionManager: sessions,
-    kernelspecs: provider.kernelspecs
-  });
+  // TODO: fix after rebasing PR.
+  let fbModel = new FileBrowserModel({ manager: provider as any });
   let fbWidget = new FileBrowserWidget({
     model: fbModel,
     manager: docManager,

+ 98 - 0
test/src/filebrowser/model.spec.ts

@@ -0,0 +1,98 @@
+// Copyright (c) Jupyter Development Team.
+// Distributed under the terms of the Modified BSD License.
+
+import expect = require('expect.js');
+
+import {
+  MockServiceManager
+} from 'jupyter-js-services/lib/mockmanager';
+
+import {
+  FileBrowserModel
+} from '../../../lib/filebrowser';
+
+
+describe('filebrowser/model', () => {
+
+  describe('FileBrowserModel', () => {
+
+    describe('#constructor()', () => {
+
+      it('should construct a new file browser model', () => {
+        let model = new FileBrowserModel({ manager });
+        expect(model).to.be.a(FileBrowserModel);
+      });
+
+    });
+
+    describe('#pathChanged', () => {
+
+    });
+
+    describe('#refreshed', () => {
+
+    });
+
+    describe('#fileChanged', () => {
+
+    });
+
+    describe('#path', () => {
+
+    });
+
+    describe('#items', () => {
+
+    });
+
+    describe('#isDisposed', () => {
+
+    });
+
+    describe('#sessions', () => {
+
+    });
+
+    describe('#kernelspecs', () => {
+
+    });
+
+    describe('#dispose()', () => {
+
+    });
+
+    describe('#cd()', () => {
+
+    });
+
+    describe('#refresh()', () => {
+
+    });
+
+    describe('#deleteFile()', () => {
+
+    });
+
+    describe('#download()', () => {
+
+    });
+
+    describe('#newUntitled()', () => {
+
+    });
+
+    describe('#rename()', () => {
+
+    });
+
+    describe('#upload()', () => {
+
+    });
+
+    describe('#shutdown()', () => {
+
+    });
+
+  });
+
+});

+ 2 - 0
test/src/index.ts

@@ -5,6 +5,8 @@ import './dialog/dialog.spec';
 
 import './docregistry/default.spec';
 
+import './filebrowser/model.spec';
+
 import './renderers/renderers.spec';
 import './renderers/latex.spec';