tryruby 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2b4cc48e47a9d65df21ad93130190174011d3fdd
|
4
|
+
data.tar.gz: df7553c3ae34839e4102efd03c2af1acce62d92a
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d5dbef89b29e262fe4afa77e16e44e43c84a2ad5725fba3bfdc1459ed1d803bfcf060a338e5dc2d851b2a0d9c40315bda9dc8d7db68b9395579c38482a481e08
|
7
|
+
data.tar.gz: 450ee857525876adfebf6fa40fcc5733194cf48a4ae1e372b8c27a1d54673bcf9e2f2312808a6281decb49602a415f1d60731073ce5741b8828c73ace5b12e6b
|
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2015 Jakub Červenka
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Try Ruby!
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/cvut/tryruby.svg?branch=master)](https://travis-ci.org/cvut/tryruby)
|
4
|
+
|
5
|
+
A command line version of [tryruby.org](http://tryruby.org/) in case that the website goes down or is having troubles with too many simultaneous connections you can use this REPL shell to try ruby!
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
$ gem install tryruby
|
10
|
+
|
11
|
+
## Usage
|
12
|
+
|
13
|
+
$ tryruby
|
14
|
+
|
15
|
+
## Contributing
|
16
|
+
|
17
|
+
1. Fork it ( https://github.com/cvut/tryruby/fork )
|
18
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
19
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
20
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
21
|
+
5. Create a new Pull Request
|
22
|
+
|
23
|
+
## TODO
|
24
|
+
|
25
|
+
* convert Why's drawings to ASCII art
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
data/bin/tryruby
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# coding: utf-8
|
3
|
+
require_relative File.expand_path('../lib/tryruby/runner', File.dirname(__FILE__))
|
4
|
+
|
5
|
+
ARGV << 'Tryruby::DefaultLevels' unless ARGV.length > 0
|
6
|
+
tutorials = ARGV.map do |tutorial|
|
7
|
+
snake_case = tutorial.gsub(/::/, '/')
|
8
|
+
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
9
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
10
|
+
.tr('-', '_')
|
11
|
+
.downcase
|
12
|
+
begin
|
13
|
+
require snake_case
|
14
|
+
rescue LoadError
|
15
|
+
require_relative File.expand_path("../lib/#{snake_case}", File.dirname(__FILE__))
|
16
|
+
end
|
17
|
+
Object.const_get(tutorial).new
|
18
|
+
end
|
19
|
+
ARGV.clear
|
20
|
+
|
21
|
+
Tryruby::Runner.start(tutorials)
|
data/lib/tryruby.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'sourcify'
|
2
|
+
|
3
|
+
module Tryruby
|
4
|
+
# Single tutorial challenge
|
5
|
+
class Challenge
|
6
|
+
attr_reader :help, :display_setup
|
7
|
+
|
8
|
+
def initialize(help, test = nil, setup = nil, display_setup = false)
|
9
|
+
@help, @test, @setup, @display_setup = help, test, setup, display_setup
|
10
|
+
end
|
11
|
+
|
12
|
+
def test(repl, result, vars, output)
|
13
|
+
repl.instance_exec(result, vars, output, &@test) if @test
|
14
|
+
end
|
15
|
+
|
16
|
+
def setup(repl, vars)
|
17
|
+
repl.instance_exec(vars, &@setup) if @setup
|
18
|
+
end
|
19
|
+
|
20
|
+
# Challenge setup as text
|
21
|
+
def setup_source
|
22
|
+
return '' unless @setup
|
23
|
+
vars_name = @setup.parameters[0][1]
|
24
|
+
source = @setup.to_source(strip_enclosure: true)
|
25
|
+
source.gsub(/#{vars_name}\[:([^\]]+)\]/, '\1')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require_relative 'challenge'
|
2
|
+
require_relative 'colors'
|
3
|
+
|
4
|
+
module Tryruby
|
5
|
+
# Single challenge level builder
|
6
|
+
class ChallengeBuilder
|
7
|
+
include Colors
|
8
|
+
|
9
|
+
attr_writer :help, :test, :setup, :display_setup
|
10
|
+
alias_method :help, :help=
|
11
|
+
alias_method :display_setup, :display_setup=
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@help = ''
|
15
|
+
@test = nil
|
16
|
+
@setup = nil
|
17
|
+
@display_setup = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def test(&block)
|
21
|
+
self.test = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def setup(&block)
|
25
|
+
self.setup = block
|
26
|
+
end
|
27
|
+
|
28
|
+
def challenge
|
29
|
+
Challenge.new(@help, @test, @setup, @display_setup)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'paint'
|
2
|
+
|
3
|
+
module Tryruby
|
4
|
+
# ANSI formatting shortcuts
|
5
|
+
module Colors
|
6
|
+
Paint::SHORTCUTS[:tryruby] = {
|
7
|
+
black: Paint.color(:black),
|
8
|
+
red: Paint.color(:red),
|
9
|
+
green: Paint.color(:green),
|
10
|
+
yellow: Paint.color(:yellow),
|
11
|
+
blue: Paint.color(:blue),
|
12
|
+
magenta: Paint.color(:magenta),
|
13
|
+
cyan: Paint.color(:cyan),
|
14
|
+
white: Paint.color(:white),
|
15
|
+
bg_black: Paint.color(nil, :black),
|
16
|
+
bg_red: Paint.color(nil, :red),
|
17
|
+
bg_green: Paint.color(nil, :green),
|
18
|
+
bg_yellow: Paint.color(nil, :yellow),
|
19
|
+
bg_blue: Paint.color(nil, :blue),
|
20
|
+
bg_magenta: Paint.color(nil, :magenta),
|
21
|
+
bg_cyan: Paint.color(nil, :cyan),
|
22
|
+
bg_white: Paint.color(nil, :white),
|
23
|
+
reset: Paint.color(:reset),
|
24
|
+
bold: Paint.color(:bold),
|
25
|
+
underline: Paint.color(:underline),
|
26
|
+
inverse: Paint.color(:inverse),
|
27
|
+
hide: Paint.color(:hide),
|
28
|
+
title: Paint.color(:bold, :underline),
|
29
|
+
result: Paint.color(:blue),
|
30
|
+
error: Paint.color(:red, :bold),
|
31
|
+
success: Paint.color(:green, :bold)
|
32
|
+
}
|
33
|
+
|
34
|
+
include Paint::Tryruby
|
35
|
+
|
36
|
+
def paint(*args)
|
37
|
+
Paint[*args]
|
38
|
+
end
|
39
|
+
|
40
|
+
module_function(*Paint::SHORTCUTS[:tryruby].keys)
|
41
|
+
module_function :paint
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,858 @@
|
|
1
|
+
require_relative 'tutorial'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
module Tryruby
|
5
|
+
# Levels and challenges form tryruby.org
|
6
|
+
class DefaultLevels < Tutorial
|
7
|
+
level do
|
8
|
+
challenge do
|
9
|
+
help <<-EOF
|
10
|
+
#{title 'Got 15 minutes? Give Ruby a shot right now!'}
|
11
|
+
|
12
|
+
Ruby is a programming language from Japan (available at ruby-lang.org) which is
|
13
|
+
revolutionizing the web. The beauty of Ruby is found in its balance between
|
14
|
+
simplicity and power.
|
15
|
+
|
16
|
+
Try out Ruby code in the prompt. In addition to Ruby's built-in methods, the
|
17
|
+
following commands are available:
|
18
|
+
|
19
|
+
* #{inverse 'help'} → Repeat last instructions.
|
20
|
+
* #{inverse 'next'} → Allows you to skip to the next section of a lesson.
|
21
|
+
* #{inverse 'back'} → Allows you to return to the previous section of a \
|
22
|
+
lesson.
|
23
|
+
EOF
|
24
|
+
end
|
25
|
+
|
26
|
+
challenge do
|
27
|
+
help <<-EOF
|
28
|
+
#{title 'Using the Prompt'}
|
29
|
+
|
30
|
+
The prompt below is a Ruby prompt.
|
31
|
+
|
32
|
+
Here you'll be able to type a line of Ruby code, hit #{inverse 'Enter'}, and \
|
33
|
+
watch it run!
|
34
|
+
|
35
|
+
For your first bit of Ruby, try typing some math, like: #{result '2 + 6'}
|
36
|
+
EOF
|
37
|
+
test { |result| result == 8 }
|
38
|
+
end
|
39
|
+
|
40
|
+
challenge do
|
41
|
+
help <<-EOF
|
42
|
+
Great! You did a little bit of math.
|
43
|
+
|
44
|
+
See how the answer was returned? Ruby uses a fat arrow for responses to your
|
45
|
+
entries.
|
46
|
+
|
47
|
+
Ruby recognizes numbers and mathematics operators. You could also try some other
|
48
|
+
math like:
|
49
|
+
|
50
|
+
* #{result '4 * 10'}
|
51
|
+
* #{result '5 - 12'}
|
52
|
+
* #{result '40 / 4'}
|
53
|
+
|
54
|
+
Even though we've placed a space between the numbers and the operators above,
|
55
|
+
it's not required. For now, stick with these basic operations; we'll try a few
|
56
|
+
others later.
|
57
|
+
|
58
|
+
When you're finished experimenting, type #{result 'next'} to move to the next \
|
59
|
+
lesson when
|
60
|
+
you're finished.
|
61
|
+
EOF
|
62
|
+
end
|
63
|
+
|
64
|
+
challenge do
|
65
|
+
help <<-EOF
|
66
|
+
#{title 'Say Your Name'}
|
67
|
+
|
68
|
+
Welp, we already know that computers are handy and fast for math.
|
69
|
+
|
70
|
+
But what about something really useful. Like, say, seeing the letters of your
|
71
|
+
name reversed!
|
72
|
+
|
73
|
+
To do that super-cool task, let's first get you familiar with text in Ruby. Type
|
74
|
+
your first name in quotes, like this: #{result '"Jimmy"'}
|
75
|
+
EOF
|
76
|
+
test { |res| res.is_a?(String) && res.length > 0 }
|
77
|
+
end
|
78
|
+
|
79
|
+
challenge do
|
80
|
+
help <<-EOF
|
81
|
+
#{title 'Say Your Name Reversed'}
|
82
|
+
|
83
|
+
Perfect, you've formed a #{inverse 'string'} from the letters of your name. \
|
84
|
+
A string is an
|
85
|
+
ordered set of characters that the computer can process.
|
86
|
+
|
87
|
+
Imagine the letters are on a string of laundry line and the quotes are
|
88
|
+
clothespins holding the ends. The quotes mark the beginning and the end of the
|
89
|
+
string, and are required.
|
90
|
+
|
91
|
+
Now, let's get to reversing your name.
|
92
|
+
|
93
|
+
Type: #{result '"Jimmy".reverse'}, using your own name where the string goes. \
|
94
|
+
(Don't forget
|
95
|
+
the dot!)
|
96
|
+
EOF
|
97
|
+
test { |res| res.is_a?(String) && res.length > 0 }
|
98
|
+
end
|
99
|
+
|
100
|
+
challenge do
|
101
|
+
help <<-EOF
|
102
|
+
#{title 'Counting the Letters'}
|
103
|
+
|
104
|
+
You have used the #{inverse 'reverse'} method on your name!
|
105
|
+
|
106
|
+
By enclosing your name in quotes, you made a string. Next, you used a dot to
|
107
|
+
access a hidden list of methods that belong to all strings. In this case, you
|
108
|
+
then called the #{inverse 'reverse'} method, which works on strings to flip \
|
109
|
+
the order of the
|
110
|
+
string’s characters. Cool, huh?
|
111
|
+
|
112
|
+
Now, let's use a different option which will show us how many letters are in
|
113
|
+
your name. Try typing #{result '"Jimmy".length'} using your name where the \
|
114
|
+
string goes.
|
115
|
+
EOF
|
116
|
+
test { |res| res.is_a?(Fixnum) && res > 0 }
|
117
|
+
end
|
118
|
+
|
119
|
+
challenge do
|
120
|
+
help <<-EOF
|
121
|
+
#{title 'On Repeat'}
|
122
|
+
|
123
|
+
Now, maybe you're wondering what any of this is actually good for. Have you ever
|
124
|
+
encountered a website that yelled at you for choosing a password that was too
|
125
|
+
short? Turns out, the #{inverse 'length'} property is often what that site \
|
126
|
+
uses to check for
|
127
|
+
a correct length.
|
128
|
+
|
129
|
+
Let's get crazy now, and multiply your name by 5. Follow the following format:
|
130
|
+
#{result '"Jimmy" * 5'}
|
131
|
+
EOF
|
132
|
+
test { |res| res.is_a?(String) && res.length > 0 }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
level do
|
137
|
+
challenge do
|
138
|
+
help <<-EOF
|
139
|
+
#{title 'Hey, Whoa, Level #2 Already'}
|
140
|
+
|
141
|
+
Let's look at what you've learned in the first minute.
|
142
|
+
|
143
|
+
* #{bold 'The prompt'}. Typing code into the prompt gives you an answer.
|
144
|
+
* #{bold 'Numbers and strings'} are Ruby's math and text objects.
|
145
|
+
* #{bold 'Methods'}. You've used English-language methods like \
|
146
|
+
#{inverse 'reverse'} and symbolic
|
147
|
+
methods like #{inverse '*'} (the multiplication method.) Methods are \
|
148
|
+
actions!
|
149
|
+
|
150
|
+
This is the essence of your learning. Taking simple things, toying with them and
|
151
|
+
turning them into new things! Feeling comfortable yet? I promise that we'll get
|
152
|
+
you there..
|
153
|
+
|
154
|
+
But now, let's do something a little uncomfortable. Try using that \
|
155
|
+
#{inverse 'reverse'}
|
156
|
+
method on a number: #{result '40.reverse'}
|
157
|
+
EOF
|
158
|
+
test { |res| res.is_a?(NoMethodError) }
|
159
|
+
end
|
160
|
+
|
161
|
+
challenge do
|
162
|
+
help <<-EOF
|
163
|
+
#{title 'Stop, You\'re Barking Mad!'}
|
164
|
+
|
165
|
+
You can't reverse the number #{inverse '40'}. I guess you can hold your \
|
166
|
+
monitor up to the
|
167
|
+
mirror, but reversing a number just doesn't make sense! Ruby has tossed you
|
168
|
+
a useful error message.
|
169
|
+
|
170
|
+
The message is telling you that there is no method #{inverse 'reverse'} for \
|
171
|
+
number values in
|
172
|
+
Ruby!
|
173
|
+
|
174
|
+
But, hmm...maybe if you can turn it into a string. Try this: \
|
175
|
+
#{result '40.to_s.reverse'}.
|
176
|
+
EOF
|
177
|
+
test { |res| res == '04' }
|
178
|
+
end
|
179
|
+
|
180
|
+
challenge do
|
181
|
+
help <<-EOF
|
182
|
+
#{title 'Boys are Different From Girls'}
|
183
|
+
|
184
|
+
...just like numbers are different from strings. While you can use methods on
|
185
|
+
any object in Ruby, some methods only work on certain types of values. A really
|
186
|
+
cool thing about Ruby is that you can always convert between different types
|
187
|
+
using Ruby's "to" methods.
|
188
|
+
|
189
|
+
* #{bold 'to_s'} converts values to strings.
|
190
|
+
* #{bold 'to_i'} converts values to integers (numbers.)
|
191
|
+
* #{bold 'to_a'} converts values to arrays.
|
192
|
+
|
193
|
+
What in the world are arrays, you might ask?! They are simply lists. Let's make
|
194
|
+
an empty one, by typing in a pair of brackets: #{result '[]'}.
|
195
|
+
EOF
|
196
|
+
test { |res| res == [] }
|
197
|
+
end
|
198
|
+
|
199
|
+
challenge do
|
200
|
+
help <<-EOF
|
201
|
+
#{title 'Standing in Line'}
|
202
|
+
|
203
|
+
Great, you built an empty array. Now let's see what else we can do with it.
|
204
|
+
|
205
|
+
First off, a good thing to know is that arrays store their information in a
|
206
|
+
#{bold 'sequence'}. Think of this like standing in line for popcorn. You are \
|
207
|
+
behind
|
208
|
+
someone and you wouldn't dream of pushing them aside, right? And that guy behind
|
209
|
+
you, you've got a close eye on him, too. First come, first serve.
|
210
|
+
|
211
|
+
Just like that line for popcorn, the order of an array's information will stay
|
212
|
+
consistent for you after you build it...well, at least until you modify it.
|
213
|
+
|
214
|
+
To try building an array with some stuff in it, here's a list of lottery numbers
|
215
|
+
for you: #{result '[12, 47, 35]'}. See those commas? They're important!
|
216
|
+
EOF
|
217
|
+
test { |res| res == [12, 47, 35] }
|
218
|
+
end
|
219
|
+
|
220
|
+
challenge do
|
221
|
+
help <<-EOF
|
222
|
+
#{title 'One Raises Its Hand'}
|
223
|
+
|
224
|
+
Sweet, you've got a short list of lottery numbers. Now, what if we wanted to
|
225
|
+
know which one is the highest in the array?
|
226
|
+
|
227
|
+
Try this: #{result '[12, 47, 35].max'}.
|
228
|
+
EOF
|
229
|
+
test { |res| res == 47 }
|
230
|
+
end
|
231
|
+
|
232
|
+
challenge do
|
233
|
+
help <<-EOF
|
234
|
+
#{title 'Tucking a List Away'}
|
235
|
+
|
236
|
+
Good, good. But it would be pretty annoying to have to retype that list every
|
237
|
+
time, right?
|
238
|
+
|
239
|
+
Let's fix that by using a Ruby #{bold 'variable'}, which helps us store \
|
240
|
+
important data.
|
241
|
+
Each variable has a unique name, so that it can be summoned up whenever we need
|
242
|
+
the info it contains.
|
243
|
+
|
244
|
+
Call your new variable #{inverse 'ticket'} and place your lottery numbers \
|
245
|
+
inside it, like so:
|
246
|
+
#{result 'ticket = [12, 47, 35]'}. That equal sign you see is what assigns \
|
247
|
+
your array to the
|
248
|
+
new variable.
|
249
|
+
EOF
|
250
|
+
test { |_, vars| vars[:ticket] == [12, 47, 35] }
|
251
|
+
end
|
252
|
+
|
253
|
+
challenge do
|
254
|
+
help <<-EOF
|
255
|
+
#{title 'Now Type Ticket'}
|
256
|
+
|
257
|
+
Sweet, now check this out. Type: #{result 'ticket'}
|
258
|
+
EOF
|
259
|
+
test { |res| res == [12, 47, 35] }
|
260
|
+
setup { |vars| vars[:ticket] = [12, 47, 35] }
|
261
|
+
end
|
262
|
+
|
263
|
+
challenge do
|
264
|
+
help <<-EOF
|
265
|
+
#{title 'Saved, Tucked Away'}
|
266
|
+
|
267
|
+
Fantastic! You've hung on to your lotto numbers, tucking them away inside a
|
268
|
+
#{bold 'variable'} called #{inverse 'ticket'}.
|
269
|
+
|
270
|
+
Now let's put your lotto numbers in order...sound good? Ruby has a great method
|
271
|
+
for that. Use: #{result 'ticket.sort!'}
|
272
|
+
|
273
|
+
You might notice that the method has an exclamation point at its end. This just
|
274
|
+
signals that we intend for Ruby to directly modify the same array that we've
|
275
|
+
built, rather than make a brand new copy that is sorted. You'll notice that if
|
276
|
+
you try calling #{inverse 'ticket'} again, it will be sorted permanently!
|
277
|
+
|
278
|
+
When you want to move on, just type #{result 'next'}
|
279
|
+
EOF
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
level do
|
284
|
+
poem = <<-EOF
|
285
|
+
My toast has flown from my hand
|
286
|
+
And my toast has gone to the moon.
|
287
|
+
But when I saw it on television,
|
288
|
+
Planting our flag on Halley's comet,
|
289
|
+
More still did I want to eat it.
|
290
|
+
EOF
|
291
|
+
challenge do
|
292
|
+
help <<-EOF
|
293
|
+
#{title 'Level #3 is Upon Us'}
|
294
|
+
|
295
|
+
You built a list. Then you sorted the list. And as you've seen, the \
|
296
|
+
#{inverse 'ticket'}
|
297
|
+
variable is now changed.
|
298
|
+
|
299
|
+
Now, let's look at how your second level went down:
|
300
|
+
|
301
|
+
* #{bold 'Errors'}. If you try to reverse a number or do anything fishy, \
|
302
|
+
Ruby will skip
|
303
|
+
the prompt and tell you to straighten up.
|
304
|
+
* #{bold 'Arrays'} are lists of stored information.
|
305
|
+
* #{bold 'Variables'} are a place to save stuff you might need again, as \
|
306
|
+
well as give
|
307
|
+
that stuff a name. You used the equals sign to do this, in a process called
|
308
|
+
assignment.
|
309
|
+
Like: #{inverse 'ticket = [14, 37, 18]'}.
|
310
|
+
|
311
|
+
In all, there are just five levels in this course. You are already two-fifths
|
312
|
+
of the way to the end! This is simple stuff, don't you think? More good stuff up
|
313
|
+
ahead.
|
314
|
+
|
315
|
+
Let's change directions for a moment. I've stuffed a bit of poetry for you in a
|
316
|
+
certain variable. Take a look, by typing #{result 'print poem'}
|
317
|
+
EOF
|
318
|
+
setup do |vars|
|
319
|
+
vars[:poem] = poem.dup
|
320
|
+
end
|
321
|
+
test do |_, _, output|
|
322
|
+
output == poem
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
challenge do
|
327
|
+
help <<-EOF
|
328
|
+
#{title 'Sadly, You Hate Toast Poetry'}
|
329
|
+
|
330
|
+
Look, it's okay. You don't have to like it. You may even want to hack it up.
|
331
|
+
Welp, be my guest.
|
332
|
+
|
333
|
+
Instead of toast, maybe go for a melon or something. Try this one:
|
334
|
+
#{result 'poem[\'toast\'] = \'honeydew\''}
|
335
|
+
EOF
|
336
|
+
setup do |vars|
|
337
|
+
vars[:poem] = poem.dup
|
338
|
+
end
|
339
|
+
test do |_, vars|
|
340
|
+
vars[:poem] == poem.sub('toast', 'honeydew')
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
challenge do
|
345
|
+
help <<-EOF
|
346
|
+
Now type #{result 'print poem'} once again to see the new poem.
|
347
|
+
|
348
|
+
See how you only changed the first toast? The joke's on you, bread hater.
|
349
|
+
|
350
|
+
When you want to move on, type #{result 'next'}
|
351
|
+
EOF
|
352
|
+
setup do |vars|
|
353
|
+
vars[:poem] = poem.sub('toast', 'honeydew')
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
challenge do
|
358
|
+
help <<-EOF
|
359
|
+
#{title 'Ready, Aim'}
|
360
|
+
|
361
|
+
The square brackets you just used are very common in Ruby. Remember, you typed:
|
362
|
+
#{inverse 'poem[\'toast\'] = \'honeydew\''}. That box that holds the word \
|
363
|
+
toast uses a square
|
364
|
+
bracket on each side. See 'em?
|
365
|
+
|
366
|
+
The two brackets are like a crosshairs used to line up precisely on a target.
|
367
|
+
These brackets mean, "I am looking for ____ somewhere in here." Ready ... aim
|
368
|
+
... #{bold 'data'}. Here, you were looking specifically for toast in order to \
|
369
|
+
swap it out
|
370
|
+
with a fruit.
|
371
|
+
|
372
|
+
Let's see if your new experience can help you produce the answer to this
|
373
|
+
question: what happens when we reverse this whole poem? #{result 'poem.reverse'}
|
374
|
+
EOF
|
375
|
+
setup do |vars|
|
376
|
+
vars[:poem] = poem.sub('toast', 'honeydew')
|
377
|
+
end
|
378
|
+
test do |result|
|
379
|
+
result == poem.sub('toast', 'honeydew').reverse
|
380
|
+
end
|
381
|
+
end
|
382
|
+
|
383
|
+
challenge do
|
384
|
+
help <<-EOF
|
385
|
+
#{title 'Too Much Reversal'}
|
386
|
+
|
387
|
+
Okay, I suppose that was expected. The whole poem's been turned backwards,
|
388
|
+
letter by letter. But say I really just wanted to reverse the lines only. In
|
389
|
+
other words, move the last line up to first and the first line down to last.
|
390
|
+
Backwards, yes, but not that backwards.
|
391
|
+
|
392
|
+
Ruby has a way. Try this: #{result 'poem.lines.to_a.reverse'}
|
393
|
+
EOF
|
394
|
+
setup do |vars|
|
395
|
+
vars[:poem] = poem.sub('toast', 'honeydew')
|
396
|
+
end
|
397
|
+
test do |result|
|
398
|
+
result == poem.sub('toast', 'honeydew').lines.to_a.reverse
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
challenge do
|
403
|
+
help <<-EOF
|
404
|
+
#{title 'Ringlets of Chained Methods'}
|
405
|
+
|
406
|
+
So...what actually happened there? You typed \
|
407
|
+
#{inverse 'poem.lines.to_a.reverse'} and
|
408
|
+
produced some Ruby magic.
|
409
|
+
|
410
|
+
First, you turned the #{inverse 'poem'} into a list using \
|
411
|
+
#{inverse 'lines.to_a'}. The #{inverse 'lines'} component
|
412
|
+
decided the way the string should be split up, and then one of our "to" methods,
|
413
|
+
#{inverse 'to_a'}, converted those splits into an Array. (to_array.)
|
414
|
+
|
415
|
+
Different methods, such as #{inverse 'bytes'} and #{inverse 'chars'} can be \
|
416
|
+
used in place of #{inverse 'lines'}. By
|
417
|
+
using #{inverse 'lines'} here, Ruby split the poem up according to each new \
|
418
|
+
line.
|
419
|
+
|
420
|
+
After that, you #{inverse 'reverse'}'d your Array. You had each line prepared \
|
421
|
+
in advance. And
|
422
|
+
then you reversed them. That's it!
|
423
|
+
|
424
|
+
And now, let's tack one more method on the end there, if you don't mind. Try:
|
425
|
+
#{result 'print poem.lines.to_a.reverse.join'}.
|
426
|
+
EOF
|
427
|
+
setup do |vars|
|
428
|
+
vars[:poem] = poem.sub('toast', 'honeydew')
|
429
|
+
end
|
430
|
+
test do |_, _, output|
|
431
|
+
output == poem.sub('toast', 'honeydew').lines.to_a.reverse.join
|
432
|
+
end
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
level do
|
437
|
+
challenge do
|
438
|
+
help <<-EOF
|
439
|
+
#{title 'Brace Yourselves! Level #4 is Here Now'}
|
440
|
+
|
441
|
+
Good show, my friend! The join method took that list of reversed lines and put
|
442
|
+
them together into a single string. (Sure, you could have also just used to_s.)
|
443
|
+
|
444
|
+
Time for a quick review.
|
445
|
+
|
446
|
+
* Exclamation Points. Methods may have exclamation points in their name, which
|
447
|
+
just means to impact the current data, rather than making a copy. No big
|
448
|
+
deal.
|
449
|
+
* Square Brackets. With these, you can target and find things. You can even
|
450
|
+
replace them if necessary.
|
451
|
+
* Chaining methods lets you get a lot more done in a single command. Break up
|
452
|
+
a poem, reverse it, reassemble it: poem.lines.to_a.reverse.join.
|
453
|
+
|
454
|
+
Guess what? Methods can also have question marks. Try: poem.include? "my hand"
|
455
|
+
to check it out.
|
456
|
+
|
457
|
+
At this point, you may want to tinker with the poem a bit more. A complete list
|
458
|
+
of all the String methods is here: http://ruby-doc.org/core/classes/String.html.
|
459
|
+
Go ahead and try a few (such as poem.downcase or poem.delete.)
|
460
|
+
|
461
|
+
And now on to something new. When you're ready to move on, type: books = {}
|
462
|
+
EOF
|
463
|
+
setup do |vars|
|
464
|
+
vars[:poem] = <<-EOF
|
465
|
+
My honeydew has flown from my hand
|
466
|
+
And my toast has gone to the moon.
|
467
|
+
But when I saw it on television,
|
468
|
+
Planting our flag on Halley's comet,
|
469
|
+
More still did I want to eat it.
|
470
|
+
EOF
|
471
|
+
end
|
472
|
+
test do |_, vars|
|
473
|
+
vars[:books] == {}
|
474
|
+
end
|
475
|
+
end
|
476
|
+
|
477
|
+
challenge do
|
478
|
+
help <<-EOF
|
479
|
+
A Wee Blank Book
|
480
|
+
|
481
|
+
You've made an empty hash, also known as: a dictionary. Hashes store related
|
482
|
+
information by giving reusable labels to pieces of our data.
|
483
|
+
|
484
|
+
We're going to stuff some miniature book reviews in this hash. Here's our rating
|
485
|
+
system:
|
486
|
+
|
487
|
+
* :splendid → a masterpiece.
|
488
|
+
* :quite_good → enjoyed, sure, yes.
|
489
|
+
* :mediocre → equal parts great and terrible.
|
490
|
+
* :quite_not_good → notably bad.
|
491
|
+
* :abysmal → steaming wreck.
|
492
|
+
|
493
|
+
To rate a book, put the title in square brackets and put the rating after the
|
494
|
+
equals.
|
495
|
+
|
496
|
+
For example: books["Gravity's Rainbow"] = :splendid
|
497
|
+
EOF
|
498
|
+
setup do |vars|
|
499
|
+
vars[:books] = {}
|
500
|
+
end
|
501
|
+
test do |_, vars|
|
502
|
+
vars[:books] == { 'Gravity\'s Rainbow' => :splendid }
|
503
|
+
end
|
504
|
+
end
|
505
|
+
|
506
|
+
challenge do
|
507
|
+
help <<-EOF
|
508
|
+
More Bite-Size Reviews
|
509
|
+
|
510
|
+
Keep going! Fill it up with some useful reviews. And if you want to see the
|
511
|
+
whole list, just type: books
|
512
|
+
|
513
|
+
Again, the available ratings are: :splendid, :quite_good, :mediocre,
|
514
|
+
:quite_not_good, and :abysmal.
|
515
|
+
|
516
|
+
Notice that these ratings are not strings. When you place a colon in front of
|
517
|
+
a simple word, you get a Ruby symbol. Symbols are much cheaper than strings (in
|
518
|
+
terms of computer memory.) If you need to use a word over and over in your
|
519
|
+
program itself, use a symbol. Rather than having thousands of copies of that
|
520
|
+
word in memory, the computer will store a symbol only once, and refer to it over
|
521
|
+
and over.
|
522
|
+
|
523
|
+
Once you've got three or four books in there, type: books.length. You should see
|
524
|
+
the right amount.
|
525
|
+
EOF
|
526
|
+
setup do |vars|
|
527
|
+
vars[:books] = { 'Gravity\'s Rainbow' => :splendid }
|
528
|
+
end
|
529
|
+
test do |result, vars|
|
530
|
+
vars[:books].length > 2 && result == vars[:books].length
|
531
|
+
end
|
532
|
+
end
|
533
|
+
|
534
|
+
challenge do
|
535
|
+
help <<-EOF
|
536
|
+
Wait, Did I Like Gravity's Rainbow?
|
537
|
+
|
538
|
+
See, the length method works on strings, list and hashes. One great thing about
|
539
|
+
Ruby is that method names are often reused, which means a lot less stuff for you
|
540
|
+
to remember.
|
541
|
+
|
542
|
+
If you'd like to look up one of your old reviews, just put the title of the book
|
543
|
+
in the square box again. Leave off the equal sign this time, though, since
|
544
|
+
you're not assigning any information. You're just researching!
|
545
|
+
|
546
|
+
Do it like this: books["Gravity's Rainbow"]
|
547
|
+
EOF
|
548
|
+
test do |result, vars|
|
549
|
+
result == vars[:books]["Gravity's Rainbow"]
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
challenge do
|
554
|
+
help <<-EOF
|
555
|
+
Hashes as Pairs
|
556
|
+
|
557
|
+
Keep in mind that hashes won't keep things in order. That's not their job. It'll
|
558
|
+
just pair up two things: a key and a value. In your reviews, the key is the
|
559
|
+
book's title and the value is the rating, in this case a symbol.
|
560
|
+
|
561
|
+
If you want to see a nice list of the book titles you've reviewed: books.keys
|
562
|
+
|
563
|
+
When you want to move on, type next
|
564
|
+
EOF
|
565
|
+
end
|
566
|
+
|
567
|
+
challenge do
|
568
|
+
help <<-EOF
|
569
|
+
Are You Harsh?
|
570
|
+
|
571
|
+
So are you giving out harsh, unfair reviews? Let's keep score with this hash:
|
572
|
+
ratings = Hash.new(0)
|
573
|
+
EOF
|
574
|
+
test do |_, vars|
|
575
|
+
vars[:ratings] == Hash.new(0)
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
challenge do
|
580
|
+
help <<-EOF
|
581
|
+
Are You Harsh?
|
582
|
+
|
583
|
+
That command was another way to build an empty hash. The zero you passed in will
|
584
|
+
set all of your initial rating counts to zero.
|
585
|
+
|
586
|
+
Okay, now let's count up your reviews. Stay with me on this one.
|
587
|
+
|
588
|
+
Type: books.values.each { |rate| ratings[rate] += 1 }
|
589
|
+
|
590
|
+
(That | in the code is called the pipe character. It's probably located right
|
591
|
+
above the Enter key on your keyboard.)
|
592
|
+
|
593
|
+
This code will turn all your unique values in books...into keys within the new
|
594
|
+
ratings hash. Crazy, right? Then, as it looks at each rating you originally gave
|
595
|
+
in books, it will increase the count value for that rating in ratings
|
596
|
+
|
597
|
+
After you've build your new hash of count values, type ratings again to see the
|
598
|
+
full tally. This new hash will show you a rating followed by the number of times
|
599
|
+
you've given that rating.
|
600
|
+
|
601
|
+
When you want to move on, type next
|
602
|
+
EOF
|
603
|
+
setup do |vars|
|
604
|
+
vars[:ratings] = Hash.new(0)
|
605
|
+
vars[:books] = {
|
606
|
+
'Gravity\'s Rainbow' => :splendid
|
607
|
+
} unless vars[:books].is_a?(Hash) && vars[:books].length > 0
|
608
|
+
end
|
609
|
+
end
|
610
|
+
|
611
|
+
challenge do
|
612
|
+
help <<-EOF
|
613
|
+
A Tally
|
614
|
+
|
615
|
+
One of the amazing new things we've just used is called a block. Basically,
|
616
|
+
a block is a chunk of Ruby code surrounded by curly braces. We'll take a closer
|
617
|
+
look at them later.
|
618
|
+
|
619
|
+
But for now, let's try another block:
|
620
|
+
|
621
|
+
5.times { print "Odelay!" }
|
622
|
+
|
623
|
+
When you want to move on, type next. You want the badge, don't you?
|
624
|
+
EOF
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
level do
|
629
|
+
comics = <<-EOF
|
630
|
+
Achewood: http://achewood.com/
|
631
|
+
Dinosaur Comics: http://qwantz.com/
|
632
|
+
Perry Bible Fellowship: http://cheston.com/pbf/archive.html
|
633
|
+
Get Your War On: http://mnftiu.cc/
|
634
|
+
EOF
|
635
|
+
new_comics = comics + 'Cat and Girl: http://catandgirl.com/'
|
636
|
+
|
637
|
+
challenge do
|
638
|
+
help <<-EOF
|
639
|
+
Now Arriving at Level #5
|
640
|
+
|
641
|
+
Blocks are always attached to methods. You saw this with the times method, which
|
642
|
+
took the block and ran its code over and over. (In this case: five times.)
|
643
|
+
|
644
|
+
This last lesson was a bit longer. You've probably used up three minutes
|
645
|
+
learning about:
|
646
|
+
|
647
|
+
* Hashes. The little 'dictionary' with the curly braces: {}.
|
648
|
+
* Symbols. Tiny, efficiently reusable code words with a colon: :splendid.
|
649
|
+
* Blocks. Chunks of code which can be tacked on to many of Ruby's methods.
|
650
|
+
Here's the code you used to build a scorecard:
|
651
|
+
books.values.each { |rate| ratings[rate] += 1 }.
|
652
|
+
|
653
|
+
On to the next thing, okay? On your computer, you probably have a lot of
|
654
|
+
different files. Some files have pictures in them, some have programs in them.
|
655
|
+
And files are often organized into folders, also called: directories.
|
656
|
+
|
657
|
+
I've prepared a few directories for you. Take a look, using the following
|
658
|
+
command: Dir.entries "/"
|
659
|
+
EOF
|
660
|
+
test do |result|
|
661
|
+
result == Dir.entries('/')
|
662
|
+
end
|
663
|
+
end
|
664
|
+
|
665
|
+
challenge do
|
666
|
+
help <<-EOF
|
667
|
+
The Private Collection of Dr. Dir
|
668
|
+
|
669
|
+
You've just listed out everything in the top directory, which is called the
|
670
|
+
root. It's indicated by the single slash in your string parameter. It contains
|
671
|
+
some programs, as well as other tutorials and such.
|
672
|
+
|
673
|
+
So, what exactly is that Dir.entries method? Well, it's just a method, like the
|
674
|
+
others you've seen. Dir has a collection of methods for checking out file
|
675
|
+
directories, and entries is being called on the Dir variable. The entries method
|
676
|
+
just lists everything in the directory you've indicated!
|
677
|
+
|
678
|
+
One other little thing we haven't really talked about quite yet: method
|
679
|
+
arguments. A few are highlighted below.
|
680
|
+
|
681
|
+
* Dir.entries "/" -- Anything listed after a method is considered an
|
682
|
+
'attachment'.
|
683
|
+
* print poem -- See, print is just an ordinary method, while the poem is what
|
684
|
+
got attached for printing.
|
685
|
+
* print "pre", "event", "ual", "ism" -- This bit has several arguments! Ruby
|
686
|
+
makes us use commas to distinguish between them.
|
687
|
+
|
688
|
+
Next up, we'll list just the text files in our directory using a bracket
|
689
|
+
notation. Remember how it searches?
|
690
|
+
|
691
|
+
Try: Dir["*.txt"]
|
692
|
+
EOF
|
693
|
+
setup do
|
694
|
+
File.write('comics.txt', comics)
|
695
|
+
end
|
696
|
+
test do |result|
|
697
|
+
result == Dir['*.txt']
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
challenge do
|
702
|
+
help <<-EOF
|
703
|
+
Come, Read Comics With Me
|
704
|
+
|
705
|
+
The Dir[] syntax is kind of like entries, but instead searches for files with
|
706
|
+
wildcard characters.
|
707
|
+
|
708
|
+
Here, we see those square brackets again! Notice how they still mean, "I am
|
709
|
+
looking for _____?"
|
710
|
+
|
711
|
+
Dir["*.txt"] says to Ruby: "I am looking for any files which end with .txt."
|
712
|
+
The asterisk indicates the "any file" part. Ruby then hands us every file that
|
713
|
+
matches our request.
|
714
|
+
|
715
|
+
Alright, let's crack open this comics file, then! We'll use a new method to do
|
716
|
+
it.
|
717
|
+
|
718
|
+
Here's the way: print File.read("comics.txt")
|
719
|
+
EOF
|
720
|
+
test do |_, _, output|
|
721
|
+
output == File.read('comics.txt')
|
722
|
+
end
|
723
|
+
end
|
724
|
+
|
725
|
+
challenge do
|
726
|
+
help <<-EOF
|
727
|
+
Mi Comicas, Tu Comicas
|
728
|
+
|
729
|
+
Alright, then! Now we can start to use files to store things. This is great
|
730
|
+
because normally when we exit Ruby, all our variables will be gone. Ruby, by
|
731
|
+
itself, forgets these things. But if we save things in files, we can read those
|
732
|
+
files in future Ruby escapades.
|
733
|
+
|
734
|
+
Hey, and guess what? The Home directory is yours now! I gave it to you! Am I
|
735
|
+
generous or what?!
|
736
|
+
|
737
|
+
First thing we'll do is make a copy of the comics file and put in new folder
|
738
|
+
called 'Home'.
|
739
|
+
|
740
|
+
To do that, you'll want to use a copying method called cp on a variable called
|
741
|
+
FileUtils.
|
742
|
+
|
743
|
+
Use FileUtils.cp('comics.txt', 'Home/comics.txt')
|
744
|
+
EOF
|
745
|
+
setup do
|
746
|
+
Dir.mkdir('Home') unless Dir.exist?('Home')
|
747
|
+
end
|
748
|
+
test do
|
749
|
+
File.exist?('Home/comics.txt') && \
|
750
|
+
File.read('Home/comics.txt') == comics
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
challenge do
|
755
|
+
help <<-EOF
|
756
|
+
Okay, you've got a copy, and it's located in the right directory. Check it out!
|
757
|
+
|
758
|
+
Use Dir["Home/*.txt"]
|
759
|
+
|
760
|
+
Type next to move to the next lesson when you're finished.
|
761
|
+
EOF
|
762
|
+
setup do
|
763
|
+
File.write('Home/comics.txt', comics)
|
764
|
+
end
|
765
|
+
end
|
766
|
+
|
767
|
+
challenge do
|
768
|
+
help <<-EOF
|
769
|
+
Your Own Turf
|
770
|
+
|
771
|
+
To add your own comic to the list, let's open the file in append mode,
|
772
|
+
which we indicate with the "a" parameter. This will allow us to put new stuff
|
773
|
+
at the end of the file.
|
774
|
+
|
775
|
+
Start by entering this code: File.open("Home/comics.txt", "a") do |f|
|
776
|
+
|
777
|
+
And Now For the Startling Conclusion
|
778
|
+
|
779
|
+
So your prompt has changed, see that? Your prompt is a double dot now.
|
780
|
+
|
781
|
+
In this tutorial, this prompt means that Ruby is expecting you to type a little
|
782
|
+
bit more. As you write further lines of Ruby code, the double-dots will continue
|
783
|
+
until the tutorial sees you are completely finished.
|
784
|
+
|
785
|
+
Alright, so here's more code. You've already typed the first line, so just enter
|
786
|
+
the second line.
|
787
|
+
|
788
|
+
File.open("Home/comics.txt", "a") do |f|
|
789
|
+
f << "Cat and Girl: http://catandgirl.com/"
|
790
|
+
end
|
791
|
+
|
792
|
+
Ruby Sits Still
|
793
|
+
|
794
|
+
The last line will add the Cat and Girl comic to the list, but Ruby's going to
|
795
|
+
wait until you're totally finished to take action.
|
796
|
+
|
797
|
+
This means we'll also need to wrap up the code block you've started. Turns out,
|
798
|
+
you actually opened a new block when you typed that do keyword.
|
799
|
+
|
800
|
+
So far the blocks we've seen have used curly braces, but this time we'll be
|
801
|
+
using do and end instead. A lot of Rubyists will use a do...end setup when the
|
802
|
+
block goes on for many lines.
|
803
|
+
|
804
|
+
Let's get that block finished now, with your very own end.
|
805
|
+
EOF
|
806
|
+
test do
|
807
|
+
File.exist?('Home/comics.txt') && \
|
808
|
+
File.read('Home/comics.txt') == new_comics
|
809
|
+
end
|
810
|
+
end
|
811
|
+
|
812
|
+
challenge do
|
813
|
+
help <<-EOF
|
814
|
+
Ruby Sits Still
|
815
|
+
|
816
|
+
Sweet! You've added that brand new comic to the end of the file. You can see for
|
817
|
+
yourself, using the read method you saw earlier:
|
818
|
+
print File.read("Home/comics.txt").
|
819
|
+
|
820
|
+
When you want to move on to the next lesson, type next.
|
821
|
+
EOF
|
822
|
+
setup do
|
823
|
+
File.write('Home/comics.txt', new_comics)
|
824
|
+
end
|
825
|
+
end
|
826
|
+
|
827
|
+
challenge do
|
828
|
+
help <<-EOF
|
829
|
+
The Clock Nailed To the File
|
830
|
+
|
831
|
+
I wonder, what time was it when you changed your file? We can check that out.
|
832
|
+
|
833
|
+
Type: File.mtime("Home/comics.txt").
|
834
|
+
EOF
|
835
|
+
test do |result|
|
836
|
+
result == File.mtime('Home/comics.txt')
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
challenge do
|
841
|
+
help <<-EOF
|
842
|
+
Just the Hour Hand
|
843
|
+
|
844
|
+
Excellent, there's the exact time, precisely when you added to the file.
|
845
|
+
The mtime method gives you a nice Ruby Time object.
|
846
|
+
|
847
|
+
If you want to check just what hour it was, hit the up arrow key to put your
|
848
|
+
previous entry in the console. Then modify the line to say:
|
849
|
+
File.mtime("Home/comics.txt").hour.
|
850
|
+
EOF
|
851
|
+
test do |result|
|
852
|
+
result == File.mtime('Home/comics.txt').hour
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
end
|
857
|
+
end
|
858
|
+
end
|