#!/usr/bin/ruby

# Extension of buil-in classes

class Custom {
    method in_blue {
        "in_blue('#{self}')";
    }
}

# Inherit a used-defined class into the String class
class String << Custom {
    method in_red {
        "#f00 --#{self}--"
    }
}

"hello".in_red.contains("#f00")     || "error -1".die;
"hello".in_blue.contains("in_blue") || "error 0".die;

# Define a hash of colors
var COLORS = Hash.new(
           black   => "000",
           red     => "f00",
           green   => "0f0",
           yellow  => "ff0",
           blue    => "00f",
           magenta => "f0f",
           cyan    => "0ff",
           white   => "fff",
        );

# Redefine the String class and define new methods in it with 'def_method' keyword
# Inspired from: https://en.wikipedia.org/wiki/Ruby_(programming_language)#Metaprogramming
class String {
    COLORS.each { |color,code|
       __CLASS__.def_method("in_#{color}", func(self) {
            "<span style=\"color: ##{code}\">#{self}</span>"
        })
    }
}

"hello".in_red.contains("#f00")    || "error 1".die;
"hello".in_yellow.contains("#ff0") || "error 2".die;
"hello".in_green.contains("#0f0")  || "error 3".die;

class String(a,b) {

    method to_s {
        "String(#{a.dump}, #{b.dump})"
    }

    method in_blue {
        "#{self} in blue"
    }
}

do {
    var obj1 = String("foo")                 # calls the String class
    var obj2 = main::String("foo", "bar")    # calls the user-defined class with two parameters

    assert_eq(obj1, "foo")
    assert_eq(obj2.in_blue, %q<String("foo", "bar") in blue>)
}

module Test1 {
    class Number() {
        method foo {
            "test1"
        }
    }

    var obj = Test1::Number()
    assert_eq(obj.foo, "test1")
}

module Test2 {
    class Test2::Number {
        method bar {
            "test2"
        }
    }

    var obj = Test2::Number()
    assert_eq(obj.bar, "test2")
}

class main::Number {
    method baz {
        "test3"
    }
    method sqrt {
        "sqrt method"
    }
}

do {
    var obj = main::Number()
    assert_eq(obj.baz, "test3")
    assert_eq(obj.sqrt, "sqrt method")
    assert_eq(25.sqrt, 5)
}

class Number {
    method baz {
        "test4"
    }
}

module Test5 {
    class Number() {
        method baz {
            "test5"
        }
    }

    var obj = Test5::Number()
    assert_eq(obj.baz, "test5")
}

assert_eq(42.baz, "test4")

class Test::Number {
    method foo {
        "in foo"
    }
}

do {
    var obj = Test::Number()
    assert_eq(obj.foo, "in foo")
}

module Test {
    class Number() {
        method bar {
            "in bar"
        }
    }
}

do {
    var obj = Test::Number()
    assert_eq(obj.foo, "in foo")
    assert_eq(obj.bar, "in bar")
}

say "** Test passed!"