Intro
This is the start of The Odin Project’s instruction of Ruby as a language.
Basic Data Types
Introduction
Ruby is a STRONG OOP language, where everything is indeed an object, even basic data types even the primitive types(integers, floats, strings, symbols, and Booleans).
Numbers
Ruby’s numerical/math operators are the common + - * /
. But to find the exponent of a number,
aka the square, use **
and the modulo is the regular %
.
The two main types of numbers in Ruby are Integers and Floats.
Like Python, Ruby will return an integer when working with integers and a float when a float is brought into the mix.
For example:
19 / 4 # => 4 Since this is integer division it returns an int
19 / 4.0 # => 4.75
Converting number types
It’s very easy to convert number types. Ruby uses these underscore methods that are easy to recall.
- To convert an integer to a float use the method
to_f
- To convert a float to an integer…
to_i
167.8.to_i # => 167
167.to_f # => 167.0
Notice how Ruby DOESN’T round up numbers, the decimals are simply cut off.
Very useful number methods
- Use
.even?
to determine a number is even. - Use
.odd?
to determine a number is…odd. - To get the floor of a float
.floor
- To get the ceiling of a float
.ceil
Strings
I always have a hard time explaining strings but there’s nothing to it. Strings are just sequences of characters…I.e. text. We write strings in single or double quotation marks, these are known as StringLiterals. The difference between double and single quotations is small, a notable difference is String Interpolation, more on that later, and escape characters ONLY work in doubles.
String Concatenation
Like Python, get used to Python comparisons as they’re both OOP and share some philosophies, there’s a few ways to do this.
There’s the old timey overloaded +
operator. But there’s also a method, .concat
and some cool arrow wizardry.
"His" << "Name Is" << "BOB"
"He's".concat(" very awesome with ").concat("the Ladies")
Substrings
Since strings are sequences and they’re also objects like everything else…you can access substrings from a string. By indexing into them like an array. If you know C, then you’d know this is because every string is actually just an array of characters.
"Hello"[0] #=> "H" this is by index 0
"Hello"[0..1] #=> "He" this is range by indices, 0 to 1
"Hello"[0..3] #=> "Hell"
"Hello"[0...3] #=> "el"
"Hello World"[0, 4] # => "Hell" this syntax starts from index 0 with a range of 4
This is done by slicing.
String#[]
(also aliased asString#slice
) returns a slice copied fromself
.String#[]=
returns a copy ofself
with a slice replaced.String#slice!
returnsself
with a slice removed. Each of the above methods takes arguments that determine the slice to be copied or replaced. The arguments have several forms. For stringstring
, the forms are:string[index]
.string[start, length]
.string[range]
.string[regexp, capture = 0]
.String#[]
(also aliased asString#slice
) returns a slice copied fromself
.String#[]=
returns a copy ofself
with a slice replaced.String#slice!
returnsself
with a slice removed. Each of the above methods takes arguments that determine the slice to be copied or replaced. The arguments have several forms. For stringstring
, the forms and there syntax are:string[index]
.string[start, length]
.string[range]
.string[regexp, capture = 0]
.string[substring]
.
Quickly creating an array of strings
We can use this syntax to quickly create an array of strings array_name = %W(elements)
The elements MUST be written without any quotation marks and separated with whitespace.
strings = %W(a b BOB JIM Prime) #=> ["a", "b", "BOB", "JIM", "Prime"]
Escape Characters
These allow you to write representations of whitespace chars and include quotes inside a string without messing with it. Remember they only work in double quotation marks. Here’s a bunch of them
\\ for backslash
\b for backspace
/r for carriage return
\n for newline char
\s for space
\t for tab
\" for double quote "
\' for single quote '
String Interpolation
String Interpolation allows us to manipulate strings that have placeholder variables in them. Similar to JS ${}
notation or Python f'hello, {name}'
.
Here’s an example:\
name = "Bob"
puts("He is the best! And his name is #{name}")
# => "He is the best! And his name is Bob"
String methods
- To capitalize a string
.capatalize
- To query a string for a substring use
.include?(substring)
- To convert to uppercase use
.upcase()
- To convert to lowercase use
.downcase()
- To determine if a string is empty,
.empty?
- Use
.legnth()
to find the length of a string - If you want to reverse a string, it’s easy thanks to
.reverse()
- Split a string can be using
.split(split_char)
this returns an array of the split strings
"Hello BOB!".split() #=> ["Hello", "BOB"] default split char is white space \s
"Hello BOB!".split("") #=> ["H", "e", "l..."B","O","B] since the split char is an empty string it splits every char in the string
- To strip a string of whitespace, use
.strip()
by default it strips trailing white space but can pass args to change that. We’ll have a look later.
For now, here’s a few other methods we’ll explore further later on.
"he77o".sub("7", "l") #=> "hel7o"
"he77o".gsub("7", "l") #=> "hello"
"hello".insert(-1, " dude") #=> "hello dude"
"hello world".delete("l") #=> "heo word"
"!".prepend("hello, ", "world") #=> "hello, world!"
Like most languages, we can convert other types to strings when needed by using the .to_s
method. For example,
nil.to_s #=>""`
48.1.to_s #=> "48.1"
Symbols
This is something new to me, it sounds familiar but not sure…
The gist of it, strings are mutable, and therefore every time one is used or changed in Ruby, it needs to be stored in memory as a separate value, even if an identical string is already in memory. However, Symbols are only stored in memory once. This makes them faster in some instances than conventional strings.
A common convention is to use Symbols as keys in hashes. We’ll focus on these more later on.
To create a symbol, simply declare it like any other variable BUT with a colon at the beginning, this signals to Ruby that this is a symbol. :my_name = "Bob"
.
"string" == "string" #=> true
"string".object_id == "string".object_id #=> false
:symbol.object_id == :symbol.object_id #=> true
By using the object ID, .object_id()
method, similar to Python’s .id()
method, it returns a numeric value that’s used to identify the object in memory.
DO NOT make the noobish mistake of thinking the == checks that two objects are identical, it merely checks they’re equal in value!
To create an array of symbols you can do
symbols = %i(a b c) # => [:a,:b:,c]
Booleans
Like most languages, Ruby has boolean representation. Using true
and false
.
To represent nothing at all, we use nil
similar to None/none in many other languages.
Variables
Variables, often referred to as ‘vars’, allow us to assign a ‘label’ of sort to some data. we’ve seen this before, if not in code then in math.
Declaring variables
Declaring a variable in Ruby is pretty much the same as in Python. Simply type out the variables name and assign it a value.
For example: my_age = 23
Variable can also be reassigned values of operation, my_age = 23 + 1
We can use the basic arithmetic operators to update the value of our variable. The noobs would do my_age = my_age + 1
but instead try the following:
my_age = 23
my_age += 1 #=> 24
my_age -= 1 #=> 22
my_age *= 2 #=> 44
my_age /= 4 #=> 11
Naming variables (naming conventions)
Variable names should easily describe what the variable represents. Avoid Hungarian naming convention! Here’s a list of guidelines, but treat them as rules:
- No single char names
- No abbreviated names
- No data types
- use snake_case for multi_word_names
Variables are references
Treat variables as pointers to some bit of data in memory. This could cause some quirky side effects at times.
my_age = 23
his_age = my_age
his_age += 3
puts(his_age) # 26
puts(my_age) # 23
Some methods have unwanted side effects.
One way to modify a string is to use the upcase!
method, instead of the safe upcase
method. If the string is modified using johns_location.upcase!
then desired_location
will also reflect that change:
johns_location.upcase! #=> "BARCELONA"
desired_location #=> "BARCELONA"
johns_location #=> "BARCELONA"
This example may be hard to completely understand at this point in the lesson. The important concept is that assigning variables to other variables can have unintended side effects. Just because you can do it, doesn’t mean you should. You will have the opportunity to revisit this example in one of the following assignments.
Getting variable values from the user
One way to get a value from user input, via stdin, is the gets
method, which is short for ‘get string’. However since entering a value in the console will also append a ‘\n’ to the value we can daisy-chain the .chomp method to truncate that. `your_name = gets.chomp
Variable scope
Scope is an important thing in programming, it refers to the scope in which variable are defined. Three common scopes are:
- Block scope: in which a variable is only defined and accessible within that block, outside of that block it doesn’t exist.
- Method-definition scope: refers to variables defined within a method, they’re accessible from anywhere within that method’s definition and runtime.
- Class scope: Are class-level variables accessible from within all scopes in a class, these are often knowns as class fields or instance variables. More on these later on when we discuss OOP.
Remember, inner scopes have access to outer scope variables but not the other way around. So a global variable is accessible from block scopes. Please DO NOT use global variables, it leads to ugly side effects and reeks of unprofessionalism.
\
Constants
constants are immutable variables, their values can’t be changed post declaration, and are declared using ALL_CAPS Funny enough Ruby will only give you a warning if you try to change the value of a constant instead of outright stopping you, but you really shouldn’t! Constants are available throughout the app’s scope.
Global Variables
These are declared like normal variables but with a $
prefix, like $admin = me
. They’re also available throughout the entire app and override any scope restrictions. Most Ruby devs avoid global vars.
Class Variables
Are variables declared within a class using the prefix @@
like @@car_wheel_number = 4
. These variables are accessible to all instances of the class and the class itself. They’re used when we need a variable that’s related to the class but not every instance of the class needs its own value of it. Think of them as common variables that all the instances share…
NOTE: Class variables are rarely needed and often misused.
Instance variables
These are variables declared using a single @
prefix, like @car_width = 189.45cm
. These are accessible throughout the current instance of the parent class, i.e. every instance of this class has its own value for this variable. This is a common OOP principle.
This is what’s known as a InstanceVariable and must be initialized at the class level outside any method definitions. These are mutable.
Useful Variable methods:
- Replacing variable values in memory:
.replace
changes the value in memory that a variable points to. This is different from simply reassigning a new value to variable, as this simply points it to another block in memory entirely. use the.object_id
method to test this out.
Input & Output
Output commands
There’s a few ways to output values to the user, the two most common are the print
and puts
commands.
The two are very similar but puts
appends a newline char to the end of the argument passed to it, while print
keeps it all in one line.
Both commands return nil
.
To illustrate this try:
print "My name is Bob."; print "I am learning Ruby!"
# "My name is Bob.I am learning Ruby!"=>nil
puts "My name is Bob."; puts "I am learning Ruby!"
# "My name is Bob."
# "I am learning Ruby!"
# => nil
Notice how we chained commands using the semicolon? This is good for experimenting and for short examples but is not recommended in other cases.
When an array is passed to puts
, every element in the array is outputted individually on a new line. Whereas print will output the array in its entirety on a single line.
puts [1, 2]
1
2
print [1,2]
[1,2]
puts [1, nil, nil,2]
1
2
Another thing to note is that puts
converts everything, when it can, to a string so nil values become empty lines
The puts
command can be shortened to p
for some sweet syntax-sugar.
But p
is also good for showing ‘raw’ versions of the object passed to it as it returns its argument rather than nil
and displays invisible chars and other details, which makes it good for debugging.
The putc
command will output a single character to the console, so you need to specify otherwise it outputs the first char of a string.
Input commands
To accept input from a user via the console we can use a command we mentioned earlier, the gets
command.
your_age = gets.to_i # this will convert the input from a string to an integer
your_age # <value> as int
Unlike print
and puts
, the gets
actually returns a value, not a surprise…
Another quick reminder to use the .chomp
method for string values to truncate the ‘\n’.