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.
Files changed (49) hide show
  1. data/README.md +16 -10
  2. data/assets/favicon.ico +0 -0
  3. data/assets/images/graphic.png +0 -0
  4. data/assets/images/no_gravatar.gif +0 -0
  5. data/assets/javascripts/app.js +76 -0
  6. data/bin/aladdin +2 -17
  7. data/lib/aladdin.rb +58 -13
  8. data/lib/aladdin/app.rb +27 -7
  9. data/lib/aladdin/cli.rb +29 -0
  10. data/lib/aladdin/commands/new.rb +16 -0
  11. data/lib/aladdin/commands/server.rb +10 -0
  12. data/lib/aladdin/mixin/logger.rb +26 -0
  13. data/lib/aladdin/mixin/weak_comparator.rb +60 -0
  14. data/lib/aladdin/render/error.rb +20 -0
  15. data/lib/aladdin/render/image.rb +54 -0
  16. data/lib/aladdin/render/markdown.rb +111 -8
  17. data/lib/aladdin/render/multi.rb +40 -0
  18. data/lib/aladdin/render/navigation.rb +34 -0
  19. data/lib/aladdin/render/problem.rb +114 -0
  20. data/lib/aladdin/render/sanitize.rb +3 -1
  21. data/lib/aladdin/render/short.rb +30 -0
  22. data/lib/aladdin/render/table.rb +109 -0
  23. data/lib/aladdin/render/template.rb +32 -0
  24. data/lib/aladdin/submission.rb +92 -0
  25. data/lib/aladdin/version.rb +1 -1
  26. data/skeleton/images/graphic.png +0 -0
  27. data/skeleton/index.md +3 -0
  28. data/views/haml/exe.haml +5 -0
  29. data/views/haml/img.haml +6 -0
  30. data/views/haml/layout.haml +44 -12
  31. data/views/haml/multi.haml +18 -0
  32. data/views/haml/nav.haml +5 -0
  33. data/views/haml/short.haml +14 -0
  34. data/views/haml/table.haml +30 -0
  35. data/views/scss/app.scss +50 -44
  36. data/views/scss/mathjax.scss +5 -0
  37. data/views/scss/pygment.scss +9 -6
  38. metadata +47 -18
  39. data/assets/images/foundation/orbit/bullets.jpg +0 -0
  40. data/assets/images/foundation/orbit/left-arrow-small.png +0 -0
  41. data/assets/images/foundation/orbit/left-arrow.png +0 -0
  42. data/assets/images/foundation/orbit/loading.gif +0 -0
  43. data/assets/images/foundation/orbit/mask-black.png +0 -0
  44. data/assets/images/foundation/orbit/pause-black.png +0 -0
  45. data/assets/images/foundation/orbit/right-arrow-small.png +0 -0
  46. data/assets/images/foundation/orbit/right-arrow.png +0 -0
  47. data/assets/images/foundation/orbit/rotator-black.png +0 -0
  48. data/assets/images/foundation/orbit/timer-black.png +0 -0
  49. data/views/haml/index.haml +0 -43
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  [![Dependency Status](https://gemnasium.com/jimjh/aladdin.png)](https://gemnasium.com/jimjh/aladdin)
4
4
  [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/jimjh/aladdin)
5
5
 
6
- Generates tutorials using the set of markdown documents provided by the author.
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._ `my_tutorial`. Change into that
25
- directory, and create your notes using GitHub-Flavored Markdown. It might look
26
- like:
24
+ Create a new directory for your notes _e.g._ `lesson_0`. Change into that
25
+ directory, then execute:
27
26
 
28
- my_tutorial/
29
- 01-introduction.md
30
- 02-the-beginning.md
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
- $> aladdin
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
 
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(File.dirname(__FILE__), '..', 'lib')
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/render/sanitize'
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 for tutorial apps.
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
- # Launches the tutorial app using 'thin' as the default webserver.
16
- # @option opts [String] from path to author's markdown documents;
17
- # defaults to the current working directory
18
- def self.launch(opts = {})
19
- Aladdin::App.set :views, Aladdin::VIEWS.merge(markdown: opts[:from] || '.')
20
- Aladdin::App.run!
21
- end
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
- layout_engine: :haml
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 laddin's scss assets.
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 laddin's markdown renderer to be as close to
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, 'markdown', 'mkd', 'md'
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 pass
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
- render_or_pass { markdown path.to_sym }
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
@@ -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,10 @@
1
+ # ~*~ encoding: utf-8 ~*~
2
+ require 'aladdin'
3
+ require 'optparse'
4
+
5
+ opt_parser = OptionParser.new do |opts|
6
+ opts.banner = "Usage: aladdin server [options] [LESSON_PATH]"
7
+ end
8
+ opt_parser.parse!
9
+
10
+ Aladdin.launch from: ARGV[0]
@@ -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