From 06f99dd358e9adb08672535d5e01987d2dfad11c Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 07:29:38 -0600 Subject: [PATCH 01/12] add lesson 1.1 --- examples/1.1.0.rb | 3 +++ examples/1.1.1.rb | 5 +++++ examples_spec/1.1.1.rb | 13 ++++++++++++ lessons/1.1.json | 45 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 ++- 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 examples/1.1.0.rb create mode 100644 examples/1.1.1.rb create mode 100644 examples_spec/1.1.1.rb create mode 100644 lessons/1.1.json diff --git a/examples/1.1.0.rb b/examples/1.1.0.rb new file mode 100644 index 0000000..7481580 --- /dev/null +++ b/examples/1.1.0.rb @@ -0,0 +1,3 @@ +a = 1 +b = 4 +puts "The number #{a} is less than #{b}" \ No newline at end of file diff --git a/examples/1.1.1.rb b/examples/1.1.1.rb new file mode 100644 index 0000000..9fee635 --- /dev/null +++ b/examples/1.1.1.rb @@ -0,0 +1,5 @@ +def string_length_interpolater(incoming_string) + "The string you just gave me has a length of " +end + +puts string_length_interpolater("hello") diff --git a/examples_spec/1.1.1.rb b/examples_spec/1.1.1.rb new file mode 100644 index 0000000..5390668 --- /dev/null +++ b/examples_spec/1.1.1.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'The string you just gave me has a length of 5'" do + expect(output).to eq("The string you just gave me has a length of 5") + end +end \ No newline at end of file diff --git a/lessons/1.1.json b/lessons/1.1.json new file mode 100644 index 0000000..e1f7549 --- /dev/null +++ b/lessons/1.1.json @@ -0,0 +1,45 @@ +{ + "subsections": [ + { + "name": "String Basics", + "items": [ + { + "type": "header", + "value": "String Interpolation" + }, + { + "type": "paragraph", + "value": "It is essential to be able to replace placeholders within a string with values they represent. In the programming paradigm, this is called \"string interpolation\". In Ruby, string interpolation is extremely easy. Feel free to run the example below to see the sample in action." + }, + { + "type": "example", + "value": "1.1.0" + }, + { + "type": "paragraph", + "value": "Do remember that placeholders aren't just variables. Any valid block of Ruby code you place inside `#{}` will be evaluated and inserted at that location. Isn't that very neat?" + }, + { + "type": "paragraph", + "value": "Now let us see you wield this tool. Complete the functionality of the method below which, given a `String` as the argument, inserts the length of that `String` into another `String`:" + }, + { + "type": "test_example", + "value": "1.1.1" + }, + { + "type": "paragraph", + "value": "We've been using double quotes in all our string interpolation examples. A `String` literal created with single quotes does _not_ support interpolation." + }, + { + "type": "paragraph", + "value": "The essential difference between using single or double quotes is that double quotes allow for escape sequences while single quotes do not. What you saw above is one such example. `\"\\n\"` is interpreted as a new line and appears as a new line when rendered to the user, whereas `'\\n'` displays the actual escape sequence to the user." + }, + { + "type": "paragraph", + "value": "Let us move on ..." + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index e3dbf00..580b6a0 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -3,6 +3,7 @@ "0.0.json", "0.1.json", "0.2.json", - "1.0.json" + "1.0.json", + "1.1.json" ] } From 0389ae569fb4e1611d9f518ef54c004c5316e889 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 07:44:44 -0600 Subject: [PATCH 02/12] add another lesson section --- lessons/1.1.json | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/lessons/1.1.json b/lessons/1.1.json index e1f7549..c77e255 100644 --- a/lessons/1.1.json +++ b/lessons/1.1.json @@ -40,6 +40,51 @@ "value": "Let us move on ..." } ] + }, + { + "name": "Search in A String", + "items": [ + { + "type": "header", + "value": "Search in A String" + }, + { + "type": "paragraph", + "value": "Another common scenario is checking if a String contains any given character, word or sub-string. Considering your experience with the Ruby API and its intuitiveness, try and check whether the `String` given below includes `Yoda`." + }, + { + "type": "test_example", + "value": "1.1.2" + }, + { + "type": "paragraph", + "value": "Did you manage to figure that out by yourself? Too many cooks spoil the broth while too many hints lead the hunter astray! Now check if the string below starts with `Ruby`." + }, + { + "type": "test_example", + "value": "1.1.3" + }, + { + "type": "paragraph", + "value": "After that, checking whether the statement below ends with `Ruby` should be easy." + }, + { + "type": "test_example", + "value": "1.1.4" + }, + { + "type": "paragraph", + "value": "Are you getting the hang of the Ruby API? The previous three methods all ended with a `?`. It is conventional in Ruby to have `?` at the end of the method if that method returns only boolean values. Though it is not mandated by the syntax, this practice is highly recommended as it increases the readability of code." + }, + { + "type": "paragraph", + "value": "Sometimes we will be required to know the index of a particular character or a sub-string in a given `String` and conveniently Ruby provides a method on `String` that does exactly that. Try and find out the index of `R` in the string below:" + }, + { + "type": "test_example", + "value": "1.1.5" + } + ] } ] } From 4da1b2a3fa310673784c55e27b271e6f2243648a Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 08:01:27 -0600 Subject: [PATCH 03/12] finish out lesson 1.1, fix some bugs --- examples/1.1.2.rb | 1 + examples/1.1.3.rb | 1 + examples/1.1.4.rb | 1 + examples/1.1.5.rb | 1 + examples/1.1.6.rb | 1 + examples/1.1.7.rb | 1 + examples/1.1.8.rb | 1 + examples_spec/1.1.2.rb | 13 +++++++++++++ examples_spec/1.1.3.rb | 13 +++++++++++++ examples_spec/1.1.4.rb | 13 +++++++++++++ examples_spec/1.1.5.rb | 13 +++++++++++++ examples_spec/1.1.7.rb | 13 +++++++++++++ examples_spec/1.1.8.rb | 13 +++++++++++++ lessons/1.1.json | 39 ++++++++++++++++++++++++++++++++++++--- rp | 42 +++++++++++++++++++++++++++++------------- 15 files changed, 150 insertions(+), 16 deletions(-) create mode 100644 examples/1.1.2.rb create mode 100644 examples/1.1.3.rb create mode 100644 examples/1.1.4.rb create mode 100644 examples/1.1.5.rb create mode 100644 examples/1.1.6.rb create mode 100644 examples/1.1.7.rb create mode 100644 examples/1.1.8.rb create mode 100644 examples_spec/1.1.2.rb create mode 100644 examples_spec/1.1.3.rb create mode 100644 examples_spec/1.1.4.rb create mode 100644 examples_spec/1.1.5.rb create mode 100644 examples_spec/1.1.7.rb create mode 100644 examples_spec/1.1.8.rb diff --git a/examples/1.1.2.rb b/examples/1.1.2.rb new file mode 100644 index 0000000..9b9dae5 --- /dev/null +++ b/examples/1.1.2.rb @@ -0,0 +1 @@ +puts "[Luke:] I can’t believe it. [Yoda:] That is why you fail." \ No newline at end of file diff --git a/examples/1.1.3.rb b/examples/1.1.3.rb new file mode 100644 index 0000000..c7e9cb4 --- /dev/null +++ b/examples/1.1.3.rb @@ -0,0 +1 @@ +puts "Ruby is a beautiful language" \ No newline at end of file diff --git a/examples/1.1.4.rb b/examples/1.1.4.rb new file mode 100644 index 0000000..357f7db --- /dev/null +++ b/examples/1.1.4.rb @@ -0,0 +1 @@ +puts "I can't work with any other language but Ruby" \ No newline at end of file diff --git a/examples/1.1.5.rb b/examples/1.1.5.rb new file mode 100644 index 0000000..ce0b669 --- /dev/null +++ b/examples/1.1.5.rb @@ -0,0 +1 @@ +puts "I am a Rubyist" \ No newline at end of file diff --git a/examples/1.1.6.rb b/examples/1.1.6.rb new file mode 100644 index 0000000..55f4ca3 --- /dev/null +++ b/examples/1.1.6.rb @@ -0,0 +1 @@ +puts 'i am in lowercase'.upcase #=> 'I AM IN LOWERCASE' diff --git a/examples/1.1.7.rb b/examples/1.1.7.rb new file mode 100644 index 0000000..c12ed7d --- /dev/null +++ b/examples/1.1.7.rb @@ -0,0 +1 @@ +puts 'This is Mixed CASE' \ No newline at end of file diff --git a/examples/1.1.8.rb b/examples/1.1.8.rb new file mode 100644 index 0000000..73f1b10 --- /dev/null +++ b/examples/1.1.8.rb @@ -0,0 +1 @@ +puts "ThiS iS A vErY ComPlEx SenTeNcE" \ No newline at end of file diff --git a/examples_spec/1.1.2.rb b/examples_spec/1.1.2.rb new file mode 100644 index 0000000..60f0305 --- /dev/null +++ b/examples_spec/1.1.2.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'true'" do + expect(output).to eq("true") + end +end \ No newline at end of file diff --git a/examples_spec/1.1.3.rb b/examples_spec/1.1.3.rb new file mode 100644 index 0000000..60f0305 --- /dev/null +++ b/examples_spec/1.1.3.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'true'" do + expect(output).to eq("true") + end +end \ No newline at end of file diff --git a/examples_spec/1.1.4.rb b/examples_spec/1.1.4.rb new file mode 100644 index 0000000..60f0305 --- /dev/null +++ b/examples_spec/1.1.4.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'true'" do + expect(output).to eq("true") + end +end \ No newline at end of file diff --git a/examples_spec/1.1.5.rb b/examples_spec/1.1.5.rb new file mode 100644 index 0000000..48d808d --- /dev/null +++ b/examples_spec/1.1.5.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal '7'" do + expect(output).to eq("7") + end +end \ No newline at end of file diff --git a/examples_spec/1.1.7.rb b/examples_spec/1.1.7.rb new file mode 100644 index 0000000..1cd061c --- /dev/null +++ b/examples_spec/1.1.7.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'this is mixed case'" do + expect(output).to eq("this is mixed case") + end +end \ No newline at end of file diff --git a/examples_spec/1.1.8.rb b/examples_spec/1.1.8.rb new file mode 100644 index 0000000..a126aef --- /dev/null +++ b/examples_spec/1.1.8.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'tHIs Is a VeRy cOMpLeX sENtEnCe'" do + expect(output).to eq("tHIs Is a VeRy cOMpLeX sENtEnCe") + end +end \ No newline at end of file diff --git a/lessons/1.1.json b/lessons/1.1.json index c77e255..cbbd351 100644 --- a/lessons/1.1.json +++ b/lessons/1.1.json @@ -27,10 +27,10 @@ "type": "test_example", "value": "1.1.1" }, - { + { "type": "paragraph", - "value": "We've been using double quotes in all our string interpolation examples. A `String` literal created with single quotes does _not_ support interpolation." - }, + "value": "We've been using double quotes in all our string interpolation examples. A `String` literal created with single quotes does _not_ support interpolation." + }, { "type": "paragraph", "value": "The essential difference between using single or double quotes is that double quotes allow for escape sequences while single quotes do not. What you saw above is one such example. `\"\\n\"` is interpreted as a new line and appears as a new line when rendered to the user, whereas `'\\n'` displays the actual escape sequence to the user." @@ -85,6 +85,39 @@ "value": "1.1.5" } ] + }, + { + "name": "String Case Change", + "items": [ + { + "type": "header", + "value": "String Case Change" + }, + { + "type": "paragraph", + "value": "The last thing we will look into in this lesson is manipulating the case of strings. Ruby provides us with a convenient tool-set to take care of proper and consistent casing within strings. Let's start with converting a string in lower case to upper case." + }, + { + "type": "example", + "value": "1.1.6" + }, + { + "type": "paragraph", + "value": "Similarly, one can convert a string to lower case as well. Ruby calls this method `downcase`. Convert the string below into lower case." + }, + { + "type": "test_example", + "value": "1.1.7" + }, + { + "type": "paragraph", + "value": "On a parting note, let us touch on an interesting method. When you encounter a mixed cased string, Ruby provides a way to swap the case of every character in it i.e. this method would convert `\"I Am MixEd\"` to `\"i aM mIXeD\"`. Try to figure this method out and tell us if you ever come across a scenario where you find use for this. It might make a good story for a rainy night!" + }, + { + "type": "test_example", + "value": "1.1.8" + } + ] } ] } diff --git a/rp b/rp index 1b05f43..7c2a0f9 100755 --- a/rp +++ b/rp @@ -109,12 +109,14 @@ def decorate_output(location) end def run_code(code, decorate=true) + path = write_code_to_tmpfile(code) decorate_output("top") unless !decorate - command = "#{RbConfig.ruby} -e '#{code}'" + command = "#{RbConfig.ruby} #{path}" pid = spawn(command) Process.wait(pid) - puts "" + puts "" unless !decorate decorate_output("bottom") unless !decorate + File.unlink(path) end def test_code(code, example_id) @@ -184,27 +186,33 @@ def print_toc puts (" " * width).bold.underline puts "" puts " To begin learning with a specific lesson (such as 0.0), run: #{$0} 0.0" + puts " To start at a specific subsection (such as 0.0.1), run: #{$0} 0.0.1" puts "" end -def process_toc(lesson_id=nil) +def process_toc(lesson_id=nil, start_sub=0) toc = read_json_file('lessons/toc.json') toc['lessons'].each do |lesson_file| - if lesson_id + if lesson_id if lesson_file == "#{lesson_id}.json" - print_paragraph("\n\nSkipping to lesson: #{lesson_id}!") - process_lesson("lessons/#{lesson_id}.json") + if start_sub > 0 + print_paragraph("\n\nSkipping to lesson: #{lesson_id}, subsection #{start_sub + 1}!") + else + print_paragraph("\n\nSkipping to lesson: #{lesson_id}!") + end + process_lesson("lessons/#{lesson_id}.json", start_sub) end else - process_lesson("lessons/#{lesson_file}") + process_lesson("lessons/#{lesson_file}", 0) end end end -def process_lesson(lesson_file) +def process_lesson(lesson_file, start_sub = 0) lesson = read_json_file(lesson_file) - lesson['subsections'].each do |subsection| + lesson['subsections'].each_with_index do |subsection, i| + next if i < start_sub print_header(subsection['name']) subsection['items'].each do |item| case item['type'] @@ -225,10 +233,18 @@ def main(args) if args[0] == "toc" print_toc else - print_welcome() - press_enter() - lesson_id = args[0] - process_toc(lesson_id) + arg = args[0] + if arg =~ /^(\d+\.\d+)\.(\d+)$/ + lesson_id = $1 + sub_num = $2.to_i + start_sub = sub_num - 1 # 1-based to 0-based + else + lesson_id = arg + start_sub = 0 + end + print_welcome() if start_sub == 0 + press_enter() if start_sub == 0 + process_toc(lesson_id, start_sub) end end ######################################################## From 1277d4f1fcaccb6d0e92451844a46ccad214ae0e Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 12:07:47 -0600 Subject: [PATCH 04/12] add lesson 1.2 --- examples/1.2.0.rb | 1 + examples/1.2.1.rb | 1 + examples/1.2.2.rb | 1 + examples/1.2.3.rb | 1 + examples/1.2.4.rb | 1 + examples/1.2.5.rb | 1 + examples/1.2.6.rb | 1 + examples/1.2.7.rb | 1 + examples/1.2.8.rb | 1 + examples/1.2.9.rb | 1 + examples_spec/1.2.0.rb | 17 ++++++ examples_spec/1.2.3.rb | 13 +++++ examples_spec/1.2.7.rb | 13 +++++ examples_spec/1.2.9.rb | 13 +++++ lessons/1.2.json | 121 +++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- rp | 1 + 17 files changed, 190 insertions(+), 1 deletion(-) create mode 100644 examples/1.2.0.rb create mode 100644 examples/1.2.1.rb create mode 100644 examples/1.2.2.rb create mode 100644 examples/1.2.3.rb create mode 100644 examples/1.2.4.rb create mode 100644 examples/1.2.5.rb create mode 100644 examples/1.2.6.rb create mode 100644 examples/1.2.7.rb create mode 100644 examples/1.2.8.rb create mode 100644 examples/1.2.9.rb create mode 100644 examples_spec/1.2.0.rb create mode 100644 examples_spec/1.2.3.rb create mode 100644 examples_spec/1.2.7.rb create mode 100644 examples_spec/1.2.9.rb create mode 100644 lessons/1.2.json diff --git a/examples/1.2.0.rb b/examples/1.2.0.rb new file mode 100644 index 0000000..c8be537 --- /dev/null +++ b/examples/1.2.0.rb @@ -0,0 +1 @@ +'Fear is the path to the dark side' diff --git a/examples/1.2.1.rb b/examples/1.2.1.rb new file mode 100644 index 0000000..8fadd1c --- /dev/null +++ b/examples/1.2.1.rb @@ -0,0 +1 @@ +puts 'Ruby' + 'Monk' \ No newline at end of file diff --git a/examples/1.2.2.rb b/examples/1.2.2.rb new file mode 100644 index 0000000..d861e21 --- /dev/null +++ b/examples/1.2.2.rb @@ -0,0 +1 @@ +puts "Ruby".concat("Monk") \ No newline at end of file diff --git a/examples/1.2.3.rb b/examples/1.2.3.rb new file mode 100644 index 0000000..d861e21 --- /dev/null +++ b/examples/1.2.3.rb @@ -0,0 +1 @@ +puts "Ruby".concat("Monk") \ No newline at end of file diff --git a/examples/1.2.4.rb b/examples/1.2.4.rb new file mode 100644 index 0000000..7ba6c05 --- /dev/null +++ b/examples/1.2.4.rb @@ -0,0 +1 @@ +puts "I should look into your problem when I get time".sub('I','We') \ No newline at end of file diff --git a/examples/1.2.5.rb b/examples/1.2.5.rb new file mode 100644 index 0000000..3695141 --- /dev/null +++ b/examples/1.2.5.rb @@ -0,0 +1 @@ +puts "I should look into your problem when I get time".gsub('I','We') \ No newline at end of file diff --git a/examples/1.2.6.rb b/examples/1.2.6.rb new file mode 100644 index 0000000..7ca7ae9 --- /dev/null +++ b/examples/1.2.6.rb @@ -0,0 +1 @@ +puts 'RubyMonk'.gsub(/[aeiou]/,'1') \ No newline at end of file diff --git a/examples/1.2.7.rb b/examples/1.2.7.rb new file mode 100644 index 0000000..064ac8d --- /dev/null +++ b/examples/1.2.7.rb @@ -0,0 +1 @@ +puts 'RubyMonk Is Pretty Brilliant' \ No newline at end of file diff --git a/examples/1.2.8.rb b/examples/1.2.8.rb new file mode 100644 index 0000000..b6892a0 --- /dev/null +++ b/examples/1.2.8.rb @@ -0,0 +1 @@ +puts 'RubyMonk Is Pretty Brilliant'.match(/ ./) \ No newline at end of file diff --git a/examples/1.2.9.rb b/examples/1.2.9.rb new file mode 100644 index 0000000..b6892a0 --- /dev/null +++ b/examples/1.2.9.rb @@ -0,0 +1 @@ +puts 'RubyMonk Is Pretty Brilliant'.match(/ ./) \ No newline at end of file diff --git a/examples_spec/1.2.0.rb b/examples_spec/1.2.0.rb new file mode 100644 index 0000000..f3f930e --- /dev/null +++ b/examples_spec/1.2.0.rb @@ -0,0 +1,17 @@ +require 'rspec' + +describe "example code" do + let(:result) do + code = File.read('__TMPFILE__') + eval("def test_method; #{code}; end") + test_method + end + + it "should be an array" do + expect(result).to be_a(Array) + end + + it "should equal the expected array" do + expect(result).to eq(["Fear", "is", "the", "path", "to", "the", "dark", "side"]) + end +end \ No newline at end of file diff --git a/examples_spec/1.2.3.rb b/examples_spec/1.2.3.rb new file mode 100644 index 0000000..3ccab02 --- /dev/null +++ b/examples_spec/1.2.3.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'RubyMonk'" do + expect(output).to eq("RubyMonk") + end +end \ No newline at end of file diff --git a/examples_spec/1.2.7.rb b/examples_spec/1.2.7.rb new file mode 100644 index 0000000..c6f99c0 --- /dev/null +++ b/examples_spec/1.2.7.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal '0uby0onk 0s 0retty 0rilliant'" do + expect(output).to eq("0uby0onk 0s 0retty 0rilliant") + end +end \ No newline at end of file diff --git a/examples_spec/1.2.9.rb b/examples_spec/1.2.9.rb new file mode 100644 index 0000000..a223401 --- /dev/null +++ b/examples_spec/1.2.9.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal ' P'" do + expect(output).to eq(" P") + end +end \ No newline at end of file diff --git a/lessons/1.2.json b/lessons/1.2.json new file mode 100644 index 0000000..044a51e --- /dev/null +++ b/lessons/1.2.json @@ -0,0 +1,121 @@ +{ + "name": "Advanced String Operations", + "subsections": [ + { + "name": "Splitting Strings", + "items": [ + { + "type": "paragraph", + "value": "In this lesson, we will have a look at some vital methods that the `String` object provides for string manipulation." + }, + { + "type": "paragraph", + "value": "Splitting strings on a particular word, escape character or white space to get an array of sub-strings, is an oft-used technique. The method the Ruby `String` API provides for this is `String#split`. Let us begin by splitting the string below on space `' '` to get a collection of words in the string." + }, + { + "type": "test_example", + "value": "1.2.0" + }, + { + "type": "paragraph", + "value": "One can effectively manipulate strings by combining `String#split` and a Regular Expression. We will take look at Regular Expressions, their form and usage further down the lesson." + }, + { + "type": "paragraph", + "value": "It is possible to similarly split strings on new lines, and parse large amounts of data that is in the form of CSV." + } + ] + }, + { + "name": "Concatenating Strings", + "items": [ + { + "type": "paragraph", + "value": "You can create a new string by adding two strings together in Ruby, just like most other languages." + }, + { + "type": "example", + "value": "1.2.1" + }, + { + "type": "paragraph", + "value": "Ruby often provides more than one way to do the same thing. The literal and expressive method for `String` concatenation is `String#concat`." + }, + { + "type": "example", + "value": "1.2.2" + }, + { + "type": "paragraph", + "value": "Let's try a more widely used alias. You can use `<<` just like `+`, but in this case the `String` object `'Monk'` will be appended to the object represented by `'Ruby'` itself. In the first case of using `+`, the original string is not modified, as a third string `'RubyMonk'` is created. This can make a huge difference in your memory utilization, if you are doing really large scale string manipulations. Change the code above to use `<<` and see all the tests passing again." + }, + { + "type": "test_example", + "value": "1.2.3" + } + ] + }, + { + "name": "Replacing A Substring", + "items": [ + { + "type": "paragraph", + "value": "The Ruby String API provides strong support for searching and replacing within strings. We can search for sub-strings or use `Regex`. Let us first try our hands at something simple. This is how we would replace `'I'` with `'We'` in a given string:" + }, + { + "type": "example", + "value": "1.2.4" + }, + { + "type": "paragraph", + "value": "The method above only replaced the first occurrence of the term we were looking for. In order to replace all occurrences we can use a method called `gsub` which has a global scope." + }, + { + "type": "example", + "value": "1.2.5" + }, + { + "type": "paragraph", + "value": "If you haven't come across the term \"Regular Expression\" before, now is the time for introductions. Regular Expressions or RegExs are a concise and flexible means for \"matching\" particular characters, words, or patterns of characters. In Ruby you specify a RegEx by putting it between a pair of forward slashes (`/`). Now let's look at an example that replaces all the vowels with the number `1`:" + }, + { + "type": "example", + "value": "1.2.6" + }, + { + "type": "paragraph", + "value": "Could you replace all the characters in capital case with number `0` in the following problem?" + }, + { + "type": "test_example", + "value": "1.2.7" + } + ] + }, + { + "name": "Find A Substring Using RegEx", + "items": [ + { + "type": "paragraph", + "value": "We covered the art of finding the position of a substring in a previous lesson, but how do we handle those cases where we don't know exactly what we are looking for? That's where Regular Expressions come in handy. The `String#match` method converts a pattern to a `Regexp` (if it isn‘t already one), and then invokes its match method on the target `String` object. Here is how you find the characters from a `String` which are next to a whitespace:" + }, + { + "type": "example", + "value": "1.2.8" + }, + { + "type": "paragraph", + "value": "As you can see in the output, the method just returns the first match rather than all the matches. In order to find further matches, we can pass a second argument to the match method. When the second parameter is present, it specifies the position in the string to begin the search. Let's find the second character in the string `'RubyMonk Is Pretty Brilliant'` preceded by a space, which should be `'P'`." + }, + { + "type": "test_example", + "value": "1.2.9" + }, + { + "type": "paragraph", + "value": "All the complex use cases of this method involve more advanced Regular Expressions, which are outside the context of this lesson. However, on an ending note, if you ever choose to implement a parser, `String#match` might turn out to be a very good friend!" + } + ] + } + ] +} \ No newline at end of file diff --git a/lessons/toc.json b/lessons/toc.json index 580b6a0..7d77c99 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -4,6 +4,7 @@ "0.1.json", "0.2.json", "1.0.json", - "1.1.json" + "1.1.json", + "1.2.json" ] } diff --git a/rp b/rp index 7c2a0f9..40af6de 100755 --- a/rp +++ b/rp @@ -175,6 +175,7 @@ def print_toc width = 10 + max_name_length puts "" puts "Table of Contents".center(width).bold.underline + puts "" toc['lessons'].each do |lesson| id = lesson.sub('.json', '') puts " • #{id}" From 5507e5121c537e05dabe9b5a95df6d00c8ff12fd Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 15:25:35 -0600 Subject: [PATCH 05/12] update lesson format, add lesson 2.0 --- examples/2.0.0.rb | 1 + examples/2.0.1.rb | 1 + examples/2.0.2.rb | 1 + examples/2.0.3.rb | 1 + examples_spec/2.0.0.rb | 15 +++++++++ examples_spec/2.0.1.rb | 20 ++++++++++++ examples_spec/2.0.2.rb | 45 ++++++++++++++++++++++++++ examples_spec/2.0.3.rb | 20 ++++++++++++ lessons/0.0.json | 4 ++- lessons/0.1.json | 4 ++- lessons/0.2.json | 6 ++-- lessons/1.0.json | 26 +++++++++------- lessons/1.1.json | 8 ++--- lessons/1.2.json | 3 +- lessons/2.0.json | 71 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- rp | 29 ++++++++++++++--- 17 files changed, 232 insertions(+), 26 deletions(-) create mode 100644 examples/2.0.0.rb create mode 100644 examples/2.0.1.rb create mode 100644 examples/2.0.2.rb create mode 100644 examples/2.0.3.rb create mode 100644 examples_spec/2.0.0.rb create mode 100644 examples_spec/2.0.1.rb create mode 100644 examples_spec/2.0.2.rb create mode 100644 examples_spec/2.0.3.rb create mode 100644 lessons/2.0.json diff --git a/examples/2.0.0.rb b/examples/2.0.0.rb new file mode 100644 index 0000000..2db34c6 --- /dev/null +++ b/examples/2.0.0.rb @@ -0,0 +1 @@ +## update me diff --git a/examples/2.0.1.rb b/examples/2.0.1.rb new file mode 100644 index 0000000..a210184 --- /dev/null +++ b/examples/2.0.1.rb @@ -0,0 +1 @@ +age # change this expression diff --git a/examples/2.0.2.rb b/examples/2.0.2.rb new file mode 100644 index 0000000..25d5a91 --- /dev/null +++ b/examples/2.0.2.rb @@ -0,0 +1 @@ +age >= 23 && name == 'Bob' diff --git a/examples/2.0.3.rb b/examples/2.0.3.rb new file mode 100644 index 0000000..2db34c6 --- /dev/null +++ b/examples/2.0.3.rb @@ -0,0 +1 @@ +## update me diff --git a/examples_spec/2.0.0.rb b/examples_spec/2.0.0.rb new file mode 100644 index 0000000..f835b00 --- /dev/null +++ b/examples_spec/2.0.0.rb @@ -0,0 +1,15 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + + it "should return true when name is 'Bob'" do + result = eval("name = 'Bob'; #{code}") + expect(result).to eq(true) + end + + it "should return false when name is 'Sue'" do + result = eval("name = 'Sue'; #{code}") + expect(result).to eq(false) + end +end diff --git a/examples_spec/2.0.1.rb b/examples_spec/2.0.1.rb new file mode 100644 index 0000000..72c0835 --- /dev/null +++ b/examples_spec/2.0.1.rb @@ -0,0 +1,20 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + + it "should return true when age is 12" do + result = eval("age = 12; #{code}") + expect(result).to eq(true) + end + + it "should return true when age is 35" do + result = eval("age = 35; #{code}") + expect(result).to eq(true) + end + + it "should return false when age is 36" do + result = eval("age = 36; #{code}") + expect(result).to eq(false) + end +end diff --git a/examples_spec/2.0.2.rb b/examples_spec/2.0.2.rb new file mode 100644 index 0000000..460f54e --- /dev/null +++ b/examples_spec/2.0.2.rb @@ -0,0 +1,45 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + + it "should return true when age is 23 and name is 'Bob'" do + result = eval("age = 23; name = 'Bob'; #{code}") + expect(result).to eq(true) + end + + it "should return true when age is 23 and name is 'Jill'" do + result = eval("age = 23; name = 'Jill'; #{code}") + expect(result).to eq(true) + end + + it "should return false when age is 23 and name is 'Frank'" do + result = eval("age = 23; name = 'Frank'; #{code}") + expect(result).to eq(false) + end + + it "should return false when age is 20 and name is 'Bob'" do + result = eval("age = 20; name = 'Bob'; #{code}") + expect(result).to eq(false) + end + + it "should return false when age is 20 and name is 'Jill'" do + result = eval("age = 20; name = 'Jill'; #{code}") + expect(result).to eq(false) + end + + it "should return true when age is 50 and name is 'Bob'" do + result = eval("age = 50; name = 'Bob'; #{code}") + expect(result).to eq(true) + end + + it "should return true when age is 50 and name is 'Jill'" do + result = eval("age = 50; name = 'Jill'; #{code}") + expect(result).to eq(true) + end + + it "should return false when age is 50 and name is 'Frank'" do + result = eval("age = 50; name = 'Frank'; #{code}") + expect(result).to eq(false) + end +end \ No newline at end of file diff --git a/examples_spec/2.0.3.rb b/examples_spec/2.0.3.rb new file mode 100644 index 0000000..c966986 --- /dev/null +++ b/examples_spec/2.0.3.rb @@ -0,0 +1,20 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + + it "should return false when name is 'Bob'" do + result = eval("name = 'Bob'; #{code}") + expect(result).to eq(false) + end + + it "should return true when name is 'Sue'" do + result = eval("name = 'Sue'; #{code}") + expect(result).to eq(true) + end + + it "should return true when name is 'Jill'" do + result = eval("name = 'Jill'; #{code}") + expect(result).to eq(true) + end +end \ No newline at end of file diff --git a/lessons/0.0.json b/lessons/0.0.json index c49a601..d77eadf 100644 --- a/lessons/0.0.json +++ b/lessons/0.0.json @@ -1,4 +1,6 @@ { + "number": "0.0", + "name": "Introduction to Objects", "subsections": [ { "name": "Everything Is An Object", @@ -75,4 +77,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/lessons/0.1.json b/lessons/0.1.json index 1ef8a1a..81fe425 100644 --- a/lessons/0.1.json +++ b/lessons/0.1.json @@ -1,4 +1,6 @@ { + "number": "0.1", + "name": "More Objects And Methods", "subsections": [ { "name": "Looking Up Methods", @@ -71,4 +73,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/lessons/0.2.json b/lessons/0.2.json index d83cdb4..80976e4 100644 --- a/lessons/0.2.json +++ b/lessons/0.2.json @@ -1,7 +1,9 @@ { + "number": "0.2", + "name": "Syntactic Sugar for Special Methods", "subsections": [ { - "name": "Syntactic Sugar for Special Methods", + "name": "Special Methods", "items": [ { "type": "paragraph", @@ -70,4 +72,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/lessons/1.0.json b/lessons/1.0.json index 6a769e7..5da0952 100644 --- a/lessons/1.0.json +++ b/lessons/1.0.json @@ -1,12 +1,10 @@ { + "number": "1.0", + "name": "Introduction to Strings", "subsections": [ { - "name": "Introduction to Strings", + "name": "String Construction", "items": [ - { - "type": "paragraph", - "value": "String construction" - }, { "type": "paragraph", "value": "In doing lies the true path, so let us begin by doing. Type `edit` and then type `\"RubyMonk\"` in the editor (remember to include the double quotes)." @@ -22,11 +20,12 @@ { "type": "paragraph", "value": "Strings are key to communicating with a user and programming apprentices will encounter and start manipulating strings from the earliest stages of their journey. In this lesson, we will explore some of the tools Ruby provides to create and manipulate strings." - }, - { - "type": "paragraph", - "value": "Literal forms" - }, + } + ] + }, + { + "name": "Literal Forms", + "items": [ { "type": "paragraph", "value": "String construction has what is known as a literal form - the interpreter treats anything surrounded with single quotes (`'`) or double quotes(`\"`) as a string. In other words, both `'RubyMonk'` and `\"RubyMonk\"` will create instances of strings." @@ -54,7 +53,12 @@ { "type": "paragraph", "value": "All Strings are instances of the Ruby `String` class which provides a number of methods to manipulate the string. Now that you have successfully mastered creating strings let's take a look at some of the most commonly used methods." - }, + } + ] + }, + { + "name": "String Length", + "items": [ { "type": "paragraph", "value": "String Length" diff --git a/lessons/1.1.json b/lessons/1.1.json index cbbd351..5abf6e1 100644 --- a/lessons/1.1.json +++ b/lessons/1.1.json @@ -1,12 +1,10 @@ { + "number": "1.1", + "name": "String Basics", "subsections": [ { - "name": "String Basics", + "name": "String Interpolation", "items": [ - { - "type": "header", - "value": "String Interpolation" - }, { "type": "paragraph", "value": "It is essential to be able to replace placeholders within a string with values they represent. In the programming paradigm, this is called \"string interpolation\". In Ruby, string interpolation is extremely easy. Feel free to run the example below to see the sample in action." diff --git a/lessons/1.2.json b/lessons/1.2.json index 044a51e..c0b27fa 100644 --- a/lessons/1.2.json +++ b/lessons/1.2.json @@ -1,4 +1,5 @@ { + "number": "1.2", "name": "Advanced String Operations", "subsections": [ { @@ -118,4 +119,4 @@ ] } ] -} \ No newline at end of file +} diff --git a/lessons/2.0.json b/lessons/2.0.json new file mode 100644 index 0000000..ccfe6ac --- /dev/null +++ b/lessons/2.0.json @@ -0,0 +1,71 @@ +{ + "number": "2.0", + "name": "Boolean Expressions in Ruby", + "subsections": [ + { + "name": "Beginner's Guide to Expressions in Ruby", + "items": [ + { + "type": "paragraph", + "value": "Ruby uses the `==` operator for comparing two objects." + }, + { + "type": "paragraph", + "value": "Let us try a simple exercise: write an expression that checks whether the value of the variable `name` is `\"Bob\"`:" + }, + { + "type": "test_example", + "value": "2.0.0" + }, + { + "type": "paragraph", + "value": "The other usual operators like greater than (`>`), less than (`<`), greater than or equal to (`>=`) etc. are supported. The next exercise is to write an expression that validates whether `age` is less than or equal to `35`." + }, + { + "type": "test_example", + "value": "2.0.1" + }, + { + "type": "paragraph", + "value": "Boolean expressions like the above always return either the `true` or `false` objects." + }, + { + "type": "subheader", + "value": "Combining Expressions using the && and || operators" + }, + { + "type": "paragraph", + "value": "You can use the keywords `||` (read as 'or'), `&&` (read as 'and') to combine expressions. Try modifying the following expression to check whether `age` is greater than or equal to `23` and the name is either `Bob` or `Jill`." + }, + { + "type": "test_example", + "value": "2.0.2" + }, + { + "type": "paragraph", + "value": "Just like the order of operations in mathematical expressions (PEMDAS anybody?), Ruby also has a set of laws governing the precedence of its various operators. However it is not something you need to be concerned about for now. Just make sure to use parentheses generously so that the order of operation is unambiguous to Ruby as well as for someone reading your code." + }, + { + "type": "subheader", + "value": "Negating expressions" + }, + { + "type": "paragraph", + "value": "Ruby lets you negate expressions using the `!` operator (read as 'not'). For instance, `!(name == 'Jill')` will return `false` if the name is `Jill` and `true` for any other name." + }, + { + "type": "paragraph", + "value": "Now try writing a simple expression that accepts any `name` except `Bob`:" + }, + { + "type": "test_example", + "value": "2.0.3" + }, + { + "type": "paragraph", + "value": "Awesome! Now that you've learned the basics of writing boolean expressions in Ruby, let us see how we can use them to decide the flow of our application in the next lesson." + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 7d77c99..59a598c 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -5,6 +5,7 @@ "0.2.json", "1.0.json", "1.1.json", - "1.2.json" + "1.2.json", + "2.0.json" ] } diff --git a/rp b/rp index 40af6de..c326ac3 100755 --- a/rp +++ b/rp @@ -13,13 +13,25 @@ load 'lib/sayings.rb' def print_header(header) puts "" - puts "===========================" figlet = RubyFiglet::Figlet.new(header, 'standard') - puts figlet.to_s.magenta - puts "===========================" + figlet_text = figlet.to_s + max_width = figlet_text.lines.map(&:length).max + border = "=" * max_width + puts border + puts figlet_text.magenta + puts border puts "" end +def print_subheader(header) + puts header.underline +end + +def print_lesson_name(lesson_name) + figlet = RubyFiglet::Figlet.new(lesson_name, 'thin') + puts figlet.to_s.cyan +end + def print_welcome() message = <<-WELCOME Welcome to the Ruby Primer. This is a console version of the Ruby Primer @@ -156,6 +168,7 @@ end def press_enter() get_input("Press ENTER to continue") + puts "" end def read_json_file(file_name) @@ -212,15 +225,23 @@ end def process_lesson(lesson_file, start_sub = 0) lesson = read_json_file(lesson_file) + if lesson['number'] + print_lesson_name("Lesson #{lesson['number']}") + end + if lesson['name'] + print_header(lesson['name']) + end lesson['subsections'].each_with_index do |subsection, i| next if i < start_sub - print_header(subsection['name']) + print_subheader(subsection['name']) subsection['items'].each do |item| case item['type'] when "press_enter" press_enter() when "paragraph" print_paragraph(item['value']) + when "subheader" + print_subheader(item['value']) when "example" print_example(get_example(item['value'])) when "test_example" From 23568fe83ccb3fa21e456fcdec0f4ca8c704064c Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Tue, 16 Dec 2025 15:33:54 -0600 Subject: [PATCH 06/12] update formatting for "Example Code" --- rp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rp b/rp index c326ac3..e44bc44 100755 --- a/rp +++ b/rp @@ -60,7 +60,8 @@ def print_paragraph(para, mod=nil) end def print_example(example, test=false, example_id=nil) - print_paragraph("Example Code:", "underline") + puts ">>> Example Code <<<".underline.grey + puts "" print_paragraph(example, "green") ans = get_input("Type 'edit' to edit the code in vim, or type 'run' to run the code: ") if ans.downcase == "edit" From 633c22eff09ecf3fab599ba485cfa2d2cb37f8f5 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Wed, 17 Dec 2025 10:43:59 -0600 Subject: [PATCH 07/12] add lesson 2.1 --- examples/2.1.0.rb | 10 ++++++ examples/2.1.1.rb | 7 ++++ examples/2.1.2.rb | 4 +++ examples/2.1.3.rb | 6 ++++ examples/2.1.4.rb | 3 ++ examples_spec/2.1.1.rb | 20 ++++++++++++ lessons/2.1.json | 73 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- 8 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 examples/2.1.0.rb create mode 100644 examples/2.1.1.rb create mode 100644 examples/2.1.2.rb create mode 100644 examples/2.1.3.rb create mode 100644 examples/2.1.4.rb create mode 100644 examples_spec/2.1.1.rb create mode 100644 lessons/2.1.json diff --git a/examples/2.1.0.rb b/examples/2.1.0.rb new file mode 100644 index 0000000..6e5086b --- /dev/null +++ b/examples/2.1.0.rb @@ -0,0 +1,10 @@ +def check_sign(number) + if number > 0 + "#{number} is positive" + else + "#{number} is negative" + end +end +puts check_sign(-1) +puts check_sign(0) +puts check_sign(1) diff --git a/examples/2.1.1.rb b/examples/2.1.1.rb new file mode 100644 index 0000000..bae48f9 --- /dev/null +++ b/examples/2.1.1.rb @@ -0,0 +1,7 @@ +def check_sign(number) + if number > 0 + "#{number} is positive" + else + "#{number} is negative" + end +end \ No newline at end of file diff --git a/examples/2.1.2.rb b/examples/2.1.2.rb new file mode 100644 index 0000000..b59abd7 --- /dev/null +++ b/examples/2.1.2.rb @@ -0,0 +1,4 @@ +age = 10 +unless age >= 18 + puts "Sorry, you need to be at least eighteen to drive a car. Grow up fast!" +end \ No newline at end of file diff --git a/examples/2.1.3.rb b/examples/2.1.3.rb new file mode 100644 index 0000000..695570a --- /dev/null +++ b/examples/2.1.3.rb @@ -0,0 +1,6 @@ +def check_sign(number) + number > 0 ? "#{number} is positive" : "#{number} is negative" +end +puts check_sign(-1) +puts check_sign(0) +puts check_sign(1) diff --git a/examples/2.1.4.rb b/examples/2.1.4.rb new file mode 100644 index 0000000..3899b1f --- /dev/null +++ b/examples/2.1.4.rb @@ -0,0 +1,3 @@ +if 0 + puts "Hey, 0 is considered to be a truth in Ruby" +end \ No newline at end of file diff --git a/examples_spec/2.1.1.rb b/examples_spec/2.1.1.rb new file mode 100644 index 0000000..72d0560 --- /dev/null +++ b/examples_spec/2.1.1.rb @@ -0,0 +1,20 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + + it "should return '0' for 0" do + result = eval("#{code}; check_sign(0)") + expect(result).to eq("0") + end + + it "should return '5 is positive' for 5" do + result = eval("#{code}; check_sign(5)") + expect(result).to eq("5 is positive") + end + + it "should return '-3 is negative' for -3" do + result = eval("#{code}; check_sign(-3)") + expect(result).to eq("-3 is negative") + end +end \ No newline at end of file diff --git a/lessons/2.1.json b/lessons/2.1.json new file mode 100644 index 0000000..6202071 --- /dev/null +++ b/lessons/2.1.json @@ -0,0 +1,73 @@ +{ + "number": "2.1", + "name": "The if..else Construct", + "subsections": [ + { + "name": "Ruby Conditional Branching : The 'if' Statement", + "items": [ + { + "type": "paragraph", + "value": "You can use Ruby's Boolean Expressions to specifiy conditions using the usual `if..else` construct. Let us take a look at a simple example:" + }, + { + "type": "example", + "value": "2.1.0" + }, + { + "type": "paragraph", + "value": "When you run the above example, it will tell you that `0 is negative`. But we know that zero is neither positive nor negative. How do we fix this program so that zero is treated separately?" + }, + { + "type": "paragraph", + "value": "Ruby gives you the `elsif` keyword that helps you check for multiple possibilities inside an `if..else` construct. Try using `elsif` to fix the code below so that when the number is zero, it just prints `0`." + }, + { + "type": "test_example", + "value": "2.1.1" + }, + { + "type": "paragraph", + "value": "Ruby also has an `unless` keyword that can be used in places where you want to check for a negative condition. `unless x` is equivalent to `if !x`. Here is an example:" + }, + { + "type": "example", + "value": "2.1.2" + } + ] + }, + { + "name": "The Ternary Operator", + "items": [ + { + "type": "paragraph", + "value": "In English, \"ternary\" is an adjective meaning \"composed of three items.\" In a programming language, a ternary operator is simply short-hand for an `if`-`then`-`else` construct." + }, + { + "type": "paragraph", + "value": "In Ruby, `?` and `:` can be used to mean `then` and `else` respectively. Here's the first example in this lesson re-written using a ternary operator." + }, + { + "type": "example", + "value": "2.1.3" + } + ] + }, + { + "name": "Truthiness of Objects in Ruby", + "items": [ + { + "type": "paragraph", + "value": "The conditional statements `if` and `unless` can also use expressions that return an object that is not either `true` or `false`." + }, + { + "type": "paragraph", + "value": "In such cases, the objects `false` and `nil` equate to `false`. Every other object like, say, `1`, `0`, `\"\"` are all evaluated to be `true`. Here is a demonstration:" + }, + { + "type": "example", + "value": "2.1.4" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 59a598c..0f936be 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -6,6 +6,7 @@ "1.0.json", "1.1.json", "1.2.json", - "2.0.json" + "2.0.json", + "2.1.json" ] } From 43887df36ae2bdb048ef519e954fc8f85678ce67 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Wed, 17 Dec 2025 12:47:13 -0600 Subject: [PATCH 08/12] add lesson 2.2 --- examples/2.2.0.rb | 14 +++++++++ examples/2.2.1.rb | 11 +++++++ examples_spec/2.2.0.rb | 13 ++++++++ examples_spec/2.2.1.rb | 13 ++++++++ lessons/2.2.json | 71 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- 6 files changed, 124 insertions(+), 1 deletion(-) create mode 100644 examples/2.2.0.rb create mode 100644 examples/2.2.1.rb create mode 100644 examples_spec/2.2.0.rb create mode 100644 examples_spec/2.2.1.rb create mode 100644 lessons/2.2.json diff --git a/examples/2.2.0.rb b/examples/2.2.0.rb new file mode 100644 index 0000000..76caa86 --- /dev/null +++ b/examples/2.2.0.rb @@ -0,0 +1,14 @@ +monk = Object.new +def monk.nirvana? + @count ||= 0 + @count += 1 + @count > 3 +end + +def monk.meditate + puts "meditating" +end + +# you have to change the following statement (into multiple ones if needed) +# so that the monk meditates till monk.nirvana? becomes true. +monk.meditate unless monk.nirvana? diff --git a/examples/2.2.1.rb b/examples/2.2.1.rb new file mode 100644 index 0000000..118f35c --- /dev/null +++ b/examples/2.2.1.rb @@ -0,0 +1,11 @@ +bell = Object.new +def bell.ring + puts "ring" +end + +# add a loop inside this method to ring the bell 'n' times +def ring(bell, n) + bell.ring +end + +ring(bell, 3) diff --git a/examples_spec/2.2.0.rb b/examples_spec/2.2.0.rb new file mode 100644 index 0000000..ac99ae9 --- /dev/null +++ b/examples_spec/2.2.0.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'meditating\\nmeditating\\nmeditating\\nmeditating'" do + expect(output).to eq("meditating\nmeditating\nmeditating\nmeditating") + end +end \ No newline at end of file diff --git a/examples_spec/2.2.1.rb b/examples_spec/2.2.1.rb new file mode 100644 index 0000000..a5a0cd3 --- /dev/null +++ b/examples_spec/2.2.1.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:output) { `#{RbConfig.ruby} __TMPFILE__`.chomp } + + it "should be a string" do + expect(output.is_a?(String)).to be(true) + end + + it "should equal 'ring\\nring\\nring'" do + expect(output).to eq("ring\nring\nring") + end +end \ No newline at end of file diff --git a/lessons/2.2.json b/lessons/2.2.json new file mode 100644 index 0000000..39bff8e --- /dev/null +++ b/lessons/2.2.json @@ -0,0 +1,71 @@ +{ + "number": "2.2", + "name": "Loops in Ruby", + "subsections": [ + { + "name": "Loops in Ruby", + "items": [ + { + "type": "paragraph", + "value": "Loops are programming constructs that help you repeat an action an arbitrary number of times." + }, + { + "type": "paragraph", + "value": "The methods `Array#each`, `Array#select`, etc. are the most frequently used loops since the primary use of loops is to iterate over or transform a collection, something that we'll learn in the chapter on \"Arrays in Ruby.\"" + }, + { + "type": "paragraph", + "value": "Here we will cover two basic looping constructs you can use to solve most other looping requirements that may come up." + }, + { + "type": "subheader", + "value": "Infinite Loops" + }, + { + "type": "paragraph", + "value": "Infinite loops keep running till you explicitly ask them to stop. They are syntactically the simplest to write. Here goes one:" + }, + { + "type": "paragraph", + "value": "```\nloop do \n puts \"this line will be executed for an infinite amount of time\" \nend\n```" + }, + { + "type": "paragraph", + "value": "The example above does not have a termination condition and hence will run till the process is stopped. A loop can be halted from within using the `break` command." + }, + { + "type": "paragraph", + "value": "Now write an infinite loop where the monk will meditate till he achieves Nirvana. Use the `break` statement once Nirvana is reached." + }, + { + "type": "test_example", + "value": "2.2.0" + }, + { + "type": "subheader", + "value": "Run A Block of Code N Times" + }, + { + "type": "paragraph", + "value": "Say `N` is `5`, let us imagine how it might look." + }, + { + "type": "paragraph", + "value": "```\n5.times do\n # do the stuff that needs to be done\nend \n```" + }, + { + "type": "paragraph", + "value": "Well, we imagined it right. It is that simple!" + }, + { + "type": "test_example", + "value": "2.2.1" + }, + { + "type": "paragraph", + "value": "We've only scratched the surface of the various ways in which Ruby lets you write loops. However, this should be enough for the most common use cases. Let us know if you need more!" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 0f936be..141da4d 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -7,6 +7,7 @@ "1.1.json", "1.2.json", "2.0.json", - "2.1.json" + "2.1.json", + "2.2.json" ] } From a5d47392805c814893ebc93d2e6253a77b22984e Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Wed, 17 Dec 2025 13:13:39 -0600 Subject: [PATCH 09/12] add lesson 3.0 --- examples/3.0.0.rb | 1 + examples/3.0.1.rb | 1 + examples/3.0.2.rb | 1 + examples/3.0.3.rb | 1 + examples/3.0.4.rb | 1 + examples/3.0.5.rb | 1 + examples/3.0.6.rb | 1 + examples/3.0.7.rb | 1 + examples/3.0.8.rb | 1 + examples/3.0.9.rb | 1 + examples_spec/3.0.0.rb | 14 ++++++ examples_spec/3.0.1.rb | 14 ++++++ examples_spec/3.0.2.rb | 14 ++++++ examples_spec/3.0.3.rb | 14 ++++++ examples_spec/3.0.5.rb | 14 ++++++ examples_spec/3.0.7.rb | 14 ++++++ examples_spec/3.0.8.rb | 14 ++++++ examples_spec/3.0.9.rb | 14 ++++++ lessons/3.0.json | 110 +++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- 20 files changed, 234 insertions(+), 1 deletion(-) create mode 100644 examples/3.0.0.rb create mode 100644 examples/3.0.1.rb create mode 100644 examples/3.0.2.rb create mode 100644 examples/3.0.3.rb create mode 100644 examples/3.0.4.rb create mode 100644 examples/3.0.5.rb create mode 100644 examples/3.0.6.rb create mode 100644 examples/3.0.7.rb create mode 100644 examples/3.0.8.rb create mode 100644 examples/3.0.9.rb create mode 100644 examples_spec/3.0.0.rb create mode 100644 examples_spec/3.0.1.rb create mode 100644 examples_spec/3.0.2.rb create mode 100644 examples_spec/3.0.3.rb create mode 100644 examples_spec/3.0.5.rb create mode 100644 examples_spec/3.0.7.rb create mode 100644 examples_spec/3.0.8.rb create mode 100644 examples_spec/3.0.9.rb create mode 100644 lessons/3.0.json diff --git a/examples/3.0.0.rb b/examples/3.0.0.rb new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/examples/3.0.0.rb @@ -0,0 +1 @@ +[] diff --git a/examples/3.0.1.rb b/examples/3.0.1.rb new file mode 100644 index 0000000..4840402 --- /dev/null +++ b/examples/3.0.1.rb @@ -0,0 +1 @@ +Array.new \ No newline at end of file diff --git a/examples/3.0.2.rb b/examples/3.0.2.rb new file mode 100644 index 0000000..fe51488 --- /dev/null +++ b/examples/3.0.2.rb @@ -0,0 +1 @@ +[] diff --git a/examples/3.0.3.rb b/examples/3.0.3.rb new file mode 100644 index 0000000..69b57c2 --- /dev/null +++ b/examples/3.0.3.rb @@ -0,0 +1 @@ +[1, 'one', 2, 'two'] \ No newline at end of file diff --git a/examples/3.0.4.rb b/examples/3.0.4.rb new file mode 100644 index 0000000..f2bde3f --- /dev/null +++ b/examples/3.0.4.rb @@ -0,0 +1 @@ +p [1, 2, 3, 4, 5][2] diff --git a/examples/3.0.5.rb b/examples/3.0.5.rb new file mode 100644 index 0000000..a12a256 --- /dev/null +++ b/examples/3.0.5.rb @@ -0,0 +1 @@ +[1,2,3,4,5,6,7] diff --git a/examples/3.0.6.rb b/examples/3.0.6.rb new file mode 100644 index 0000000..77c8dfb --- /dev/null +++ b/examples/3.0.6.rb @@ -0,0 +1 @@ +p [1, 2, 3, 4, 5][-5] diff --git a/examples/3.0.7.rb b/examples/3.0.7.rb new file mode 100644 index 0000000..bfedf5b --- /dev/null +++ b/examples/3.0.7.rb @@ -0,0 +1 @@ +[1,2,3,4,5] diff --git a/examples/3.0.8.rb b/examples/3.0.8.rb new file mode 100644 index 0000000..bfedf5b --- /dev/null +++ b/examples/3.0.8.rb @@ -0,0 +1 @@ +[1,2,3,4,5] diff --git a/examples/3.0.9.rb b/examples/3.0.9.rb new file mode 100644 index 0000000..bfedf5b --- /dev/null +++ b/examples/3.0.9.rb @@ -0,0 +1 @@ +[1,2,3,4,5] diff --git a/examples_spec/3.0.0.rb b/examples_spec/3.0.0.rb new file mode 100644 index 0000000..73abb28 --- /dev/null +++ b/examples_spec/3.0.0.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal []" do + expect(result).to eq([]) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.1.rb b/examples_spec/3.0.1.rb new file mode 100644 index 0000000..73abb28 --- /dev/null +++ b/examples_spec/3.0.1.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal []" do + expect(result).to eq([]) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.2.rb b/examples_spec/3.0.2.rb new file mode 100644 index 0000000..4850724 --- /dev/null +++ b/examples_spec/3.0.2.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, 2, 3, 4, 5]" do + expect(result).to eq([1, 2, 3, 4, 5]) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.3.rb b/examples_spec/3.0.3.rb new file mode 100644 index 0000000..2d55625 --- /dev/null +++ b/examples_spec/3.0.3.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, \"one\", 2, \"two\"]" do + expect(result).to eq([1, "one", 2, "two"]) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.5.rb b/examples_spec/3.0.5.rb new file mode 100644 index 0000000..663cca6 --- /dev/null +++ b/examples_spec/3.0.5.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an integer" do + expect(result).to be_a(Integer) + end + + it "should equal 5" do + expect(result).to eq(5) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.7.rb b/examples_spec/3.0.7.rb new file mode 100644 index 0000000..663cca6 --- /dev/null +++ b/examples_spec/3.0.7.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an integer" do + expect(result).to be_a(Integer) + end + + it "should equal 5" do + expect(result).to eq(5) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.8.rb b/examples_spec/3.0.8.rb new file mode 100644 index 0000000..adc91bb --- /dev/null +++ b/examples_spec/3.0.8.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, 2, 3, 4, 5, \"woot\"]" do + expect(result).to eq([1, 2, 3, 4, 5, "woot"]) + end +end \ No newline at end of file diff --git a/examples_spec/3.0.9.rb b/examples_spec/3.0.9.rb new file mode 100644 index 0000000..adc91bb --- /dev/null +++ b/examples_spec/3.0.9.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, 2, 3, 4, 5, \"woot\"]" do + expect(result).to eq([1, 2, 3, 4, 5, "woot"]) + end +end \ No newline at end of file diff --git a/lessons/3.0.json b/lessons/3.0.json new file mode 100644 index 0000000..7dab0da --- /dev/null +++ b/lessons/3.0.json @@ -0,0 +1,110 @@ +{ + "number": "3.0", + "name": "Introduction to Arrays", + "subsections": [ + { + "name": "Empty Arrays", + "items": [ + { + "type": "paragraph", + "value": "It is easier than you think. There is a `[]` typed into the code editor already. This is how you create an array." + }, + { + "type": "test_example", + "value": "3.0.0" + }, + { + "type": "paragraph", + "value": "You can also do the same thing by `Array.new`." + }, + { + "type": "test_example", + "value": "3.0.1" + } + ] + }, + { + "name": "Building Arrays", + "items": [ + { + "type": "paragraph", + "value": "You can create an array with a set of values by simply placing them inside `[]` like this: `[1, 2, 3]`. Try this out by creating an array with the numbers `1` through `5`, inclusive." + }, + { + "type": "test_example", + "value": "3.0.2" + }, + { + "type": "paragraph", + "value": "Arrays in Ruby allow you to store any kind of objects in any combination with no restrictions on type. Thus, the literal array `[1, 'one', 2, 'two']` mixes `Integer`s and `String`s and is perfectly valid." + }, + { + "type": "test_example", + "value": "3.0.3" + } + ] + }, + { + "name": "Looking Up Data in Arrays", + "items": [ + { + "type": "paragraph", + "value": "Looking up values within an array is easily done using an index. Like most languages, arrays in Ruby have indexes starting from `0`. The example below demonstrates how to look up the third value in an array." + }, + { + "type": "example", + "value": "3.0.4" + }, + { + "type": "paragraph", + "value": "Now it's your turn - extract the 5th value from the array below. Remember that the nth value in an array has an index of `n-1`." + }, + { + "type": "test_example", + "value": "3.0.5" + }, + { + "type": "paragraph", + "value": "Array indexes can also start from the end of the array, rather than the beginning! In Ruby, this is achieved by using negative numbers. This is called reverse index lookup. In this case, the values of the index start at -1 and become smaller. The example below returns the first value in the array." + }, + { + "type": "example", + "value": "3.0.6" + }, + { + "type": "paragraph", + "value": "Go ahead and try it out - extract the last value from the array below." + }, + { + "type": "test_example", + "value": "3.0.7" + } + ] + }, + { + "name": "Growing Arrays", + "items": [ + { + "type": "paragraph", + "value": "In Ruby, the size of an array is not fixed. Also, any object of any type can be added to an array, not just numbers. How about appending the String `\"woot\"` to an array? Try using `<<` - that's the `append` function - to add it to the array below." + }, + { + "type": "test_example", + "value": "3.0.8" + }, + { + "type": "paragraph", + "value": "Unlike many other languages, you will always find multiple ways to perform the same action in Ruby. To append a new element to a given array, you can also use `push` method on an array. Add the string `\"woot\"` to given array by calling `push`." + }, + { + "type": "test_example", + "value": "3.0.9" + }, + { + "type": "paragraph", + "value": "Using `<<` is the most common method to add an element to an Array. There are other ways as well, but we will touch upon them later." + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 141da4d..6832ece 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -8,6 +8,7 @@ "1.2.json", "2.0.json", "2.1.json", - "2.2.json" + "2.2.json", + "3.0.json" ] } From daee4e82c3b88148c44cab5610703d47002fca1a Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Wed, 17 Dec 2025 17:57:46 -0600 Subject: [PATCH 10/12] update toc, part of lesson 3.1 --- examples/3.1.0.rb | 1 + examples/3.1.1.rb | 1 + examples/3.1.2.rb | 2 ++ examples/3.1.3.rb | 2 ++ examples_spec/3.1.1.rb | 14 ++++++++++++ lessons/3.1.json | 48 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 ++- rp | 29 +++++++++---------------- 8 files changed, 80 insertions(+), 20 deletions(-) create mode 100644 examples/3.1.0.rb create mode 100644 examples/3.1.1.rb create mode 100644 examples/3.1.2.rb create mode 100644 examples/3.1.3.rb create mode 100644 examples_spec/3.1.1.rb create mode 100644 lessons/3.1.json diff --git a/examples/3.1.0.rb b/examples/3.1.0.rb new file mode 100644 index 0000000..d5dee59 --- /dev/null +++ b/examples/3.1.0.rb @@ -0,0 +1 @@ +p [1,2,3,4,5].map { |i| i + 1 } diff --git a/examples/3.1.1.rb b/examples/3.1.1.rb new file mode 100644 index 0000000..bfedf5b --- /dev/null +++ b/examples/3.1.1.rb @@ -0,0 +1 @@ +[1,2,3,4,5] diff --git a/examples/3.1.2.rb b/examples/3.1.2.rb new file mode 100644 index 0000000..0341669 --- /dev/null +++ b/examples/3.1.2.rb @@ -0,0 +1,2 @@ +# select even numbers +p [1,2,3,4,5,6].select {|number| number % 2 == 0} diff --git a/examples/3.1.3.rb b/examples/3.1.3.rb new file mode 100644 index 0000000..2cb9fe5 --- /dev/null +++ b/examples/3.1.3.rb @@ -0,0 +1,2 @@ +names = ['rock', 'paper', 'scissors', 'lizard', 'spock'] +# your code here \ No newline at end of file diff --git a/examples_spec/3.1.1.rb b/examples_spec/3.1.1.rb new file mode 100644 index 0000000..2781596 --- /dev/null +++ b/examples_spec/3.1.1.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [3, 6, 9, 12, 15]" do + expect(result).to eq([3, 6, 9, 12, 15]) + end +end \ No newline at end of file diff --git a/lessons/3.1.json b/lessons/3.1.json new file mode 100644 index 0000000..af02924 --- /dev/null +++ b/lessons/3.1.json @@ -0,0 +1,48 @@ +{ + "number": "3.1", + "name": "Basic Array Operations", + "subsections": [ + { + "name": "Transforming Arrays", + "items": [ + { + "type": "paragraph", + "value": "Now on to more interesting things, but with a little tip from me first. Try running this:" + }, + { + "type": "example", + "value": "3.1.0" + }, + { + "type": "paragraph", + "value": "You'll notice that the output, `[2, 3, 4, 5, 6]` is the result of applying the code inside the curly brace to every single element in the array. The result is an entirely new array containing the results. In Ruby, the method `map` is used to transform the contents of an array according to a specified set of rules defined inside the code block. Go on, you try it. Multiply every element in the array below by 3 to get `[3, 6 .. 15]`." + }, + { + "type": "test_example", + "value": "3.1.1" + } + ] + }, + { + "name": "Filtering Elements of An Array", + "items": [ + { + "type": "paragraph", + "value": "Filtering elements in a collection according to a boolean expression is a very common operation in day-to-day programming. Ruby provides the rather handy `select` method to make this easy." + }, + { + "type": "example", + "value": "3.1.2" + }, + { + "type": "paragraph", + "value": "The method `select` is the standard Ruby idiom for filtering elements. In the following code, try extracting the strings that are longer than five characters." + }, + { + "type": "test_example", + "value": "3.1.3" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 6832ece..f1fc2bb 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -9,6 +9,7 @@ "2.0.json", "2.1.json", "2.2.json", - "3.0.json" + "3.0.json", + "3.1.json" ] } diff --git a/rp b/rp index e44bc44..a903ec9 100755 --- a/rp +++ b/rp @@ -60,7 +60,7 @@ def print_paragraph(para, mod=nil) end def print_example(example, test=false, example_id=nil) - puts ">>> Example Code <<<".underline.grey + puts ">>> Example #{example_id} <<<".underline.grey puts "" print_paragraph(example, "green") ans = get_input("Type 'edit' to edit the code in vim, or type 'run' to run the code: ") @@ -179,29 +179,20 @@ end def print_toc toc = read_json_file('lessons/toc.json') - max_name_length = 0 + entries = [] toc['lessons'].each do |lesson| + id = lesson.sub('.json', '') lesson_data = read_json_file("lessons/#{lesson}") - lesson_data['subsections'].each do |sub| - max_name_length = [max_name_length, sub['name'].length].max - end + name = lesson_data['name'] || id + entries << "#{id}: #{name}" end - width = 10 + max_name_length + max_entry_length = entries.map(&:length).max || 0 + width = 8 + max_entry_length puts "" puts "Table of Contents".center(width).bold.underline - puts "" - toc['lessons'].each do |lesson| - id = lesson.sub('.json', '') - puts " • #{id}" - lesson_data = read_json_file("lessons/#{lesson}") - lesson_data['subsections'].each do |sub| - puts " ◦ #{sub['name']}" - end + entries.each do |entry| + puts " • #{entry}" end - puts (" " * width).bold.underline - puts "" - puts " To begin learning with a specific lesson (such as 0.0), run: #{$0} 0.0" - puts " To start at a specific subsection (such as 0.0.1), run: #{$0} 0.0.1" puts "" end @@ -244,7 +235,7 @@ def process_lesson(lesson_file, start_sub = 0) when "subheader" print_subheader(item['value']) when "example" - print_example(get_example(item['value'])) + print_example(get_example(item['value']), false, item['value']) when "test_example" print_example(get_example(item['value']), true, item['value']) end From 0bdf3c8e1d796caa0a889f2db7a83c727fbe3f09 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Wed, 17 Dec 2025 18:10:01 -0600 Subject: [PATCH 11/12] rest of lesson 3.1 --- examples/3.1.4.rb | 2 ++ examples/3.1.5.rb | 1 + examples/3.1.6.rb | 2 ++ examples_spec/3.1.3.rb | 14 ++++++++++++++ examples_spec/3.1.4.rb | 16 ++++++++++++++++ examples_spec/3.1.5.rb | 14 ++++++++++++++ examples_spec/3.1.6.rb | 16 ++++++++++++++++ lessons/3.1.json | 33 +++++++++++++++++++++++++++++++++ 8 files changed, 98 insertions(+) create mode 100644 examples/3.1.4.rb create mode 100644 examples/3.1.5.rb create mode 100644 examples/3.1.6.rb create mode 100644 examples_spec/3.1.3.rb create mode 100644 examples_spec/3.1.4.rb create mode 100644 examples_spec/3.1.5.rb create mode 100644 examples_spec/3.1.6.rb diff --git a/examples/3.1.4.rb b/examples/3.1.4.rb new file mode 100644 index 0000000..382b323 --- /dev/null +++ b/examples/3.1.4.rb @@ -0,0 +1,2 @@ +arr = [1,3,5,4,6,7] +## how can you manipulate arr to delete 5? \ No newline at end of file diff --git a/examples/3.1.5.rb b/examples/3.1.5.rb new file mode 100644 index 0000000..bc7fa33 --- /dev/null +++ b/examples/3.1.5.rb @@ -0,0 +1 @@ +p [1,2,3,4,5,6,7].delete_if { |i| i < 4 } diff --git a/examples/3.1.6.rb b/examples/3.1.6.rb new file mode 100644 index 0000000..c10037c --- /dev/null +++ b/examples/3.1.6.rb @@ -0,0 +1,2 @@ +arr = [1,2,3,4,5,6,7,8,9] +## how can you manipulate arr to delete all even numbers? diff --git a/examples_spec/3.1.3.rb b/examples_spec/3.1.3.rb new file mode 100644 index 0000000..27377c1 --- /dev/null +++ b/examples_spec/3.1.3.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal ['scissors', 'lizard']" do + expect(result).to eq(['scissors', 'lizard']) + end +end \ No newline at end of file diff --git a/examples_spec/3.1.4.rb b/examples_spec/3.1.4.rb new file mode 100644 index 0000000..dbc8834 --- /dev/null +++ b/examples_spec/3.1.4.rb @@ -0,0 +1,16 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:context) { binding() } + + before { eval(code, context) } + + it "should have arr as an array" do + expect(eval("arr", context)).to be_a(Array) + end + + it "should have arr equal [1, 3, 4, 6, 7]" do + expect(eval("arr", context)).to eq([1, 3, 4, 6, 7]) + end +end \ No newline at end of file diff --git a/examples_spec/3.1.5.rb b/examples_spec/3.1.5.rb new file mode 100644 index 0000000..591069c --- /dev/null +++ b/examples_spec/3.1.5.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [4, 5, 6, 7]" do + expect(result).to eq([4, 5, 6, 7]) + end +end \ No newline at end of file diff --git a/examples_spec/3.1.6.rb b/examples_spec/3.1.6.rb new file mode 100644 index 0000000..d3b4241 --- /dev/null +++ b/examples_spec/3.1.6.rb @@ -0,0 +1,16 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:context) { binding() } + + before { eval(code, context) } + + it "should have arr as an array" do + expect(eval("arr", context)).to be_a(Array) + end + + it "should have arr equal [1, 3, 5, 7, 9]" do + expect(eval("arr", context)).to eq([1, 3, 5, 7, 9]) + end +end \ No newline at end of file diff --git a/lessons/3.1.json b/lessons/3.1.json index af02924..a2f6d0b 100644 --- a/lessons/3.1.json +++ b/lessons/3.1.json @@ -43,6 +43,39 @@ "value": "3.1.3" } ] + }, + { + "name": "Deleting Elements", + "items": [ + { + "type": "paragraph", + "value": "One of the reasons Ruby is so popular with developers is the intuitiveness of the API. Most of the time you can guess the method name which will perform the task you have in your mind. Try guessing the method you need to use to delete the element '5' from the array given below." + }, + { + "type": "test_example", + "value": "3.1.4" + }, + { + "type": "paragraph", + "value": "I guess that was easy. What if you want to delete all the elements less than 4 from the given array. The example below does just that." + }, + { + "type": "example", + "value": "3.1.5" + }, + { + "type": "paragraph", + "value": "You'll notice that Ruby methods with multiple words are separated by underscores (_). This convention is called 'snake_casing' because `a_longer_method_looks_kind_of_like_a_snake`. Okay! Hands-on time. Delete all the even numbers from the array given below." + }, + { + "type": "test_example", + "value": "3.1.6" + }, + { + "type": "paragraph", + "value": "Doing this in languages like C or Java would take you a lot of boiler plate code. The beauty of Ruby is in its concise but readable code." + } + ] } ] } From f9b180465cdf2fe36ebf60def607a1db4cfa5282 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Thu, 18 Dec 2025 07:10:17 -0600 Subject: [PATCH 12/12] add lesson 3.2 --- examples/3.2.0.rb | 4 ++++ examples/3.2.1.rb | 5 +++++ examples/3.2.2.rb | 4 ++++ examples/3.2.3.rb | 5 +++++ examples_spec/3.2.1.rb | 14 ++++++++++++ examples_spec/3.2.3.rb | 14 ++++++++++++ lessons/3.1.json | 2 +- lessons/3.2.json | 48 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 ++- 9 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 examples/3.2.0.rb create mode 100644 examples/3.2.1.rb create mode 100644 examples/3.2.2.rb create mode 100644 examples/3.2.3.rb create mode 100644 examples_spec/3.2.1.rb create mode 100644 examples_spec/3.2.3.rb create mode 100644 lessons/3.2.json diff --git a/examples/3.2.0.rb b/examples/3.2.0.rb new file mode 100644 index 0000000..ef6941b --- /dev/null +++ b/examples/3.2.0.rb @@ -0,0 +1,4 @@ +array = [1, 2, 3, 4, 5] +for i in array + puts i +end \ No newline at end of file diff --git a/examples/3.2.1.rb b/examples/3.2.1.rb new file mode 100644 index 0000000..d9faefc --- /dev/null +++ b/examples/3.2.1.rb @@ -0,0 +1,5 @@ +def array_copy(source) + destination = [] + # your code + return destination +end \ No newline at end of file diff --git a/examples/3.2.2.rb b/examples/3.2.2.rb new file mode 100644 index 0000000..4898a2c --- /dev/null +++ b/examples/3.2.2.rb @@ -0,0 +1,4 @@ +array = [1, 2, 3, 4, 5] +array.each do |i| + puts i +end \ No newline at end of file diff --git a/examples/3.2.3.rb b/examples/3.2.3.rb new file mode 100644 index 0000000..d9faefc --- /dev/null +++ b/examples/3.2.3.rb @@ -0,0 +1,5 @@ +def array_copy(source) + destination = [] + # your code + return destination +end \ No newline at end of file diff --git a/examples_spec/3.2.1.rb b/examples_spec/3.2.1.rb new file mode 100644 index 0000000..dccc4f4 --- /dev/null +++ b/examples_spec/3.2.1.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code + "; array_copy([1,2,3,4,5])") } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, 2, 3]" do + expect(result).to eq([1, 2, 3]) + end +end \ No newline at end of file diff --git a/examples_spec/3.2.3.rb b/examples_spec/3.2.3.rb new file mode 100644 index 0000000..dccc4f4 --- /dev/null +++ b/examples_spec/3.2.3.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code + "; array_copy([1,2,3,4,5])") } + + it "should return an array" do + expect(result).to be_a(Array) + end + + it "should equal [1, 2, 3]" do + expect(result).to eq([1, 2, 3]) + end +end \ No newline at end of file diff --git a/lessons/3.1.json b/lessons/3.1.json index a2f6d0b..c26575f 100644 --- a/lessons/3.1.json +++ b/lessons/3.1.json @@ -65,7 +65,7 @@ }, { "type": "paragraph", - "value": "You'll notice that Ruby methods with multiple words are separated by underscores (_). This convention is called 'snake_casing' because `a_longer_method_looks_kind_of_like_a_snake`. Okay! Hands-on time. Delete all the even numbers from the array given below." + "value": "You'll notice that Ruby methods with multiple words are separated by underscores (`_`). This convention is called 'snake_casing' because `a_longer_method_looks_kind_of_like_a_snake`. Okay! Hands-on time. Delete all the even numbers from the array given below." }, { "type": "test_example", diff --git a/lessons/3.2.json b/lessons/3.2.json new file mode 100644 index 0000000..46eb8d8 --- /dev/null +++ b/lessons/3.2.json @@ -0,0 +1,48 @@ +{ + "number": "3.2", + "name": "Iteration", + "subsections": [ + { + "name": "Got 'for' Loops?", + "items": [ + { + "type": "paragraph", + "value": "Ruby, like most languages, has the all-time favourite `for` loop. Interestingly, nobody uses it much - but let's leave the alternatives for the next exercise. Here's how you use a `for` loop in Ruby. Just run the code below to print all the values in the array." + }, + { + "type": "example", + "value": "3.2.0" + }, + { + "type": "paragraph", + "value": "Ok, your turn now. Copy the values less than `4` in the array stored in the `source` variable into the array in the `destination` variable." + }, + { + "type": "test_example", + "value": "3.2.1" + } + ] + }, + { + "name": "Looping with 'each'", + "items": [ + { + "type": "paragraph", + "value": "Iteration is one of the most commonly cited examples of the usage of *blocks* in Ruby. The `Array#each` method accepts a block to which each element of the array is passed in turn. You will find that `for` loops are hardly ever used in Ruby, and `Array#each` and its siblings are the de-facto standard. We'll go into the relative merits of using `each` over for loops a little later once you've had some time to get familiar with it. Let's look at an example that prints all the values in an array." + }, + { + "type": "example", + "value": "3.2.2" + }, + { + "type": "paragraph", + "value": "Ok, now let's try the same thing we did with the `for` loop earlier - copy the values less than `4` in the array stored in the `source` variable to the array in the `destination` variable." + }, + { + "type": "test_example", + "value": "3.2.3" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index f1fc2bb..2ab07f1 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -10,6 +10,7 @@ "2.1.json", "2.2.json", "3.0.json", - "3.1.json" + "3.1.json", + "3.2.json" ] }