- Introduction
- Object Oriented Programming
- Error Handling
- Unit Testing
- Tips
puts "one" | p "three" | print "five" |
---|---|---|
will not return any value back nil |
will return the printed value | nil (same as puts ) |
The way they process the array data structure: | ||
(calls to_s ) |
prints the array in its code form and returns the value back | (same as p ) but not returns |
gets
method prompts the user to enter a value.
There is an issue with gets
. it returns the value the user enters along with \n
.
To fix this issue, we can call the chomp method: chomp
removes the \n
character.
user_input = gets.chomp
name = "Ahmed"
names = ["aaa", "ddd", "231"]
Variables can also hold methods, that can be called when needed.
Ruby variables do not require semicolons or a data type to be declared. This feature is possible because Ruby is a Just-in-Time (JIT) interpreted language, which automatically recognizes the data type based on what variables are stored.
-
Local variable: scope is limited to the area where they are declared.
-
Global variable
$
: is a variable that is available for the entire application to use, and this is denoted by a$ preceding the variable
.
using global variables is not a good idea since it is hard to track the value of these variables. It is better to use variables that have limited scope, such as local or instance variables. -
Instance variable
@
OOP : is available to a particular instance. To set instance variables, you need to use the@
sign to define a variable. -
CONSTANT: The syntax is to use ALL_UPPERCASE_LETTERS_AND_UNDERSORES while naming your constant.
In Ruby you can change the constant's value. However, it gives a warning message that says that the constant was already initialized and was changed.- scope differs
- Constants are available anywhere in lower scopes
- https://stackoverflow.com/questions/6712298/dynamic-constant-assignment
- scope differs
-
Class variable
@@
: Variables that are available to a particular class.
Both single and double quotes work properly.
- allow scaping ` and \
- Shows everything else literally as is.
- Works with scaping characters like \n and \t
- String Interpolation
#{variable}
- Integrating dynamic values into a string.
- We have to use double-quotes, otherwise the string will be printed as it is without any interpolation.
"Hello, #{name}"
- Behaves like double quotes
- We can span multiple lines
we can do method chaining: `"str".upcase.downcase.swapcase.reverse
for a full list: https://ruby-doc.org/core-2.7.0/String.html
- Substitution
sub
pass two arguments — the first is the word to be replaced, the second is the replacementstr.sub "run", "walk"
- this method substitutes only the first occurrence of "run"
- To change all occurrences, we need to use the
gsub
method, which stands for global substitution.
- inplace effect with bang
(!)
- gsub!, sub! ... etc
strip
to remove white spaces from the string edgessplit
to split a sentence into an array of words or characters- Tip:
str.split(//)
split to all of the individual letters (including spaces)
- Tip:
join
to convert an array of characters into a single string.- str.join(' ')
Highly optimized strings
- Unique and Immutable
- has much less methods than strings
- Constant names. And doesn't have to be pre-declared
- Meant to be Stand for something,i.e., a flag or an indication
- Method names are symbols
p "".methods
- in earlier ruby versions, method names weren't symbols but strings
- Method names are symbols
Three main: Integer, Float and Decimal. Decimal is ideal for complex financial applications.
A good way to remember the order of operations is with the acronym PEDMAS, which stands for:
Parentheses, Exponent, Division, Multiplication, Addition, Subtraction
method name : All the words should be in lowercase and joined by an underscore.
def my_fun
p "hello"
end
my_fun
-
def self.class_method
- calling:
Class.class_method
- calling:
-
def instance_method
- calling:
instance.instance_method
- calling:
-
Calling the class method
n
time is a bad practice. This createsn
new objects in the memory.
Calling all the methods with an instance is a better practice. This won't create a new instance of the class every time.
Procs can be stored in variables. They are more flexible than methods.
We can store a set of processes inside a variable and then call the variable later.
my_proc = Proc.new { |n1, n2| n1 + n2}
We pass arguments inside pipes |...|. After the pipes is the code logic. Procs can take a code block as their parameter.
There are two ways
- Invoking the call method
my_proc.call(1, 2)
- Using brackets
myproc[1, 2]
A bit different to proc
my_lambda = lambda { |x, y| x * y }
with a different syntax: my_lambda = -> (x, y) { x * y}
There are two key differences
- lambdas check the number of the passed arguments
- Passing wrong number of arguments to lambdas raises an error
- whereas Procs ignore anything after the needed arguments
- Returning values { return }
- In case of lambda, the remaining code of the parent Block (e.g., method) continues running after a lambda return.
- whereas proc exits out of the entire block after a return and returns a
nil
.
def send_msg sender:, receiver:, message:
#Do send message
end
def send_msg seneder:, receiver:, message: message = "Hello"
We can pass any number of values to the method
- splat arguments
def people(*names)
- Keyword-based splat arguments
- Use with dynamic data that has a {key: value} structure, e.g., hash data structure
def people **names_and_ages
names_and_ids.each do |id, name|
def my_fun options = {}
p options[:second] # 123
p options[:third] # **nil**
end
my_fun first: "some string", second: 1234
NOTICE: Methods (and blocks) implicitly return the value of the last statement.
There are two ways to create blocks in Ruby
- Using curly braces
repeat = Proc.new { |msg| msg * 5 }
- should used when we put all code on the same line
- Using
do … end
Proc.new do |args|
# logic
end
- Blocks Inherit outer scope
- Blocks can become clousers
- Remerber the context where they were defined, and use this context wherever they are called
- https://www.rubyguides.com/2019/03/ruby-scope-binding/
Two ways:
- Implicit
block_given?
checks if a block is passed to the methodyield
calls the block
- Explicit
- & preceding the last_parameter
last_parameter.call
- check with
last_parameter.nil?
- check with
for i in 0...10
10 is not includedfor i in 0..10
10 is included
Good when we're iterating over collections
arr.each do |i| # we can use block variables
p i
end
or
arr.each { |i| p i }
for querying collections
even_nums= (1...10).to_a.select do |i|
i.even?
end
# shortcuts
p (1...10).to_a.select { |i| i.even? }
# shorter
# avoid using a block variable with `&`
p (1...10).to_a.select(&:even?)
p ["1", "2", "3", "4"].map(&:to_i)
p Hash[["1", "2", "3", "4"].map { |i| [i, i.to_i] }]
`puts ('a'..'e').to_a.map.with_index(1) { |c, i| "#{c}: #{i}"}.join(', ')`
# keeps track of the variable value with each iteration
# `*` is a method, not an operator
[1, 2, 3, 4, 5, 6, 7].inject(&:*)
x = [1,2,3]
x = Array.new
- arr.delete(value)
arr = [0, "hello", "world", 13, "hello"]
arr.delete("hello")
p arr.length # 3
- arr.delete_at(index)
- returns the deleted item
arr.delete_if {|element| < logic>}
- start and end are only stored
0..10
using .. is inclusive 10 is in-range(1...10).to_a
- converting a range of integers into an array
to_a
- converting a range of integers into an array
Ruby implements a thread-safe, "multi-producer, multi-consumer", queues.
- blocking
empty_q.pop
Callingpop
on an empty queue will suspend (sleep) the calling thread until new data pushed onto the queue
q = Queue.new producer = Thread.new { q << 1; sleep 3; q << 3 } consumer = Thread.new do while producer.alive? do p q.pop end end q << 2 # main thread. At this point, both `producer` and `consumer` will have been asleep. pushing `2` will awaken the `consumer` # Suspend the calling thread (main thread) and allow `consumer` thread to run and finish # No need to join `producer`, we guarantee that it will run and finish before the `consumer` exits # https://ruby-doc.org/core-2.5.0/Thread.html#method-i-join consumer.join
- non-blocking
empty_q.pop(non-block=true)
The thread won't be suspended, and ThreadError will be raised.
The order of adding elements is maintained v 1.9
people = Hash.new(0) # Return 0 when a key doesn't exist (normally returns nil)
people = { Ali: 30, Ahmed: 23, Hassan: 27 } # Ruby 1.9
people = { "Ali" => 30, "Ahmed" => 23, "Hassan" => 27 }
people = { :Ali => 30, :Ahmed => 23, :Hassan => 27 }
people[:John] = 1
# Deleting
people.delete(:John)
hash.each_key
hash.each_value
hash.each_pair
- hash.invert
- hash.merge(another_hash)
- Array(hash)
- hash.to_a
- hash.keys, hash.values
Equals to if !
unless arr.empty?
arr.each { |i| p i }
end
# shorten
arr.each { |i| p i } unless arr.empty?
# work the same as
arr.each { |i| p i } if !arr.empty?
- Class names: need to be CamelCase”
- @instance_variables are private
- access using getters and setters
- methods are public by default
class GoodMan
def initialize(name, age, address = "Egypt")
@name = name
@age = age
@address = address
end
attr_accessor :name, :age, :address # getter and setter
attr_reader :address # getter
attr_writer :address # setter
def age # getter
@age
end
def age= (value) # setter
@age = value
end
def summary
@new_var = "some new instance variable"
p @name
p age
p address
end
end
Ali = GoodMan.new
Ali.name = "Ali"
Ali.summary
p Ali.instance_variables
self
outside of method refers to class object- defining a class method
class MyClass # using self def self.class_method1(var) puts var end # using << self class << self def class_method2(var) puts var end end end # outside the class def MyClass.class_method3(var) puts var end Myclass.class_method1(1) Myclass.class_method2(2) Myclass.class_method3(3)
- every class implicitly inherits from
Object
class- and
Object
class inherits fromBasicObject
!
- and
ChildClass
<
ParentClass
- no multiple ineritance, use mixins instead
when a child class overrides the behavior provided by the parent class.
to combine the behavior of both the parent and child classes
class Child < Parent
def method
super
p "Hello from the child class"
end
end
private methods are placed after all the public methods, and preceded by private
keyword.
A way to not to have class names collision.
module MyModule
class MyClass
def my_method
end
end
end
MyModule::MyClass
- A way to share mix-in code among multiple classess. i.e, interface, contract
module MyMixin attr_accessor :name def summary puts @name end end class MyClass include MyMixin end
- We can includes built-in modules like
Enumerable
class MyClass include Enumerable attr_accessor :name, :elements def each yield name @elements.each { |element| yield element } end # or def each(&block) @elements.each { |element| block.call element } # or # elements.each(&block) end # https://stackoverflow.com/questions/7220634/how-do-i-use-the-enumerable-mixin-in-my-class end
begin
p 1/0
rescue ZeroDivisionError => e
p "Error! #{3}"
end
- Make a test class that extends
Test::Unit::TestCase
- Every method start with
test_
gets excecuted when the test class runs as a test case - Pr-test methods that excecute before every
test_
method:setup()
teardown()
- Assertions
assert_equal(expected, actual)
assert_raise Expected_Error { < code will cause an error> }
- dot . means pass, and F means fail
require "test/unit" require_relative "my_class" class MyClassTest < Test::Unit::TestCase def setup @instance = MyClass.new(0) end def test_value assert_equal 0, @instance.value end def test_raise_error assert_raise SomeError { @instance.do(-1) } end end
rspec --init
rspec --format documentation
descripe(<"String"|Class>)
- contains a set of related tests
- all tests must be inside
descripe
:each
run before/after each test:all
run once before/after all tests
- Test logic for each test case
it("Test behavior description")
-
eq, be_true, raise_error
-
be_ <predicate_mothod>, e.g., be_nil, be_car, be_even
- if the tested object has predicate/boolean methods, they will automatically appear in matchers, (include ruby built-in methods)
require "rspec" require_relative "../my_class" descripe MyClass do before { @instance = MyClass.new(0) } it "should return the val attribute" { expect(@instance.val).to eq 0 } end
people = Hash.new(0)
- Return
0
when a key doesn't existpeople["not_a_key"]
- normally returns
nil
- if a hash is the last argument of a method, (not including a block)
- we can drop the
{}
- we can drop the
- Return
select(&:even?)
- Using
&
before a method runs the method against each element in the collection.
- Using
%w(a b c).map.with_index(1)
- output:
a: 1, b: 2, c: 3
- output:
+
and*
are not operators in Ruby; rather, they are methodsself
attr_writer :age def initialize(age_val) age = age_val # local variable self.age = age_val # instance method (setter) end