tryruby 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +25 -0
- data/Rakefile +1 -0
- data/bin/tryruby +21 -0
- data/lib/tryruby.rb +5 -0
- data/lib/tryruby/challenge.rb +28 -0
- data/lib/tryruby/challenge_builder.rb +32 -0
- data/lib/tryruby/colors.rb +43 -0
- data/lib/tryruby/commands.rb +18 -0
- data/lib/tryruby/default_levels.rb +858 -0
- data/lib/tryruby/level.rb +23 -0
- data/lib/tryruby/level_builder.rb +21 -0
- data/lib/tryruby/next_fix.rb +9 -0
- data/lib/tryruby/runner.rb +19 -0
- data/lib/tryruby/shell.rb +122 -0
- data/lib/tryruby/tutorial.rb +53 -0
- data/lib/tryruby/version.rb +4 -0
- data/spec/challenge_buider_spec.rb +39 -0
- data/spec/challenge_spec.rb +91 -0
- data/spec/colors_spec.rb +69 -0
- data/spec/commands_spec.rb +50 -0
- data/spec/level_builder_spec.rb +21 -0
- data/spec/level_spec.rb +30 -0
- data/spec/next_fix_spec.rb +22 -0
- data/spec/shell_spec.rb +205 -0
- data/spec/spec_helper.rb +42 -0
- data/spec/tutorial_spec.rb +74 -0
- data/tryruby.gemspec +27 -0
- metadata +173 -0
@@ -0,0 +1,23 @@
|
|
1
|
+
module Tryruby
|
2
|
+
# Single tutorial level
|
3
|
+
class Level
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
def initialize(*challenges)
|
7
|
+
@challenges = challenges
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](n)
|
11
|
+
@challenges[n]
|
12
|
+
end
|
13
|
+
|
14
|
+
def each(&block)
|
15
|
+
@challenges.each(&block)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Number of challenges in level
|
19
|
+
def length
|
20
|
+
@challenges.length
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'level'
|
2
|
+
require_relative 'challenge_builder'
|
3
|
+
|
4
|
+
module Tryruby
|
5
|
+
# Single tutorial level builder
|
6
|
+
class LevelBuilder
|
7
|
+
def initialize
|
8
|
+
@challenges = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def challenge(&block)
|
12
|
+
builder = ChallengeBuilder.new
|
13
|
+
builder.instance_eval(&block)
|
14
|
+
@challenges << builder.challenge
|
15
|
+
end
|
16
|
+
|
17
|
+
def level
|
18
|
+
Level.new(*@challenges)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'ripl'
|
2
|
+
require 'ripl/multi_line'
|
3
|
+
require_relative 'shell'
|
4
|
+
require_relative 'commands'
|
5
|
+
|
6
|
+
module Tryruby
|
7
|
+
# Startup class of the shell
|
8
|
+
class Runner
|
9
|
+
def self.start(tutorials)
|
10
|
+
Ripl::Shell.include Tryruby::Shell
|
11
|
+
Ripl::Commands.include Tryruby::Commands
|
12
|
+
tutorials.each do |tutorial|
|
13
|
+
tutorial.each { |level| Ripl.shell.levels << level }
|
14
|
+
Ripl::Commands.include tutorial.commands
|
15
|
+
end
|
16
|
+
Ripl.start
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require_relative 'colors'
|
3
|
+
require_relative 'next_fix'
|
4
|
+
|
5
|
+
module Tryruby
|
6
|
+
# Shell extension
|
7
|
+
module Shell
|
8
|
+
include Tryruby::NextFix
|
9
|
+
attr_writer :levels
|
10
|
+
|
11
|
+
def before_loop
|
12
|
+
super
|
13
|
+
@level, @challenge = 0, -1
|
14
|
+
next_challenge
|
15
|
+
setup_challenge if @challenge_changed
|
16
|
+
@challenge_changed = false
|
17
|
+
@output = ''
|
18
|
+
end
|
19
|
+
|
20
|
+
def loop_once
|
21
|
+
throw :ripl_exit unless challenge
|
22
|
+
super
|
23
|
+
result = @error_raised ? @error : @result
|
24
|
+
if challenge && \
|
25
|
+
challenge.test(repl_self, result, read_variables, @output)
|
26
|
+
puts Colors.success('Success!')
|
27
|
+
next_challenge
|
28
|
+
end
|
29
|
+
setup_challenge if @challenge_changed
|
30
|
+
@challenge_changed = false
|
31
|
+
end
|
32
|
+
|
33
|
+
def loop_eval(str)
|
34
|
+
stdout_saved = $stdout
|
35
|
+
$stdout = StringIO.new
|
36
|
+
begin
|
37
|
+
super(str)
|
38
|
+
ensure
|
39
|
+
@output = $stdout.string
|
40
|
+
$stdout = stdout_saved
|
41
|
+
puts @output if @output != ''
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def print_eval_error(error)
|
46
|
+
@error = error
|
47
|
+
super(error)
|
48
|
+
end
|
49
|
+
|
50
|
+
def format_error(error)
|
51
|
+
Colors.error(super(error))
|
52
|
+
end
|
53
|
+
|
54
|
+
def format_result(result)
|
55
|
+
Colors.result(super(result))
|
56
|
+
end
|
57
|
+
|
58
|
+
def levels
|
59
|
+
@levels ||= []
|
60
|
+
end
|
61
|
+
|
62
|
+
def level
|
63
|
+
levels[@level] if @level >= 0
|
64
|
+
end
|
65
|
+
|
66
|
+
def challenge
|
67
|
+
level[@challenge] if level && @challenge >= 0
|
68
|
+
end
|
69
|
+
|
70
|
+
def next_challenge
|
71
|
+
@challenge += 1
|
72
|
+
while level && !challenge
|
73
|
+
@challenge = 0
|
74
|
+
@level += 1
|
75
|
+
end
|
76
|
+
@challenge_changed = true if challenge
|
77
|
+
nil
|
78
|
+
end
|
79
|
+
|
80
|
+
def prev_challenge
|
81
|
+
lvl, chall = @level, @challenge - 1
|
82
|
+
while lvl > 0 && chall < 0
|
83
|
+
lvl -= 1
|
84
|
+
chall = levels[lvl].length - 1
|
85
|
+
end
|
86
|
+
return if chall < 0
|
87
|
+
@level, @challenge = lvl, chall
|
88
|
+
@challenge_changed = true
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
|
92
|
+
def help_challenge
|
93
|
+
puts challenge.help if challenge
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def repl_self
|
99
|
+
eval('self', @binding)
|
100
|
+
end
|
101
|
+
|
102
|
+
def read_variables
|
103
|
+
vars = eval('local_variables', @binding).map do |var|
|
104
|
+
[var, @binding.local_variable_get(var)]
|
105
|
+
end
|
106
|
+
Hash[vars]
|
107
|
+
end
|
108
|
+
|
109
|
+
def write_variables(vars)
|
110
|
+
vars.each { |sym, value| @binding.local_variable_set(sym, value) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def setup_challenge
|
114
|
+
return unless challenge
|
115
|
+
vars = read_variables
|
116
|
+
challenge.setup(repl_self, vars)
|
117
|
+
write_variables(vars)
|
118
|
+
puts challenge.setup_source if challenge.display_setup
|
119
|
+
help_challenge
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'level_builder'
|
2
|
+
|
3
|
+
module Tryruby
|
4
|
+
# Base module for level DSL
|
5
|
+
class Tutorial
|
6
|
+
extend Enumerable
|
7
|
+
|
8
|
+
# Commands to be imported
|
9
|
+
module Commands
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@levels = []
|
14
|
+
self.class.each do |block|
|
15
|
+
builder = LevelBuilder.new
|
16
|
+
builder.instance_eval(&block)
|
17
|
+
@levels << builder.level
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def [](i)
|
22
|
+
@levels[i]
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(&block)
|
26
|
+
@levels.each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def length
|
30
|
+
@levels.length
|
31
|
+
end
|
32
|
+
|
33
|
+
def commands
|
34
|
+
self.class::Commands
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.each(&block)
|
38
|
+
levels.each(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.level(&block)
|
42
|
+
levels << block
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.levels
|
46
|
+
@levels ||= []
|
47
|
+
end
|
48
|
+
|
49
|
+
class << self
|
50
|
+
private :level, :levels
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/tryruby/challenge_builder'
|
3
|
+
|
4
|
+
describe Tryruby::ChallengeBuilder do
|
5
|
+
subject { described_class.new }
|
6
|
+
|
7
|
+
it 'passes help' do
|
8
|
+
subject.help <<-EOF
|
9
|
+
HELP! HELP!
|
10
|
+
I'm being oppressed!
|
11
|
+
EOF
|
12
|
+
expect(subject.challenge.help).to eq "HELP! HELP!\nI'm being oppressed!\n"
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'passes test' do
|
16
|
+
called = []
|
17
|
+
subject.test do |result, vars, out|
|
18
|
+
called << [self, result, vars, out]
|
19
|
+
true
|
20
|
+
end
|
21
|
+
expect(subject.challenge.test('self', 42, { foo: 'bar' }, '')).to eq true
|
22
|
+
expect(called).to eq [['self', 42, { foo: 'bar' }, '']]
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'passes setup' do
|
26
|
+
called = []
|
27
|
+
subject.setup do |vars|
|
28
|
+
called << [self, vars]
|
29
|
+
true
|
30
|
+
end
|
31
|
+
expect(subject.challenge.setup('self', foo: 'bar')).to eq true
|
32
|
+
expect(called).to eq [['self', foo: 'bar']]
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'passes display_setup' do
|
36
|
+
subject.display_setup true
|
37
|
+
expect(subject.challenge.display_setup).to eq true
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/tryruby/challenge'
|
3
|
+
|
4
|
+
describe Tryruby::Challenge do
|
5
|
+
let(:help) { '' }
|
6
|
+
let(:test) { nil }
|
7
|
+
let(:setup) { nil }
|
8
|
+
let(:display_setup) { false }
|
9
|
+
|
10
|
+
subject { described_class.new(help, test, setup, display_setup) }
|
11
|
+
|
12
|
+
describe '#help' do
|
13
|
+
let(:help) { 'This is help' }
|
14
|
+
it 'returns help' do
|
15
|
+
expect(subject.help).to eq 'This is help'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '#test' do
|
20
|
+
context 'non-empty test' do
|
21
|
+
called = []
|
22
|
+
let(:test) do
|
23
|
+
proc do |res, vars, out|
|
24
|
+
called << self << res << vars.clone << out
|
25
|
+
vars[:foobar] = 24
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'runs the test' do
|
31
|
+
expect(subject.test('self', 42, vars = { foo: 'bar' }, '')).to eq true
|
32
|
+
expect(called).to eq ['self', 42, { foo: 'bar' }, '']
|
33
|
+
expect(vars[:foobar]).to eq 24
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'empty test' do
|
38
|
+
it 'returns nil' do
|
39
|
+
expect(subject.test(nil, nil, {}, '')).to eq nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe '#setup' do
|
45
|
+
context 'non-empty setup' do
|
46
|
+
called = []
|
47
|
+
let(:setup) do
|
48
|
+
proc do |vars|
|
49
|
+
called << self << vars.clone
|
50
|
+
vars[:foobar] = 24
|
51
|
+
true
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'runs the setup' do
|
56
|
+
expect(subject.setup('self', vars = { foo: 'bar' })).to eq true
|
57
|
+
expect(called).to eq ['self', { foo: 'bar' }]
|
58
|
+
expect(vars[:foobar]).to eq 24
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context 'empty setup' do
|
63
|
+
it 'returns nil' do
|
64
|
+
expect(subject.setup(nil, {})).to eq nil
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#setup_source' do
|
70
|
+
let(:setup) do
|
71
|
+
proc do |vars|
|
72
|
+
puts 42
|
73
|
+
vars[:foo] = vars[:bar]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'formats setup source' do
|
78
|
+
expect(subject.setup_source).to eq <<-EOS
|
79
|
+
puts(42)
|
80
|
+
foo = bar
|
81
|
+
EOS
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe '#display_setup' do
|
86
|
+
let(:display_setup) { true }
|
87
|
+
it 'returns display_setup' do
|
88
|
+
expect(subject.display_setup).to eq true
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/spec/colors_spec.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
require_relative '../lib/tryruby/colors'
|
3
|
+
require 'paint'
|
4
|
+
|
5
|
+
describe Tryruby::Colors do
|
6
|
+
let(:string) { 'test' }
|
7
|
+
subject { described_class }
|
8
|
+
|
9
|
+
context 'module' do
|
10
|
+
it 'formats strings' do
|
11
|
+
expect(subject.black(string)).to eq Paint[string, :black]
|
12
|
+
expect(subject.red(string)).to eq Paint[string, :red]
|
13
|
+
expect(subject.green(string)).to eq Paint[string, :green]
|
14
|
+
expect(subject.yellow(string)).to eq Paint[string, :yellow]
|
15
|
+
expect(subject.blue(string)).to eq Paint[string, :blue]
|
16
|
+
expect(subject.magenta(string)).to eq Paint[string, :magenta]
|
17
|
+
expect(subject.cyan(string)).to eq Paint[string, :cyan]
|
18
|
+
expect(subject.white(string)).to eq Paint[string, :white]
|
19
|
+
expect(subject.bg_black(string)).to eq Paint[string, nil, :black]
|
20
|
+
expect(subject.bg_red(string)).to eq Paint[string, nil, :red]
|
21
|
+
expect(subject.bg_green(string)).to eq Paint[string, nil, :green]
|
22
|
+
expect(subject.bg_yellow(string)).to eq Paint[string, nil, :yellow]
|
23
|
+
expect(subject.bg_blue(string)).to eq Paint[string, nil, :blue]
|
24
|
+
expect(subject.bg_magenta(string)).to eq Paint[string, nil, :magenta]
|
25
|
+
expect(subject.bg_cyan(string)).to eq Paint[string, nil, :cyan]
|
26
|
+
expect(subject.bg_white(string)).to eq Paint[string, nil, :white]
|
27
|
+
expect(subject.reset(string)).to eq Paint[string, :reset]
|
28
|
+
expect(subject.bold(string)).to eq Paint[string, :bold]
|
29
|
+
expect(subject.underline(string)).to eq Paint[string, :underline]
|
30
|
+
expect(subject.inverse(string)).to eq Paint[string, :inverse]
|
31
|
+
expect(subject.hide(string)).to eq Paint[string, :hide]
|
32
|
+
expect(subject.title(string)).to eq Paint[string, :bold, :underline]
|
33
|
+
expect(subject.result(string)).to eq Paint[string, :blue]
|
34
|
+
expect(subject.error(string)).to eq Paint[string, :red, :bold]
|
35
|
+
expect(subject.success(string)).to eq Paint[string, :green, :bold]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'included' do
|
40
|
+
include described_class
|
41
|
+
it 'formats strings' do
|
42
|
+
expect(black(string)).to eq Paint[string, :black]
|
43
|
+
expect(red(string)).to eq Paint[string, :red]
|
44
|
+
expect(green(string)).to eq Paint[string, :green]
|
45
|
+
expect(yellow(string)).to eq Paint[string, :yellow]
|
46
|
+
expect(blue(string)).to eq Paint[string, :blue]
|
47
|
+
expect(magenta(string)).to eq Paint[string, :magenta]
|
48
|
+
expect(cyan(string)).to eq Paint[string, :cyan]
|
49
|
+
expect(white(string)).to eq Paint[string, :white]
|
50
|
+
expect(bg_black(string)).to eq Paint[string, nil, :black]
|
51
|
+
expect(bg_red(string)).to eq Paint[string, nil, :red]
|
52
|
+
expect(bg_green(string)).to eq Paint[string, nil, :green]
|
53
|
+
expect(bg_yellow(string)).to eq Paint[string, nil, :yellow]
|
54
|
+
expect(bg_blue(string)).to eq Paint[string, nil, :blue]
|
55
|
+
expect(bg_magenta(string)).to eq Paint[string, nil, :magenta]
|
56
|
+
expect(bg_cyan(string)).to eq Paint[string, nil, :cyan]
|
57
|
+
expect(bg_white(string)).to eq Paint[string, nil, :white]
|
58
|
+
expect(reset(string)).to eq Paint[string, :reset]
|
59
|
+
expect(bold(string)).to eq Paint[string, :bold]
|
60
|
+
expect(underline(string)).to eq Paint[string, :underline]
|
61
|
+
expect(inverse(string)).to eq Paint[string, :inverse]
|
62
|
+
expect(hide(string)).to eq Paint[string, :hide]
|
63
|
+
expect(title(string)).to eq Paint[string, :bold, :underline]
|
64
|
+
expect(result(string)).to eq Paint[string, :blue]
|
65
|
+
expect(error(string)).to eq Paint[string, :red, :bold]
|
66
|
+
expect(success(string)).to eq Paint[string, :green, :bold]
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|