Ruby is designed to make programmers happy. – Yukihiro Matsumoto, the creator of Ruby
Ruby is a completely object-oriented language, where every value is an object.
Ruby is a scripted language, so it executes the program starting with the first line and continues line by line until the end.
1.class # => Fixnum: the number 1 is a Fixnum
false.class # => FalseClass
nil.class # => NilClass
Because everything is an object, you can invoke methods on integers:
3.times { print "Ruby!" } # => "Ruby! Ruby! Ruby!"
1.upto(9) { |x| print x } # => "123456789"
You can also call iterators on arrays.
a = [3, 2, 1]
a.each do |el|
print el+1
end
In Ruby, parentheses are optional.
Hashes are another data structure used to map key objects to value objects.
h = {
:one => 1,
:two => 2
}
h[:one] # => 1
h[:three] = 3 # adds new key/value pair
h.each do |key, value|
print "#{value}:#{key} "
end
Methods are defined with the def keyword.
def square(x)
x*x
end
So Ruby has implicit returns and they call functions “methods” instead, it would be similar to this in TypeScript:
function square(x: number) {
return x*x;
}
In Ruby, classes and modules are “open” so you can modify and extend them at runtime, the following code would override the Math module square function:
def Math.square(x)
x*x
end
So even if something is part of the core you can easily customise it to suit your needs.
In Ruby you can assign more than one value at a time:
x, y = 1, 2
Methods can also return more than one value:
def coordinates(gps)
# do something
[x, y]
end
lat, long = coordinates(x)
In Ruby your functions can also end with ? or ! where a function like empty? returns a boolean, and delete! is used to signal caution. For instance, you could use sort() when working with immutable data, but sort!() when it would mutate the data.
You can also easily create ranges with ellipsis:
1..3 # All x where 1 <= x <= 3
1...3 # All x where 1 <= x < 3
This could be useful for switch statements:
generation = case birthyear
when 1946..1963: "Baby Boomer"
when 1964..1976: "Generation X"
when 1977..2000: "Generation Y"
else nil
end
Some things the author suggests to watch out for when getting started with Ruby is that strings are mutable, and values like 0 and "" are truthy (while in JavaScript they are falsy).
If you want to test Ruby and see the output, use the puts method as that will call the equivalent of toString() on each value, while print does more or less the same thing without adding a newline character.
To use the interactive shell use irb and to read the documentation use ri:
ri Array
ri Array.sort
ri Hash#each
ri Math::sqrt
Ruby also has symbols which are probably like atoms in Elixir:
:symbol # A Symbol literal
And nil is used for null values, and it seems because everything is an object you can also call the nil? function to get a boolean value:
o == nil # is o nil?
o.nil? # another way
You can also easily get the class and superclass of objects because everything is an object… an interesting way of programming…
o.class
o.class.superclass
o.class == String
o.instance_of? String
o.is_a? Integer
You can also make objects immutable with the freeze method:
s = "water"
s.freeze
s.frozen?
s.upcase! # Error: can't modify frozen string
I don’t really like the syntax without curly braces for functions that’s optional… either make it forced or not allowed… I find it a lot easier to reason about blocks of code if it has clear indentation and braces. Even parentheses are optional:
puts "Hello World"
puts("Hello World")
So even calling functions is done without parentheses, so it seems like magic:
greeting = "Hello"
x = greeting.length
x = greeting.length() # same
x.class
x.class() # same
That would probably make Ruby easier to adopt if you could see all the parentheses of these magic functions, it looks a lot more like PHP or JavaScript then.
Ruby has an excellent API for reflection which makes it a good candidate for meta-programming, which is writing programs to help you write other programs. This is great for generating DSLs or Domain-Specific Languages, such as this custom XML generator:
pagetitle = "Test Page for XML.generate"
XML.generate(STDOUT) do
html do
head do
title { pagetitle }
comment "This is a test"
end
body do
h1(:style => "font-family:sans-serif") { pagetitle }
ul :type=>"square" do
li { Time.now }
li { RUBY_VERSION }
end
end
end
end
The code has a lot of custom functions like head, comment, h1 that map to XML tags. That looks really nice… the rest of the meta-programming was a bit too complex for me at this stage but I have a much better understanding of what they mean when they use the term now.