Skip to content

Latest commit

 

History

History
864 lines (636 loc) · 15.8 KB

basic.adoc

File metadata and controls

864 lines (636 loc) · 15.8 KB

Ruby basics

Instalation

Linux

Tip
Do not use prepackaged Ruby on Linux distributions.

On Linux use rbenv package manager with the ruby-build extension to install Ruby.

Windows

For windows there is an installer Ruby Installer simplifies the installation to the "windows standard approach".

macOS

The simplest way to install Ruby on macOS is to use homebrew package manager

$ brew install ruby

in case there is a need to have multiple installations rbenv is a good tool to use. Another popular tool is called rvm but nowadays they do too much magic and we recommend using rbenv instead.

Running Ruby

Tip
Usually you put ruby and relates command on PATH so you do not need to always specify the whole path to the executable. With tools like rbenv this is done for you.
$ ruby -v

runs Ruby and prints it’s version. Simplest way to verify that your instalation works as expected.

$ ruby script.rb

interprets the script.rb. However instead of running the script explicitly, you can add the shebang line to your script

#!/usr/bin/env ruby

and make it executable (chmox +x script.rb) and run it simply as ./script.rb.

Interactive Ruby

Ruby comes with an interactive interpreter called irb it allows you to enter commands and see results instantly.

$ irb

About Ruby

Ruby a programming language with following aspects

Interpreted

The Ruby code is interpreted when the script is load, there is no compile phase.

Universal

Ruby can be used for writing scripts, one-liners, web applications and even for mobile and desktop applications.

Fully Object oriented

In Ruby everything is an object and you send messages to those objects.

Everything is an expression

Everything command in Ruby has a return value.

Atomic memory management

MRI

MRI is the Ruby reference implementation, with several other implementations like JRuby, Rubinius, IronRuby or MagLev. As well, there is ISO standard implemented in mruby.

Strongly typed

There are some implicit conversions (e.g. every object has to_s method to provide string representation) but in most cases in case you try to operate on different types Ruby will complain, unless such operation is explicitly provided.

5 + "a" # => TypeError: String can't be coerced into Fixnum

Dynamically typed

The type of a variable is defined by the value assigned to that variable. There is no explicit type information in the code.

a = "string"

What is written in Ruby?

  • programming languages: Ruby (Rubinius), compilers (LessJS)

  • web applications: Github, Gitlab, Redmine, BaseCamp

  • devops tools: Puppet/Chef/Vagrant

  • cloud platforms: OpenShift (v2)

  • cloud management: Foreman, ManageIQ

  • VIM/Emacs scripts

  • static pages generators - Jekyll

  • programming tools

Conventions

  • class names are CamelCase

  • file names reflect class names in snake_case format

  • method names are snake_case

  • constants are UPPER_CASE

  • indent by 2 spaces

  • methods returning boolean values end with ?

  • methods mutating state end with !

Basic syntax

Basic and most explicit syntax looks like

puts("hello world!");

However the parenthesis are optional in case the only one interpretation of the expression

puts "hello world!";

and semicolons are optional as well

puts "hello world!"

String use either quotation marks " or apostrophes '. Apostrophes does not provide string substitution as quotations marks do.

a = 'Hello'      # => "Hello"
b = "#{a} world" # => "Hello world"
b = '#{a} world' # => "\#{a} world"

Operator priority

Special characters in method names

Ruby allows usage of special characted in method names. The standard is to use

  • the question mark ? for methods that return boolean value

  • the exclamation mark ! for methods that mutate the object

1.even?                 # => false
"ruby".upcase.reverse   # => 'YBUR'
"ruby".size.even?       # => true

Comments

In the code above the single line comment is used. It starts with hash # and follows to the end of the line. In case of commenting multiple lines, it is customary to comment every line with single line comment.

# you should
# do this
# to comment
# multiple lines

Data types

As mentioned above in Ruby everything is an object, including arrays or numbers. However there are special syntax shorthands to create instances of special classes.

Strings

As mentioned above String are create by quoting the charachters

a = "string"
b = 'string'

Numbers

Ruby has two basic number classes Fixnum and Float.

a = 1   # => 1
a.class # => Fixnum

b = 1.1 # => 1.1
b.class # => Float

Empty value

Special value that represents "nothing" is nil.

a = nil # => nil

In a boolean expression, nil is considered false, i.e. it’s only of two possible values that are not considered true.

Booleans

As usual there is either true or false.

a = true   # => true
b = false  # => false
a && b     # => false
a || b     # => true

Arrays

Arrays is an ordered sequence of values. There are no restrictions on what types can be in a single array.

["a", 1, true] # => ["a", 1, true]

Hashes

Hash is a structure that maps key to a value.

{"a" => true, "b": false} # => {"a"=>true, :b=>false}

There are two approaches how to write the mapping, either rocket style

key => value

or json style

key: value

You can use both syntaxes, however with the json style the value is converted to symbol, so in case you need to use String or some other type, or get the name of the key from a variable, you need to use the rocket style. Several well-known coding guidelines recommend (and enforce) using rockets everywhere.

Symbols

Symbol is a keyword. It always maps to the same object instance

a = "a" # => "a"
b = "a" # => "a"

a.object_id # => 70224766839340
b.object_id # => 70224750415480

a = :a # => :a
b = :a # => :a

a.object_id # => 722268
b.object_id # => 722268

Objects and methods

Methods are called by using the .. Operators are actually methods.

3 + 3  # => 6
3.+(3) # => 6

[1,2][0]    #=> 1
[1,2].[](0) # => 1

Variables

Ruby has global variables prefixed by $.

$stdout

Classes and object can use class variables, though there are not used very much. Prefer @instance_variables in class-level methods, as they have more predicatable behavior.

@@class_variables = 1

Objects have instance variables.

@instance_variable = 1

Local variables have no prefix.

local_variable = 1

And finally constants are all upper case.

CONSTANT = 1

Conditions

Everything is considered true except false and nil.

a = nil
b = ""

if a
  "we do not get in here"
elsif b
  "we got here"
else
  "we did not get here"
end

Ruby has negative variant to if called unless. Essentially unless bool_expr is equivalent to if !(bool_expr). It is used the same way as normal if.

a = nil
b = ""

unless a
  "we do get in here"
elsif b
  "we did not get here"
else
  "we did not get here"
end

Ruby has inline method of using conditionals called modifier statements.

puts "Hello" if true
puts "Hello" unless false

Ternary operator is available as well.

experssion ? 'was evaluated true' : 'was evaluated false'

Another way to do conditions is to use case statement.

case input
  when 'q', 'e'
    quit
  when 'f'
    format
  else
    help
end

Case statement can as well check on variable class.

case var
  when String
    "it's string"
  when Class
    "it's class"
  when Number
    "it's number"
end

Another way to use case statement is to use it as if and elsif.

case
  when a == "a"
    "a equals a"
  when b == "b"
    "b equals b"
end

Logical operators

There are basic logical expressions

  • and &&

  • or ||

  • not !

as well && can be replaced with and, || can be replaced with or and ! can be replaced with not.

There are basic comparison operators

  • equal ==

  • not equal !=

  • lesser then <

  • greater then >

  • lesser then or equal

  • greater then or equal >=

  • regular expression match =~

Regular expressions

Regular expressions are enclosed with /. The simplest way is to use the regexp operator.

string = 'localhost:2000'
string =~ /.*:.+/     # 0
string =~ /(.)*:(.)+/ # sets $1 a $2

as well there is a match method on string.

data = string.match(/^(.):(\d+)$/)
data[1] # => localhost
data[2] # => 2000

Loops

while repeats as long as the condition is true.

while a < b
  a += 1
end

To go through the body of the loop at least once

begin
  a += 1
end while a < b

There is as well inline way to write the loop

a += 1 while a < b

And finally the negative counterpart until

until a > b
  a += 1
end

Methods

Methods in Ruby always return some value. If it is not explicitly returned using the return keyword, the return value is the value of the last expression in the method. Return as usual returns from method and ends the execution of the method.

# Simple method with two arguments
def mth(a, b)
end

# Method with default value for 2nd argument
def mth(a, b=1)
end

# Method accepting any number of arguments, available as Array args
def mth(*args)
end

# Method requiring at least two arguments
def mth(a, b, *args)
end

Reusing code from other files

The require method loads code from another file. Ruby keeps track of required files and skips loading files that would be loaded 2nd time. Files are looked up using Ruby’s load path, which is represented using an array in $LOAD_PATH and $:. The load method does not keep track of loaded files.

In case the required file ends with [rb, so, o, dll, bundle, jar] extension, the extension may be omitted. There two commands are equivalent

require "somefile"
require "somefile.rb"

To keep track of required files, Ruby keep list of all files that were required in the $" variable.

Blocks

Blocks have many uses-cases. One of the use cases is the replacement for for cycles another use case is anonnymous functions. Block are not executed when defined, but have to be called through the call method (though the calling of call method is most of the times hidden from the develop as in the examples below).

Arrays have method called each that accepts block and calls the block for every single element in the array.

arr = [1,2,3,4]
arr.each do |el|
  puts el
end

will print all four values to the standard output. Blocks can be written in one more way

arr = [1,2,3,4]
arr.each { |el| puts el }

this variant is usually used for single-line blocks.

Block see their own scope plus can access scope in which were defined.

sum = 0
arr = [1,2,3,4]
arr.each { |el| sum += el }

Any method can accept a block and call it

def mth
  return nil unless block_given?
  yield
end

This method will return nil if no block was given or will call the block without any argument and the return value of the block will be return from the method.

Method may also accept blocks as a named argument which is prefixed by &.

def mth(num, &block)
  block.call(num)
end

this method will call block saved in the variable block and will pass one argument which is the first argument passed to the method itself.

Objects

In Ruby everything is an object. Object is an instance of some class. Even every class is an instance of class that inherits from Class. Object can have methods

class Hello
  def say
    "Hello, world!"
  end
end

puts Hello.new.say

and instance variables

class Hello
  def initialize(msg=nil)
    @msg = msg
  end

  def say
    @msg
  end
end

puts Hello.new("Hello, world!").say

To make your instance variables accessible from outside, you define them as attributes. Attributes can be either read-only, write-only or both.

class Hello
  attr_reader :one       # allows reading by using the .one method
  attr_writer :two       # allows writing by using the .two = "xy" method
  attr_accessor :three   # allows both, reading and writing
end

Inheritance

Ruby allows object inheritance. All methods including constructor are inherited. Methods can be overridden by children. super is then used to call the original method.

class A
  def a
    "hello"
  end
end

puts A.new.a # => hello

class B < A
end

puts B.new.a # => hello

class C < A
  def a
    super + " world"
  end
end

puts C.new.a # => hello world

Class methods and attributes

As known from other language, except in Ruby class variables are not used because of some pitfalls in their inheritance.

class A
  def self.a
    "hello"
  end
end

puts A.a # => hello

Modules

Modules are a way to organize your classes in a similar fashion to namespaces. Classes can be included into modules or into other classes.

class A
  class B
  end
end

module Some
  class Thing
  end
end

Module are however used as well as mixins. When module is included into class all methods defined for that module are available in the class as instance methods.

module Helper
  def something
  end
end

class A
  include Helper
end

A.new.something

and when used with extend the mehtods are included as class methods

module Helper
  def something
  end
end

class A
  extend Helper
end

A.something

Ruby has only single inheritance, mixins allow to get around this and provide a way to get some kind of multiple inheritance.

Method access

By default methods are public, explicitly methods can be made protected or private.

class A
  def public_method
  end

  protected

  def protected_method
  end

  private

  def private_method
  end
end

Duck typing

Ruby encourages to react based on behaviour rather then on identity.

class Hunter
  def shoot(animal)
    bang! if animal.class == Duck
  end
end

in this case the code checks if it’s a duck and shoots it, however

class Hunter
  def shoot(animal)
    animal.respond_to?(:quack) && bang!
  end
end

in this case we care if the animal quacks and the it’s shot.

Exceptions

Exceptions represnt a special state in the execution in a program. When an exception is raised, it will bubling thorugh the stack until is caught.

Exceptions are raised using the raise keyword

raise "This is not expected"

On the other hand when an exception needs to be caught, code block is extended with rescue statement that is called when an exception is caught and optionally ensure that is called after both exceptional and non-exceptional state. Unless an exception class is specified explicitly after the rescue keyword, the StandardError class and it’s ancestors are rescued.

begin
  raise "This is not expected"
rescue => e
  puts e.message
ensure
  puts "always"
end
Important
Don’t inherit directly from Exception class but use StandardError instead. The direct descendants of Exception are usually exceptions one doesn’t want to rescue from, such as SystemExit or NoMemoryError.