From 777ba16533042c27d27473db5003d8ed3ce64be0 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Fri, 1 Jan 2016 17:40:44 -0800 Subject: [PATCH 01/11] Initial commit of zeppelin-display-utils --- pom.xml | 1 + zeppelin-display-utils/pom.xml | 179 ++++++++++++++++++ .../zeppelin/display/AngularDisplayElem.scala | 83 ++++++++ .../display/AngularDisplayElemTest.scala | 63 ++++++ 4 files changed, 326 insertions(+) create mode 100644 zeppelin-display-utils/pom.xml create mode 100644 zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala create mode 100644 zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala diff --git a/pom.xml b/pom.xml index 88d38aa85f6..da744144e6e 100755 --- a/pom.xml +++ b/pom.xml @@ -86,6 +86,7 @@ zeppelin-interpreter zeppelin-zengine + zeppelin-display-utils spark-dependencies spark markdown diff --git a/zeppelin-display-utils/pom.xml b/zeppelin-display-utils/pom.xml new file mode 100644 index 00000000000..f80d40f006d --- /dev/null +++ b/zeppelin-display-utils/pom.xml @@ -0,0 +1,179 @@ + + + + + 4.0.0 + + + zeppelin + org.apache.zeppelin + 0.6.0-incubating-SNAPSHOT + .. + + + org.apache.zeppelin + zeppelin-display-utils + jar + 0.6.0-incubating-SNAPSHOT + Zeppelin: Display utils + http://incubator.zeppelin.apache.org + + + 2.10.4 + 2.10 + + + + + + org.scala-lang + scala-library + ${scala.version} + + + + org.scala-lang + scala-compiler + ${scala.version} + + + + org.scala-lang + scalap + ${scala.version} + + + + + + + ${project.groupId} + zeppelin-interpreter + ${project.version} + + + + org.slf4j + slf4j-api + + + + org.slf4j + slf4j-log4j12 + + + + junit + junit + test + + + + org.scala-lang + scala-library + + + + org.scalatest + scalatest_2.10 + 2.1.1 + test + + + + + + + org.apache.rat + apache-rat-plugin + + + **/.idea/ + **/*.iml + .git/ + .gitignore + **/.settings/* + **/.classpath + **/.project + **/target/** + **/README.md + + + + + + maven-failsafe-plugin + 2.16 + + + + integration-test + verify + + + + + -Xmx2048m + + + + + org.apache.maven.plugins + maven-deploy-plugin + 2.7 + + true + + + + + org.scala-tools + maven-scala-plugin + 2.15.2 + + + + testCompile + + + + + + + org.scalatest + scalatest-maven-plugin + 1.0 + + + + 1 + + + + + test + integration-test + + test + + + + + + + diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala new file mode 100644 index 00000000000..c7f46d95132 --- /dev/null +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala @@ -0,0 +1,83 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.display + +import org.apache.zeppelin.interpreter.InterpreterContext + +import scala.xml._ + +class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], + val angularFunctions: Map[String, AngularFunction], + prefix: String, + label: String, + attributes1: MetaData, + scope: NamespaceBinding, + minimizeEmpty: Boolean, + child: Node*) + extends Elem(prefix, label, attributes1, scope, minimizeEmpty, child:_*) { + + val uniqueId = java.util.UUID.randomUUID.toString.replaceAll("-", "_") + + /** + * On click element + * @return + */ + def onClick(callback: () => Unit) : AngularDisplayElem = { + onEvent("ng-click", callback) + } + + /** + * + * @param eventName angular directive like ng-click, ng-change, etc. + * @return + */ + def onEvent(eventName: String, callback: () => Unit) : AngularDisplayElem = { + val interpreterContext = InterpreterContext.get + val registry = interpreterContext.getAngularObjectRegistry + + + // create AngularFunction in current paragraph + val functionName = uniqueId + "_" + eventName + val elem = this % Attribute(None, eventName, Text("functionName()"), Null) + + val fn = new AngularFunction( + registry, + functionName, + interpreterContext.getNoteId, + interpreterContext.getParagraphId, + new AngularFunctionRunnable { + override def run(args: AnyRef*): Unit = { + callback() + } + }) + + new AngularDisplayElem( + angularObjects, + angularFunctions + (eventName -> fn), + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + } +} + +object AngularDisplayElem { + implicit def Elem2AngularDisplayElem(elem: Elem) : AngularDisplayElem = { + new AngularDisplayElem( + Map[String, AngularObject[Any]](), + Map[String, AngularFunction](), + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); + } +} \ No newline at end of file diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala new file mode 100644 index 00000000000..ebd5549c179 --- /dev/null +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display + +import java.util + +import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} +import org.scalatest.{Matchers, BeforeAndAfterEach, BeforeAndAfter, FlatSpec} + +/** + * Test + */ +class AngularDisplayElemTest + extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Matchers { + import AngularDisplayElem._ + + override def beforeEach() { + val intpGroup = new InterpreterGroup() + val context = new InterpreterContext("note", "id", "title", "text", + new util.HashMap[String, Object](), new GUI(), new AngularObjectRegistry( + intpGroup.getId(), null), + new util.LinkedList[InterpreterContextRunner]()) + + InterpreterContext.set(context) + super.beforeEach() // To be stackable, must call super.beforeEach + } + + "DisplayUtilsElement" should "provide onclick method" in { + var a = 0 + val elem =
.onClick(() => { + a = a + 1 + }) + + click(elem) + + a should be(1) + } + + def click(elem: AngularDisplayElem) = { + fireEvent("ng-click", elem) + } + + // simulate click + def fireEvent(eventName: String, elem: AngularDisplayElem) = { + val angularFunction = elem.angularFunctions(eventName); + val angularObject = angularFunction.angularObject.asInstanceOf[AngularObject[Object]] + angularObject.set("invoke") + } +} From 991fd1f13ec315ca15160512a97350c7bd09f6a2 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Fri, 1 Jan 2016 17:58:40 -0800 Subject: [PATCH 02/11] fix test --- zeppelin-display-utils/pom.xml | 7 ------- .../apache/zeppelin/display/AngularDisplayElemTest.scala | 7 +++++-- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/zeppelin-display-utils/pom.xml b/zeppelin-display-utils/pom.xml index f80d40f006d..721e2bb8447 100644 --- a/zeppelin-display-utils/pom.xml +++ b/zeppelin-display-utils/pom.xml @@ -158,16 +158,9 @@ org.scalatest scalatest-maven-plugin 1.0 - - - - 1 - - test - integration-test test diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala index ebd5549c179..fd5d35231ed 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala @@ -19,13 +19,14 @@ package org.apache.zeppelin.display import java.util import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} +import org.scalatest.concurrent.Eventually import org.scalatest.{Matchers, BeforeAndAfterEach, BeforeAndAfter, FlatSpec} /** * Test */ class AngularDisplayElemTest - extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Matchers { + extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { import AngularDisplayElem._ override def beforeEach() { @@ -47,7 +48,9 @@ class AngularDisplayElemTest click(elem) - a should be(1) + // click create thread for callback function to run. So it'll may not immediately invoked + // after click. therefore eventually should be + eventually { a should be(1) } } def click(elem: AngularDisplayElem) = { From fa4e72b9aed01ea10d638fe7a2639f0df8e9a2db Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Fri, 1 Jan 2016 21:35:53 -0800 Subject: [PATCH 03/11] Using onEvent based on AngularObject --- .../zeppelin/display/AngularDisplayElem.scala | 59 +++++++++++++------ .../display/AngularDisplayElemTest.scala | 27 +++++++-- 2 files changed, 62 insertions(+), 24 deletions(-) diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala index c7f46d95132..1a18df873f5 100644 --- a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala @@ -22,7 +22,6 @@ import org.apache.zeppelin.interpreter.InterpreterContext import scala.xml._ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], - val angularFunctions: Map[String, AngularFunction], prefix: String, label: String, attributes1: MetaData, @@ -47,37 +46,59 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], * @return */ def onEvent(eventName: String, callback: () => Unit) : AngularDisplayElem = { - val interpreterContext = InterpreterContext.get - val registry = interpreterContext.getAngularObjectRegistry + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry // create AngularFunction in current paragraph - val functionName = uniqueId + "_" + eventName - val elem = this % Attribute(None, eventName, Text("functionName()"), Null) - - val fn = new AngularFunction( - registry, - functionName, - interpreterContext.getNoteId, - interpreterContext.getParagraphId, - new AngularFunctionRunnable { - override def run(args: AnyRef*): Unit = { - callback() - } - }) + val functionName = uniqueId + "_" + eventName.replaceAll("-", "_") + val elem = this % Attribute(None, eventName, + Text(s"""function(){${functionName}($$event)}"""), + Null) + + val angularObject = registry.add(functionName, "", ic.getNoteId) + .asInstanceOf[AngularObject[Any]] + + angularObject.addWatcher(new AngularObjectWatcher(ic) { + override def watch(oldObject: scala.Any, newObject: scala.Any, context: InterpreterContext) + : Unit = { + callback() + } + }) new AngularDisplayElem( - angularObjects, - angularFunctions + (eventName -> fn), + angularObjects + (eventName -> angularObject), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) } + + /** + * disassociate this element and it's child from front-end + * by removing angularobject + */ + def disassociate() = { + remove(this) + } + + /** + * Remove all angularObject recursively + * @param node + */ + private def remove(node: Node) : Unit = { + if (node.isInstanceOf[AngularDisplayElem]) { + node.asInstanceOf[AngularDisplayElem].angularObjects.values.foreach{ ao => + val ic = InterpreterContext.get() + ic.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId) + } + } + + node.child.foreach(remove _) + } } object AngularDisplayElem { implicit def Elem2AngularDisplayElem(elem: Elem) : AngularDisplayElem = { new AngularDisplayElem( Map[String, AngularObject[Any]](), - Map[String, AngularFunction](), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); } } \ No newline at end of file diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala index fd5d35231ed..7161a478f03 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala @@ -41,16 +41,34 @@ class AngularDisplayElemTest } "DisplayUtilsElement" should "provide onclick method" in { + registry.getAll("note").size() should be(0) + var a = 0 val elem =
.onClick(() => { a = a + 1 }) - click(elem) + registry.getAll("note").size() should be(1) // click create thread for callback function to run. So it'll may not immediately invoked // after click. therefore eventually should be - eventually { a should be(1) } + click(elem) + eventually { + a should be(1) + } + + click(elem) + eventually { + a should be(2) + } + + // disassociate + elem.disassociate() + registry.getAll("note").size() should be(0) + } + + def registry = { + InterpreterContext.get().getAngularObjectRegistry } def click(elem: AngularDisplayElem) = { @@ -59,8 +77,7 @@ class AngularDisplayElemTest // simulate click def fireEvent(eventName: String, elem: AngularDisplayElem) = { - val angularFunction = elem.angularFunctions(eventName); - val angularObject = angularFunction.angularObject.asInstanceOf[AngularObject[Object]] - angularObject.set("invoke") + val angularObject:AngularObject[Any] = elem.angularObjects(eventName); + angularObject.set("event"); } } From 819b7c37407911dc356dd7c15397de2ecdbcfbc5 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Fri, 1 Jan 2016 23:55:50 -0800 Subject: [PATCH 04/11] add display method --- zeppelin-display-utils/pom.xml | 15 ++++++++++++ .../zeppelin/display/AngularDisplayElem.scala | 24 +++++++++++++++++-- .../display/AngularDisplayElemTest.scala | 13 ++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) diff --git a/zeppelin-display-utils/pom.xml b/zeppelin-display-utils/pom.xml index 721e2bb8447..44434180ea8 100644 --- a/zeppelin-display-utils/pom.xml +++ b/zeppelin-display-utils/pom.xml @@ -147,9 +147,24 @@ 2.15.2 + compile + + compile + + compile + + + test-compile testCompile + test-compile + + + process-resources + + compile + diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala index 1a18df873f5..a68fa1c2c5e 100644 --- a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala @@ -51,9 +51,9 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], // create AngularFunction in current paragraph - val functionName = uniqueId + "_" + eventName.replaceAll("-", "_") + val functionName = eventName.replaceAll("-", "_") + "_" + uniqueId val elem = this % Attribute(None, eventName, - Text(s"""function(){${functionName}($$event)}"""), + Text(s"""${functionName}=$$event.timeStamp"""), Null) val angularObject = registry.add(functionName, "", ic.getNoteId) @@ -71,6 +71,24 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) } + /** + * Provided for the convinience in repl + * @return + */ + def display(out : java.io.PrintStream = Console.out) : Unit = { + val ic = InterpreterContext.get() + if (AngularDisplayElem.angularDirectivePrinted != ic.hashCode()) { + AngularDisplayElem.angularDirectivePrinted = ic.hashCode() + out.print("%angular ") + } + out.print(this.toString) + out.flush() + } + + def display() : Unit = { + display(Console.out) + } + /** * disassociate this element and it's child from front-end * by removing angularobject @@ -101,4 +119,6 @@ object AngularDisplayElem { Map[String, AngularObject[Any]](), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); } + + var angularDirectivePrinted: Int = 0 } \ No newline at end of file diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala index 7161a478f03..37339e15c58 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala @@ -16,6 +16,7 @@ */ package org.apache.zeppelin.display +import java.io.{PrintStream, ByteArrayOutputStream} import java.util import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} @@ -67,6 +68,18 @@ class AngularDisplayElemTest registry.getAll("note").size() should be(0) } + "display()" should "print angular display directive only once in a paragraph" in { + val out = new ByteArrayOutputStream() + val printOut = new PrintStream(out) + +
.display(printOut) + out.toString should be("%angular
") + + out.reset +
.display(printOut) + out.toString should be("
") + } + def registry = { InterpreterContext.get().getAngularObjectRegistry } From f14b0ffb8c22714bec08b03cce34cc1367d242cd Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 2 Jan 2016 10:09:56 -0800 Subject: [PATCH 05/11] AngularModel --- ...larDisplayElem.scala => AngularElem.scala} | 118 +++++++++++++++--- .../zeppelin/display/AngularModel.scala | 86 +++++++++++++ ...ayElemTest.scala => AngularElemTest.scala} | 41 +++++- .../zeppelin/display/AngularModelTest.scala | 68 ++++++++++ 4 files changed, 287 insertions(+), 26 deletions(-) rename zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/{AngularDisplayElem.scala => AngularElem.scala} (51%) create mode 100644 zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala rename zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/{AngularDisplayElemTest.scala => AngularElemTest.scala} (72%) create mode 100644 zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala similarity index 51% rename from zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala rename to zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala index a68fa1c2c5e..2de985825d6 100644 --- a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularDisplayElem.scala +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala @@ -19,9 +19,11 @@ package org.apache.zeppelin.display import org.apache.zeppelin.interpreter.InterpreterContext +import scala.collection.JavaConversions import scala.xml._ -class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], +class AngularElem(val modelName: String, + val angularObjects: Map[String, AngularObject[Any]], prefix: String, label: String, attributes1: MetaData, @@ -29,31 +31,91 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], minimizeEmpty: Boolean, child: Node*) extends Elem(prefix, label, attributes1, scope, minimizeEmpty, child:_*) { - val uniqueId = java.util.UUID.randomUUID.toString.replaceAll("-", "_") /** * On click element + * @param callback * @return */ - def onClick(callback: () => Unit) : AngularDisplayElem = { + def onClick(callback: () => Unit): AngularElem = { onEvent("ng-click", callback) } + /** + * On + * @param callback + * @return + */ + def onChange(callback: () => Unit): AngularElem = { + onEvent("ng-change", callback) + } + + /** + * Bind angularObject to ng-model directive + * @param name name of angularObject + * @param value initialValue + * @return + */ + def model(name: String, value: Any): AngularElem = { + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry + + // create AngularFunction in current paragraph + val elem = this % Attribute(None, "ng-model", + Text(s"${name}"), + Null) + + val angularObject = registry.add(name, value, ic.getNoteId) + .asInstanceOf[AngularObject[Any]] + + new AngularElem( + name, + angularObjects + (name -> angularObject), + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + } + + + def model(name: String): AngularElem = { + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry + + // create AngularFunction in current paragraph + val elem = this % Attribute(None, "ng-model", + Text(s"${name}"), + Null) + + new AngularElem( + name, + angularObjects, + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + } + + /** + * Retrieve value of model + * @return + */ + def model(): Any = { + if (angularObjects.contains(modelName)) { + angularObjects(modelName).get() + } else { + None + } + } + /** * * @param eventName angular directive like ng-click, ng-change, etc. * @return */ - def onEvent(eventName: String, callback: () => Unit) : AngularDisplayElem = { + def onEvent(eventName: String, callback: () => Unit): AngularElem = { val ic = InterpreterContext.get val registry = ic.getAngularObjectRegistry - // create AngularFunction in current paragraph val functionName = eventName.replaceAll("-", "_") + "_" + uniqueId val elem = this % Attribute(None, eventName, - Text(s"""${functionName}=$$event.timeStamp"""), + Text(s"${functionName}=$$event.timeStamp"), Null) val angularObject = registry.add(functionName, "", ic.getNoteId) @@ -61,31 +123,35 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], angularObject.addWatcher(new AngularObjectWatcher(ic) { override def watch(oldObject: scala.Any, newObject: scala.Any, context: InterpreterContext) - : Unit = { + :Unit = { callback() } }) - new AngularDisplayElem( + new AngularElem( + modelName, angularObjects + (eventName -> angularObject), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) } /** - * Provided for the convinience in repl + * Print with %angular prefix * @return */ - def display(out : java.io.PrintStream = Console.out) : Unit = { + def display(out: java.io.PrintStream = Console.out): Unit = { val ic = InterpreterContext.get() - if (AngularDisplayElem.angularDirectivePrinted != ic.hashCode()) { - AngularDisplayElem.angularDirectivePrinted = ic.hashCode() + if (AngularElem.angularDirectivePrinted != ic.hashCode()) { + AngularElem.angularDirectivePrinted = ic.hashCode() out.print("%angular ") } out.print(this.toString) out.flush() } - def display() : Unit = { + /** + * Print with %angular prefix + */ + def display(): Unit = { display(Console.out) } @@ -101,9 +167,9 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], * Remove all angularObject recursively * @param node */ - private def remove(node: Node) : Unit = { - if (node.isInstanceOf[AngularDisplayElem]) { - node.asInstanceOf[AngularDisplayElem].angularObjects.values.foreach{ ao => + private def remove(node: Node): Unit = { + if (node.isInstanceOf[AngularElem]) { + node.asInstanceOf[AngularElem].angularObjects.values.foreach{ ao => val ic = InterpreterContext.get() ic.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId) } @@ -113,12 +179,24 @@ class AngularDisplayElem(val angularObjects: Map[String, AngularObject[Any]], } } -object AngularDisplayElem { - implicit def Elem2AngularDisplayElem(elem: Elem) : AngularDisplayElem = { - new AngularDisplayElem( +object AngularElem { + implicit def Elem2AngularDisplayElem(elem: Elem) : AngularElem = { + new AngularElem(null, Map[String, AngularObject[Any]](), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); } - var angularDirectivePrinted: Int = 0 + private var angularDirectivePrinted: Int = 0 + + /** + * Disassociate (remove) all angular object in this notebook + */ + def disassociate() = { + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry + + JavaConversions.asScalaBuffer(registry.getAll(ic.getNoteId)).foreach(ao => + registry.remove(ao.getName, ao.getNoteId) + ) + } } \ No newline at end of file diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala new file mode 100644 index 00000000000..7b6c7b5e836 --- /dev/null +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala @@ -0,0 +1,86 @@ +package org.apache.zeppelin.display + +import org.apache.zeppelin.interpreter.InterpreterContext + +/** + * Represents ng-model + */ +class AngularModel(name: String) { + val registry = InterpreterContext.get.getAngularObjectRegistry + val noteId = InterpreterContext.get.getNoteId + + def this(name: String, newValue: Any) = { + this(name) + + value(newValue) + } + + + /** + * Get value of the model + * @return + */ + def apply(): Any = { + value() + } + + /** + * Get value of the model + * @return + */ + def value(): Any = { + val angularObject = registry.get(name, noteId) + if (angularObject == null) { + None + } else { + angularObject.get + } + } + + + def apply(newValue: Any): Unit = { + value(newValue) + } + + def :=(newValue: Any) = { + new AngularModel(name, newValue) + } + + /** + * Set value of the model + * @param newValue + */ + def value(newValue: Any): Unit = { + var angularObject = registry.get(name, noteId) + if (angularObject == null) { + // create new object + angularObject = registry.add(name, newValue, noteId) + } else { + angularObject.asInstanceOf[AngularObject[Any]].set(newValue) + } + angularObject.get() + } + + def remove(): Any = { + val angularObject = registry.get(name, noteId) + registry.remove(name, noteId) + + if (angularObject == null) { + None + } else { + angularObject.get + } + } +} + + +object AngularModel { + def apply(name: String): AngularModel = { + new AngularModel(name) + } + + def apply(name: String, newValue: Any): AngularModel = { + new AngularModel(name, newValue) + } + +} \ No newline at end of file diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala similarity index 72% rename from zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala rename to zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala index 37339e15c58..b301b7efe2c 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularDisplayElemTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala @@ -26,9 +26,9 @@ import org.scalatest.{Matchers, BeforeAndAfterEach, BeforeAndAfter, FlatSpec} /** * Test */ -class AngularDisplayElemTest +class AngularElemTest extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { - import AngularDisplayElem._ + import AngularElem._ override def beforeEach() { val intpGroup = new InterpreterGroup() @@ -41,7 +41,7 @@ class AngularDisplayElemTest super.beforeEach() // To be stackable, must call super.beforeEach } - "DisplayUtilsElement" should "provide onclick method" in { + "AngularElem" should "provide onclick method" in { registry.getAll("note").size() should be(0) var a = 0 @@ -68,7 +68,7 @@ class AngularDisplayElemTest registry.getAll("note").size() should be(0) } - "display()" should "print angular display directive only once in a paragraph" in { + "AngularElem" should "print angular display directive only once in a paragraph" in { val out = new ByteArrayOutputStream() val printOut = new PrintStream(out) @@ -80,16 +80,45 @@ class AngularDisplayElemTest out.toString should be("
") } + "AngularElem" should "bind angularObject to ng-model directive " in { +
.model("name", "value").toString should be("
") +
.model("name", "value").model() should be("value") +
.model() should be(None) + } + + "AngularElem" should "able to disassociate AngularObjects" in { + val elem1 =
.model("name1", "value1") + val elem2 =
.model("name2", "value2") + val elem3 =
.model("name3", "value3") + + registrySize should be(3) + + elem1.disassociate() + registrySize should be(2) + + AngularElem.disassociate() + registrySize should be(0) + } + + def registry = { InterpreterContext.get().getAngularObjectRegistry } - def click(elem: AngularDisplayElem) = { + def registrySize = { + registry.getAll(noteId).size + } + + def noteId = { + InterpreterContext.get().getNoteId + } + + def click(elem: AngularElem) = { fireEvent("ng-click", elem) } // simulate click - def fireEvent(eventName: String, elem: AngularDisplayElem) = { + def fireEvent(eventName: String, elem: AngularElem) = { val angularObject:AngularObject[Any] = elem.angularObjects(eventName); angularObject.set("event"); } diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala new file mode 100644 index 00000000000..1d412d8fe9d --- /dev/null +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala @@ -0,0 +1,68 @@ +package org.apache.zeppelin.display + +import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} +import org.scalatest.concurrent.Eventually +import org.scalatest.{Matchers, BeforeAndAfter, BeforeAndAfterEach, FlatSpec} + +/** + * Test for AngularModel + */ +class AngularModelTest extends FlatSpec +with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { + override def beforeEach() { + val intpGroup = new InterpreterGroup() + val context = new InterpreterContext("note", "id", "title", "text", + new java.util.HashMap[String, Object](), new GUI(), new AngularObjectRegistry( + intpGroup.getId(), null), + new java.util.LinkedList[InterpreterContextRunner]()) + + InterpreterContext.set(context) + super.beforeEach() // To be stackable, must call super.beforeEach + } + + "AngularModel" should "able to create AngularObject" in { + val registry = InterpreterContext.get().getAngularObjectRegistry + registrySize should be(0) + + AngularModel("model1")() should be(None) + registrySize should be(0) + + AngularModel("model1", "value1")() should be("value1") + registrySize should be(1) + + AngularModel("model1")() should be("value1") + registrySize should be(1) + } + + "AngularModel" should "able to update AngularObject" in { + val registry = InterpreterContext.get().getAngularObjectRegistry + + val model1 = AngularModel("model1", "value1") + model1() should be("value1") + registrySize should be(1) + + model1.value("newValue1") + model1() should be("newValue1") + registrySize should be(1) + + AngularModel("model1", "value2")() should be("value2") + registrySize should be(1) + } + + "AngularModel" should "able to remove AngularObject" in { + AngularModel("model1", "value1") + registrySize should be(1) + + AngularModel("model1").remove() + registrySize should be(0) + } + + + def registry() = { + InterpreterContext.get().getAngularObjectRegistry + } + + def registrySize() = { + registry().getAll(InterpreterContext.get().getNoteId).size + } +} From e0ce18f1d259b3d94f8a4f4c6846cadb45f5d288 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 2 Jan 2016 11:28:24 -0800 Subject: [PATCH 06/11] set InterpreterContext for callback function --- .../apache/zeppelin/display/AngularElem.scala | 46 +++++++++---------- .../zeppelin/display/AngularModel.scala | 16 +++++++ .../zeppelin/display/AngularElemTest.scala | 13 ++++++ .../zeppelin/display/AngularModelTest.scala | 16 +++++++ 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala index 2de985825d6..acc36f38fdf 100644 --- a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala @@ -22,14 +22,15 @@ import org.apache.zeppelin.interpreter.InterpreterContext import scala.collection.JavaConversions import scala.xml._ -class AngularElem(val modelName: String, - val angularObjects: Map[String, AngularObject[Any]], - prefix: String, - label: String, - attributes1: MetaData, - scope: NamespaceBinding, - minimizeEmpty: Boolean, - child: Node*) +class AngularElem(val interpreterContext: InterpreterContext, + val modelName: String, + val angularObjects: Map[String, AngularObject[Any]], + prefix: String, + label: String, + attributes1: MetaData, + scope: NamespaceBinding, + minimizeEmpty: Boolean, + child: Node*) extends Elem(prefix, label, attributes1, scope, minimizeEmpty, child:_*) { val uniqueId = java.util.UUID.randomUUID.toString.replaceAll("-", "_") @@ -58,18 +59,18 @@ class AngularElem(val modelName: String, * @return */ def model(name: String, value: Any): AngularElem = { - val ic = InterpreterContext.get - val registry = ic.getAngularObjectRegistry + val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph val elem = this % Attribute(None, "ng-model", Text(s"${name}"), Null) - val angularObject = registry.add(name, value, ic.getNoteId) + val angularObject = registry.add(name, value, interpreterContext.getNoteId) .asInstanceOf[AngularObject[Any]] new AngularElem( + interpreterContext, name, angularObjects + (name -> angularObject), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) @@ -77,8 +78,7 @@ class AngularElem(val modelName: String, def model(name: String): AngularElem = { - val ic = InterpreterContext.get - val registry = ic.getAngularObjectRegistry + val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph val elem = this % Attribute(None, "ng-model", @@ -86,6 +86,7 @@ class AngularElem(val modelName: String, Null) new AngularElem( + interpreterContext, name, angularObjects, elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) @@ -109,8 +110,7 @@ class AngularElem(val modelName: String, * @return */ def onEvent(eventName: String, callback: () => Unit): AngularElem = { - val ic = InterpreterContext.get - val registry = ic.getAngularObjectRegistry + val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph val functionName = eventName.replaceAll("-", "_") + "_" + uniqueId @@ -118,17 +118,19 @@ class AngularElem(val modelName: String, Text(s"${functionName}=$$event.timeStamp"), Null) - val angularObject = registry.add(functionName, "", ic.getNoteId) + val angularObject = registry.add(functionName, "", interpreterContext.getNoteId) .asInstanceOf[AngularObject[Any]] - angularObject.addWatcher(new AngularObjectWatcher(ic) { + angularObject.addWatcher(new AngularObjectWatcher(interpreterContext) { override def watch(oldObject: scala.Any, newObject: scala.Any, context: InterpreterContext) :Unit = { + InterpreterContext.set(interpreterContext) callback() } }) new AngularElem( + interpreterContext, modelName, angularObjects + (eventName -> angularObject), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) @@ -139,9 +141,8 @@ class AngularElem(val modelName: String, * @return */ def display(out: java.io.PrintStream = Console.out): Unit = { - val ic = InterpreterContext.get() - if (AngularElem.angularDirectivePrinted != ic.hashCode()) { - AngularElem.angularDirectivePrinted = ic.hashCode() + if (AngularElem.angularDirectivePrinted != interpreterContext.hashCode()) { + AngularElem.angularDirectivePrinted = interpreterContext.hashCode() out.print("%angular ") } out.print(this.toString) @@ -170,8 +171,7 @@ class AngularElem(val modelName: String, private def remove(node: Node): Unit = { if (node.isInstanceOf[AngularElem]) { node.asInstanceOf[AngularElem].angularObjects.values.foreach{ ao => - val ic = InterpreterContext.get() - ic.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId) + interpreterContext.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId) } } @@ -181,7 +181,7 @@ class AngularElem(val modelName: String, object AngularElem { implicit def Elem2AngularDisplayElem(elem: Elem) : AngularElem = { - new AngularElem(null, + new AngularElem(InterpreterContext.get(), null, Map[String, AngularObject[Any]](), elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); } diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala index 7b6c7b5e836..06929260b19 100644 --- a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala +++ b/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.apache.zeppelin.display import org.apache.zeppelin.interpreter.InterpreterContext diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala index b301b7efe2c..ff562935c86 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala @@ -100,6 +100,19 @@ class AngularElemTest registrySize should be(0) } + "AngularElem" should "allow access to InterpreterContext inside of callback function" in { + AngularModel("name", "value") + var modelValue = "" + + val elem =
.onClick(() => + modelValue = AngularModel("name")().toString + ) + + click(elem) + + eventually { modelValue should be("value")} + } + def registry = { InterpreterContext.get().getAngularObjectRegistry diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala index 1d412d8fe9d..7bf18aa30a5 100644 --- a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala +++ b/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.apache.zeppelin.display import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} From f7b0d9c7efbf384f55d38d983f862910b4340ac4 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 2 Jan 2016 20:47:09 -0800 Subject: [PATCH 07/11] zeppelin-display-utils -> zeppelin-display --- pom.xml | 2 +- spark/pom.xml | 6 ++++++ {zeppelin-display-utils => zeppelin-display}/pom.xml | 4 ++-- .../scala/org/apache/zeppelin/display/AngularElem.scala | 0 .../scala/org/apache/zeppelin/display/AngularModel.scala | 0 .../scala/org/apache/zeppelin/display/AngularElemTest.scala | 0 .../org/apache/zeppelin/display/AngularModelTest.scala | 0 7 files changed, 9 insertions(+), 3 deletions(-) rename {zeppelin-display-utils => zeppelin-display}/pom.xml (98%) rename {zeppelin-display-utils => zeppelin-display}/src/main/scala/org/apache/zeppelin/display/AngularElem.scala (100%) rename {zeppelin-display-utils => zeppelin-display}/src/main/scala/org/apache/zeppelin/display/AngularModel.scala (100%) rename {zeppelin-display-utils => zeppelin-display}/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala (100%) rename {zeppelin-display-utils => zeppelin-display}/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala (100%) diff --git a/pom.xml b/pom.xml index da744144e6e..9fe07ff43ff 100755 --- a/pom.xml +++ b/pom.xml @@ -86,7 +86,7 @@ zeppelin-interpreter zeppelin-zengine - zeppelin-display-utils + zeppelin-display spark-dependencies spark markdown diff --git a/spark/pom.xml b/spark/pom.xml index 174595a19e6..f43c3e7b622 100644 --- a/spark/pom.xml +++ b/spark/pom.xml @@ -54,6 +54,12 @@ slf4j-log4j12 + + ${project.groupId} + zeppelin-display + ${project.version} + + ${project.groupId} zeppelin-interpreter diff --git a/zeppelin-display-utils/pom.xml b/zeppelin-display/pom.xml similarity index 98% rename from zeppelin-display-utils/pom.xml rename to zeppelin-display/pom.xml index 44434180ea8..1a8328ec277 100644 --- a/zeppelin-display-utils/pom.xml +++ b/zeppelin-display/pom.xml @@ -27,10 +27,10 @@ org.apache.zeppelin - zeppelin-display-utils + zeppelin-display jar 0.6.0-incubating-SNAPSHOT - Zeppelin: Display utils + Zeppelin: Display system apis http://incubator.zeppelin.apache.org diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala similarity index 100% rename from zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularElem.scala rename to zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala diff --git a/zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularModel.scala similarity index 100% rename from zeppelin-display-utils/src/main/scala/org/apache/zeppelin/display/AngularModel.scala rename to zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularModel.scala diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala similarity index 100% rename from zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala rename to zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala diff --git a/zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala similarity index 100% rename from zeppelin-display-utils/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala rename to zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala From 3268a9d954d6439f260258a637fa944bd7627c45 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 2 Jan 2016 23:18:58 -0800 Subject: [PATCH 08/11] update travis --- .travis.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index ad6ff569dde..bf06d7f6640 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,28 +36,28 @@ script: - ./testing/stopSparkCluster.sh 1.5.2 2.3 # spark 1.4 - rm -rf `pwd`/interpreter/spark - - mvn package -DskipTests -Pspark-1.4 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,spark-dependencies,spark' + - mvn package -DskipTests -Pspark-1.4 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,zeppelin-display,spark-dependencies,spark' - ./testing/startSparkCluster.sh 1.4.1 2.3 - echo "export SPARK_HOME=`pwd`/spark-1.4.1-bin-hadoop2.3" > conf/zeppelin-env.sh - mvn package -Pspark-1.4 -Phadoop-2.3 -B -pl 'zeppelin-interpreter,zeppelin-zengine,zeppelin-server' -Dtest=org.apache.zeppelin.rest.*Test -DfailIfNoTests=false - ./testing/stopSparkCluster.sh 1.4.1 2.3 # spark 1.3 - rm -rf `pwd`/interpreter/spark - - mvn package -DskipTests -Pspark-1.3 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,spark-dependencies,spark' + - mvn package -DskipTests -Pspark-1.3 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,zeppelin-display,spark-dependencies,spark' - ./testing/startSparkCluster.sh 1.3.1 2.3 - echo "export SPARK_HOME=`pwd`/spark-1.3.1-bin-hadoop2.3" > conf/zeppelin-env.sh - mvn package -Pspark-1.3 -Phadoop-2.3 -B -pl 'zeppelin-interpreter,zeppelin-zengine,zeppelin-server' -Dtest=org.apache.zeppelin.rest.*Test -DfailIfNoTests=false - ./testing/stopSparkCluster.sh 1.3.1 2.3 # spark 1.2 - rm -rf `pwd`/interpreter/spark - - mvn package -Pspark-1.2 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,spark-dependencies,spark' + - mvn package -Pspark-1.2 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,zeppelin-display,spark-dependencies,spark' - ./testing/startSparkCluster.sh 1.2.1 2.3 - echo "export SPARK_HOME=`pwd`/spark-1.2.1-bin-hadoop2.3" > conf/zeppelin-env.sh - mvn package -Pspark-1.2 -Phadoop-2.3 -B -pl 'zeppelin-interpreter,zeppelin-zengine,zeppelin-server' -Dtest=org.apache.zeppelin.rest.*Test -DfailIfNoTests=false - ./testing/stopSparkCluster.sh 1.2.1 2.3 # spark 1.1 - rm -rf `pwd`/interpreter/spark - - mvn package -Pspark-1.1 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,spark-dependencies,spark' + - mvn package -Pspark-1.1 -Phadoop-2.3 -Ppyspark -B -pl 'zeppelin-interpreter,zeppelin-display,spark-dependencies,spark' - ./testing/startSparkCluster.sh 1.1.1 2.3 - echo "export SPARK_HOME=`pwd`/spark-1.1.1-bin-hadoop2.3" > conf/zeppelin-env.sh - mvn package -Pspark-1.1 -Phadoop-2.3 -B -pl 'zeppelin-interpreter,zeppelin-zengine,zeppelin-server' -Dtest=org.apache.zeppelin.rest.*Test -DfailIfNoTests=false From e3b681298d0499b0dd51094aaa0dd2cac9ed1067 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Wed, 20 Jan 2016 17:54:34 -0800 Subject: [PATCH 09/11] Update angular display utils display output to InterpreterOutput instead of Console.out by default --- .../org/apache/zeppelin/display/AngularElem.scala | 7 +++++-- .../apache/zeppelin/display/AngularElemTest.scala | 13 +++++++++++-- .../apache/zeppelin/display/AngularModelTest.scala | 13 +++++++++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala index acc36f38fdf..0b8913def52 100644 --- a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala @@ -17,6 +17,8 @@ package org.apache.zeppelin.display +import java.io.PrintStream + import org.apache.zeppelin.interpreter.InterpreterContext import scala.collection.JavaConversions @@ -140,7 +142,7 @@ class AngularElem(val interpreterContext: InterpreterContext, * Print with %angular prefix * @return */ - def display(out: java.io.PrintStream = Console.out): Unit = { + def display(out: java.io.PrintStream): Unit = { if (AngularElem.angularDirectivePrinted != interpreterContext.hashCode()) { AngularElem.angularDirectivePrinted = interpreterContext.hashCode() out.print("%angular ") @@ -153,7 +155,8 @@ class AngularElem(val interpreterContext: InterpreterContext, * Print with %angular prefix */ def display(): Unit = { - display(Console.out) + val out = interpreterContext.out + display(new PrintStream(out)) } /** diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala index ff562935c86..229ebf25364 100644 --- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala @@ -19,7 +19,7 @@ package org.apache.zeppelin.display import java.io.{PrintStream, ByteArrayOutputStream} import java.util -import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} +import org.apache.zeppelin.interpreter._ import org.scalatest.concurrent.Eventually import org.scalatest.{Matchers, BeforeAndAfterEach, BeforeAndAfter, FlatSpec} @@ -35,7 +35,16 @@ class AngularElemTest val context = new InterpreterContext("note", "id", "title", "text", new util.HashMap[String, Object](), new GUI(), new AngularObjectRegistry( intpGroup.getId(), null), - new util.LinkedList[InterpreterContextRunner]()) + new util.LinkedList[InterpreterContextRunner](), + new InterpreterOutput(new InterpreterOutputListener() { + override def onAppend(out: InterpreterOutput, line: Array[Byte]): Unit = { + // nothing to do + } + + override def onUpdate(out: InterpreterOutput, output: Array[Byte]): Unit = { + // nothing to do + } + })) InterpreterContext.set(context) super.beforeEach() // To be stackable, must call super.beforeEach diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala index 7bf18aa30a5..5f8268dbd31 100644 --- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala @@ -16,7 +16,7 @@ */ package org.apache.zeppelin.display -import org.apache.zeppelin.interpreter.{InterpreterContextRunner, InterpreterContext, InterpreterGroup} +import org.apache.zeppelin.interpreter._ import org.scalatest.concurrent.Eventually import org.scalatest.{Matchers, BeforeAndAfter, BeforeAndAfterEach, FlatSpec} @@ -30,7 +30,16 @@ with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { val context = new InterpreterContext("note", "id", "title", "text", new java.util.HashMap[String, Object](), new GUI(), new AngularObjectRegistry( intpGroup.getId(), null), - new java.util.LinkedList[InterpreterContextRunner]()) + new java.util.LinkedList[InterpreterContextRunner](), + new InterpreterOutput(new InterpreterOutputListener() { + override def onAppend(out: InterpreterOutput, line: Array[Byte]): Unit = { + // nothing to do + } + + override def onUpdate(out: InterpreterOutput, output: Array[Byte]): Unit = { + // nothing to do + } + })) InterpreterContext.set(context) super.beforeEach() // To be stackable, must call super.beforeEach From f1fb7697b58e32a07cbc048852b3f62b7fed6701 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 23 Jan 2016 15:36:00 -0800 Subject: [PATCH 10/11] provide two different packages for notebook scope and paragraph scope --- .../AbstractAngularElem.scala} | 119 ++++++++---------- .../AbstractAngularModel.scala} | 47 +++---- .../angular/notebookscope/AngularElem.scala | 84 +++++++++++++ .../angular/notebookscope/AngularModel.scala | 52 ++++++++ .../angular/paragraphscope/AngularElem.scala | 86 +++++++++++++ .../angular/paragraphscope/AngularModel.scala | 53 ++++++++ .../AbstractAngularElemTest.scala} | 63 +++++----- .../AbstractAngularModelTest.scala} | 28 +++-- .../notebookscope/AngularElemTest.scala | 41 ++++++ .../notebookscope/AngularModelTest.scala | 32 +++++ .../paragraphscope/AngularElemTest.scala | 41 ++++++ .../paragraphscope/AngularModelTest.scala | 32 +++++ 12 files changed, 545 insertions(+), 133 deletions(-) rename zeppelin-display/src/main/scala/org/apache/zeppelin/display/{AngularElem.scala => angular/AbstractAngularElem.scala} (59%) rename zeppelin-display/src/main/scala/org/apache/zeppelin/display/{AngularModel.scala => angular/AbstractAngularModel.scala} (66%) create mode 100644 zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElem.scala create mode 100644 zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModel.scala create mode 100644 zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElem.scala create mode 100644 zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModel.scala rename zeppelin-display/src/test/scala/org/apache/zeppelin/display/{AngularElemTest.scala => angular/AbstractAngularElemTest.scala} (63%) rename zeppelin-display/src/test/scala/org/apache/zeppelin/display/{AngularModelTest.scala => angular/AbstractAngularModelTest.scala} (76%) create mode 100644 zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElemTest.scala create mode 100644 zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModelTest.scala create mode 100644 zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElemTest.scala create mode 100644 zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModelTest.scala diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularElem.scala similarity index 59% rename from zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala rename to zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularElem.scala index 0b8913def52..80e36996bd1 100644 --- a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularElem.scala +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularElem.scala @@ -14,26 +14,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - -package org.apache.zeppelin.display +package org.apache.zeppelin.display.angular import java.io.PrintStream -import org.apache.zeppelin.interpreter.InterpreterContext +import org.apache.zeppelin.display.{AngularObjectWatcher, AngularObject} +import org.apache.zeppelin.interpreter.{InterpreterResult, InterpreterContext} -import scala.collection.JavaConversions import scala.xml._ -class AngularElem(val interpreterContext: InterpreterContext, - val modelName: String, - val angularObjects: Map[String, AngularObject[Any]], - prefix: String, - label: String, - attributes1: MetaData, - scope: NamespaceBinding, - minimizeEmpty: Boolean, - child: Node*) +/** + * Element that binded to Angular object + */ +abstract class AbstractAngularElem(val interpreterContext: InterpreterContext, + val modelName: String, + val angularObjects: Map[String, AngularObject[Any]], + prefix: String, + label: String, + attributes1: MetaData, + scope: NamespaceBinding, + minimizeEmpty: Boolean, + child: Node*) extends Elem(prefix, label, attributes1, scope, minimizeEmpty, child:_*) { + val uniqueId = java.util.UUID.randomUUID.toString.replaceAll("-", "_") /** @@ -41,7 +44,7 @@ class AngularElem(val interpreterContext: InterpreterContext, * @param callback * @return */ - def onClick(callback: () => Unit): AngularElem = { + def onClick(callback: () => Unit): AbstractAngularElem = { onEvent("ng-click", callback) } @@ -50,7 +53,7 @@ class AngularElem(val interpreterContext: InterpreterContext, * @param callback * @return */ - def onChange(callback: () => Unit): AngularElem = { + def onChange(callback: () => Unit): AbstractAngularElem = { onEvent("ng-change", callback) } @@ -60,7 +63,7 @@ class AngularElem(val interpreterContext: InterpreterContext, * @param value initialValue * @return */ - def model(name: String, value: Any): AngularElem = { + def model(name: String, value: Any): AbstractAngularElem = { val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph @@ -68,18 +71,18 @@ class AngularElem(val interpreterContext: InterpreterContext, Text(s"${name}"), Null) - val angularObject = registry.add(name, value, interpreterContext.getNoteId) + val angularObject = addAngularObject(name, value) .asInstanceOf[AngularObject[Any]] - new AngularElem( + newElem( interpreterContext, name, angularObjects + (name -> angularObject), - elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + elem) } - def model(name: String): AngularElem = { + def model(name: String): AbstractAngularElem = { val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph @@ -87,11 +90,11 @@ class AngularElem(val interpreterContext: InterpreterContext, Text(s"${name}"), Null) - new AngularElem( + newElem( interpreterContext, name, angularObjects, - elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + elem) } /** @@ -111,7 +114,7 @@ class AngularElem(val interpreterContext: InterpreterContext, * @param eventName angular directive like ng-click, ng-change, etc. * @return */ - def onEvent(eventName: String, callback: () => Unit): AngularElem = { + def onEvent(eventName: String, callback: () => Unit): AbstractAngularElem = { val registry = interpreterContext.getAngularObjectRegistry // create AngularFunction in current paragraph @@ -120,8 +123,7 @@ class AngularElem(val interpreterContext: InterpreterContext, Text(s"${functionName}=$$event.timeStamp"), Null) - val angularObject = registry.add(functionName, "", interpreterContext.getNoteId) - .asInstanceOf[AngularObject[Any]] + val angularObject = addAngularObject(functionName, "") angularObject.addWatcher(new AngularObjectWatcher(interpreterContext) { override def watch(oldObject: scala.Any, newObject: scala.Any, context: InterpreterContext) @@ -131,33 +133,19 @@ class AngularElem(val interpreterContext: InterpreterContext, } }) - new AngularElem( + newElem( interpreterContext, modelName, angularObjects + (eventName -> angularObject), - elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*) + elem) } - /** - * Print with %angular prefix - * @return - */ - def display(out: java.io.PrintStream): Unit = { - if (AngularElem.angularDirectivePrinted != interpreterContext.hashCode()) { - AngularElem.angularDirectivePrinted = interpreterContext.hashCode() - out.print("%angular ") - } - out.print(this.toString) - out.flush() - } + protected def addAngularObject(name: String, value: Any): AngularObject[Any] - /** - * Print with %angular prefix - */ - def display(): Unit = { - val out = interpreterContext.out - display(new PrintStream(out)) - } + protected def newElem(interpreterContext: InterpreterContext, + name: String, + angularObjects: Map[String, AngularObject[Any]], + elem: scala.xml.Elem): AbstractAngularElem /** * disassociate this element and it's child from front-end @@ -172,34 +160,33 @@ class AngularElem(val interpreterContext: InterpreterContext, * @param node */ private def remove(node: Node): Unit = { - if (node.isInstanceOf[AngularElem]) { - node.asInstanceOf[AngularElem].angularObjects.values.foreach{ ao => - interpreterContext.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId) + if (node.isInstanceOf[AbstractAngularElem]) { + node.asInstanceOf[AbstractAngularElem].angularObjects.values.foreach{ ao => + interpreterContext.getAngularObjectRegistry.remove(ao.getName, ao.getNoteId, ao + .getParagraphId) } } node.child.foreach(remove _) } -} -object AngularElem { - implicit def Elem2AngularDisplayElem(elem: Elem) : AngularElem = { - new AngularElem(InterpreterContext.get(), null, - Map[String, AngularObject[Any]](), - elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); + /** + * Print into provided print stream + * @return + */ + def display(out: java.io.PrintStream): Unit = { + out.print(this.toString) + out.flush() } - private var angularDirectivePrinted: Int = 0 - /** - * Disassociate (remove) all angular object in this notebook + * Print into InterpreterOutput */ - def disassociate() = { - val ic = InterpreterContext.get - val registry = ic.getAngularObjectRegistry - - JavaConversions.asScalaBuffer(registry.getAll(ic.getNoteId)).foreach(ao => - registry.remove(ao.getName, ao.getNoteId) - ) + def display(): Unit = { + val out = interpreterContext.out + out.setType(InterpreterResult.Type.ANGULAR) + out.write(this.toString()) + out.flush() } -} \ No newline at end of file +} + diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularModel.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularModel.scala similarity index 66% rename from zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularModel.scala rename to zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularModel.scala index 06929260b19..ff504380412 100644 --- a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/AngularModel.scala +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/AbstractAngularModel.scala @@ -14,23 +14,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.zeppelin.display +package org.apache.zeppelin.display.angular +import org.apache.zeppelin.display.AngularObject import org.apache.zeppelin.interpreter.InterpreterContext /** - * Represents ng-model + * Represents ng-model with angular object */ -class AngularModel(name: String) { - val registry = InterpreterContext.get.getAngularObjectRegistry - val noteId = InterpreterContext.get.getNoteId +abstract class AbstractAngularModel(name: String) { + val context = InterpreterContext.get + val registry = context.getAngularObjectRegistry + + /** + * Create AngularModel with initial Value + * @param name name of model + * @param newValue value + */ def this(name: String, newValue: Any) = { this(name) - value(newValue) } + protected def getAngularObject(): AngularObject[Any] + protected def addAngularObject(value: Any): AngularObject[Any] /** * Get value of the model @@ -45,7 +53,7 @@ class AngularModel(name: String) { * @return */ def value(): Any = { - val angularObject = registry.get(name, noteId) + val angularObject = getAngularObject() if (angularObject == null) { None } else { @@ -58,45 +66,30 @@ class AngularModel(name: String) { value(newValue) } - def :=(newValue: Any) = { - new AngularModel(name, newValue) - } /** * Set value of the model * @param newValue */ def value(newValue: Any): Unit = { - var angularObject = registry.get(name, noteId) + var angularObject = getAngularObject() if (angularObject == null) { // create new object - angularObject = registry.add(name, newValue, noteId) + angularObject = addAngularObject(newValue) } else { - angularObject.asInstanceOf[AngularObject[Any]].set(newValue) + angularObject.set(newValue) } angularObject.get() } def remove(): Any = { - val angularObject = registry.get(name, noteId) - registry.remove(name, noteId) + val angularObject = getAngularObject() if (angularObject == null) { None } else { + registry.remove(name, angularObject.getNoteId(), angularObject.getParagraphId()) angularObject.get } } } - - -object AngularModel { - def apply(name: String): AngularModel = { - new AngularModel(name) - } - - def apply(name: String, newValue: Any): AngularModel = { - new AngularModel(name, newValue) - } - -} \ No newline at end of file diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElem.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElem.scala new file mode 100644 index 00000000000..9ba88ffd916 --- /dev/null +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElem.scala @@ -0,0 +1,84 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.display.angular.notebookscope + +import org.apache.zeppelin.display.angular.AbstractAngularElem +import org.apache.zeppelin.display.{angular, AngularObject} +import org.apache.zeppelin.interpreter.InterpreterContext + +import scala.collection.JavaConversions +import scala.xml._ + +/** + * AngularElement in notebook scope + */ +class AngularElem(override val interpreterContext: InterpreterContext, + override val modelName: String, + override val angularObjects: Map[String, AngularObject[Any]], + prefix: String, + label: String, + attributes1: MetaData, + scope: NamespaceBinding, + minimizeEmpty: Boolean, + child: Node*) + extends AbstractAngularElem( + interpreterContext, modelName, angularObjects, prefix, label, attributes1, scope, + minimizeEmpty, child: _*) { + + override protected def addAngularObject(name: String, value: Any): AngularObject[Any] = { + val registry = interpreterContext.getAngularObjectRegistry + registry.add(name, value, interpreterContext.getNoteId, null).asInstanceOf[AngularObject[Any]] + + } + + override protected def newElem(interpreterContext: InterpreterContext, + name: String, + angularObjects: Map[String, AngularObject[Any]], + elem: scala.xml.Elem): angular.AbstractAngularElem = { + new AngularElem( + interpreterContext, + name, + angularObjects, + elem.prefix, + elem.label, + elem.attributes, + elem.scope, + elem.minimizeEmpty, + elem.child:_*) + } +} + +object AngularElem { + implicit def Elem2AngularDisplayElem(elem: Elem): AbstractAngularElem = { + new AngularElem(InterpreterContext.get(), null, + Map[String, AngularObject[Any]](), + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); + } + + /** + * Disassociate (remove) all angular object in this notebook + */ + def disassociate() = { + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry + + JavaConversions.asScalaBuffer(registry.getAll(ic.getNoteId, null)).foreach(ao => + registry.remove(ao.getName, ao.getNoteId, null) + ) + } +} \ No newline at end of file diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModel.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModel.scala new file mode 100644 index 00000000000..1ef89831204 --- /dev/null +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModel.scala @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.notebookscope + +import org.apache.zeppelin.display.AngularObject +import org.apache.zeppelin.display.angular.AbstractAngularModel +import org.apache.zeppelin.interpreter.InterpreterContext + +/** + * Represents ng-model in notebook scope + */ +class AngularModel(name: String) + extends org.apache.zeppelin.display.angular.AbstractAngularModel(name) { + + def this(name: String, newValue: Any) = { + this(name) + value(newValue) + } + + override protected def getAngularObject(): AngularObject[Any] = { + registry.get(name, context.getNoteId, null).asInstanceOf[AngularObject[Any]] + } + + override protected def addAngularObject(value: Any): AngularObject[Any] = { + registry.add(name, value, context.getNoteId, null).asInstanceOf[AngularObject[Any]] + } +} + + +object AngularModel { + def apply(name: String): AbstractAngularModel = { + new AngularModel(name) + } + + def apply(name: String, newValue: Any): AbstractAngularModel = { + new AngularModel(name, newValue) + } +} \ No newline at end of file diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElem.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElem.scala new file mode 100644 index 00000000000..50bd0ed4094 --- /dev/null +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElem.scala @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.zeppelin.display.angular.paragraphscope + + +import org.apache.zeppelin.display.angular.AbstractAngularElem +import org.apache.zeppelin.display.{angular, AngularObject} +import org.apache.zeppelin.interpreter.InterpreterContext + +import scala.collection.JavaConversions +import scala.xml._ + +/** + * AngularElement in paragraph scope + */ +class AngularElem(override val interpreterContext: InterpreterContext, + override val modelName: String, + override val angularObjects: Map[String, AngularObject[Any]], + prefix: String, + label: String, + attributes1: MetaData, + scope: NamespaceBinding, + minimizeEmpty: Boolean, + child: Node*) + extends AbstractAngularElem( + interpreterContext, modelName, angularObjects, prefix, label, attributes1, scope, + minimizeEmpty, child: _*) { + + override protected def addAngularObject(name: String, value: Any): AngularObject[Any] = { + val registry = interpreterContext.getAngularObjectRegistry + registry.add(name, value, interpreterContext.getNoteId, interpreterContext.getParagraphId) + .asInstanceOf[AngularObject[Any]] + + } + + override protected def newElem(interpreterContext: InterpreterContext, + name: String, + angularObjects: Map[String, AngularObject[Any]], + elem: scala.xml.Elem): angular.AbstractAngularElem = { + new AngularElem( + interpreterContext, + name, + angularObjects, + elem.prefix, + elem.label, + elem.attributes, + elem.scope, + elem.minimizeEmpty, + elem.child:_*) + } +} + +object AngularElem { + implicit def Elem2AngularDisplayElem(elem: Elem): AbstractAngularElem = { + new AngularElem(InterpreterContext.get(), null, + Map[String, AngularObject[Any]](), + elem.prefix, elem.label, elem.attributes, elem.scope, elem.minimizeEmpty, elem.child:_*); + } + + /** + * Disassociate (remove) all angular object in this notebook + */ + def disassociate() = { + val ic = InterpreterContext.get + val registry = ic.getAngularObjectRegistry + + JavaConversions.asScalaBuffer(registry.getAll(ic.getNoteId, ic.getParagraphId)).foreach(ao => + registry.remove(ao.getName, ao.getNoteId, ao.getParagraphId) + ) + } +} \ No newline at end of file diff --git a/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModel.scala b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModel.scala new file mode 100644 index 00000000000..ed28687290b --- /dev/null +++ b/zeppelin-display/src/main/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModel.scala @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.paragraphscope + +import org.apache.zeppelin.display.AngularObject +import org.apache.zeppelin.display.angular.AbstractAngularModel + +/** + * Represents ng-model in paragraph scope + */ +class AngularModel(name: String) + extends org.apache.zeppelin.display.angular.AbstractAngularModel(name) { + + def this(name: String, newValue: Any) = { + this(name) + value(newValue) + } + + override protected def getAngularObject(): AngularObject[Any] = { + registry.get(name, + context.getNoteId, context.getParagraphId).asInstanceOf[AngularObject[Any]] + } + + override protected def addAngularObject(value: Any): AngularObject[Any] = { + registry.add(name, value, + context.getNoteId, context.getParagraphId).asInstanceOf[AngularObject[Any]] + } +} + + +object AngularModel { + def apply(name: String): AbstractAngularModel = { + new AngularModel(name) + } + + def apply(name: String, newValue: Any): AbstractAngularModel = { + new AngularModel(name, newValue) + } +} \ No newline at end of file diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala similarity index 63% rename from zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala rename to zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala index 229ebf25364..2c82c8e4e66 100644 --- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularElemTest.scala +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularElemTest.scala @@ -14,25 +14,25 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.zeppelin.display +package org.apache.zeppelin.display.angular -import java.io.{PrintStream, ByteArrayOutputStream} +import java.io.{ByteArrayOutputStream, PrintStream} import java.util +import org.apache.zeppelin.display.{AngularObject, AngularObjectRegistry, GUI} import org.apache.zeppelin.interpreter._ import org.scalatest.concurrent.Eventually -import org.scalatest.{Matchers, BeforeAndAfterEach, BeforeAndAfter, FlatSpec} +import org.scalatest.{BeforeAndAfter, BeforeAndAfterEach, FlatSpec, Matchers} /** * Test */ -class AngularElemTest +trait AbstractAngularElemTest extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { - import AngularElem._ override def beforeEach() { val intpGroup = new InterpreterGroup() - val context = new InterpreterContext("note", "id", "title", "text", + val context = new InterpreterContext("note", "paragraph", "title", "text", new util.HashMap[String, Object](), new GUI(), new AngularObjectRegistry( intpGroup.getId(), null), new util.LinkedList[InterpreterContextRunner](), @@ -50,15 +50,19 @@ class AngularElemTest super.beforeEach() // To be stackable, must call super.beforeEach } + def angularElem(elem: scala.xml.Elem): AbstractAngularElem; + def angularModel(name: String): AbstractAngularModel; + + "AngularElem" should "provide onclick method" in { - registry.getAll("note").size() should be(0) + registrySize should be(0) var a = 0 - val elem =
.onClick(() => { + val elem = angularElem(
).onClick(() => { a = a + 1 }) - - registry.getAll("note").size() should be(1) + elem.angularObjects.get("ng-click") should not be(null) + registrySize should be(1) // click create thread for callback function to run. So it'll may not immediately invoked // after click. therefore eventually should be @@ -74,47 +78,50 @@ class AngularElemTest // disassociate elem.disassociate() - registry.getAll("note").size() should be(0) + registrySize should be(0) } "AngularElem" should "print angular display directive only once in a paragraph" in { val out = new ByteArrayOutputStream() val printOut = new PrintStream(out) -
.display(printOut) - out.toString should be("%angular
") + angularElem(
).display(printOut) + out.toString should be("
") out.reset -
.display(printOut) + angularElem(
).display(printOut) out.toString should be("
") } "AngularElem" should "bind angularObject to ng-model directive " in { -
.model("name", "value").toString should be("
") -
.model("name", "value").model() should be("value") -
.model() should be(None) + angularElem(
) + .model("name", "value").toString should be("
") + angularElem(
).model("name", "value").model() should be("value") + angularElem(
).model() should be(None) } "AngularElem" should "able to disassociate AngularObjects" in { - val elem1 =
.model("name1", "value1") - val elem2 =
.model("name2", "value2") - val elem3 =
.model("name3", "value3") + val elem1 = angularElem(
).model("name1", "value1") + val elem2 = angularElem(
).model("name2", "value2") + val elem3 = angularElem(
).model("name3", "value3") registrySize should be(3) elem1.disassociate() registrySize should be(2) - AngularElem.disassociate() + elem2.disassociate() + elem3.disassociate() registrySize should be(0) } "AngularElem" should "allow access to InterpreterContext inside of callback function" in { - AngularModel("name", "value") + angularModel("name").value("value") + var modelValue = "" - val elem =
.onClick(() => - modelValue = AngularModel("name")().toString + val elem = angularElem(
).onClick(() => + modelValue = angularModel("name")().toString ) click(elem) @@ -128,20 +135,20 @@ class AngularElemTest } def registrySize = { - registry.getAll(noteId).size + registry.getAllWithGlobal("note").size() } def noteId = { InterpreterContext.get().getNoteId } - def click(elem: AngularElem) = { + def click(elem: org.apache.zeppelin.display.angular.AbstractAngularElem) = { fireEvent("ng-click", elem) } // simulate click - def fireEvent(eventName: String, elem: AngularElem) = { - val angularObject:AngularObject[Any] = elem.angularObjects(eventName); + def fireEvent(eventName: String, elem: org.apache.zeppelin.display.angular.AbstractAngularElem) = { + val angularObject: AngularObject[Any] = elem.angularObjects(eventName); angularObject.set("event"); } } diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala similarity index 76% rename from zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala rename to zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala index 5f8268dbd31..942390ac8f8 100644 --- a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/AngularModelTest.scala +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/AbstractAngularModelTest.scala @@ -14,16 +14,17 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.zeppelin.display +package org.apache.zeppelin.display.angular +import org.apache.zeppelin.display.{AngularObjectRegistry, GUI} import org.apache.zeppelin.interpreter._ import org.scalatest.concurrent.Eventually -import org.scalatest.{Matchers, BeforeAndAfter, BeforeAndAfterEach, FlatSpec} +import org.scalatest.{BeforeAndAfter, BeforeAndAfterEach, FlatSpec, Matchers} /** - * Test for AngularModel + * Abstract Test for AngularModel */ -class AngularModelTest extends FlatSpec +trait AbstractAngularModelTest extends FlatSpec with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { override def beforeEach() { val intpGroup = new InterpreterGroup() @@ -45,24 +46,27 @@ with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { super.beforeEach() // To be stackable, must call super.beforeEach } + def angularModel(name: String): AbstractAngularModel + def angularModel(name: String, value: Any): AbstractAngularModel + "AngularModel" should "able to create AngularObject" in { val registry = InterpreterContext.get().getAngularObjectRegistry registrySize should be(0) - AngularModel("model1")() should be(None) + angularModel("model1")() should be(None) registrySize should be(0) - AngularModel("model1", "value1")() should be("value1") + angularModel("model1", "value1")() should be("value1") registrySize should be(1) - AngularModel("model1")() should be("value1") + angularModel("model1")() should be("value1") registrySize should be(1) } "AngularModel" should "able to update AngularObject" in { val registry = InterpreterContext.get().getAngularObjectRegistry - val model1 = AngularModel("model1", "value1") + val model1 = angularModel("model1", "value1") model1() should be("value1") registrySize should be(1) @@ -70,15 +74,15 @@ with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { model1() should be("newValue1") registrySize should be(1) - AngularModel("model1", "value2")() should be("value2") + angularModel("model1", "value2")() should be("value2") registrySize should be(1) } "AngularModel" should "able to remove AngularObject" in { - AngularModel("model1", "value1") + angularModel("model1", "value1") registrySize should be(1) - AngularModel("model1").remove() + angularModel("model1").remove() registrySize should be(0) } @@ -88,6 +92,6 @@ with BeforeAndAfter with BeforeAndAfterEach with Eventually with Matchers { } def registrySize() = { - registry().getAll(InterpreterContext.get().getNoteId).size + registry().getAllWithGlobal(InterpreterContext.get().getNoteId).size } } diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElemTest.scala new file mode 100644 index 00000000000..a3effb05deb --- /dev/null +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularElemTest.scala @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.notebookscope + + +import org.apache.zeppelin.display.angular.{AbstractAngularElem, AbstractAngularModel, AbstractAngularElemTest} + +import scala.xml.Elem + +/** + * Test + */ +class AngularElemTest extends AbstractAngularElemTest { + + override def angularElem(elem: Elem): AbstractAngularElem = { + AngularElem.Elem2AngularDisplayElem(elem) + } + + override def angularModel(name: String): AbstractAngularModel = { + AngularModel(name) + } + + "AngularElem" should "able to be created from implicit conversion" in { + import AngularElem._ +
.model("modelname") + } +} diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModelTest.scala new file mode 100644 index 00000000000..10197939bab --- /dev/null +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/notebookscope/AngularModelTest.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.notebookscope + +import org.apache.zeppelin.display.angular.{AbstractAngularModel, AbstractAngularModelTest} + +/** + * Test for AngularModel + */ +class AngularModelTest extends AbstractAngularModelTest { + override def angularModel(name: String): AbstractAngularModel = { + AngularModel(name) + } + + override def angularModel(name: String, value: Any): AbstractAngularModel = { + AngularModel(name, value) + } +} diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElemTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElemTest.scala new file mode 100644 index 00000000000..4135dedf2ff --- /dev/null +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularElemTest.scala @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.paragraphscope + + +import org.apache.zeppelin.display.angular.{AbstractAngularElem, AbstractAngularModel, AbstractAngularElemTest} + +import scala.xml.Elem + +/** + * Test + */ +class AngularElemTest extends AbstractAngularElemTest { + + override def angularElem(elem: Elem): AbstractAngularElem = { + AngularElem.Elem2AngularDisplayElem(elem) + } + + override def angularModel(name: String): AbstractAngularModel = { + AngularModel(name) + } + + "AngularElem" should "able to be created from implicit conversion" in { + import AngularElem._ +
.model("modelname") + } +} diff --git a/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModelTest.scala b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModelTest.scala new file mode 100644 index 00000000000..c6e1eb08863 --- /dev/null +++ b/zeppelin-display/src/test/scala/org/apache/zeppelin/display/angular/paragraphscope/AngularModelTest.scala @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.zeppelin.display.angular.paragraphscope + +import org.apache.zeppelin.display.angular.{AbstractAngularModel, AbstractAngularModelTest} + +/** + * Test for AngularModel + */ +class AngularModelTest extends AbstractAngularModelTest { + override def angularModel(name: String): AbstractAngularModel = { + AngularModel(name) + } + + override def angularModel(name: String, value: Any): AbstractAngularModel = { + AngularModel(name, value) + } +} From 9195021281cf61d5a3f098897043576bd4f70f08 Mon Sep 17 00:00:00 2001 From: Lee moon soo Date: Sat, 23 Jan 2016 15:54:52 -0800 Subject: [PATCH 11/11] Remove paragraph scope angular object on paragraph removal and add unittest --- .../org/apache/zeppelin/notebook/Note.java | 9 +++- .../apache/zeppelin/notebook/Notebook.java | 10 ++++ .../zeppelin/notebook/NotebookTest.java | 51 +++++++++++++++++-- 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java index 27e2f77cee2..52e7ea3482d 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Note.java @@ -29,6 +29,7 @@ import org.apache.zeppelin.display.AngularObjectRegistry; import org.apache.zeppelin.display.Input; import org.apache.zeppelin.interpreter.*; +import org.apache.zeppelin.interpreter.remote.RemoteAngularObjectRegistry; import org.apache.zeppelin.notebook.repo.NotebookRepo; import org.apache.zeppelin.notebook.utility.IdHashes; import org.apache.zeppelin.scheduler.Job; @@ -413,7 +414,13 @@ private void removeAllAngularObjectInParagraph(String paragraphId) { for (InterpreterSetting setting : settings) { InterpreterGroup intpGroup = setting.getInterpreterGroup(); AngularObjectRegistry registry = intpGroup.getAngularObjectRegistry(); - registry.removeAll(id, paragraphId); + + if (registry instanceof RemoteAngularObjectRegistry) { + // remove paragraph scope object + ((RemoteAngularObjectRegistry) registry).removeAllAndNotifyRemoteProcess(id, paragraphId); + } else { + registry.removeAll(id, paragraphId); + } } } diff --git a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java index cae4210c45f..7a87c92a995 100644 --- a/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java +++ b/zeppelin-zengine/src/main/java/org/apache/zeppelin/notebook/Notebook.java @@ -284,8 +284,18 @@ public void removeNote(String id) { for (InterpreterSetting settings : replFactory.get()) { AngularObjectRegistry registry = settings.getInterpreterGroup().getAngularObjectRegistry(); if (registry instanceof RemoteAngularObjectRegistry) { + // remove paragraph scope object + for (Paragraph p : note.getParagraphs()) { + ((RemoteAngularObjectRegistry) registry).removeAllAndNotifyRemoteProcess(id, p.getId()); + } + // remove notebook scope object ((RemoteAngularObjectRegistry) registry).removeAllAndNotifyRemoteProcess(id, null); } else { + // remove paragraph scope object + for (Paragraph p : note.getParagraphs()) { + registry.removeAll(id, p.getId()); + } + // remove notebook scope object registry.removeAll(id, null); } } diff --git a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java index 506b682b293..55cd6ba6714 100644 --- a/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java +++ b/zeppelin-zengine/src/test/java/org/apache/zeppelin/notebook/NotebookTest.java @@ -320,18 +320,59 @@ public void testAngularObjectRemovalOnNotebookRemove() throws InterruptedExcepti .getInterpreterSettings().get(0).getInterpreterGroup() .getAngularObjectRegistry(); - // add local scope object - registry.add("o1", "object1", note.id(), null); + Paragraph p1 = note.addParagraph(); + + // add paragraph scope object + registry.add("o1", "object1", note.id(), p1.getId()); + + // add notebook scope object + registry.add("o2", "object2", note.id(), null); + // add global scope object - registry.add("o2", "object2", null, null); + registry.add("o3", "object3", null, null); // remove notebook notebook.removeNote(note.id()); - // local object should be removed + // notebook scope or paragraph scope object should be removed assertNull(registry.get("o1", note.id(), null)); + assertNull(registry.get("o2", note.id(), p1.getId())); + // global object sould be remained - assertNotNull(registry.get("o2", null, null)); + assertNotNull(registry.get("o3", null, null)); + } + + @Test + public void testAngularObjectRemovalOnParagraphRemove() throws InterruptedException, + IOException { + // create a note and a paragraph + Note note = notebook.createNote(); + note.getNoteReplLoader().setInterpreters(factory.getDefaultInterpreterSettingList()); + + AngularObjectRegistry registry = note.getNoteReplLoader() + .getInterpreterSettings().get(0).getInterpreterGroup() + .getAngularObjectRegistry(); + + Paragraph p1 = note.addParagraph(); + + // add paragraph scope object + registry.add("o1", "object1", note.id(), p1.getId()); + + // add notebook scope object + registry.add("o2", "object2", note.id(), null); + + // add global scope object + registry.add("o3", "object3", null, null); + + // remove notebook + note.removeParagraph(p1.getId()); + + // paragraph scope should be removed + assertNull(registry.get("o1", note.id(), null)); + + // notebook scope and global object sould be remained + assertNotNull(registry.get("o2", note.id(), null)); + assertNotNull(registry.get("o3", null, null)); } @Test