aladdin 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +16 -10
- data/assets/favicon.ico +0 -0
- data/assets/images/graphic.png +0 -0
- data/assets/images/no_gravatar.gif +0 -0
- data/assets/javascripts/app.js +76 -0
- data/bin/aladdin +2 -17
- data/lib/aladdin.rb +58 -13
- data/lib/aladdin/app.rb +27 -7
- data/lib/aladdin/cli.rb +29 -0
- data/lib/aladdin/commands/new.rb +16 -0
- data/lib/aladdin/commands/server.rb +10 -0
- data/lib/aladdin/mixin/logger.rb +26 -0
- data/lib/aladdin/mixin/weak_comparator.rb +60 -0
- data/lib/aladdin/render/error.rb +20 -0
- data/lib/aladdin/render/image.rb +54 -0
- data/lib/aladdin/render/markdown.rb +111 -8
- data/lib/aladdin/render/multi.rb +40 -0
- data/lib/aladdin/render/navigation.rb +34 -0
- data/lib/aladdin/render/problem.rb +114 -0
- data/lib/aladdin/render/sanitize.rb +3 -1
- data/lib/aladdin/render/short.rb +30 -0
- data/lib/aladdin/render/table.rb +109 -0
- data/lib/aladdin/render/template.rb +32 -0
- data/lib/aladdin/submission.rb +92 -0
- data/lib/aladdin/version.rb +1 -1
- data/skeleton/images/graphic.png +0 -0
- data/skeleton/index.md +3 -0
- data/views/haml/exe.haml +5 -0
- data/views/haml/img.haml +6 -0
- data/views/haml/layout.haml +44 -12
- data/views/haml/multi.haml +18 -0
- data/views/haml/nav.haml +5 -0
- data/views/haml/short.haml +14 -0
- data/views/haml/table.haml +30 -0
- data/views/scss/app.scss +50 -44
- data/views/scss/mathjax.scss +5 -0
- data/views/scss/pygment.scss +9 -6
- metadata +47 -18
- data/assets/images/foundation/orbit/bullets.jpg +0 -0
- data/assets/images/foundation/orbit/left-arrow-small.png +0 -0
- data/assets/images/foundation/orbit/left-arrow.png +0 -0
- data/assets/images/foundation/orbit/loading.gif +0 -0
- data/assets/images/foundation/orbit/mask-black.png +0 -0
- data/assets/images/foundation/orbit/pause-black.png +0 -0
- data/assets/images/foundation/orbit/right-arrow-small.png +0 -0
- data/assets/images/foundation/orbit/right-arrow.png +0 -0
- data/assets/images/foundation/orbit/rotator-black.png +0 -0
- data/assets/images/foundation/orbit/timer-black.png +0 -0
- data/views/haml/index.haml +0 -43
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
[](https://gemnasium.com/jimjh/aladdin)
|
4
4
|
[](https://codeclimate.com/github/jimjh/aladdin)
|
5
5
|
|
6
|
-
Generates
|
6
|
+
Generates lessons using the markdown document and tests provided by the author.
|
7
7
|
|
8
8
|
## Installation
|
9
9
|
Add this line to your application's Gemfile:
|
@@ -21,18 +21,24 @@ Or install it yourself as:
|
|
21
21
|
$> gem install aladdin
|
22
22
|
|
23
23
|
## Usage
|
24
|
-
Create a new directory for your notes _e.g._ `
|
25
|
-
directory,
|
26
|
-
like:
|
24
|
+
Create a new directory for your notes _e.g._ `lesson_0`. Change into that
|
25
|
+
directory, then execute:
|
27
26
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
03-the-end.md
|
27
|
+
```sh
|
28
|
+
$> aladdin new
|
29
|
+
```
|
32
30
|
|
33
|
-
Finally, execute aladdin to launch the Sinatra server:
|
31
|
+
Update `index.md` and provide your unit tests in the lesson directory. Finally, execute aladdin to launch the Sinatra server:
|
34
32
|
|
35
|
-
|
33
|
+
```sh
|
34
|
+
$> aladdin server
|
35
|
+
```
|
36
|
+
|
37
|
+
Note that the following directory names are reserved:
|
38
|
+
|
39
|
+
- javascripts
|
40
|
+
- stylesheets
|
41
|
+
- verify
|
36
42
|
|
37
43
|
## Contributing
|
38
44
|
|
data/assets/favicon.ico
ADDED
Binary file
|
Binary file
|
Binary file
|
@@ -0,0 +1,76 @@
|
|
1
|
+
/* ========================================================================
|
2
|
+
* app.js
|
3
|
+
* http://github.com/jimjh/aladdin
|
4
|
+
* ========================================================================
|
5
|
+
* Copyright (c) 2012 Carnegie Mellon University
|
6
|
+
* License: https://raw.github.com/jimjh/aladdin/master/LICENSE
|
7
|
+
* ========================================================================
|
8
|
+
*/
|
9
|
+
/*jshint strict:true unused:true*/
|
10
|
+
/*global $*/
|
11
|
+
|
12
|
+
;(function () {
|
13
|
+
'use strict';
|
14
|
+
|
15
|
+
// TODO: refactor
|
16
|
+
|
17
|
+
// Shows results of last submission at the given button and form.
|
18
|
+
var showResult = function(button, form) {
|
19
|
+
return function(result) {
|
20
|
+
switch(result) {
|
21
|
+
case true:
|
22
|
+
button.addClass('success');
|
23
|
+
form.removeClass('error');
|
24
|
+
break;
|
25
|
+
case false:
|
26
|
+
button.removeClass('success');
|
27
|
+
form.addClass('error');
|
28
|
+
break;
|
29
|
+
default:
|
30
|
+
$.each(result, function(i, row) {
|
31
|
+
$.each(row, function(j, cell) {
|
32
|
+
var input = form.find("input[name='answer["+i+"]["+j+"]']");
|
33
|
+
if (cell) input.addClass('success'); else input.addClass('error');
|
34
|
+
});
|
35
|
+
});
|
36
|
+
}
|
37
|
+
};
|
38
|
+
};
|
39
|
+
|
40
|
+
// Adds click listeners to the submit buttons.
|
41
|
+
var observeSubmitButton = function() {
|
42
|
+
|
43
|
+
$('a.button.submit').click(function(e) {
|
44
|
+
var button = $(e.target);
|
45
|
+
var form = button.parents('form');
|
46
|
+
var id = form.find('input.q-id').val();
|
47
|
+
$.post('/verify/quiz/' + id, form.serialize(), showResult(button, form));
|
48
|
+
return false;
|
49
|
+
});
|
50
|
+
|
51
|
+
};
|
52
|
+
|
53
|
+
// Adds click listeners to the run buttons.
|
54
|
+
// var run = function() {
|
55
|
+
// $('a.button.run').click(function(e){
|
56
|
+
// var form = $(e.target).parents('form');
|
57
|
+
// var raw = form.find('.ex-raw').val();
|
58
|
+
// var id = form.find('.ex-id').val();
|
59
|
+
// $.post('/verify/code/' + id, raw,
|
60
|
+
// function(data) { console.log(data); }
|
61
|
+
// );
|
62
|
+
// return false;
|
63
|
+
// });
|
64
|
+
// };
|
65
|
+
|
66
|
+
var genie = {
|
67
|
+
|
68
|
+
launch: function() {
|
69
|
+
observeSubmitButton();
|
70
|
+
}
|
71
|
+
|
72
|
+
};
|
73
|
+
|
74
|
+
$(genie.launch());
|
75
|
+
|
76
|
+
})();
|
data/bin/aladdin
CHANGED
@@ -1,20 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
$:.unshift File.join
|
3
|
+
$:.unshift File.join File.dirname(__FILE__), *%w(.. lib)
|
4
4
|
|
5
|
-
require 'aladdin'
|
6
|
-
require 'optparse'
|
7
|
-
|
8
|
-
opt_parser = OptionParser.new do |opts|
|
9
|
-
|
10
|
-
opts.banner = 'Usage: aladdin /path/to/tutorial/directory'
|
11
|
-
|
12
|
-
opts.on_tail('--version', 'Display current gem version.') do
|
13
|
-
puts 'aladdin v' + Aladdin::VERSION
|
14
|
-
exit 0
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
opt_parser.parse!
|
19
|
-
|
20
|
-
Aladdin.launch from: ARGV[0]
|
5
|
+
require 'aladdin/cli'
|
data/lib/aladdin.rb
CHANGED
@@ -4,27 +4,66 @@ require 'zurb-foundation'
|
|
4
4
|
require 'albino'
|
5
5
|
require 'haml'
|
6
6
|
require 'redcarpet'
|
7
|
+
require 'htmlentities'
|
7
8
|
require 'sanitize'
|
9
|
+
require 'yaml'
|
10
|
+
require 'json'
|
8
11
|
|
9
|
-
require 'aladdin/
|
12
|
+
require 'aladdin/mixin/logger'
|
13
|
+
require 'aladdin/mixin/weak_comparator'
|
14
|
+
require 'aladdin/submission'
|
10
15
|
require 'aladdin/render/markdown'
|
11
16
|
|
12
|
-
# Aladdin is
|
17
|
+
# Aladdin is a gem that tutorial authors can use to preview and test their
|
18
|
+
# tutorials locally.
|
13
19
|
module Aladdin
|
14
20
|
|
15
|
-
#
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
# Name of configuration file.
|
22
|
+
CONFIG_FILE = '.genie.yml'
|
23
|
+
|
24
|
+
# Default configuration options.
|
25
|
+
DEFAULT_CONFIG = {
|
26
|
+
'verify' => {
|
27
|
+
'bin' => 'make',
|
28
|
+
'arg_prefix' => ''
|
29
|
+
},
|
30
|
+
'title' => 'Lesson X',
|
31
|
+
'description' => 'This is a placeholder description. You should provide your own',
|
32
|
+
'categories' => []
|
33
|
+
}
|
34
|
+
|
35
|
+
class << self
|
36
|
+
|
37
|
+
attr_reader :config, :root
|
38
|
+
|
39
|
+
# Launches the tutorial app using 'thin' as the default webserver.
|
40
|
+
# @option opts [String] from path to author's markdown documents;
|
41
|
+
# defaults to the current working directory
|
42
|
+
def launch(opts = {})
|
43
|
+
@root = opts[:from] || '.'
|
44
|
+
configure
|
45
|
+
Aladdin::App.set :views, Aladdin::VIEWS.merge(markdown: root)
|
46
|
+
Aladdin::App.run!
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
# Reads configuration options from +.genie.yml+ and merges it into
|
52
|
+
# {DEFAULT_CONFIG}.
|
53
|
+
def configure
|
54
|
+
config_file = File.expand_path CONFIG_FILE, root
|
55
|
+
config = File.exists?(config_file) ? YAML.load_file(config_file) : {}
|
56
|
+
@config = DEFAULT_CONFIG.merge(config) { |k, l, r|
|
57
|
+
(l.is_a?(Hash) and r.is_a?(Hash)) ? l.merge(r) : r
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Converts a hash to struct.
|
62
|
+
def to_struct(hash)
|
63
|
+
Struct.new( *(k = hash.keys) ).new( *hash.values_at( *k ) )
|
64
|
+
end
|
22
65
|
|
23
|
-
# Converts a hash to struct.
|
24
|
-
def self.to_struct(hash)
|
25
|
-
Struct.new( *(k = hash.keys) ).new( *hash.values_at( *k ) )
|
26
66
|
end
|
27
|
-
private_class_method :to_struct
|
28
67
|
|
29
68
|
# Paths to different types of views.
|
30
69
|
VIEWS = {
|
@@ -38,6 +77,12 @@ module Aladdin
|
|
38
77
|
assets: File.expand_path('../../assets', __FILE__),
|
39
78
|
).freeze
|
40
79
|
|
80
|
+
# File extension for solution files.
|
81
|
+
DATA_EXT = '.sol'
|
82
|
+
|
83
|
+
# @todo TODO allow configuration?
|
84
|
+
DATA_DIR = Dir.tmpdir
|
85
|
+
|
41
86
|
end
|
42
87
|
|
43
88
|
require 'aladdin/app'
|
data/lib/aladdin/app.rb
CHANGED
@@ -7,15 +7,19 @@ module Aladdin
|
|
7
7
|
# Adapted from https://github.com/jerodsanto/sinatra-foundation-skeleton/
|
8
8
|
class App < Sinatra::Base
|
9
9
|
|
10
|
+
# Default page
|
11
|
+
INDEX = :index
|
12
|
+
|
10
13
|
# Default markdown options.
|
11
14
|
MARKDOWN_OPTIONS = {
|
12
15
|
renderer: Aladdin::Render::HTML,
|
16
|
+
layout_engine: :haml,
|
13
17
|
no_intra_emphasis: true,
|
14
18
|
tables: true,
|
15
19
|
fenced_code_blocks: true,
|
16
20
|
autolink: true,
|
17
21
|
strikethrough: true,
|
18
|
-
|
22
|
+
tables: true,
|
19
23
|
}
|
20
24
|
|
21
25
|
class << self
|
@@ -40,7 +44,7 @@ module Aladdin
|
|
40
44
|
set :public_folder, Aladdin::PATHS.assets
|
41
45
|
end
|
42
46
|
|
43
|
-
# Configures ZURB's compass to compile
|
47
|
+
# Configures ZURB's compass to compile aladdin's scss assets.
|
44
48
|
# @return [void]
|
45
49
|
def configure_compass
|
46
50
|
Compass.configuration do |config|
|
@@ -50,12 +54,12 @@ module Aladdin
|
|
50
54
|
set :scss, Compass.sass_engine_options
|
51
55
|
end
|
52
56
|
|
53
|
-
# Registers redcarpet2 and
|
54
|
-
# the github-flavored markdown as possible.
|
57
|
+
# Registers redcarpet2 and configures aladdin's markdown renderer.
|
55
58
|
# @return [void]
|
56
59
|
def configure_markdown
|
57
|
-
Tilt.register Tilt::RedcarpetTemplate::Redcarpet2,
|
60
|
+
Tilt.register Tilt::RedcarpetTemplate::Redcarpet2, *%w(markdown mkd md)
|
58
61
|
set :markdown, MARKDOWN_OPTIONS
|
62
|
+
set :haml, escape_html: true
|
59
63
|
end
|
60
64
|
|
61
65
|
end
|
@@ -64,10 +68,13 @@ module Aladdin
|
|
64
68
|
# @param block block to call within wrapper
|
65
69
|
def render_or_pass(&block)
|
66
70
|
begin return block.call
|
67
|
-
rescue
|
71
|
+
rescue Exception => e
|
72
|
+
logger.error e.message
|
73
|
+
pass
|
68
74
|
end
|
69
75
|
end
|
70
76
|
|
77
|
+
enable :logging
|
71
78
|
configure_views
|
72
79
|
configure_markdown
|
73
80
|
|
@@ -80,8 +87,21 @@ module Aladdin
|
|
80
87
|
render_or_pass { scss path.to_sym }
|
81
88
|
end
|
82
89
|
|
90
|
+
get '/img/*' do |path|
|
91
|
+
send_file File.join('img', path)
|
92
|
+
end
|
93
|
+
|
83
94
|
get '/*' do |path|
|
84
|
-
|
95
|
+
path = path.empty? ? INDEX : path.to_sym
|
96
|
+
render_or_pass do
|
97
|
+
markdown(path, locals: Aladdin.config)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
post '/verify/:type/:id' do
|
102
|
+
input = request.body.read
|
103
|
+
content_type :json
|
104
|
+
Submission.new(params[:id], params[:type], params, input).verify
|
85
105
|
end
|
86
106
|
|
87
107
|
end
|
data/lib/aladdin/cli.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
|
3
|
+
Signal.trap('INT') { puts; exit(1) }
|
4
|
+
|
5
|
+
case ARGV.first
|
6
|
+
when '--version', '-v'
|
7
|
+
puts "Aladdin #{Aladdin::VERSION}"
|
8
|
+
exit(0)
|
9
|
+
when 'new'
|
10
|
+
ARGV.shift
|
11
|
+
require_relative 'commands/new'
|
12
|
+
when 'server'
|
13
|
+
ARGV.shift
|
14
|
+
require_relative 'commands/server'
|
15
|
+
else
|
16
|
+
puts <<-eos
|
17
|
+
Usage:
|
18
|
+
aladdin COMMAND [options]
|
19
|
+
|
20
|
+
Commands:
|
21
|
+
new # generates the skeleton for a new lesson
|
22
|
+
server # launches a preview server
|
23
|
+
|
24
|
+
Aladdin Options:
|
25
|
+
-v, [--version] # show version number and quit
|
26
|
+
-h, [--help] # show this help message and quit
|
27
|
+
eos
|
28
|
+
exit ['-h', '--help'].include?(ARGV.first) ? 0 : 1
|
29
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'aladdin'
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
# Array of skeleton files to be copied over.
|
6
|
+
SKELETON_FILES = %w(.genie.yml index.md images)
|
7
|
+
|
8
|
+
opt_parser = OptionParser.new do |opts|
|
9
|
+
opts.banner = "Usage: aladdin new [options] [LESSON_PATH]"
|
10
|
+
end
|
11
|
+
opt_parser.parse!
|
12
|
+
|
13
|
+
root = ARGV[0] || Dir.pwd
|
14
|
+
|
15
|
+
Dir.chdir File.join File.dirname(__FILE__), *%w(.. .. .. skeleton)
|
16
|
+
FileUtils.cp_r SKELETON_FILES, root, verbose: true
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Aladdin
|
5
|
+
|
6
|
+
# aladdin-mixin module contains all other mixin modules.
|
7
|
+
module Mixin
|
8
|
+
|
9
|
+
# @example
|
10
|
+
# require 'logger'
|
11
|
+
# logger.info "hey"
|
12
|
+
module Logger
|
13
|
+
|
14
|
+
# Global Logger.
|
15
|
+
LOGGER = ::Logger.new STDOUT
|
16
|
+
|
17
|
+
# Retrieves the global logger.
|
18
|
+
def logger
|
19
|
+
Logger::LOGGER
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# ~*~ encoding: utf-8 ~*~
|
2
|
+
module Aladdin
|
3
|
+
|
4
|
+
module Mixin
|
5
|
+
|
6
|
+
# Provides a richer comparison with weak-typing.
|
7
|
+
# @see #same?
|
8
|
+
module WeakComparator
|
9
|
+
|
10
|
+
# Compares +submitted+ against +saved+ and returns a simple diff. Assumes
|
11
|
+
# that:
|
12
|
+
# - +saved+ is a ruby-marshalled value that carries rich type information
|
13
|
+
# - +submitted+ is a user-submitted value that is either a string or an
|
14
|
+
# hash
|
15
|
+
#
|
16
|
+
# === Booleans
|
17
|
+
# If +saved+ is +true+, then it will accept any +submitted+ value that
|
18
|
+
# begins with 'T' or 't'. Similarly, if +saved+ is +false+, then it will
|
19
|
+
# accept any +submitted+ value that begins with 'F' or 'f'.
|
20
|
+
#
|
21
|
+
# === Numerics
|
22
|
+
# If +saved+ is a numeric, then it will accept any +submitted+ value that
|
23
|
+
# is numerically equivalent to +saved+. For example, if +saved+ is 0,
|
24
|
+
# then +'0.0'+ and +'0'+ will both be accepted.
|
25
|
+
#
|
26
|
+
# @param [String, Hash] submitted
|
27
|
+
# @param [String, Numeric, Boolean, Hash] saved
|
28
|
+
# @return [Boolean, Hash] diff
|
29
|
+
def same?(submitted, saved)
|
30
|
+
case saved
|
31
|
+
when Hash
|
32
|
+
Hash === submitted and same_hash? submitted, saved
|
33
|
+
when String then saved == submitted
|
34
|
+
when Numeric then saved == JSON.parse(%|[#{submitted}]|).first
|
35
|
+
when TrueClass, FalseClass then saved.to_s[0] == submitted.downcase[0]
|
36
|
+
else false end
|
37
|
+
rescue
|
38
|
+
false
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Compares two hash and returns a simple diff. It checks that both
|
44
|
+
# +submitted+ and +saved+ have the same number of elements, and then
|
45
|
+
# compares each pair of elements in order.
|
46
|
+
#
|
47
|
+
# @param [Hash] submitted
|
48
|
+
# @param [Hash] saved
|
49
|
+
# @return [Boolean, Hash] diff
|
50
|
+
# @see #same?
|
51
|
+
def same_hash?(submitted, saved)
|
52
|
+
saved.count == submitted.count and
|
53
|
+
Hash[saved.map { |key, value| [key, same?(submitted[key], value)] }]
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|