Third day of the 42 Ruby on Rails Piscine. This module dives into Object-Oriented Programming in Ruby: creating classes, raising and rescuing exceptions, building an HTML element tree with inheritance, and validating document structure.
A Rakefile is provided to run any exercise conveniently:
rake ex00 # Run a single exercise
rake all # Run all exercises
rake ex03_test # Run the provided test suite for ex03| Exercise | File | Topic | Description |
|---|---|---|---|
| ex00 | ex00.rb |
Classes | Create an Html class that generates and populates an HTML file |
| ex01 | ex01.rb |
Exceptions | Add error handling with raise to prevent invalid HTML generation |
| ex02 | ex02.rb |
Rescue | Implement custom exception classes that auto-correct errors |
| ex03 | ex03.rb |
OOP / Tree | Build an Elem class representing HTML elements as a composable tree |
| ex04 | ex04.rb |
Inheritance | Derive concrete HTML tag classes from Elem using metaprogramming |
| ex05 | whereto.rb |
Validation | Create a Page class that validates an HTML element tree against structural rules |
File: ex00/ex00.rb
Create an Html class that generates a valid HTML file. The constructor takes a filename (without extension), writes the HTML boilerplate, and provides methods to add content and close the file.
| Method | Description |
|---|---|
initialize(filename) |
Creates the file, writes <!DOCTYPE html>, <html>, <head>, <title>, </head>, <body> |
page_name |
attr_reader returning the filename |
dump(text) |
Appends a <p> tag with the given text inside <body> |
finish |
Writes the closing </body> tag |
File.openwith"w"(write) and"a"(append) modesattr_readerfor exposing instance variables- Private methods (
headis called only from the constructor) $PROGRAM_NAME == __FILE__guard for inline tests
File: ex01/ex01.rb
Builds on ex00 by adding error handling through raise to prevent generating invalid HTML pages.
| Condition | Error message |
|---|---|
| File already exists | "<filename>.html already exist!" |
dump called before <body> exists |
"There is no body tag in <filename>.html" |
dump called after </body> |
"The body has already been closed in <filename>.html" |
finish called twice |
"<filename>.html has already been closed" |
raisewithRuntimeErrormessages- Instance variable flags (
@finished,@has_body) to track document state File.exist?to check for duplicate filesbegin/rescueblocks in test code to catch and display exceptions
File: ex02/ex02.rb
Extends ex01 by creating custom exception classes that not only report errors but also fix them automatically.
| Class | Inherits from | Trigger | Recovery action |
|---|---|---|---|
Dup_file |
StandardError |
Creating a file that already exists | Appends .new before .html (e.g., test.new.html, test.new.new.html) |
Body_closed |
StandardError |
Writing after </body> |
Removes closing tag, inserts text, re-adds closing tag |
| Method | Role |
|---|---|
show_state |
Display the state before correction (full path of conflicting file, or line number of closing tag) |
correct |
Fix the error (rename file or rewrite file contents) |
explain |
Display the state after correction |
- Custom exception classes inheriting from
StandardError File.expand_pathfor full path displayFile.readlinesandFile.writefor in-place file modificationrescueblocks that invoke corrective methods before continuing
File: ex03/ex03.rb
A shift in approach: instead of writing to files, represent HTML as an in-memory tree of Elem objects. Calling to_s on any element renders it as HTML.
| Class | Constructor | Description |
|---|---|---|
Text |
Text.new(string) |
Wraps a plain string; to_s returns the string |
Elem |
Elem.new(tag, content, tag_type, opt) |
Represents an HTML element with tag name, content (array or single element), type ("double" or "simple"), and attributes hash |
| Method | Description |
|---|---|
add_content(*elems) |
Append one or more child elements |
to_html |
Returns the raw HTML string with newlines |
to_s |
Returns the formatted string representation |
- Composable tree structure (an
Elemcan contain otherEleminstances) attr_accessorfor exposingtag,content,tag_type,opt- Recursive
to_s/to_htmlrendering - Handling both array content and single-element content
File: ex04/ex04.rb
Derive concrete HTML tag classes from Elem using Ruby metaprogramming, avoiding repetitive class definitions.
| Category | Tags |
|---|---|
| Double (paired) | Html, Head, Body, Title, Table, Th, Tr, Td, Ul, Ol, Li, H1, H2, P, Div, Span |
| Simple (self-closing) | Meta, Img, Hr, Br |
puts Html.new([Head.new([Title.new("Hello ground!")]),
Body.new([H1.new("Oh no, not again!"),
Img.new([], { 'src': "http://i.imgur.com/pfp3T.jpg" })])])Object.const_setto dynamically define classes at runtimeClass.new(Elem)for programmatic inheritancedefine_methodto create constructors inside dynamic classes- DRY principle: two loops generate 20+ classes in ~10 lines
File: ex05/whereto.rb
Create a Page class that validates an HTML element tree against a set of structural rules, returning true if valid and false otherwise.
| Element | Rule |
|---|---|
| All nodes | Must be one of the known types (Html, Head, Body, Title, Meta, Img, Table, Th, Tr, Td, Ul, Ol, Li, H1, H2, P, Div, Span, Hr, Br, Text) |
Html |
Must contain exactly one Head followed by one Body |
Head |
Must contain only one Title |
Body, Div |
May only contain H1, H2, Div, Table, Ul, Ol, Span, Text, P, Img, Hr, Br |
Title, H1, H2, Li, Th, Td |
Must contain exactly one Text |
P |
May only contain Text elements |
Span |
May only contain Text or P elements |
Ul, Ol |
Must contain at least one Li and only Li elements |
Tr |
Must contain at least one Th or Td; Th and Td are mutually exclusive |
Table |
May only contain Tr elements |
Img |
Must have a src attribute whose value is of type Text |