This chapter is a tightrope walk between oversimplification and a degree of detail that is unnecessary for a Rails newbie. After all, the objective is not becoming a Ruby guru, but understanding Ruby on Rails. I am going to elaborate on the most important points. The rest is then up to you. If you would like to know more about Ruby, then I recommend the book "The Ruby Programming Language" by David Flanagan and Yukihiro Matsumoto.
It is easy to program in Ruby, but Ruby is not a simple language.
We are going to use Ruby 2.4. You can check the installed Ruby version
by running the command ruby -v
:
$ ruby -v
ruby 2.4.0p0 (2016-12-24 revision 57164) [x86_64-darwin16]
$
Ruby is a scripting language. So it is not compiled and then executed, but read by an interpreter and then processed line by line.
A simple hello-world.rb
program consists of one line of code:
puts 'Hello World!'
Use your favorite editor to open a new file with the filename
hello-world.rb
and insert the above line into it. You can then execute
this Ruby program in the command line as follows:
$ ruby hello-world.rb
Hello World!
$
Note
|
A program line in a Ruby program does not have to end with a semicolon. The Ruby interpreter is even so intelligent that it recognizes if a program line was split over two or more lines for the sake of readability. Indenting code is also not necessary. But it does make it much easier to read for human beings! |
If you go looking for examples on Ruby on the Internet, you will find two typical ways of printing text on the screen:
-
puts
prints a string, followed by a newline. -
print
prints a string (without newline).
Example program (an extension of the program hello-world.rb
):
puts 'Hello World!'
puts
puts 'zzz'
print 'Hello World!'
print
puts 'zzz'
On the screen, you will see this:
$ ruby hello-world.rb
Hello World!
zzz
Hello World!zzz
A comment in a Ruby program starts with a #
-sign and ends with a
newline. As an example, I can add a comment to the hello-world.rb
above:
# Program for displaying "Hello World!"
# by Stefan Wintermeyer
puts 'Hello World!'
A comment can also follow a program line:
puts 'Hello World!' # Example comment
A #
-sign within strings in inverted commas is not treated as the start
of a comment. Example program:
# Example program
# by Stefan Wintermeyer
puts 'Hello World!'
puts '############'
puts
puts '1#2#3#4#5#6#' # Comment on this
When programming, you do not always have a Ruby handbook available.
Fortunately, the Ruby developers thought of this and provided a built-in
help feature in form of the program ri
.
Note
|
Of course only if you have installed the documentation which is
the default. If you used rvm to install Ruby you can run
rvm docs generate to generate the documentation
|
This is a typical chicken and egg situation. How can I explain the Ruby help feature, if we are only just getting started with Ruby? So I am going to jump ahead a little and show you how you can search for information on the class String:
$ ri String
[...]
$
Tip
|
Many times it is easier and more informative to use Google instead of ri. |
irb
stands for Interactive Ruby and is a kind of sandbox where
you can play around with Ruby at your leisure. irb
is launched by
entering irb
on the shell and ends if you enter exit
.
An example is worth a thousand words:
$ irb
irb(main):001:0> puts 'Hello World!'
Hello World!
=> nil
irb(main):002:0> exit
$
Note
|
I the future examples I use IRB.conf[:PROMPT_MODE] = :SIMPLE in
my .irbrc config file to generate shorter irb output (without
the irb(main):001:0> part). You can do the
same by using irb --simple-prompt .
|
Ruby only knows objects. Everything is an object (sounds almost like
Zen). Every object is an instance of a class. You can find out the class
of an object via the method .class
.
An object in Ruby is encapsulated and can only be reached from the outside via the methods of the corresponding object. What does this mean? I cannot change any property of an object directly from the outside. The corresponding object has to offer a method with which I can do so.
Note
|
Please do not panic if you have no idea what a class and an object is. I won’t tell anyone and you can still work with it just fine without worrying too much. This topic alone could fill whole volumes. Roughly speaking, an object is a container for something and a method changes something in that container. Please go on reading and have a look at the examples. The puzzle will gradually get clearer. |
In other programming languages, the terms you would use for Ruby methods would be: functions, procedures, subroutines and of course methods.
Note
|
Here we go with the oversimplification. We can not compare non-Object oriented programming languages with OO ones. Plus there are two kinds of methods (class methods and instance methods). I do not want to make it too complicated. So I simply ignore those "fine" distinctions. |
At this point you start looking for a good example, but all I can think of are silly ones. The problem is the assumption that we are only allowed to use knowledge that has already been described previously in this book.
So let’s assume that we use the following code sequence repeatedly (for whatever reason):
puts 'Hello World!'
puts 'Hello World!'
puts 'Hello World!'
So we want to output the string “Hello World!” three times in separate rows. As
this makes our daily work routine much longer, we are now going to
define a method (with the meaningless name three\_times
), with which
this can all be done in one go.
Important
|
Names of methods are always written in lower case. |
def three_times
puts 'Hello World!'
puts 'Hello World!'
puts 'Hello World!'
end
Let’s test this by starting the irb
and loading the program with the
command load './hello-worldx3b.rb'
. After that we have access to the
three_times
method.
$ irb
>> load './hello-worldx3b.rb'
=> true
>> three_times
Hello World!
Hello World!
Hello World!
=> nil
>> exit
When defining a method, you can define required parameters and use them within the method. This enables us to create a method to which we pass a string as parameter and we can then output it three times.
def three_times(value)
puts value
puts value
puts value
end
$ irb
>> load './hello-worldx3c.rb'
=> true
>> three_times('Hello World!')
Hello World!
Hello World!
Hello World!
=> nil
Incidentally, you can omit the brackets when calling the method.
>> three_times 'Hello World!'
Hello World!
Hello World!
Hello World!
=> nil
Tip
|
Ruby gurus and would-be gurus are going to turn up their noses on the subject of “unnecessary” brackets in your programs and will probably pepper you with more or less stupid comments with comparisons to Java and other programming languages. There is one simple rule in the Ruby community: the fewer brackets, the cooler you are! ;-) But you won’t get a medal for using fewer brackets. Decide for yourself what makes you happy. |
If you do not specify a parameter with the above method, you will get
the error message: wrong number of arguments (0 for 1)
:
>> three_times
ArgumentError: wrong number of arguments (given 0, expected 1)
from /Users/.../hello-worldx3c.rb:1:in `three_times'
from (irb):2
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>> exit
You can give the variable value
a default value and then you can also
call the method without parameter:
def three_times(value = 'blue')
puts value
puts value
puts value
end
$ irb
>> load './hello-worldx3d.rb'
=> true
>> three_times('Example')
Example
Example
Example
=> nil
>> three_times
blue
blue
blue
=> nil
>> exit
For now you can think of a class as a collection of methods. The name of
a class always starts with an upper case letter. Let’s assume that the
method belongs to the new class This_and_that
. It would then be
defined as follows in a Ruby program:
class This_and_that
def three_times
puts 'Hello World!'
puts 'Hello World!'
puts 'Hello World!'
end
end
Let’s play it through in irb
:
$ irb
>> load './hello-worldx3e.rb'
=> true
Now we try to call the method three_times
:
>> This_and_that.three_times
NoMethodError: undefined method `three_times' for This_and_that:Class
from (irb):2
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>>
This results in an error message, because This_and_that
is a class
and not an instance. As we are working with instance methods, it only
works if we have first created a new object (a new instance) of the
class This_and_that
with the class method new
. Let’s name it abc
:
>> abc = This_and_that.new
=> #<This_and_that:0x007fb01b02dcd0>
>> abc.three_times
Hello World!
Hello World!
Hello World!
=> nil
>> exit
I will explain the difference between instance and class methods in more detail in the section called "Class Methods and Instance Methods". Another chicken and egg problem.
Quite often it makes sense to only call a method within its own class or
own instance. Such methods are referred to as private methods (as
opposed to public methods), and they are listed below the keyword
private
within a class.
class Example
def a
puts 'a'
end
private
def b
puts 'b'
end
end
We run this in an irb. First the public and than the private method which raises an error:
$ irb
>> load './pm-example.rb'
=> true
>> abc = Example.new
=> #<Example:0x007fa530037910>
>> abc.a
a
=> nil
>> abc.b
NoMethodError: private method `b' called for #<Example:0x007fa530037910>
from (irb):4
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>> exit
If a new instance is created (by calling the method new), the method
that is processed first and automatically is the method initialize
.
The method is automatically a private method, even if it not listed
explicitly under private
.
class Room
def initialize
puts 'abc'
end
end
irb test of it:
$ irb
>> load './initialize-example-a.rb'
=> true
>> kitchen = Room.new
abc
=> #<Room:0x007f830704edb8>
>> exit
The instance kitchen
is created with Room.new
and the method
initialize is processed automatically.
The method new accepts the parameters specified for the method initialize:
class Example
def initialize(value)
puts value
end
end
$ irb
>> load './initialize-example-b.rb'
=> true
>> abc = Example.new('Hello World!')
Hello World!
=> #<Example:0x007fbb0b845f30>
>> exit
puts
is nice to demonstrate an example in this book but normally you
need a way to return the result of something. The return
statement can
be used for that:
def area_of_a_circle(radius)
pi = 3.14
area = pi * radius * radius
return area
end
$ irb
>> load './circle-a.rb'
=> true
>> area_of_a_circle(10)
=> 314.0
>> exit
But it wouldn’t be Ruby if you couldn’t do it shorter. You can simply skip return:
def area_of_a_circle(radius)
pi = 3.14
area = pi * radius * radius
area
end
You can actually even skip the last line because Ruby returns the value of the last expression as a default:
def area_of_a_circle(radius)
pi = 3.14
area = pi * radius * radius
end
Obviously you can go one step further with this code:
def area_of_a_circle(radius)
pi = 3.14
pi * radius * radius
end
return
is sometimes useful to make a method easier to read. But you
don’t have to use it in case you feel more comfortable with out.
A class can inherit from another class. When defining the class, the
parent class must be added with a <
(smaller than) sign:
class Example < ParentClass
Rails makes use of this approach very frequently (otherwise I would not be bothering you with it).
In the following example, we define the class Abc
and which contains
the methods a
, b
and c
. Then we define a class Abcd
and let it
inherit the class Abc
and add a new method d
. The new instances
example1
and example2
are created with the Class-Methods new
and
show that example2
has access to the methods a
, b
, c
and d
but
example1
only to a
, b
and c
.
class Abc
def a
'a'
end
def b
'b'
end
def c
'c'
end
end
class Abcd < Abc
def d
'd'
end
end
Run in in the irb:
$ irb
>> load './inheritance-example-a.rb'
=> true
>> example1 = Abc.new
=> #<Abc:0x007fac5a845630>
>> example2 = Abcd.new
=> #<Abcd:0x007fac5a836630>
>> example2.d
=> "d"
>> example2.a
=> "a"
>> example1.d
NoMethodError: undefined method `d' for #<Abc:0x007fac5a845630>
from (irb):6
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>> example1.a
=> "a"
>> exit
Tip
|
It is important to read the Error-Messages. They tell you what happened
and where to search for the problem. In this example Ruby said that
there is an |
There are two important kinds of methods: class methods and instance methods.
You now already know what a class it. And an instance of such a class is
created via the class method new
. A class method can only be called in
connection with the class (for example, the method new
is a class
method). An instance method is a method that only works with an
instance. So you cannot apply the method new
to an instance.
Let’s first try to call an instance method as class method:
class Knowledge
def pi
3.14
end
end
Run it in irb:
$ irb
>> load 'pi-a.rb'
=> true
>> Knowledge.pi
NoMethodError: undefined method `pi' for Knowledge:Class
from (irb):2
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>>
So that does not work. Well, then let’s create a new instance of the class and try again:
>> example = Knowledge.new
=> #<Knowledge:0x007fe620010938>
>> example.pi
=> 3.14
>> exit
Now we just need to find out how to define a class method. Hardcore Rails gurus would now whisk you away into the depths of the source code and pick out examples from ActiveRecord. I will spare you this and show an abstract example:
class Knowledge
def self.pi
3.14
end
end
$ irb
>> load './pi-b.rb'
=> true
>> Knowledge.pi
=> 3.14
>>
And the proof to the contrary:
>> example = Knowledge.new
=> #<Knowledge:0x007fa8da045198>
>> example.pi
NoMethodError: undefined method `pi' for #<Knowledge:0x007fa8da045198>
from (irb):4
from /Users/stefan/.rvm/rubies/ruby-2.4.0/bin/irb:11:in `<main>'
>> exit
There are different notations for defining class methods. The two most
common ones are self.xyz
and class << self
:
# Variant 1
# with self.xyz
#
class Knowledge
def self.pi
3.14
end
end
# Variant 2
# with class << self
#
class Knowledge
class << self
def pi
3.14
end
end
end
The result is always the same.
Of course you can use the same method name for a class and an instance
method. Obviously that doesn’t make code easier to read. Here is
an example with pi
as a class and an instance method:
class Knowledge
def pi
3.14
end
def self.pi
3.14159265359
end
end
$ irb
>> load './pi-c.rb'
=> true
>> Knowledge.pi
=> 3.14159265359
>> example = Knowledge.new
=> #<Knowledge:0x007f8379846f30>
>> example.pi
=> 3.14
>> exit
You can read out all defined methods for a class with the method
instance_methods
. We try it out with the class Knowledge
(first we
create it once again in the irb):
class Knowledge
def pi
3.14
end
end
$ irb
>> load './pi-a.rb'
=> true
>> Knowledge.instance_methods
=> [:pi, :instance_of?, :kind_of?, :is_a?, :tap, :public_send,
:remove_instance_variable, :singleton_method, :instance_variable_set,
:define_singleton_method, :method, :public_method, :extend, :to_enum,
:enum_for, :<=>, :===, :=~, :!~, :eql?, :respond_to?, :freeze,
:inspect, :object_id, :send, :display, :to_s, :nil?, :hash, :class,
:singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint,
:untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods,
:protected_methods, :private_methods, :public_methods,
:instance_variable_get, :instance_variables,
:instance_variable_defined?, :!, :==, :!=, :__send__, :equal?,
:instance_eval, :instance_exec, :__id__]
>>
But that is much more than we have defined! Why? It’s because Ruby gives every new class a basic set of methods by default. If we only want to list the methods that we have defined, then we can do it like this:
>> Knowledge.instance_methods(false)
=> [:pi]
>> exit
There are many pre defined Classes available in Ruby. For a newbie the probably most important ones handle numbers and strings.
Let’s experiment a little bit in the irb
. The method .class
tells us
which class we are dealing with.
$ irb
>> "First test"
=> "First test"
>> "First test".class
=> String
That was easy. As you can see, Ruby “automagically” creates an object of
the class String
. We could also do this by explicitly calling the
method new
:
>> String.new("Second test")
=> "Second test"
>> String.new("Second test").class
=> String
If we call String.new
or String.new()
without a parameter, this also
creates an object of the class String
. But it is an empty String:
>> String.new
=> ""
>> String.new.class
=> String
>> exit
Strings can be defined either in single quotes or double quotes.
There is a special feature for the double quotes: you can integrate
expressions with the construct #{}
. The result is then automatically
inserted in the corresponding place in the string.
To show this we have to jump ahead and use variables in the example:
$ irb
>> a = "blue"
=> "blue"
>> b = "Color: #{a}"
=> "Color: blue"
>> exit
If the result of the expression is not a string, Ruby tries to apply the
method to_s
in order to convert the value of the object into a string.
Let’s try that by integrating an Integer into a String:
$ irb
>> a = 1
=> 1
>> b = "A test: #{a}"
=> "A test: 1"
>> a.class
=> Integer
>> b.class
=> String
>> exit
Note
|
If we mention single or double quotation marks in the
context of strings, we do not mean typographically correct
curly quotation marks (see wikipedia.org/wiki/Quotation_mark), but the ASCII symbols referred to as apostrophe (' ) or quotation mark(" ).
|
Most classes already come with a bundle of very useful methods. These methods are always written after the relevant object, separated by a point.
Here are a few examples for methods of the class String
.
$ irb
>> a = 'A dog'
=> "A dog"
>> a.class
=> String
>> a.size
=> 5
>> a.downcase
=> "a dog"
>> a.upcase
=> "A DOG"
>> a.reverse
=> "god A"
>> exit
With instance_methods(false)
you can get a list of the build in
methods:
$ irb
>> String.instance_methods(false)
=> [:include?, :%, :*, :+, :to_c, :unicode_normalize, :unicode_normalize!,
:unicode_normalized?, :count, :partition, :unpack, :unpack1, :sum, :next,
:casecmp, :casecmp?, :insert, :bytesize, :match, :match?, :succ!, :+@,
:-@, :index, :rindex, :<=>, :replace, :clear, :upto, :getbyte, :==, :===,
:setbyte, :=~, :scrub, :[], :[]=, :chr, :scrub!, :dump, :byteslice,
:upcase, :next!, :empty?, :eql?, :downcase, :capitalize, :swapcase,
:upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split,
:lines, :reverse, :chars, :codepoints, :prepend, :bytes, :concat, :<<,
:freeze, :inspect, :intern, :end_with?, :crypt, :ljust, :reverse!, :chop,
:scan, :gsub, :ord, :start_with?, :length, :size, :rstrip, :succ, :center,
:sub, :chomp!, :sub!, :chomp, :rjust, :lstrip!, :gsub!, :chop!, :strip,
:to_str, :to_sym, :rstrip!, :tr, :tr_s, :delete, :to_s, :to_i, :tr_s!,
:delete!, :squeeze!, :each_line, :squeeze, :strip!, :each_codepoint,
:lstrip, :slice!, :rpartition, :each_byte, :each_char, :to_f, :slice,
:ascii_only?, :encoding, :force_encoding, :b, :valid_encoding?, :tr!,
:encode, :encode!, :hash, :to_r]
>> exit
Ruby used to have different types of Integers depending on the length of the
number. With version 2.4 things got easier: We just deal with Integer
.
$ irb
>> 23.class
=> Integer
>> 230000000000000000000.class
=> Integer
>> (23*10000).class
=> Integer
>> exit
Float
is a class for real numbers (“floating point numbers”). The
decimal separator is a point.
$ irb
>> a = 20.424
=> 20.424
>> a.class
=> Float
>> exit
For boolean values (true
and false
) and for nil
(no value) there
are separate classes:
$ irb
>> true.class
=> TrueClass
>> false.class
=> FalseClass
>> nil.class
=> NilClass
>> exit
nil
(no value) is, by the way, the contraction of the Latin word
nihil (nothing) or, if you look at it in terms of programming history,
the term derives from “not in list” from the legacy of the programming
language Lisp (the name is an acronym of List Processing).
Normal variables are written in lower case. Please use snake_case
. Same goes
for symbols and methods.
$ irb
>> pi = 3.14
=> 3.14
>> exit
Constants start with an upper case letter.
Warning
|
A constant can also be overwritten with a new value since Ruby 2.3 (but you will get a warning message). So please do not rely on the constancy of a constant. |
$ irb
>> Pi = 3.14
=> 3.14
>> Pi = 123
(irb):2: warning: already initialized constant Pi
(irb):1: warning: previous definition of Pi was here
=> 123
>> puts Pi
123
=> nil
>> exit
You are on the safe side if you are using only ASCII symbols. But with Ruby 2.4 and the right encoding, you could also use special characters (for example German Umlaute) more or less without any problems in a variable name. But if you want to be polite towards other programmers who probably do not have those characters directly available on their keyboards, it is better to stick to pure ASCII.
Variables have a different scope (or “reach”) within the Ruby application and therefore also within a Ruby on Rails application.
Important
|
You need to keep this scope in mind while programming. Otherwise you can end up with odd effects. |
Local variables either start with a lower case letter or an underscore
(_
). Their scope is limited to the current environment (for example
the current method). The following example defines two methods which use
the same local variable radius. Because they are local they don’t
interact with each other:
def area(radius)
3.14 * radius * radius
end
def circumference(radius)
2 * 3.14 * radius
end
$ irb
>> load './variable-a.rb'
=> true
>> area(10)
=> 314.0
>> circumference(1)
=> 6.28
>> exit
A global variable starts with a $
-sign and is accessible in the entire
programm. Example programm:
$value = 10
def example
$value = 20
end
puts $value
example
puts $value
$ ruby variable-b.rb
10
20
Global variables are used very rarely! You wouldn’t harm yourself by forgetting that they exist right now.
Instance variables (“*A*ttributes”, hence the @
) only apply within a
class, but everywhere in it – a mini version of global variables, so to
speak. Unlike global variables, you will find instance variables all
over the place in a Rails application. Let’s tackle them in form of an
example program with the name color.rb
:
class Wall
def initialize
@color = 'white'
end
def color
@color
end
def paint_it(value)
@color = value
end
end
my_wall = Wall.new
puts my_wall.color
my_wall.paint_it('red')
puts my_wall.color
If you start this program, the following output will appear:
$ ruby color.rb
white
red
$
In the method initialize
we set the instance variable @color
to the
value “white”. The method paint_it(value)
changes this instance
variable.
With the method color
we can access the value of @color
outside of
the instance. This kind of method is called a setter method.
In order to keep the amount of chicken and egg problems in this chapter at a manageable level, we need to go back to the topic Methods and combine what we have learned so far.
You may not think of it straight away, but once you have got used to working with Ruby, then it makes perfect sense (and is perfectly logical) to chain different methods.
$ irb
>> a = 'a blue car'
=> "a blue car"
>> a.upcase
=> "A BLUE CAR"
>> a.upcase.reverse
=> "RAC EULB A"
>> exit
As instance variables (“attributes”) only exist
within the relevant instance, you always need to write a “getter” method
for exporting such a variable. If we define a class Room
that has the
instance variables @doors
and @windows
(for the number of doors and
windows in the room), then we can create the getter methods doors
und
windows
(example program room.rb
):
class Room
def initialize
@doors = 1
@windows = 1
end
def doors
@doors
end
def windows
@windows
end
end
kitchen = Room.new
puts "D: #{kitchen.doors}"
puts "W: #{kitchen.windows}"
The execution of the program:
$ ruby room.rb
D: 1
W: 1
$
As this scenario – wanting to simply return a value in identical form –
is so common, there is already a ready-made getter method for it with
the name attr_reader
, which you would apply as follows in the program
room.rb
:
class Room
def initialize
@doors = 1
@windows = 1
end
attr_reader :doors, :windows
end
kitchen = Room.new
puts "D: #{kitchen.doors}"
puts "W: #{kitchen.windows}"
attr_reader
is a method which is called on the Room
class. That is
the reason why we use Symbols (e.g. :doors
and :windows
) instead of
variables (e.g. @doors
and @windows
) as parameter.
Note
|
attr_reader is a good example for meta programming in Ruby.
When working with Rails, you will frequently come across meta
programming and be grateful for how it works automagically.
|
If you want to change the number of doors or windows from the outside, you need a “setter” method. It can be implemented as follows:
class Room
def initialize
@doors = 1
@windows = 1
end
attr_reader :doors, :windows
def doors=(value)
@doors = value
end
def windows=(value)
@windows = value
end
end
kitchen = Room.new
kitchen.windows = 2
puts "D: #{kitchen.doors}"
puts "W: #{kitchen.windows}"
The corresponding output is this:
$ **ruby room.rb**
D: 1
W: 2
$
As you can probably imagine, there is of course also a ready-made and
easier way of doing this. Via the setter method attr_writer
you can
simplify the code of room.rb
further:
class Room
def initialize
@doors = 1
@windows = 1
end
attr_reader :doors, :windows
attr_writer :doors, :windows
end
kitchen = Room.new
kitchen.windows = 2
puts "D: #{kitchen.doors}"
puts "W: #{kitchen.windows}"
And (who would have thought!) there is even a method attr_accessor
that combines getters and setters. The code for room.rb
would then
look like this:
class Room
def initialize
@doors = 1
@windows = 1
end
attr_accessor :doors, :windows
end
kitchen = Room.new
kitchen.windows = 2
puts "D: #{kitchen.doors}"
puts "W: #{kitchen.windows}"
There is a whole range of useful instance methods for converting
(“casting”) objects from one class to another. First, let’s use the
method .to_s
to convert a Fixnum
to a String
.
$ irb
>> a = 10
=> 10
>> a.class
=> Integer
>> b = a.to_s
=> "10"
>> b.class
=> String
>> exit
Note
|
Incidentally, that is exactly what puts does if you use puts
to output a Fixnum or a Float (for non-strings, it simply
implicitly adds the method .to_s and outputs the result).
|
Now we use the method .to_i
to change a Float
to a Fixnum
.
irb
>> c = 10.0
=> 10.0
>> c.class
=> Float
>> d = c.to_i
=> 10
>> d.class
=> Integer
>> exit
Integrating a to_s
method is often useful. Then you can simply
output a corresponding object via puts
(puts
automatically outputs
an object via the method to_s
).
Here is an example:
class Person
def initialize(first_name, last_name)
@first_name = first_name
@last_name = last_name
end
def to_s
"#{@first_name} #{@last_name}"
end
end
$ irb
>> load './person-a.rb'
=> true
>> sw = Person.new('Stefan', 'Wintermeyer')
=> #<Person:0x007fa95d030558 @first_name="Stefan", @last_name="Wintermeyer">
>> puts sw
Stefan Wintermeyer
=> nil
>> exit
Why is there also a plus symbol in the list of methods for String? Let’s
find out by looking it up in ri
:
$ ri -T String.+
String.+
(from ruby site)
------------------------------------------------------------------------------
str + other_str -> new_str
------------------------------------------------------------------------------
Concatenation---Returns a new String containing other_str
concatenated to str.
"Hello from " + self.to_s #=> "Hello from main"
hmmm … Let’s see what it says for Integer
:
$ ri -T Integer.+
Integer.+
(from ruby site)
------------------------------------------------------------------------------
int + numeric -> numeric_result
------------------------------------------------------------------------------
Performs addition: the class of the resulting object depends on the class of
numeric and on the magnitude of the result. It may return a Bignum.
Let’s have a go and play around with this in irb
. So we should be able
to add the +
to an object, just as any other method, separated by a dot
and add the second number in brackets as parameter:
$ irb
>> 10 + 10
=> 20
>> 10+10
=> 20
>> 10.+10
=> 20
>> 10.+(10)
=> 20
>> exit
Aha! The plus symbol is indeed a method, and this method takes the next value as parameter. Really we should put this value in brackets, but thanks to Ruby’s well thought-out syntax this is not necessary.
Yes, you can overwrite any method. Logically, this does not make much
sense for methods such as +
, unless you want to drive your fellow
programmers mad. I am going to show you a little demo in irb
so you
will believe me.
The aim is overwriting the method +
for Fixnum
. We want the result
of every addition to be the number 42. We write a so call "monkey patch":
class Integer
def +(name, *args, &blk)
42
end
end
Now we use the +
method before and after that monkey patch:
irb
>> 10 + 10
=> 20
>> load './monkey-patch-a.rb'
=> true
>> 10 + 10
=> 42
>> exit
First we perform a normal addition. Than we redefine the method +
for
the class Integer
, and after that we do the calculation again. But this
time, with different results.
An abstract if
-condition looks like this:
if expression
program
end
The program between the expression and end
is executed if the result
of the expression is not false
and not nil
.
Note
|
You can also use a if expression then
program
end |
The construct for a simple if
-branch in a Ruby program looks like the
following example program:
a = 10
if a == 10
puts 'a is 10'
end
Important
|
The == is used to compare two values.
Please don’t mix it up with the single = .
|
You can test an expression really well in irb
:
$ irb
>> a = 10
=> 10
>> a == 10
=> true
>> exit
$
A frequently used shorthand notation of an if
-condition can be found
in the following code:
a = 10
# long version
#
if a == 10
puts 'a is 10'
end
# short version
#
puts 'a is 10' if a == 10
You can probably imagine how this works, but for the sake of completeness, here is a little example:
a = 10
if a == 10
puts 'a is 10'
else
puts 'a is not 10'
end
There are different ways of implementing loops in Ruby. The iterator variation is used particularly often in the Rails environment.
An abstract while loop looks like this:
while expression do
program
end
Note
|
The while expression
program
end |
Here is an irb
example:
$ irb
>> i = 0
=> 0
>> while i < 3 do
?> puts i
>> i = i + 1
>> end
0
1
2
=> nil
>> exit
Until
loops are built similarly:
until expression
program
ends
Again, here is the corresponding irb
example:
$ irb
>> i = 5
=> 5
>> until i == 0
>> i = i - 1
>> puts i
>> end
4
3
2
1
0
=> nil
>> exit
“Block” and “iterator” are some of the favorite words of many Ruby programmers. Now I am going to show you why.
In the loop
5.times { |i| puts i }
i
is the iterator and puts i
is the block.
You can also express the whole thing in the following syntax:
5.times do |i|
puts i
end
Iterators are just a specific type of method. As you probably know, the
word “iterate” means to repeat something. For example, the class
Integer
has the iterator times()
. Let’s see what help ri Integer.times
offers:
$ ri -T Integer.times
Integer.times
(from ruby site)
------------------------------------------------------------------------------
int.times {|i| block } -> self
int.times -> an_enumerator
------------------------------------------------------------------------------
Iterates the given block int times, passing in values from zero to
int - 1.
If no block is given, an Enumerator is returned instead.
5.times do |i|
print i, " "
end
#=> 0 1 2 3 4
And it also gives a nice example that we are going to try out in irb:
$ irb
>> 5.times do |i|
?> puts i
>> end
0
1
2
3
4
=> 5
>> exit
There is also a single-line notation for small blocks:
$ irb
>> 5.times { |i| puts i }
0
1
2
3
4
=> 5
>> exit
By the way, an iterator does not necessarily have to pass a variable to the block:
$ irb
>> 5.times { puts 'example' }
example
example
example
example
example
=> 5
>> exit
A block is the code that is triggered by an iterator. In the block, you have access to the local variable(s) passed by the iterator.
Apart from times
there is also the method upto
, for easily
implementing a loop. ri
offers a nice example for this, too:
$ ri -T Integer.upto
Integer.upto
(from ruby site)
------------------------------------------------------------------------------
int.upto(limit) {|i| block } -> self
int.upto(limit) -> an_enumerator
------------------------------------------------------------------------------
Iterates the given block, passing in integer values from int up to and
including limit.
If no block is given, an Enumerator is returned instead.
For example:
5.upto(10) { |i| print i, " " }
#=> 5 6 7 8 9 10
As in many programming languages, arrays and hashes are popular structures in Ruby for storing data.
An array is a list of objects. Let’s play around in irb:
$ irb
>> a = [1,2,3,4,5]
=> [1, 2, 3, 4, 5]
>> a.class
=> Array
>> exit
That is simple and easy to understand.
Let’s see if it also works with strings in the array:
$ irb
>> a = ['Test', 'Banana', 'blue']
=> ["Test", "Banana", "blue"]
>> a.class
=> Array
>> a[1]
=> "Banana"
>> a[1].class
=> String
>> exit
That also works.
So all that’s missing now is an array with a mixture of both. Obviously
that will work, too, because the array stores objects and it does not
matter which kind of objects they are (i.e. String
, Integer
, Float
,
…). But a little test can’t hurt:
$ irb
>> a = [1, 2.2, 'House', nil]
=> [1, 2.2, "House", nil]
>> a.class
=> Array
>> a[0]
=> 1
>> a[0].class
=> Integer
>> a[1].class
=> Float
>> a[2].class
=> String
>> a[3].class
=> NilClass
>> exit
Arrays can also be created via the method new
(like
any class). Individual new elements can then be added at the end of an
array via the method <<
. Here is the corresponding example:
$ irb
>> a = Array.new
=> []
>> a << 'first item'
=> ["first item"]
>> a << 'second item'
=> ["first item", "second item"]
>> exit
You can work your way through an array piece by piece via the method
each
. Example:
$ irb
>> cart = ['eggs', 'butter']
=> ["eggs", "butter"]
>> cart.each do |item|
?> puts item
>> end
eggs
butter
=> ["eggs", "butter"]
>> exit
ri Array.each
provides help and an example in case you forget how to
use each
.
A Hash is a list of key/value pairs. Here is an example with strings as keys:
$ irb
>> prices = { 'egg' => 0.1, 'butter' => 0.99 }
=> {"egg"=>0.1, "butter"=>0.99}
>> prices['egg']
=> 0.1
>> prices.count
=> 2
>> exit
Of course, hashes can store not just strings as objects in the values, but - as with arrays - also classes that you define yourself (see the section called "Arrays").
Symbols are a strange concept and difficult to explain. But they are very useful and used frequently, amongst others with hashes.
Normally, variables always create new objects:
$ irb
>> a = 'Example 1'
=> "Example 1"
>> a.object_id
=> 70124141350360
>> a = 'Example 2'
=> "Example 2"
>> a.object_id
=> 70124141316700
>> exit
In both cases, we have the variable a
, but object ID is different. We
could carry on in this way indefinitely. Each time, it would generate a
different object ID and therefore a new object. In principle, this is no
big deal and entirely logical in terms of object orientation. But it is
also rather a waste of memory space.
A symbol is defined by a colon before the name and cannot store any values itself, but it always has the same object ID, so it is very well suited to be a key:
$ irb
>> :a.class
=> Symbol
>> :a.object_id
=> 702428
>> exit
Let’s do another little experiment to make the difference clearer. We
use a string object with the content “white
” three times in a row and
then the symbol :white
three times in a row. For "white"
, a new
object is created each time. For the symbol :white
, only the first
time:
$ irb
>> 'white'.object_id
=> 70342874305700
>> 'white'.object_id
=> 70342874300640
>> 'white'.object_id
=> 70342874271720
>> :white.object_id
=> 1088668
>> :white.object_id
=> 1088668
>> :white.object_id
=> 1088668
>> exit
Using symbols as key for hashes is much more memory efficient:
$ irb
>> colors = { black: '#000000', white: '#FFFFFF' }
=> {:black=>"#000000", :white=>"#FFFFFF"}
>> puts colors[:white]
#FFFFFF
=> nil
>> exit
You will frequently see symbols in Rails. If you want to find out more
about symbols, go to the help page about the class Symbol via
ri Symbol
.
With the method each
you can work your way through a Hash
step by
step. Example:
$ irb
>> colors = {black: '#000000', white: '#FFFFFF' }
=> {:black=>"#000000", :white=>"#FFFFFF"}
>> colors.each do |key, value|
?> puts "#{key} #{value}"
>> end
black #000000
white #FFFFFF
=> {:black=>"#000000", :white=>"#FFFFFF"}
>> exit
Again, ri Hash.each
offers help and an example, in case you cannot remember one
day how to use each.
The class Range represents an interval. The start and end points of the interval are defined enclosed in normal brackets and separated by two dots in between them. Here is an example in which we use a range like an iterator with each:
$ irb
>> (0..3)
=> 0..3
>> (0..3).class
=> Range
>> (0..3).each do |i|
?> puts i
>> end
0
1
2
3
=> 0..3
>>
Via the method to_a
you can generate an array from a Range
:
>> (0..3).to_a
=> [0, 1, 2, 3]
>>
A range can be generated from objects of any type. Important is only
that the objects can be compared via <⇒
and use the method succ
for
counting on to the next value. So you can also use Range
to represent
letters:
>> ('a'..'h').to_a
=> ["a", "b", "c", "d", "e", "f", "g", "h"]
>>
As alternative notation, you may sometimes come across Range.new()
. In
this case, the start and end points are not separated by two dots, but
by a comma. This is what it looks like:
>> (0..3) == Range.new(0,3)
=> true
>> exit