From 64b33094524fa8d12cf69dbfb364b3f568fbc2d3 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 09:54:53 -0600 Subject: [PATCH 1/7] Update AGENTS.md with installation, Ruby version, CI details, and terminal support --- AGENTS.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 2dd94cf..94b9d77 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,11 +1,18 @@ ## Ruby Primer Agent Instructions +### Installation + +- **Prerequisites**: Ruby 3.2+, pandoc, xelatex, imagemagick, viu, IBM Plex Serif Light font. +- **Setup**: Run `./install.sh` to install Ruby gems, utilities, and configure the app. + ### Build, Lint, and Test +- **Ruby Version**: Requires Ruby 3.2 or higher. - **Testing**: This project uses RSpec. - Run all tests: `bundle exec rspec` - Run a single test file: `bundle exec rspec examples_spec/0.0.2.rb` - **Linting**: There is no linting configuration. Please adhere to the style of existing code. +- **CI**: GitHub Actions run Ruby and JSON syntax checks on pull requests (`.github/workflows/ruby-syntax.yml` and `json-syntax.yml`). Checks run on all PRs and validate `.rb` files, `rp`, and `.json` files using `ruby -c` and `jq`. ### Code Style @@ -13,7 +20,7 @@ - **Naming**: Use `snake_case` for variables and methods. - **Strings**: Use double quotes for strings, especially with interpolation. Use single quotes for `require` statements. - **Methods**: Use parentheses for method definitions with arguments. -- **Error Handling**: Not specified. - **Imports**: Use `require`. - **Types**: Not specified. +- **Terminal Support**: Check for minimum 135 columns x 40 rows at startup. Detect Kitty or iTerm2 for image display; generate PDFs/PNGs for formatted text. - **General**: Follow existing patterns in the code. Explicit `return` is preferred. From ddb2a0078aafa1e2cd2ff1f4d89d28e5e0a3d1bf Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 10:15:47 -0600 Subject: [PATCH 2/7] update toc formatting --- rp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rp b/rp index f60b39e..7350f16 100755 --- a/rp +++ b/rp @@ -206,7 +206,10 @@ def print_toc puts "" puts "Table of Contents".center(width).bold.underline entries.each do |entry| - puts " • #{entry}" + id, _ = entry.split(': ', 2) + level = id.split('.').first.to_i + indent = ' ' * (6 + level * 2) + puts "#{indent}• #{entry}" end puts "" end From 0fef56d13b37ccf5cae569dbd4755b91ff526e1c Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 12:18:35 -0600 Subject: [PATCH 3/7] update toc formatting --- rp | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/rp b/rp index 7350f16..cdc5bbc 100755 --- a/rp +++ b/rp @@ -201,15 +201,24 @@ def print_toc name = lesson_data['name'] || id entries << "#{id}: #{name}" end + max_entry_length = entries.map(&:length).max || 0 - width = 8 + max_entry_length + width = 10 + max_entry_length puts "" puts "Table of Contents".center(width).bold.underline + entries.each do |entry| - id, _ = entry.split(': ', 2) - level = id.split('.').first.to_i - indent = ' ' * (6 + level * 2) - puts "#{indent}• #{entry}" + id, name = entry.split(': ', 2) + n_parts = id.split('.') + n_major = n_parts.first.to_i + n_minor = n_parts.last.to_i + fig_lines = RubyFiglet::Figlet.new(n_major.to_s, 'smshadow').to_s.lines.map(&:chomp) + padded_fig_line = sprintf("%-6s", fig_lines[n_minor] || (" " * 5)) + if n_minor == 2 + padded_fig_line.sub!(/ $/, ". ") + end + indent = " " * (n_major * 2) + puts "#{padded_fig_line}#{indent}• #{entry}" end puts "" end From bcfcc2f693b522649672d0019b3dd5e77da9c184 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 12:29:33 -0600 Subject: [PATCH 4/7] update toc formatting --- rp | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/rp b/rp index cdc5bbc..3f8be32 100755 --- a/rp +++ b/rp @@ -207,18 +207,27 @@ def print_toc puts "" puts "Table of Contents".center(width).bold.underline - entries.each do |entry| - id, name = entry.split(': ', 2) - n_parts = id.split('.') - n_major = n_parts.first.to_i - n_minor = n_parts.last.to_i - fig_lines = RubyFiglet::Figlet.new(n_major.to_s, 'smshadow').to_s.lines.map(&:chomp) - padded_fig_line = sprintf("%-6s", fig_lines[n_minor] || (" " * 5)) - if n_minor == 2 - padded_fig_line.sub!(/ $/, ". ") + (0..3).each do |major| + fig_lines = RubyFiglet::Figlet.new(major.to_s, 'smshadow').to_s.lines.map(&:chomp) + (0..2).each do |minor| + padded_fig_line = sprintf("%-6s", fig_lines[minor] || (" " * 5)) + if minor == 2 + padded_fig_line.sub!(/ $/, ". ") + end + indent = " " * (major * 2) + lesson = entries.find { |e| e.start_with?("#{major}.#{minor}:") } + if lesson + puts "#{padded_fig_line}#{indent}• #{lesson}" + else + puts "#{padded_fig_line}" + end + end + # Print extra lessons with minor > 2 + extra_lessons = entries.select { |e| e.start_with?("#{major}.") && e.split('.')[1].to_i > 2 }.sort_by { |e| e.split('.')[1].to_i } + extra_lessons.each do |lesson| + indent = " " * (major * 2) + puts "#{' ' * 6}#{indent}• #{lesson}" end - indent = " " * (n_major * 2) - puts "#{padded_fig_line}#{indent}• #{entry}" end puts "" end From c9384bd45b0b3f6269fe7a02dbc8c4610bbd5ec7 Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 13:23:23 -0600 Subject: [PATCH 5/7] add lesson 4.0 --- examples/4.0.0.rb | 1 + examples/4.0.1.rb | 6 +++ examples/4.0.2.rb | 1 + examples_spec/4.0.0.rb | 14 +++++++ examples_spec/4.0.1.rb | 10 +++++ examples_spec/4.0.2.rb | 13 +++++++ lessons/4.0.json | 85 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- rp | 3 +- 9 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 examples/4.0.0.rb create mode 100644 examples/4.0.1.rb create mode 100644 examples/4.0.2.rb create mode 100644 examples_spec/4.0.0.rb create mode 100644 examples_spec/4.0.1.rb create mode 100644 examples_spec/4.0.2.rb create mode 100644 lessons/4.0.json diff --git a/examples/4.0.0.rb b/examples/4.0.0.rb new file mode 100644 index 0000000..1780989 --- /dev/null +++ b/examples/4.0.0.rb @@ -0,0 +1 @@ +restaurant_menu = # Fill the hash here. diff --git a/examples/4.0.1.rb b/examples/4.0.1.rb new file mode 100644 index 0000000..d5e7bdd --- /dev/null +++ b/examples/4.0.1.rb @@ -0,0 +1,6 @@ +restaurant_menu = { + "Ramen" => 3, + "Dal Makhani" => 4, + "Tea" => 2 +} +## how do you use restaurant_menu to get the price of Ramen? diff --git a/examples/4.0.2.rb b/examples/4.0.2.rb new file mode 100644 index 0000000..cc8f2fc --- /dev/null +++ b/examples/4.0.2.rb @@ -0,0 +1 @@ +restaurant_menu = {} diff --git a/examples_spec/4.0.0.rb b/examples_spec/4.0.0.rb new file mode 100644 index 0000000..c094243 --- /dev/null +++ b/examples_spec/4.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 a hash" do + expect(result).to be_a(Hash) + end + + it "should have correct keys and values" do + expect(result).to eq({"Ramen" => 3, "Dal Makhani" => 4, "Tea" => 2}) + end +end \ No newline at end of file diff --git a/examples_spec/4.0.1.rb b/examples_spec/4.0.1.rb new file mode 100644 index 0000000..a0a244e --- /dev/null +++ b/examples_spec/4.0.1.rb @@ -0,0 +1,10 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return 3" do + expect(result).to eq(3) + end +end \ No newline at end of file diff --git a/examples_spec/4.0.2.rb b/examples_spec/4.0.2.rb new file mode 100644 index 0000000..305191c --- /dev/null +++ b/examples_spec/4.0.2.rb @@ -0,0 +1,13 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:context) { binding() } + + before { eval(code, context) } + + it "should set restaurant_menu correctly" do + expect(eval("restaurant_menu", context)).to be_a(Hash) + expect(eval("restaurant_menu", context)).to eq({"Dal Makhani" => 4.5, "Tea" => 2}) + end +end \ No newline at end of file diff --git a/lessons/4.0.json b/lessons/4.0.json new file mode 100644 index 0000000..084b820 --- /dev/null +++ b/lessons/4.0.json @@ -0,0 +1,85 @@ +{ + "number": "4.0", + "name": "Introduction to Ruby Hashes", + "subsections": [ + { + "name": "Creating A Hash", + "items": [ + { + "type": "paragraph", + "value": "A Hash is a collection of key-value pairs. You retrieve or create a new entry in a Hash by referring to its key. Hashes are also called 'associative arrays', 'dictionary', 'HashMap' etc. in other languages." + }, + { + "type": "paragraph", + "value": "A blank hash can be declared using two curly braces -- `{}`. Here is an example of a Hash in Ruby:" + }, + { + "type": "paragraph", + "value": "`student_ages = {`\n\n` \"Jack\" => 10,`\n\n` \"Jill\" => 12,`\n\n` \"Bob\" => 14`\n\n`}`" + }, + { + "type": "paragraph", + "value": "The names (`Jack`, `Jill`, `Bob`) are the 'keys', and `10`, `12` and `14` are the corresponding values. Now try creating a simple hash with the following keys and values:" + }, + { + "type": "paragraph", + "value": "`Ramen = 3`\n\n`Dal Makhani = 4`\n\n`Tea = 2`" + }, + { + "type": "test_example", + "value": "4.0.0" + } + ] + }, + { + "name": "Fetch Values from A Hash", + "items": [ + { + "type": "paragraph", + "value": "You can retrieve values from a Hash object using the `[]` operator. The key of the required value should be enclosed within these square brackets." + }, + { + "type": "paragraph", + "value": "Now try finding the price of a Ramen from the `restaurant_menu` hash: write the name of the object, follow it with a square bracket, and place the key inside the brackets. In this case the key is a string so enclose the key in quotes." + }, + { + "type": "test_example", + "value": "4.0.1" + }, + { + "type": "paragraph", + "value": "That was very simple, wasn't it? One small step at a time!" + } + ] + }, + { + "name": "Modifying A Hash", + "items": [ + { + "type": "paragraph", + "value": "Once you've created a `Hash` object, you would want to add new key-value pairs as well as modify existing values." + }, + { + "type": "paragraph", + "value": "Here is how you would set the price of the `\"Ramen\"` key in the `restaurant_menu` hash:" + }, + { + "type": "paragraph", + "value": "`restaurant_menu[\"Ramen\"] = 3`" + }, + { + "type": "paragraph", + "value": "In fact, you can create a blank hash and add all the values later. Now why don't you try assigning the following values to an empty hash?" + }, + { + "type": "paragraph", + "value": "`Dal Makhani = 4.5`\n\n`Tea = 2`" + }, + { + "type": "test_example", + "value": "4.0.2" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 2ab07f1..5c10fc1 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -11,6 +11,7 @@ "2.2.json", "3.0.json", "3.1.json", - "3.2.json" + "3.2.json", + "4.0.json" ] } diff --git a/rp b/rp index 3f8be32..b91819d 100755 --- a/rp +++ b/rp @@ -207,7 +207,8 @@ def print_toc puts "" puts "Table of Contents".center(width).bold.underline - (0..3).each do |major| + major_levels = entries.map { |e| e.split('.').first.to_i }.uniq.sort + major_levels.each do |major| fig_lines = RubyFiglet::Figlet.new(major.to_s, 'smshadow').to_s.lines.map(&:chomp) (0..2).each do |minor| padded_fig_line = sprintf("%-6s", fig_lines[minor] || (" " * 5)) From 20db5b648c07d7d2eeb1a186345ec0265c50ccbe Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 16:44:17 -0600 Subject: [PATCH 6/7] add through chapter 4 --- examples/4.1.0.rb | 5 +++ examples/4.1.1.rb | 1 + examples/4.1.2.rb | 1 + examples/4.1.3.rb | 9 ++++ examples/4.1.4.rb | 2 + examples/4.1.5.rb | 9 ++++ examples_spec/4.1.0.rb | 14 +++++++ examples_spec/4.1.1.rb | 14 +++++++ examples_spec/4.1.2.rb | 14 +++++++ examples_spec/4.1.3.rb | 10 +++++ examples_spec/4.1.4.rb | 14 +++++++ examples_spec/4.1.5.rb | 14 +++++++ lessons/4.1.json | 93 ++++++++++++++++++++++++++++++++++++++++++ lessons/toc.json | 3 +- rp | 6 +-- 15 files changed, 205 insertions(+), 4 deletions(-) create mode 100644 examples/4.1.0.rb create mode 100644 examples/4.1.1.rb create mode 100644 examples/4.1.2.rb create mode 100644 examples/4.1.3.rb create mode 100644 examples/4.1.4.rb create mode 100644 examples/4.1.5.rb create mode 100644 examples_spec/4.1.0.rb create mode 100644 examples_spec/4.1.1.rb create mode 100644 examples_spec/4.1.2.rb create mode 100644 examples_spec/4.1.3.rb create mode 100644 examples_spec/4.1.4.rb create mode 100644 examples_spec/4.1.5.rb create mode 100644 lessons/4.1.json diff --git a/examples/4.1.0.rb b/examples/4.1.0.rb new file mode 100644 index 0000000..06606c0 --- /dev/null +++ b/examples/4.1.0.rb @@ -0,0 +1,5 @@ +restaurant_menu = {"Ramen" => 3, "Dal Makhani" => 4, "Coffee" => 2} +restaurant_menu.each do |item, price| + puts "#{item}: $#{price}" +end +restaurant_menu \ No newline at end of file diff --git a/examples/4.1.1.rb b/examples/4.1.1.rb new file mode 100644 index 0000000..4878885 --- /dev/null +++ b/examples/4.1.1.rb @@ -0,0 +1 @@ +restaurant_menu = {"Ramen" => 3, "Dal Makhani" => 4, "Coffee" => 2} diff --git a/examples/4.1.2.rb b/examples/4.1.2.rb new file mode 100644 index 0000000..4878885 --- /dev/null +++ b/examples/4.1.2.rb @@ -0,0 +1 @@ +restaurant_menu = {"Ramen" => 3, "Dal Makhani" => 4, "Coffee" => 2} diff --git a/examples/4.1.3.rb b/examples/4.1.3.rb new file mode 100644 index 0000000..a762ec3 --- /dev/null +++ b/examples/4.1.3.rb @@ -0,0 +1,9 @@ +normal = Hash.new +was_not_there = normal[:zig] +puts "Wasn't there:" +p was_not_there + +usually_brown = Hash.new("brown") +pretending_to_be_there = usually_brown[:zig] +puts "Pretending to be there:" +p pretending_to_be_there diff --git a/examples/4.1.4.rb b/examples/4.1.4.rb new file mode 100644 index 0000000..6884546 --- /dev/null +++ b/examples/4.1.4.rb @@ -0,0 +1,2 @@ +chuck_norris = Hash[:punch, 99, :kick, 98, :stops_bullets_with_hands, true] +p chuck_norris diff --git a/examples/4.1.5.rb b/examples/4.1.5.rb new file mode 100644 index 0000000..ce63324 --- /dev/null +++ b/examples/4.1.5.rb @@ -0,0 +1,9 @@ +def artax + a = [:punch, 0] + b = [:kick, 72] + c = [:stops_bullets_with_hands, false] + key_value_pairs = # you can do this. you are a champion. + # unlike Artax, who gave up in a swamp. + Hash[key_value_pairs] +end +artax diff --git a/examples_spec/4.1.0.rb b/examples_spec/4.1.0.rb new file mode 100644 index 0000000..850021c --- /dev/null +++ b/examples_spec/4.1.0.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return a hash" do + expect(result).to be_a(Hash) + end + + it "should have correct keys and values" do + expect(result).to eq({"Ramen" => 3, "Dal Makhani" => 4, "Coffee" => 2}) + end +end \ No newline at end of file diff --git a/examples_spec/4.1.1.rb b/examples_spec/4.1.1.rb new file mode 100644 index 0000000..b03262f --- /dev/null +++ b/examples_spec/4.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 a hash" do + expect(result).to be_a(Hash) + end + + it "should have increased prices" do + expect(result).to eq({"Ramen" => 3.3, "Dal Makhani" => 4.4, "Coffee" => 2.2}) + end +end \ No newline at end of file diff --git a/examples_spec/4.1.2.rb b/examples_spec/4.1.2.rb new file mode 100644 index 0000000..55f5e73 --- /dev/null +++ b/examples_spec/4.1.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 contain the keys" do + expect(result.sort).to eq(["Coffee", "Dal Makhani", "Ramen"]) + end +end \ No newline at end of file diff --git a/examples_spec/4.1.3.rb b/examples_spec/4.1.3.rb new file mode 100644 index 0000000..b736d0f --- /dev/null +++ b/examples_spec/4.1.3.rb @@ -0,0 +1,10 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return nil" do + expect(result).to be_nil + end +end \ No newline at end of file diff --git a/examples_spec/4.1.4.rb b/examples_spec/4.1.4.rb new file mode 100644 index 0000000..018a977 --- /dev/null +++ b/examples_spec/4.1.4.rb @@ -0,0 +1,14 @@ +require 'rspec' + +describe "example code" do + let(:code) { File.read('__TMPFILE__') } + let(:result) { eval(code) } + + it "should return a hash" do + expect(result).to be_a(Hash) + end + + it "should have correct key-value pairs" do + expect(result).to eq({:punch => 0, :kick => 72, :stops_bullets_with_hands => false}) + end +end \ No newline at end of file diff --git a/examples_spec/4.1.5.rb b/examples_spec/4.1.5.rb new file mode 100644 index 0000000..018a977 --- /dev/null +++ b/examples_spec/4.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 a hash" do + expect(result).to be_a(Hash) + end + + it "should have correct key-value pairs" do + expect(result).to eq({:punch => 0, :kick => 72, :stops_bullets_with_hands => false}) + end +end \ No newline at end of file diff --git a/lessons/4.1.json b/lessons/4.1.json new file mode 100644 index 0000000..52382bb --- /dev/null +++ b/lessons/4.1.json @@ -0,0 +1,93 @@ +{ + "number": "4.1", + "name": "Hashes, In And Out", + "subsections": [ + { + "name": "Iterating over A Hash", + "items": [ + { + "type": "paragraph", + "value": "You can use the `each` method to iterate over all the elements in a Hash. However unlike `Array#each`, when you iterate over a Hash using `each`, it passes two values to the block: the key and the value of each element." + }, + { + "type": "paragraph", + "value": "Let us see how we can use the `each` method to display the restaurant menu." + }, + { + "type": "example", + "value": "4.1.0" + }, + { + "type": "paragraph", + "value": "The restaurant is doing well, but it is forced to raise prices due to increasing costs. Use the `each` method to increase the price of all the items in the `restaurant_menu` by 10%." + }, + { + "type": "paragraph", + "value": "Remember: in the previous example we only displayed the keys and values of each item in the hash. But in this exercise, you have to modify the hash and increase the value of each item." + }, + { + "type": "test_example", + "value": "4.1.1" + }, + { + "type": "paragraph", + "value": "Ideally, any transformation of a collection (like in the example above) should produce a new collection with the original unchanged making the code easier to understand and manage." + }, + { + "type": "paragraph", + "value": "However, speed and memory considerations often (and usually wrongly) trump maintainability and so the approach above is used quite frequently." + } + ] + }, + { + "name": "Extracting The Keys And Values from A Hash", + "items": [ + { + "type": "paragraph", + "value": "Every Hash object has two methods: `keys` and `values`. The `keys` method returns an array of all the keys in the `Hash`. Similarly `values` returns an array of just the values." + }, + { + "type": "paragraph", + "value": "Try getting an array of all the keys in the `restaurant_menu` hash:" + }, + { + "type": "test_example", + "value": "4.1.2" + } + ] + }, + { + "name": "Newer, Faster", + "items": [ + { + "type": "paragraph", + "value": "There are some little-known shortcuts for creating new hashes. They all provide a slightly different convenience. The latter two generate a hash directly from pre-existing key-value pairs. The first simply sets a default value for all elements in the hash. Let's take a look at that first." + }, + { + "type": "example", + "value": "4.1.3" + }, + { + "type": "paragraph", + "value": "As you can see, where a \"normal\" hash always returns `nil` by default, specifying a default in the `Hash` constructor will always return your custom default for any failed lookups on that hash instance." + }, + { + "type": "paragraph", + "value": "The other two shortcuts actually use the `Hash` class's convenience method: `Hash::[]`. They're fairly straightforward. The first takes a flat list of parameters, arranged in pairs. The second takes just one parameter: an array containing arrays which are themselves key-value pairs. Where! That's a mouthful. Let's try the first form:" + }, + { + "type": "example", + "value": "4.1.4" + }, + { + "type": "paragraph", + "value": "Cool. And easy! You have to now use the second form the \"new hash\" shortcut in this exercise. Again, it takes an array of key-value pairs. These key-value pairs will just be 2-element arrays. I'll give you the key-value pairs to start with. Your objective is to build a Hash out of this array." + }, + { + "type": "test_example", + "value": "4.1.5" + } + ] + } + ] +} diff --git a/lessons/toc.json b/lessons/toc.json index 5c10fc1..3919304 100644 --- a/lessons/toc.json +++ b/lessons/toc.json @@ -12,6 +12,7 @@ "3.0.json", "3.1.json", "3.2.json", - "4.0.json" + "4.0.json", + "4.1.json" ] } diff --git a/rp b/rp index b91819d..78f6ddf 100755 --- a/rp +++ b/rp @@ -203,15 +203,15 @@ def print_toc end max_entry_length = entries.map(&:length).max || 0 - width = 10 + max_entry_length + width = 12 + max_entry_length puts "" - puts "Table of Contents".center(width).bold.underline + puts " " + "Table of Contents".center(width).bold.underline major_levels = entries.map { |e| e.split('.').first.to_i }.uniq.sort major_levels.each do |major| fig_lines = RubyFiglet::Figlet.new(major.to_s, 'smshadow').to_s.lines.map(&:chomp) (0..2).each do |minor| - padded_fig_line = sprintf("%-6s", fig_lines[minor] || (" " * 5)) + padded_fig_line = sprintf(" \u2502 %-6s", fig_lines[minor] || (" " * 5)) if minor == 2 padded_fig_line.sub!(/ $/, ". ") end From e4d7fe6054cf85480da6f17c6a7f92a61ad0652b Mon Sep 17 00:00:00 2001 From: Jason Reeves Date: Fri, 19 Dec 2025 16:57:40 -0600 Subject: [PATCH 7/7] remove superfluous specs --- examples_spec/3.1.5.rb | 14 -------------- examples_spec/4.1.0.rb | 14 -------------- examples_spec/4.1.3.rb | 10 ---------- examples_spec/4.1.4.rb | 14 -------------- 4 files changed, 52 deletions(-) delete mode 100644 examples_spec/3.1.5.rb delete mode 100644 examples_spec/4.1.0.rb delete mode 100644 examples_spec/4.1.3.rb delete mode 100644 examples_spec/4.1.4.rb diff --git a/examples_spec/3.1.5.rb b/examples_spec/3.1.5.rb deleted file mode 100644 index 591069c..0000000 --- a/examples_spec/3.1.5.rb +++ /dev/null @@ -1,14 +0,0 @@ -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/4.1.0.rb b/examples_spec/4.1.0.rb deleted file mode 100644 index 850021c..0000000 --- a/examples_spec/4.1.0.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rspec' - -describe "example code" do - let(:code) { File.read('__TMPFILE__') } - let(:result) { eval(code) } - - it "should return a hash" do - expect(result).to be_a(Hash) - end - - it "should have correct keys and values" do - expect(result).to eq({"Ramen" => 3, "Dal Makhani" => 4, "Coffee" => 2}) - end -end \ No newline at end of file diff --git a/examples_spec/4.1.3.rb b/examples_spec/4.1.3.rb deleted file mode 100644 index b736d0f..0000000 --- a/examples_spec/4.1.3.rb +++ /dev/null @@ -1,10 +0,0 @@ -require 'rspec' - -describe "example code" do - let(:code) { File.read('__TMPFILE__') } - let(:result) { eval(code) } - - it "should return nil" do - expect(result).to be_nil - end -end \ No newline at end of file diff --git a/examples_spec/4.1.4.rb b/examples_spec/4.1.4.rb deleted file mode 100644 index 018a977..0000000 --- a/examples_spec/4.1.4.rb +++ /dev/null @@ -1,14 +0,0 @@ -require 'rspec' - -describe "example code" do - let(:code) { File.read('__TMPFILE__') } - let(:result) { eval(code) } - - it "should return a hash" do - expect(result).to be_a(Hash) - end - - it "should have correct key-value pairs" do - expect(result).to eq({:punch => 0, :kick => 72, :stops_bullets_with_hands => false}) - end -end \ No newline at end of file