Browse Source

Initial commit.

Herby Vojčík 4 years ago
commit
7a456ccd27
20 changed files with 1017 additions and 0 deletions
  1. 10 0
      .gitignore
  2. 173 0
      Gruntfile.js
  3. 22 0
      LICENSE-MIT
  4. 31 0
      README.md
  5. 8 0
      deploy.js
  6. 11 0
      devel.js
  7. 48 0
      index.html
  8. 8 0
      lambda.js
  9. 4 0
      lambda/index.js
  10. 5 0
      local.amd.json
  11. 56 0
      package.json
  12. 38 0
      parts.html
  13. 59 0
      src/MiniMVP-Backend.js
  14. 23 0
      src/MiniMVP-Backend.st
  15. 9 0
      src/MiniMVP-Tests.js
  16. 5 0
      src/MiniMVP-Tests.st
  17. 375 0
      src/MiniMVP.js
  18. 65 0
      src/MiniMVP.st
  19. 10 0
      testing.js
  20. 57 0
      uninstall.html

+ 10 - 0
.gitignore

@@ -0,0 +1,10 @@
+#Dependencies defined in `package.json` got via `npm install`
+/node_modules/
+
+#Generated when running tests from cli via `npm test` / `grunt test`
+/test_runner.js
+
+#Generated when `grunt devel`, `grunt deploy` or `grunt deploy:lambda`
+/config.js
+/the.js
+/lambda/the.js

+ 173 - 0
Gruntfile.js

@@ -0,0 +1,173 @@
+'use strict';
+
+module.exports = function (grunt) {
+    // These plugins provide necessary tasks.
+    grunt.loadNpmTasks('grunt-contrib-clean');
+    grunt.loadNpmTasks('grunt-contrib-requirejs');
+    grunt.loadNpmTasks('grunt-exec');
+    grunt.loadNpmTasks('@ambers/sdk');
+
+    var path = require('path'),
+        helpers = require('@ambers/sdk').helpers;
+
+    // Default task.
+    grunt.registerTask('default', ['amdconfig:app', 'amberc:all']);
+    grunt.registerTask('test', ['amdconfig:app', 'requirejs:test_runner', 'exec:test_runner', 'clean:test_runner']);
+    grunt.registerTask('devel', ['amdconfig:app', 'requirejs:devel']);
+    grunt.registerTask('deploy', ['amdconfig:app', 'requirejs:deploy']);
+    grunt.registerTask('deploy:lambda', ['amdconfig:app', 'requirejs:deploy_lambda']);
+
+    var id = function (x) {
+        return x
+    };
+
+    function mkDefine (deps, cb) {
+        return "define(" + JSON.stringify(deps) + "," + cb + ");"
+    }
+
+    var cbRequireAndPromiseMain = function (require) {
+        return new Promise(function (resolve, reject) {
+            require(["app/main"], resolve, reject);
+        });
+    };
+
+    var lambdaExports = function (amberPromised) {
+        return function (className) {
+            var worker, workerPromise = amberPromised.then(function (amber) {
+                worker = amber.globals[className]._new();
+            });
+            return function (selector) {
+                var jsSelector, jsSelectorPromise = amberPromised.then(function (amber) {
+                    jsSelector = amber.api.st2js(selector);
+                });
+                var readyPromise = Promise.all([workerPromise, jsSelectorPromise]);
+                return function (event, context) {
+                    return readyPromise.then(function () {
+                        return worker[jsSelector](event, context);
+                    });
+                };
+            };
+        };
+    };
+
+    // Project configuration.
+    grunt.initConfig({
+        // Metadata.
+        // pkg: grunt.file.readJSON(''),
+        banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' +
+        '<%= grunt.template.today("yyyy-mm-dd") %>\n' +
+        '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' +
+        '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' +
+        ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n',
+        // task configuration
+        amberc: {
+            options: {
+                amber_dir: path.join(__dirname, "node_modules", "@ambers", "lang"),
+                configFile: "config.js"
+            },
+            all: {
+                src: [
+                    'src/MiniMVP.st', 'src/MiniMVP-Backend.st', // list all sources in dependency order
+                    'src/MiniMVP-Tests.st' // list all tests in dependency order
+                ],
+                amd_namespace: 'mini-mvp',
+                libraries: ['amber/core/SUnit', 'amber/web/Web', 'silk/Silk']
+            }
+        },
+
+        amdconfig: {app: {dest: 'config.js'}},
+
+        requirejs: {
+            options: {
+                mainConfigFile: "config.js",
+                paths: {
+                    "es6-promise/auto": "node_modules/es6-promise/dist/es6-promise.auto"
+                },
+                useStrict: true
+            },
+            deploy: {
+                options: {
+                    rawText: {
+                        "helios/index": "",
+                        "app": mkDefine(["require", "es6-promise/auto"], cbRequireAndPromiseMain),
+                        "app/main": mkDefine(["deploy", "amber/core/Platform-Browser"], id)
+                    },
+                    pragmas: {
+                        excludeIdeData: true,
+                        excludeDebugContexts: true
+                    },
+                    include: ['config', 'node_modules/requirejs/require', 'app'],
+                    findNestedDependencies: true,
+                    exclude: ['helios/index'],
+                    optimize: "uglify2",
+                    out: "the.js"
+                }
+            },
+            devel: {
+                options: {
+                    rawText: {
+                        "app": mkDefine(["require", "es6-promise/auto"], cbRequireAndPromiseMain),
+                        "app/main": mkDefine(["devel", "amber/core/Platform-Browser"], id)
+                    },
+                    include: ['config', 'node_modules/requirejs/require', 'app', 'app/main'],
+                    exclude: ['devel', 'amber/core/Platform-Browser'],
+                    out: "the.js"
+                }
+            },
+            deploy_lambda: {
+                options: {
+                    rawText: {
+                        "helios/index": "",
+                        "app": mkDefine(["app/main"], lambdaExports),
+                        "app/main": mkDefine(["lambda", "amber/core/Platform-Node"], function (amber) {
+                            return amber.initialize().then(function () {
+                                return amber;
+                            });
+                        })
+                    },
+                    pragmas: {
+                        excludeIdeData: true,
+                        excludeDebugContexts: true
+                    },
+                    include: ['app'],
+                    findNestedDependencies: true,
+                    exclude: ['helios/index'],
+                    wrap: {start: helpers.nodeWrapper.start, end: "return require('app');" + helpers.nodeWrapper.end},
+                    optimize: "uglify2",
+                    out: "lambda/the.js"
+                }
+            },
+            test_runner: {
+                options: {
+                    rawText: {
+                        "jquery": "/* do not load in node test runner */",
+                        "app": mkDefine(["testing", "amber/core/Platform-Node", "amber_devkit/NodeTestRunner"], function (amber) {
+                            amber.initialize().then(function () {
+                                amber.globals.NodeTestRunner._main();
+                            });
+                        })
+                    },
+                    paths: {"amber_devkit": helpers.libPath},
+                    pragmas: {
+                        excludeIdeData: true
+                    },
+                    include: ['app'],
+                    findNestedDependencies: true,
+                    insertRequire: ['app'],
+                    optimize: "none",
+                    wrap: helpers.nodeWrapperWithShebang,
+                    out: "test_runner.js"
+                }
+            }
+        },
+
+        exec: {
+            test_runner: 'node test_runner.js'
+        },
+
+        clean: {
+            test_runner: ['test_runner.js']
+        }
+    });
+
+};

+ 22 - 0
LICENSE-MIT

@@ -0,0 +1,22 @@
+Copyright (c) 2020 Herby Vojčík
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.

+ 31 - 0
README.md

@@ -0,0 +1,31 @@
+# Amber Mini MVP
+
+Minimalistic presenter glue for Amber, usable w/ Silk and Web.
+
+## Use MiniMVP library in your project
+
+**DELETE THIS PARAGRAPH, IF MiniMVP IS A LIBRARY. DELETE WHOLE THIS SECTION IF MiniMVP IS AN APP, NOT A LIBRARY.**
+
+If not already done, install Amber and create an Amber project with `amber init`,
+as shown in [Amber Instructions](https://lolg.it/amber/amber#prerequisities).
+
+In a project, run
+
+```sh
+npm install mini-mvp --save
+grunt devel
+amber serve
+```
+
+Go to `http://localhost:4000/` in your browser and
+in all packages that use MiniMVP,
+add `'mini-mvp/MiniMVP'` to the package imports,
+save the change and commit the package. Reload.
+
+## Contributing
+
+  1. Have amber cli tooling installed. If you don't, see first box in these [instructions](https://lolg.it/amber/amber#getting-amber-and-setting-up-an-initial-project).
+
+  1. Fork and/or clone the project, then bring it alive by: `npm run init`.
+
+  1. Developing the project: Start server with `amber serve` and go to `http://localhost:4000/` in your browser. Overlay with buttons to open IDE should appear shortly.

+ 8 - 0
deploy.js

@@ -0,0 +1,8 @@
+define([
+    'amber/deploy',
+    // --- packages to be deployed begin here ---
+    'mini-mvp/MiniMVP'
+    // --- packages to be deployed end here ---
+], function (amber) {
+    return amber;
+});

+ 11 - 0
devel.js

@@ -0,0 +1,11 @@
+define([
+    './testing',
+    'amber/devel',
+    // --- packages used only during development begin here ---
+    'amber/legacy/Benchfib',
+    'amber/legacy/Examples',
+    'amber/legacy/IDE'
+    // --- packages used only during development end here ---
+], function (amber) {
+    return amber;
+});

+ 48 - 0
index.html

@@ -0,0 +1,48 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+    <title>Amber Mini MVP</title>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
+    <meta name="author" content="Herby Vojčík"/>
+    <script type='text/javascript' src='the.js'></script>
+</head>
+
+<body>
+<!-- EXAMPLE APP START -->
+<p>This is a sample app. It contains some
+    <a href="parts.html">batteries included</a>
+    and should be
+    <a href="uninstall.html">cleaned up</a>
+    once you start developing your own app / lib.</p>
+<button id="amber-with">Hello from TagBrush >> with:</button>
+<button id="silk-tag">Hello from Silk >> TAG:</button>
+<button id="jquery-append">Hello from jQuery append</button>
+<button id="console-log">Log into JS console</button>
+<button id="timeout-alert">Remind me in five seconds</button>
+<button id="lambda-status">Inspect status of backend</button>
+<ol id="output-list"></ol>
+<!-- EXAMPLE APP END -->
+<script type='text/javascript'>
+    var global = typeof global === "undefined" ? window : global || window;
+    require(['app'], function (amberPromise) {
+        amberPromise.then(function (amber) {
+            amber.initialize({
+                //used for all new packages in IDE
+                'transport.defaultAmdNamespace': "mini-mvp"
+            }).then(function () {
+                // This shows IDE dialog in development mode (module is present).
+                // This silently fails in deploy mode (module is missing).
+                // Thus, no need to have different page for deploy, leading to less bugs.
+                require(["amber-ide-starter-dialog"], function (dlg) {
+                    dlg.start();
+                });
+                // Start the app itself.
+                amber.globals.MiniMVP._start();
+            });
+        });
+    });
+</script>
+</body>
+
+</html>

+ 8 - 0
lambda.js

@@ -0,0 +1,8 @@
+define([
+    'amber/deploy',
+    // --- packages to be deployed begin here ---
+    'mini-mvp/MiniMVP-Backend'
+    // --- packages to be deployed end here ---
+], function (amber) {
+    return amber;
+});

+ 4 - 0
lambda/index.js

@@ -0,0 +1,4 @@
+var app = require('./the.js')('MiniMVPBackend');
+
+exports.status = app('status');
+exports.echo = app('echoEvent:andContext:');

+ 5 - 0
local.amd.json

@@ -0,0 +1,5 @@
+{
+  "paths": {
+    "mini-mvp": "src"
+  }
+}

+ 56 - 0
package.json

@@ -0,0 +1,56 @@
+{
+  "name": "mini-mvp",
+  "title": "Amber Mini MVP",
+  "description": "Minimalistic presenter glue for Amber, usable w/ Silk and Web.",
+  "version": "0.1.0",
+  "homepage": "https://lolg.it/herby/mini-mvp",
+  "author": {
+    "name": "Herby Vojčík",
+    "email": "herby@mailbox.sk"
+  },
+  "repository": {
+    "type": "git",
+    "url": "https://lolg.it/herby/mini-mvp.git"
+  },
+  "bugs": {
+    "url": "https://lolg.it/herby/mini-mvp/issues"
+  },
+  "licenses": [
+    {
+      "type": "MIT",
+      "url": "https://lolg.it/herby/mini-mvp/blob/master/LICENSE-MIT"
+    }
+  ],
+  "engines": {
+    "node": ">=4.0.0"
+  },
+  "scripts": {
+    "reset": "npm run clean && npm run init",
+    "clean": "(rm -rf node_modules || rd /s/q node_modules)",
+    "init": "npm install && grunt default devel",
+    "test": "grunt test"
+  },
+  "dependencies": {
+    "@ambers/contrib-jquery": "^0.6.0",
+    "@ambers/contrib-web": "^0.8.2",
+    "@ambers/domite": "^0.9.0",
+    "@ambers/lang": "^0.27.0",
+    "@ambers/silk": "^0.6.0",
+    "es6-promise": "^4.2.6"
+  },
+  "devDependencies": {
+    "@ambers/contrib-legacy": "^0.10.1",
+    "@ambers/helios": "^0.12.2",
+    "@ambers/ide-starter-modal": "^0.2.0",
+    "@ambers/sdk": "^0.12.1",
+    "grunt": "^1.0.3",
+    "grunt-contrib-clean": "^1.1.0",
+    "grunt-contrib-requirejs": "^1.0.0",
+    "grunt-exec": "^3.0.0",
+    "requirejs": "^2.3.5"
+  },
+  "keywords": [
+    "Amber",
+    "Smalltalk"
+  ]
+}

+ 38 - 0
parts.html

@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+    <title>Amber Mini MVP</title>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <meta name="author" content="Herby Vojčík" />
+  </head>
+
+  <body>
+  <p>The sample app contains a few additional parts beyond Amber itself:</p>
+  <ul style="width: 40ex">
+      <li>The common infrastructure: testing.js, devel.js, *.json,
+          .gitignore, big part of Gruntfile.js,
+          <code>devDependencies</code> in package.json
+      </li>
+      <li>Common code (MiniMVP-Tests package).</li>
+      <li>These introductory files: parts.html and uninstall.html</li>
+      <li>Polyfilling: some dependencies in package.json
+          and their use in Gruntfile.js and index.html.
+      </li>
+      <li>The webapp-specific code (MiniMVP package).</li>
+      <li>The webapp-specific dependencies in package.json
+          (modules silk, domite, contrib-web and contrib-jquery).
+      </li>
+      <li>The webapp entry point index.html.</li>
+      <li>The webapp-specific infrastructure: deploy.js
+          and parts of Gruntfile.js that use webapp-specific pieces.
+      </li>
+      <li>The lambda-specific code (MiniMVP-Backend package).</li>
+      <li>The lambda handler file lambda/index.js.</li>
+      <li>The lambda-specific infrastructure: lambda.js
+          and parts of Gruntfile.js that use lambda-specific pieces.
+      </li>
+  </ul>
+  </body>
+
+</html>

+ 59 - 0
src/MiniMVP-Backend.js

@@ -0,0 +1,59 @@
+define(["amber/boot", "require", "amber/core/Kernel-Objects"], function($boot,requirejs){"use strict";
+var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;
+var $pkg = $core.addPackage("MiniMVP-Backend");
+$pkg.innerEval = function (expr) { return eval(expr); };
+$pkg.transport = {"type":"amd","amdNamespace":"mini-mvp"};
+
+$core.addClass("MiniMVPBackend", $globals.Object, [], "MiniMVP-Backend");
+$core.addMethod(
+$core.method({
+selector: "echoEvent:andContext:",
+protocol: "lambda api",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["event", "context"],
+source: "echoEvent: event andContext: context\x0a\x09^ #{\x0a\x09\x09#statusCode -> 200.\x0a\x09\x09#headers->#{'Access-Control-Allow-Origin' -> '*'}.\x0a\x09\x09#body -> (JSON stringify: #{#event -> event. #context -> context})\x0a\x09}",
+referencedClasses: ["JSON"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["stringify:"]
+}, function ($methodClass){ return function (event,context){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $globals.HashedCollection._newFromPairs_(["statusCode",(200),"headers",$globals.HashedCollection._newFromPairs_(["Access-Control-Allow-Origin","*"]),"body",$recv($globals.JSON)._stringify_($globals.HashedCollection._newFromPairs_(["event",event,"context",context]))]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"echoEvent:andContext:",{event:event,context:context})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVPBackend);
+
+$core.addMethod(
+$core.method({
+selector: "status",
+protocol: "lambda api",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "status\x0a\x09^ #{\x0a\x09\x09#statusCode -> 200.\x0a\x09\x09#headers->#{'Access-Control-Allow-Origin' -> '*'}.\x0a\x09\x09#body -> (JSON stringify: #{#ok -> true. #amber -> Smalltalk version. #node -> process version})\x0a\x09}",
+referencedClasses: ["JSON", "Smalltalk"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["stringify:", "version"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $globals.HashedCollection._newFromPairs_(["statusCode",(200),"headers",$globals.HashedCollection._newFromPairs_(["Access-Control-Allow-Origin","*"]),"body",$recv($globals.JSON)._stringify_($globals.HashedCollection._newFromPairs_(["ok",true,"amber",[$recv($globals.Smalltalk)._version()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["version"]=1
+//>>excludeEnd("ctx");
+][0],"node",$recv(process)._version()]))]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"status",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVPBackend);
+
+
+});

+ 23 - 0
src/MiniMVP-Backend.st

@@ -0,0 +1,23 @@
+Smalltalk createPackage: 'MiniMVP-Backend'!
+Object subclass: #MiniMVPBackend
+	slots: {}
+	package: 'MiniMVP-Backend'!
+
+!MiniMVPBackend methodsFor: 'lambda api'!
+
+echoEvent: event andContext: context
+	^ #{
+		#statusCode -> 200.
+		#headers->#{'Access-Control-Allow-Origin' -> '*'}.
+		#body -> (JSON stringify: #{#event -> event. #context -> context})
+	}
+!
+
+status
+	^ #{
+		#statusCode -> 200.
+		#headers->#{'Access-Control-Allow-Origin' -> '*'}.
+		#body -> (JSON stringify: #{#ok -> true. #amber -> Smalltalk version. #node -> process version})
+	}
+! !
+

+ 9 - 0
src/MiniMVP-Tests.js

@@ -0,0 +1,9 @@
+define(["amber/boot", "require", "amber/core/SUnit"], function($boot,requirejs){"use strict";
+var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;
+var $pkg = $core.addPackage("MiniMVP-Tests");
+$pkg.innerEval = function (expr) { return eval(expr); };
+$pkg.transport = {"type":"amd","amdNamespace":"mini-mvp"};
+
+$core.addClass("MiniMVPTest", $globals.TestCase, [], "MiniMVP-Tests");
+
+});

+ 5 - 0
src/MiniMVP-Tests.st

@@ -0,0 +1,5 @@
+Smalltalk createPackage: 'MiniMVP-Tests'!
+TestCase subclass: #MiniMVPTest
+	instanceVariableNames: ''
+	package: 'MiniMVP-Tests'!
+

+ 375 - 0
src/MiniMVP.js

@@ -0,0 +1,375 @@
+define(["amber/boot", "require", "amber/core/Kernel-Objects"], function($boot,requirejs){"use strict";
+var $core=$boot.api,nil=$boot.nilAsValue,$nil=$boot.nilAsReceiver,$recv=$boot.asReceiver,$globals=$boot.globals;
+var $pkg = $core.addPackage("MiniMVP");
+$pkg.innerEval = function (expr) { return eval(expr); };
+$pkg.imports = ["amber/web/Web", "amber/web/Web-JQuery", "silk/Silk"];
+//>>excludeStart("imports", pragmas.excludeImports);
+$pkg.isReady = new Promise(function (resolve, reject) { requirejs(["amber/web/Web", "amber/web/Web-JQuery", "silk/Silk"], function () {resolve();}, reject); });
+//>>excludeEnd("imports");
+$pkg.transport = {"type":"amd","amdNamespace":"mini-mvp"};
+
+$core.addClass("MiniMVP", $globals.Object, [], "MiniMVP");
+$core.addMethod(
+$core.method({
+selector: "alert:after:",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["aString", "aNumberOfMilliseconds"],
+source: "alert: aString after: aNumberOfMilliseconds\x0a\x09[ Terminal alert: aString ] valueWithTimeout: aNumberOfMilliseconds",
+referencedClasses: ["Terminal"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["valueWithTimeout:", "alert:"]
+}, function ($methodClass){ return function (aString,aNumberOfMilliseconds){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($globals.Terminal)._alert_(aString);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))._valueWithTimeout_(aNumberOfMilliseconds);
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"alert:after:",{aString:aString,aNumberOfMilliseconds:aNumberOfMilliseconds})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "augmentPage",
+protocol: "starting",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "augmentPage\x0a    '#lambda-status' asSilk on: #click bind: [ self doInspectStatus ].\x0a\x09'#console-log' asSilk on: #click bind: [ self doConsoleLog ].\x0a\x09'#timeout-alert' asSilk on: #click bind: [ self alert: 'REMINDER!' after: 5000 ].\x0a\x09'#amber-with' asBrush onClick: [ self doAmberWith ].\x0a\x09'#silk-tag' asSilk on: #click bind: [ self doSilkTAG ].\x0a\x09'#jquery-append' asJQuery click: [ self doJQueryAppend ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["on:bind:", "asSilk", "doInspectStatus", "doConsoleLog", "alert:after:", "onClick:", "asBrush", "doAmberWith", "doSilkTAG", "click:", "asJQuery", "doJQueryAppend"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+[$recv(["#lambda-status"._asSilk()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["asSilk"]=1
+//>>excludeEnd("ctx");
+][0])._on_bind_("click",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._doInspectStatus();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["on:bind:"]=1
+//>>excludeEnd("ctx");
+][0];
+[$recv(["#console-log"._asSilk()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["asSilk"]=2
+//>>excludeEnd("ctx");
+][0])._on_bind_("click",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._doConsoleLog();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,2)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["on:bind:"]=2
+//>>excludeEnd("ctx");
+][0];
+[$recv(["#timeout-alert"._asSilk()
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["asSilk"]=3
+//>>excludeEnd("ctx");
+][0])._on_bind_("click",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._alert_after_("REMINDER!",(5000));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,3)});
+//>>excludeEnd("ctx");
+}))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["on:bind:"]=3
+//>>excludeEnd("ctx");
+][0];
+$recv("#amber-with"._asBrush())._onClick_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._doAmberWith();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,4)});
+//>>excludeEnd("ctx");
+}));
+$recv("#silk-tag"._asSilk())._on_bind_("click",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._doSilkTAG();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,5)});
+//>>excludeEnd("ctx");
+}));
+$recv("#jquery-append"._asJQuery())._click_((function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $self._doJQueryAppend();
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,6)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"augmentPage",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "doAmberWith",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "doAmberWith\x0a\x09'#output-list' asBrush with: [ :html | html li: 'Amber Web #with: added me!' ]",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["with:", "asBrush", "li:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv("#output-list"._asBrush())._with_((function(html){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(html)._li_("Amber Web #with: added me!");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({html:html},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"doAmberWith",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "doConsoleLog",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "doConsoleLog\x0a\x09| greeting target |\x0a\x09greeting := 'Hello'.\x0a\x09target := 'world'.\x0a\x09console log: #{ #greeting -> greeting. #target -> target. #callback -> [ console log: greeting, ', ', target, '!' ] }",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["log:", ","]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+var greeting,target;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+greeting="Hello";
+target="world";
+[$recv(console)._log_($globals.HashedCollection._newFromPairs_(["greeting",greeting,"target",target,"callback",(function(){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv(console)._log_([$recv([$recv($recv(greeting).__comma(", ")).__comma(target)
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx[","]=2
+//>>excludeEnd("ctx");
+][0]).__comma("!")
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx2.sendIdx[","]=1
+//>>excludeEnd("ctx");
+][0]);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({},$ctx1,1)});
+//>>excludeEnd("ctx");
+})]))
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+,$ctx1.sendIdx["log:"]=1
+//>>excludeEnd("ctx");
+][0];
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"doConsoleLog",{greeting:greeting,target:target})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "doInspectStatus",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "doInspectStatus\x0a\x09(self getApi: '/status')\x0a\x09\x09then: {#json. #inspect}\x0a\x09\x09catch: [ :err | Terminal alert: err ]",
+referencedClasses: ["Terminal"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["then:catch:", "getApi:", "alert:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($self._getApi_("/status"))._then_catch_(["json","inspect"],(function(err){
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx2) {
+//>>excludeEnd("ctx");
+return $recv($globals.Terminal)._alert_(err);
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx2) {$ctx2.fillBlock({err:err},$ctx1,1)});
+//>>excludeEnd("ctx");
+}));
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"doInspectStatus",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "doJQueryAppend",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "doJQueryAppend\x0a\x09'#output-list' asJQuery append: '<li>jQuery append added me!</li>'",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["append:", "asJQuery"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv("#output-list"._asJQuery())._append_("<li>jQuery append added me!</li>");
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"doJQueryAppend",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "doSilkTAG",
+protocol: "action",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "doSilkTAG\x0a\x09'#output-list' asSilk LI: 'Silk TAG: added me!'",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["LI:", "asSilk"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv("#output-list"._asSilk())._LI_("Silk TAG: added me!");
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"doSilkTAG",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "endpoint",
+protocol: "backend",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "endpoint\x0a\x09\x22Return something like 'https://<char mess>.execute-api.eu-central-1.amazonaws.com/default'\x22\x0a\x09^ self error: 'Not yet implemented'",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["error:"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $self._error_("Not yet implemented");
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"endpoint",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+$core.addMethod(
+$core.method({
+selector: "getApi:",
+protocol: "backend",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: ["path"],
+source: "getApi: path\x0a\x09^ Platform fetch: self endpoint, path",
+referencedClasses: ["Platform"],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["fetch:", ",", "endpoint"]
+}, function ($methodClass){ return function (path){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+return $recv($globals.Platform)._fetch_($recv($self._endpoint()).__comma(path));
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"getApi:",{path:path})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP);
+
+
+$core.addMethod(
+$core.method({
+selector: "start",
+protocol: "starting",
+//>>excludeStart("ide", pragmas.excludeIdeData);
+args: [],
+source: "start\x0a\x09self new augmentPage",
+referencedClasses: [],
+//>>excludeEnd("ide");
+pragmas: [],
+messageSends: ["augmentPage", "new"]
+}, function ($methodClass){ return function (){
+var self=this,$self=this;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+return $core.withContext(function($ctx1) {
+//>>excludeEnd("ctx");
+$recv($self._new())._augmentPage();
+return self;
+//>>excludeStart("ctx", pragmas.excludeDebugContexts);
+}, function($ctx1) {$ctx1.fill(self,"start",{})});
+//>>excludeEnd("ctx");
+}; }),
+$globals.MiniMVP.a$cls);
+
+});

+ 65 - 0
src/MiniMVP.st

@@ -0,0 +1,65 @@
+Smalltalk createPackage: 'MiniMVP'!
+(Smalltalk packageAt: 'MiniMVP' ifAbsent: [ self error: 'Package not created: MiniMVP' ]) imports: {'amber/web/Web'. 'amber/web/Web-JQuery'. 'silk/Silk'}!
+Object subclass: #MiniMVP
+	slots: {}
+	package: 'MiniMVP'!
+
+!MiniMVP methodsFor: 'action'!
+
+alert: aString after: aNumberOfMilliseconds
+	[ Terminal alert: aString ] valueWithTimeout: aNumberOfMilliseconds
+!
+
+doAmberWith
+	'#output-list' asBrush with: [ :html | html li: 'Amber Web #with: added me!!' ]
+!
+
+doConsoleLog
+	| greeting target |
+	greeting := 'Hello'.
+	target := 'world'.
+	console log: #{ #greeting -> greeting. #target -> target. #callback -> [ console log: greeting, ', ', target, '!!' ] }
+!
+
+doInspectStatus
+	(self getApi: '/status')
+		then: {#json. #inspect}
+		catch: [ :err | Terminal alert: err ]
+!
+
+doJQueryAppend
+	'#output-list' asJQuery append: '<li>jQuery append added me!!</li>'
+!
+
+doSilkTAG
+	'#output-list' asSilk LI: 'Silk TAG: added me!!'
+! !
+
+!MiniMVP methodsFor: 'backend'!
+
+endpoint
+	"Return something like 'https://<char mess>.execute-api.eu-central-1.amazonaws.com/default'"
+	^ self error: 'Not yet implemented'
+!
+
+getApi: path
+	^ Platform fetch: self endpoint, path
+! !
+
+!MiniMVP methodsFor: 'starting'!
+
+augmentPage
+    '#lambda-status' asSilk on: #click bind: [ self doInspectStatus ].
+	'#console-log' asSilk on: #click bind: [ self doConsoleLog ].
+	'#timeout-alert' asSilk on: #click bind: [ self alert: 'REMINDER!!' after: 5000 ].
+	'#amber-with' asBrush onClick: [ self doAmberWith ].
+	'#silk-tag' asSilk on: #click bind: [ self doSilkTAG ].
+	'#jquery-append' asJQuery click: [ self doJQueryAppend ]
+! !
+
+!MiniMVP class methodsFor: 'starting'!
+
+start
+	self new augmentPage
+! !
+

+ 10 - 0
testing.js

@@ -0,0 +1,10 @@
+define([
+    './deploy',
+    './lambda',
+    'amber/core/SUnit',
+    // --- packages used only during automated testing begin here ---
+    'mini-mvp/MiniMVP-Tests'
+    // --- packages used only during automated testing end here ---
+], function (amber) {
+    return amber;
+});

+ 57 - 0
uninstall.html

@@ -0,0 +1,57 @@
+<!DOCTYPE html>
+<html>
+
+  <head>
+    <title>Amber Mini MVP</title>
+    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
+    <meta name="author" content="Herby Vojčík" />
+  </head>
+
+  <body>
+  <p>Once you start developing your own app / lib / backend, you should
+      remove all unnecessary parts left from the sample app.</p>
+  <ul style="width: 40ex">
+      <li>You can choose to polyfill less or not at all in Gruntfile.js / index.html,
+          as a consequence polyfilling dependencies can be uninstalled.
+      </li>
+      <li>In case you don't need webapp part at all, you can remove
+          deploy.js (and its appearance in testing.js), MiniMVP package,
+          webapp-specific dependencies in package.json
+          (modules silk, domite, contrib-web and contrib-jquery),
+          remove "start the app" call in index.html
+          and remove webapp-specific parts in Gruntfile.js.
+      </li>
+      <li>You should keep index.html (you need it to develop),
+          but in cleaned form (see below).</li>
+      <li>In index.html <code>&lt;body&gt;</code> tag,
+          everything should be removed, except
+          the <code>&lt;script&gt;</code> tag
+          that initializes Amber and starts your app;
+          then you can add elements specific to your own webapp
+          to the page (or leave it bare if case in previous item happened).</li>
+      <li>In case you will develop the webapp part,
+          you will probably only use one set of libraries presented
+          (there are more options so you can see and choose).
+          So either you keep silk which depends on domite,
+          or you keep contrib-web which depends on contrib-jquery.
+          Your Gruntfile.js may contain references
+          to these modules in <code>libraries</code>
+          field(s). Fix those fields to not contain removed libraries.</li>
+      <li>In case you are developing an Amber library (eg. not the app),
+          you'll probably end up not needing any of the aformentioned libraries
+          (depends on how UI-heavy your library are),
+          and you likely can remove the intro MiniMVP class
+          and the "start the app" call in index.html.
+      </li>
+      <li>In case you don't need the lambda / backend part at all,
+          you can remove lambda.js (and its appearance in testing.js),
+          MiniMVP-Backend package, lambda directory
+          and remove lambda-specific parts of Gruntfile.js.
+      </li>
+      <li>These introductory files: parts.html and uninstall.html can be deleted.</li>
+  </ul>
+  <p>As the last step, make sure the dependency tree is in a correct state
+      by recreating it: run <code>npm run reset</code> from your CLI.</p>
+  </body>
+
+</html>