├── exercises
├── test_exercise
│ ├── problem.md
│ ├── solution
│ │ ├── index.html
│ │ └── solution.js
│ └── exercise.js
└── menu.json
├── README.md
├── .gitignore
├── demo-workshopper.js
├── package.json
└── LICENSE
/exercises/test_exercise/problem.md:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/exercises/menu.json:
--------------------------------------------------------------------------------
1 | [
2 | "TEST_EXERCISE"
3 | ]
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | demo-workshopper
2 | ================
3 |
4 | An example Node.js workshopper lesson.
5 |
--------------------------------------------------------------------------------
/exercises/test_exercise/solution/index.html:
--------------------------------------------------------------------------------
1 |
2 |
Hello Handling
3 |
4 | Hello Handling
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | npm-debug.log
4 | dump.rdb
5 | node_modules
6 | results.tap
7 | results.xml
8 | npm-shrinkwrap.json
9 | .DS_Store
10 | */.DS_Store
11 | */*/.DS_Store
12 | ._*
13 | */._*
14 | */*/._*
--------------------------------------------------------------------------------
/exercises/test_exercise/solution/solution.js:
--------------------------------------------------------------------------------
1 | var Hapi = require('hapi');
2 | var path = require('path');
3 |
4 | var server = Hapi.createServer('localhost', Number(process.argv[2] || 8080));
5 | server.route({
6 | method: 'GET',
7 | path: '/',
8 | handler: {
9 | file: path.join(__dirname, '/index.html')
10 | }
11 | });
12 | server.start();
13 |
--------------------------------------------------------------------------------
/demo-workshopper.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | const workshopper = require('workshopper'),
4 | path = require('path')
5 |
6 | function fpath (f) {
7 | return path.join(__dirname, f)
8 | }
9 |
10 | workshopper({
11 | name : 'demo-workshopper',
12 | title : 'Demo Workshopper',
13 | subtitle : 'Learn how to create a workshopper lesson',
14 | appDir : __dirname,
15 | menuItems : [],
16 | exerciseDir : fpath('./exercises/')
17 | })
18 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo-workshopper",
3 | "version": "1.0.4",
4 | "repository": {
5 | "type": "git",
6 | "url": "git://github.com/linclark/demo-workshopper"
7 | },
8 | "license": "BSD-3-Clause",
9 | "bin": "./demo-workshopper.js",
10 | "dependencies": {
11 | "workshopper": "1.x",
12 | "workshopper-exercise": "^0.2.2",
13 | "workshopper-wrappedexec": "^0.1.1",
14 | "workshopper-boilerplate": "0.0.1",
15 | "hyperquest": "0.1.x",
16 | "hapi": "6.x.x",
17 | "colors-tmpl": "^1.0.0",
18 | "through2": "^0.4.1",
19 | "bl": "^0.8.0"
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014, Lin Clark
2 | All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | * Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | * Redistributions in binary form must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided with the distribution.
13 |
14 | * Neither the name of the {organization} nor the names of its
15 | contributors may be used to endorse or promote products derived from
16 | this software without specific prior written permission.
17 |
18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/exercises/test_exercise/exercise.js:
--------------------------------------------------------------------------------
1 | var through2 = require('through2');
2 | var hyperquest = require('hyperquest');
3 | var bl = require('bl');
4 | var exercise = require('workshopper-exercise')();
5 | var filecheck = require('workshopper-exercise/filecheck');
6 | var execute = require('workshopper-exercise/execute');
7 | var comparestdout = require('workshopper-exercise/comparestdout');
8 |
9 | // the output will be long lines so make the comparison take that into account
10 | exercise.longCompareOutput = true;
11 |
12 | // checks that the submission file actually exists
13 | exercise = filecheck(exercise);
14 |
15 | // execute the solution and submission in parallel with spawn()
16 | exercise = execute(exercise);
17 |
18 | function rndport() {
19 | return 1024 + Math.floor(Math.random() * 64511);
20 | }
21 |
22 | // set up the data file to be passed to the submission
23 | exercise.addSetup(function (mode, callback) {
24 |
25 | this.submissionPort = rndport();
26 | this.solutionPort = this.submissionPort + 1;
27 |
28 | this.submissionArgs = [this.submissionPort];
29 | this.solutionArgs = [this.solutionPort];
30 |
31 | process.nextTick(callback);
32 | });
33 |
34 | // add a processor for both run and verify calls, added *before*
35 | // the comparestdout processor so we can mess with the stdouts
36 | exercise.addProcessor(function (mode, callback) {
37 |
38 | this.submissionStdout.pipe(process.stdout);
39 |
40 | // replace stdout with our own streams
41 | this.submissionStdout = through2();
42 | if (mode == 'verify') {
43 | this.solutionStdout = through2();
44 | }
45 |
46 | setTimeout(query.bind(this, mode), 500);
47 |
48 | process.nextTick(function () {
49 | callback(null, true)
50 | });
51 | });
52 |
53 | // delayed for 500ms to wait for servers to start so we can start
54 | // playing with them
55 | function query (mode) {
56 | var exercise = this
57 |
58 | function verify (port, stream) {
59 |
60 | var url = 'http://localhost:' + port;
61 |
62 | function error (err) {
63 | exercise.emit('fail', 'Error connecting to http://localhost:' + port + ': ' + err.code)
64 | }
65 |
66 | hyperquest.get(url)
67 | .on('error', error)
68 | .pipe(bl(function (err, data) {
69 |
70 | if (err)
71 | return stream.emit('error', err)
72 |
73 | stream.write(data.toString() + '\n');
74 | stream.end();
75 | }));
76 | }
77 |
78 | verify(this.submissionPort, this.submissionStdout)
79 |
80 | if (mode == 'verify') {
81 | verify(this.solutionPort, this.solutionStdout);
82 | }
83 | }
84 |
85 | // compare stdout of solution and submission
86 | exercise = comparestdout(exercise)
87 |
88 | module.exports = exercise;
89 |
--------------------------------------------------------------------------------