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.
@@ -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
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
@@ -0,0 +1,2 @@
1
+ Lint/Eval:
2
+ Enabled: false
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ sudo: false
3
+ rvm:
4
+ - 2.0.0
5
+ - 2.1.5
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in try_ruby_cli.gemspec
4
+ gemspec
@@ -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.
@@ -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
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -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)
@@ -0,0 +1,5 @@
1
+ require 'tryruby/version'
2
+
3
+ # CLI version of tryruby.org
4
+ module Tryruby
5
+ end
@@ -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,18 @@
1
+ require 'ripl'
2
+
3
+ module Tryruby
4
+ # Basic commands
5
+ module Commands
6
+ def next
7
+ Ripl.shell.next_challenge
8
+ end
9
+
10
+ def back
11
+ Ripl.shell.prev_challenge
12
+ end
13
+
14
+ def help
15
+ Ripl.shell.help_challenge
16
+ end
17
+ end
18
+ 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