As you might guess from the name, Ruby was inspired by Perl. It's a "scripting" language, but otherwise not that similar. It does keep Perl's love of funny symbols and multiple odd ways of doing the same thing. It claims to be more Object Oriented, but I don't see it. The most obnoxious feature is marking blocks with BEGIN-END tags. All-in-all, Ruby is clunky.
Why use it? I guess for web-app Ruby on Rails, and legacy applications.
home
Ruby uses modern scripting sloptastic whitespace rules. Semi-colons go between statements on the same line, but aren't always needed. Newlines end statements, unless they shouldn't. Bizarrely, you may need a keyword to stay on the same line. If conditions, loops, and function calls don't need parens.
Basic types are Integer, Float, String, Array, Class. There's no char type, only short strings. There's also no boolean type! Instead true
is TrueClass
and false
is FalseClass
(are you starting to hate it yet?) Ruby int's can be any size (internally integers are broken into 32-bit Fixnum and unlimited Bignum).
More boolean weirdness, function names are allowed to end in a question mark, mostly used for bool-returning. A.include?(3)
is a normal "contains" function. To repeat, the ? means nothing special -- it's just another letter. ! is used the same way. Functions which modify the caller should have ! on the end. w.upcase!
converts w
to uppercase. But again, the ! is just another letter in the name.
Python-style, variables have no type, objects do. Which means variables aren't declared. You're allowed to change types (but you get a warning):
x=6; y=7.6 w='red' # strings use double or single quotes print(x.class, y.class, w.class) # Integer Float String d=3<8; print d.class # TrueClass (good lord!)
Comments use #, but a block of comments
uses, I'm not joking, =begin
and =end
:
=begin This is a multi-line comment with only 2 lines =end
Printed multi-line text has a shortcut using a crazy "pick your delimiter" << command:
# a multi-line print shortcut print <<CAT # we've declared CAT as the sentinel these are some lines printed until we see CAT CAT # the final CAT marks the end of the multi-line print
There are 2 common prints: print
and puts
. print is simple, but has no endline. puts
prints each input on a line by itself (including array elements).
The rules for names starting with capital letters are ... odd. Class and module names must start with a capital letter. Functions can be either way. Technically cap-starting identifiers are constants, sort of; they're allowed to change but you get a warning.
Because of that last thing, if you start a class or module name with a lower-case letter the confusing error message is "must be constant".
Ruby if's have the usual ==, <=, !, && and || and so on. They must end with end
-- no exceptions. Cascading if's require an elsif
(Ruby was created long after we knew how silly that was):
if n<9 puts "if case 1" elsif n==9 puts "if case 2" else puts "if case 3" end
Crazy rule: if you want to stay on the same line, you need an extra then
. Arrg. Also note there's no semi-colon required before a same-line end
(special rule):
if x==2 || y<0 then print "accept" end if !(x>l.1 && x<=10) # no THEN beeded here print "inside" end # a same-line block with more than one statement: if n==2 then n=0; x=9 end
Everything in Ruby is value-returning. If's included:
# assigning IF's return value: w2 = if n==4 then "AAA" else "BBB" end w3 = if x==0 then "zero" else if x<0 then "neg" else "pos" end end
IF's can also be after the body, in the very silly "guard" style. At least we can stop typing THEN:
n=10 if size>2
To sum uo: Ruby's IF's look like late 1950's FORTRAN.
Strangely, since if's do this already, Ruby has the value-returning ?:
operator.
In a huge throwsback to stuff we rejected as silly, Ruby has the horrible unless
(which can also be flipped with the body):
unless n<10 then puts "n is 10 or more" end
Strangely, or not, everything in Ruby has a true/false value. false and nil are false, the rest true:
if [0,0,0] then puts "arrays are true" end n=0; if n then puts "0 is true"
Ruby's switch statement can check for equality, or a range, or a type:
case n when 3 then x=0 # THEN required only for same-line when 8,9 then x=6 when 15..20 then x=7 else x=9 # arrg! re-uses ELSE end
As usual in Ruby, it has a return value. An example where we check for a type:
val = case n when String then 0 # return value when Integer, Float then Integer(n) when Array then n[0] || 0 else 0 end
nil
is used for null. It's a pointer to the nil object. if n==nil
works but the preffered way is if n.nil?
(recall the ? is simply part of the function name).
The modern "quit if null instead of giving an error", which is usualy n!.val
instead uses an ampersand(&):
sun.setBrightness 7 # error if sun is nil # safely does nothing if no sun: sun&.setBrightness 7
You also get the modern "if this would be nil, use this alternate value". Instead of using the the more common ?? for this, Ruby reuses ||:
name = w || "unknown" # use "unknown" if w is nil val = arr[0] || -1 # works since array-off-end is nil # if no-one has a name longer than 6 letters, "use Freddie" captain = players.find{|w| w.length>6} || "Freddie"
The n=a ?? 0
thing was already pointless and annoying. This manages to make it even worse by confusing it with an IF.
There's one more I'm not sure I've seen before, maybe since no one else wanted it. ||=
assigns only if you're nil
(or false, but when would that happen?):
n = w.index('x') # nil if not found n ||=0 # if n==nil n=0 # same things # n=0 if n==nil
Ruby has the basic while
, and the pointless do-while. It even brings back the until
loop. Here's a while, nothing special except the same-line symbol is DO instead of THEN:
# basic while loop: n=1 while n<20 do print "#{n}, "; n*=2 end #same, on multiple lines: while n<20 print "#{n}, " n*=2 end
Of course Ruby's primary loop is an implicit foreach, over lists or ranges:
# list foreach (nothing special): for i in [5,8,9] print i*10 end # range foreach, using (same-line DO): sum=0 for i in 1..10 do sum+=i end
3-dots in a range skips the last item. Letters are allowed ('a'..'e'
). Backwards (10..1) is no good, but we can do it with downto: for 10.downto 1 do
.
We have break and continue (called next
):
# break and continue: b=[13,14,15,20,50,51,84] for v in b if v%2==1 then next end # gah! so many keywords! break if v>50 # backwards if style puts v end
There's another foreach for list/array using each
. It takes a lambda function as input:
# same as foreach w: ["cat","dog","ant"].each {|w| print w,"X" } # with a range, a little more complicated function: (100..104).each { |cat| if cat%2==1 then print cat," " end } # without the {} shortcut: ["cat","dog","ant"].each do |w| print "[",w,"]" end
{}'s are only for lambda functions. The input(s) go inside of pipes as the 1st thing: |w|
says we have 1 parameter named w
. Ruby is just terrible.
We have the usual +=
, *=
. There's no ++. Strings get *
Integer. Exponentiation is **
.
Strings can use << as +=:
w="Arf"*3 # ArfArfArf w << "BARK" # ArfArfArfBARK n=2**8 # 256
For compares, Ruby adds a special -1/0/+1 operator, <=>
(-1 if the first is less-than the second, and so on):
print 1<=>2," ",2<=>2," ",3<=>2,"\n" # -1 0 +1
Triple equals is overloaded as the "in this range" operator. The range must come first:
if (1..10)===n then ... # n is betwenn 1 and 10 if (2.5..3.5) === val # val is between 2.5 and 3.5 if ('A'..'Z') === ch # is upper-case
Ruby has regular casts, giving an error if it can't:
n = Integer(4.8) # 4 print "abc"+String(3.1) # needed since no implicit cast to String print Float(10)/3 # 3.333 n=Integer("abc") # error
It also has member-function casts. to_s
for string, and so on:
n = 3.8.to_i # 3 print "abc"+n.to_s
Since there's no boolean type, we have to use a hack to cast to it: !!val
. It's just two !'s in-a-row. !!8
converts 8 to false, then false to true (so it casts 8 to true). So silly.
Testing a type can be done with dot-class
, or an overload of triple-equals where the class must come first:
if w.class == String then ... elsif w.class == Integer then ... if String === w then ... # String must be first
We can also test for Array
, Class
,
Method
, Symbol
, and Regexp
.
As expected, Ruby arrays are just anything in []
's with commas. They allow mixed types:
Nums = [3,8,12,25] CatInfo = ["stanley", 4.5, 6, "grey"]
A very odd rule: there's no index-out-of-bounds error. Instead
Nums[99]
is nil.
Ruby lists are array-backed (they should be -- their type is Array
). As with most "scripting" languages, there's no Linked-List list.
Lists have the usual: slicing, + to add two of them. The special Ruby << appends an item. They're mutable:
arr=[0,1,2]+[3,4,5] arr+=[6,7,8] # arr is 0-8 arr<<9 # 0 to 9 arr[0]=99 # legal arr[2..4] # slice [2,3,4] arr[2...4] # 3-dot slice still skips last one: [2,3]
Indexes (and array slices) have the standard "negative goes from the right end". arr[-1]
is the last item and arr[0..-2]
is everything but the last item.
Giving 2 indexes picks out a subarray: arr[2,3]
is items 2, 3 and 4 (3 items, starting from 2).
They also have the usual length, insert(index, item), delete(item), delete_at(index), push, and so on. A bit odd, there's no "is this in the array" shortcut (if 3 in arr
doesn't exist).
Very silly, there are shortcuts for making some types of lists. They let you skip comas, quotes, and replace {} with () (why?). For strings, start with %w
:
animals = %w(cat dog rat) # same as ['cat', 'dog', 'rat']
Shockingly, Ruby maps work the usual way:
m = {"cat"=>4, "dog"=>8} # a 2-item HashMap m["frog"]=9 #legal to look for missing items: if m["cow"]!=nil then ... if m.includes? "cow" then ...
Maps have m.keys
(array of keys) and m.values
, or
for k in m
automatically goes through the keys.
Function calls get a special map-passing shortcut. You can leave out the curly braces. This calls f
with a single 2-item hash:
n=f("cat"=>4, "dog"=>8) # f has 1 input # or more common and weirder-looking: n=f "cat"=>4, "dog"=>8
Functions begin with def
(seriously?) and of course end with end
. Parameters have no types; Ruby functions are Duck-type. A return may be omitted before the final value:
def doubleMe(n) n*2 end def longerFunction(a,b,c) if a>10 then b end return c # this return is also optional end n = doubleMe(4); w=doubleMe "Cat" # anything that can use "*2" x = longerFunc 5,20,30 # 30 (since 5 isn't more than 10)
Defualt parameters use the standard n=8
syntax.
Named parameters are written into the function, forcing callers
to use them (i.e.: the bad way). Defualt values of named parms omit the =:
# basic defualt parm: def doubler(n=6) n*2 end # named parms, with a default: def combiner(tens:, ones:5) tens*10+ones end n=combiner(tens:4, ones:8) # 48 n2=combiner(tens:3) # 35
They have the usual bonuses: *a
collects extra args in an array, and **a
collects extra named-args into a dictionary. It's an error for a parameter to start with a capital letter (which is a constant, so should be legal, you'd think).
A star before an array flattens it out for a call:
nums=[3,9,12] f *nums # same as f(3,9,12)
There are no tuples. Seems odd, But there it is.
Ruby has 3 and a half types of classes: formal classes, simpler Struct's (somewhat obsolete), OpenStruct's -- Python-style "add any fields any time" -- and finally, the half, it's common to lazily use a mixed-type array.
Member variables aren't declared -- they're created in the constructor. But to make them public we appear to declare them. All use of member variables requires a leading AT-sign @. Constructors are named initialize
:
class Point attr_accessor :x, :y # makes them public def initialize(x=0,y=0) # constructor @x=x; @y=y # assign paramters to member vars end # normal member function: def to_s() "(#{@x}, #{@y})" end end
Note the many @x
's and @y
's. That's all normal member variable use; @x=@y+1
and so forth. The constructor can't be overloaded. Instead we do tricks with default values.
Now it gets weird. Member variables are always private. No exceptions. But attr_accessor
auto-creates get/set functions (like C#'s auto-properties). So attr_accessor :x, :y
makes fields effectively public (note how :x
is a symbol). With that line p1.x=5
is allowed and calls a setter for x
.
Creating a class object is funny. new
is called from the class:
p1=Point.new(45,6) p1=Point.new # no inputs, so (0,0)
While fields are always private, member functions start out as public. private
can change in two ways: by itself it applies to everything below; but at the start of a line it applies to only that function:
private def f1() ... # appplies only to this function def f2() # this is back to being public private def f3() ... # these are both private def f4() ... # public # turns off the private
You can have private attr-accessor :x, :y
, but it won't do much. You get to use self.x
(the accessor function) instead of @x
.
If you really, really want to see a private field, from anywhere, use:
p1.instance_variable_get(:@x)
Again, the colon makes it a symbol amd the @
is for a member variable.
Overloading operators is super-easy. Use the operator as the function name:
def +(p) # + for point return Point.new(@x+p.x, @y+p.y) end
But there's one crazy complication: Ruby can't have what we think of as normal private variables. Ruby's private variables are for that instance only. In other words p.x
in the code above is an error unless x
is public (with attr_accessor
). Yikes! Ruby member vars are either public, or super-private.
Private member functions are the same way; they can only be called by that instance. p1.privateFunc
is always an error, even in a class function.
Ruby class statics work the usual way. static variables are always private -- only visible to member functions. The syntax was inspired by the Necronomicon. It's hacks and nonsense
class STest static variables begin with @@: @@x = 35 # static functions begin with self-dot: def self.sqr(n) n*n end # demo of static var use: def incx() @@x+=1; puts "static x=#{@@x}" end end # class var use: STest.sqr(6) # 36
To check that @@x
is a true class var we could call it with 2 objects: s1.incx
and s2.incx
. We'll get 36 and 37. They share @@x
.
Structs are a hack to quickly make a data-only class. These days, it's probably better to make a class with attr_accessor
. A struct:
# define struct Cat: Cat = Struct.new(:name, :age) # list fields as symbols # create cats as normal: c1=Cat.new("fluffy",5) c1.name="Biff";
Cat
is actually a class, a subclass of class Struct
with an overloaded new
and tricks to create your public variables.
We can add member functions to a Struct, but it's weird. Add do
and end
and write functions with
no parameters which use the members without the @ (Gah!):
Cat = Struct.new(:name, :age) do def info() "Name #{name}, Age: #{age}" end def monthsOld() age*12 end end
Again, skip these and just use a class.
The last is the "it's really a hash on variable names" class. Not much to it. There's no constructor since you add whatever you need later:
# a Ruby-style include: require 'ostruct' d1=OpenStruct.new d1.hatSize=7 # make up whatever you want d1.hair="wavy"
A nice feature is how checking a non-existant item merely gives nil: if d1.feather!=nil then
is just fine.
As usual, you don't need to inherit for polymophism (since we have all we need with Duck-typing). But you can. Use a less-than to inherit (yes, really). All functions are virtual so overriding is automatic. A funny thing: any function can use super
, alone, to call the function it overrides. Give it inputs if it needs them.
Assume Pig has 2 inputs. This new Pig adds yummyness:
class ChesterWhite < Pig def initialize(w,s,y) super(w,s) # Pig initialize @yummyness=y end def info # override, super calls Pig.info: "#{super} Yum: #{@yummyness}" end end
The usual p2 = ChesterWhite.new(102, 3, "very")
will declare a chesterWhite pig.
A little more on super. Supose we have def gluk(n) ...
in a class. A subclass can override it and do:
def gluk(n) baseVal=super(m) # galls gluk(n) on superclass # now play with it
Keyword module
can create a namespace. It can have anything, but only for classes will be visible. Double-colon looks it up:
module CowStuff class Horns ... end class Chewing ... end end c1=CowStuff::Horns.new
Or, in a completely insane rule, a module can hold pastable member functions. A class may include
the module to get those functions. They may even refer to as-yet-unknown member variables:
module ShowN # this assume that whomever includes it will have @n: def showVar() print "var=",@n end end class AA1 def initialize(n) @n=n end # a shortcut to put the showVar function into AA1: include ShowN end
That's nuts. Ruby is supposed to be OOP; it should inherit these things like normal people.
include
can also be used the normal way -- include Animals
allows you to use any classes in it, unqualified.
require 'filename'
is the command to load other files. require './frog'
grabs frog.rb from the current directory, just like it was part of the current file
(in other words, files don't auto-create namespaces for themselves the way Python does).
"Global" variables are just odd. Variables are normally never exposed outside of their class/module. But adding $
in front makes it a "global". These can be seen, with no extra look-up, from anywhere. $yam="white"
anywhere allows anyone anywhere else to use just $yam
to see it.
This seems really bad.
Passing functions works the usual way, but with more syntax. Calling a function variable is f.call(n)
. Yuck. Anonymous functions are passed as normal, but existing function
f
must be passed as method(:f)
. Yuck-yuck. Here's a function taking a function as input which counts certain items in a list:
def countSome(f,arry) sum=0 for n in arry if f.call(n) then sum+=1 end # <= f.call(n), not f(n) end sum # return value end
So far only 1 extra thing, that silly f.call
.
Now let's run it with a pre-made oneToTen boolean function:
# A simple bool func (the ? is decoration only): def oneToTen?(n) n>=1 && n<=10 end c1 = countSome( method(:oneToTen?), nums )
Anonymous functions don't need that, but writing them is really weird. The inputs go inside of pipes, with the whole thing wrapped in curly-braces (the only place curlies are used):
even = lambda {|n| n%2==0} # OR... even = lambda do |n| n%2==0 end # lambdas are used directly: c1 = countSome(even, nums)
Strangely, we're not allowed the ending-? style with lambdas. Naming it even?
would be an error.
Ruby can capture variables in the usual way. A typical curried x+y example (note all of the call
's):
# nested lambda: adder = lambda {|x| lambda {|y| x+y }} # create a function with x captured as 2: plus2=adder.call(2) print "plus2=",plus2.call(6)," ", adder.call(5).call(4) # how to supply both inputs
The trick also works with normal def
functions. This creates an "are you in this range" function:
def inRange?(low, high) lambda{|n| n>=low && n<=high} end c1 = countSome(inRange?(40,60), nums) is1to10 = inRange?(1,10) if countSome(is1to10, nums)==0 then ...
Then we get two more super-silly ways to do the same thing. For one, every function can automatically be passed a block that comes after the end of the call, yield
in the function runs it. Here doLots
expects a block, which it calls for every item in a sequence:
def doLots(i0, i1) for n in i0..i1 yield n # calls the 1 special block we were passed end end
To use a function like this, we call it with our block afterwards. This call prints each number in the range with a > in front:
doLots(3,6) {|a| print ">",a }
Many Ruby built-ins use this style -- the parameters don't list a function to run, you just have to know it comes afterwards. Other languages use this style, except you at least list the block, with a name, and call it like a normal person.
The other super-silly one looks like a lambda but
has extra junk. First, it's created with Proc.new
instead of lambda:
# p1 is another stored function: p1 = Proc.new {|x| x*2}
Next the function using it needs an & before the parameter, and the call does also:
def useProc(&pr) # & in front is required for "Procs" print pr.call(8) end useProc(&p1) # passing a "Proc" also requires the &
Procs have the special ability to use return
to
completely escape the function calling them. That hardly seems worth the effort.
Ruby regexps are almost completely standard. ^\W{2,3}
looks for 2 or 3 alphanumeric characters at the start, and [a-z]+7..\d
looks for at least 1 lowercase letter, a "7", two wildcards and any number. Ruby even has the () groups trick.
Things to know: slashes go around a reg-exp, like /ca.t/
, and =~
checks them, returning nil or the first spot where it matched:
i= "rat cat cantor: =~ /ca.t/ # 8 (start of "cant" matching ca.t)They often look like garbage, due to the fighting slashes, for example
/\d{3,5}.\d/
.
Strings have some really fun regexp builtins. w[regexp]
returns the first match. w.gsub(regexp]{block}
runs the block on every match:
# NOTE: *? is the non-greedy "match shortest" version of *: 'the XcowX is here'[/X.*?X/] # XcowX w='the XcowX is in XcaveX now' w.gsub(/X.*?X/) {|w| puts w[1..-2]} cow cave
Note to self: how to put that {|w|...}
into a variable and use it? Because that won't work now.
Symbols are a general-purpose trick. Technically they're just immutable strings. They can replace strings, or be used to hack-up enums. The method
function requires a symbol, which means that tricks with passing functions often require symbols. Anything with a colon in front is a symbol. The characters themselves can be just anything. :abc
is a symbol.
A sort of enum with symbols:
# put them in an array, since why not?: carTypes=[:car, :truck, :van, :sedan] ct1 = :truck if ct1 == :sedan then ...
What's the advantage? Well, it's easier to read than numbers, and just as fast (symbols are compared by object id). I suppose code-completion can help better than with strings. It's better than nothing, but it seems really odd that Ruby has no real enums.
Symbols are also popular as a string replacement in Hashes, merely for speed. Which seems odd since Ruby isn't generally concerned with speed. Here :red
and :purple
symbols represent RGB colors:
colors={:red=>[255,0,0], :purple=>[255,255,0]} rgb = colors[:red]
Again, this is the same as using "red"=>
and colors["red"]
. Just a tad faster (and more Rubyish).
Assigning a pre-written function to a variable requires symbols. The problem it solves is that Ruby doesn't require parens, so calling doSomething(f)
thinks you want to call f()
first. The fix is making its name into a symbol and converting that to a method: doSomething(method(:f))
.
We can turn strings into symbols with dot-to_sym
. This allows us to call functions given their names as strings::
w="oneToTen?" method(w.to_sym).call(12) # runs "oneTo10?"(12)
For fun, using method
in a class goes on the last part:
w="sqrt" op = Math.method(w.to_sym) # op is Math.sqrt n = op.call(25) # 5