Ruby: General Concepts
nilis the nullish value keyword.- Ruby is an inherently Synchronous language. There's nothing that corresponds to a native Promise.
- Modules are Mixins that can be included in a Class. This also provides Java-like Interface and Abstract Class reuse.
- All Function, Method, Procs, and Lambda-types are Closures.
Ruby: Closures
All Function, Method, Procs, and Lambda-types are Closures.
Examples
# More Closures
## Closures include any of procs, lambdas, methods, functions, blocks
#-----------------------------------#
# Procs (functions)
## Lambda proc with no variable name
-> (arg) {p arg +1 }.call(2)
## Proc new keyword with no assignment
Proc.new {| n | p n + 111}.call(2)
## Proc new keyword with assignment
proc1 = Proc.new {| n | n ** 2}
p proc1.call(4)
## Proc from block
def make_proc(&block)
block
end
proc2 = make_proc {|x| x+7 }
p proc2.call(4)
#-----------------------------------#
# Lambdas
## With variable name
varName = -> (arg){p arg + 1}
varName.call(2)
#-----------------------------------#
# Blocks
## Can be a method
def example1
yield
end
example1{p 2+ 4}
def example2(&block)
block.call
end
example2{p 5+ 8}
## Helpful for arrays
[1,2,3,4].each do |x|
p x
end
[1,2,3,4].each{|x| p x}
Lambda Proc with no variable name (Anonymous Function called with an actual parameter or argument):
-> (arg) {p arg +1 }.call(2)
Methods
# Methods
## Last line is automatically returned
## No explicit return type needed
def scoring(x, y)
x + y
end
## It can be added however
def add(x, y)
return x + y
end
puts(scoring(1,2))
puts(add(1,4))
## Parameterization - key arguments
def order_irrelevant_key_args(arg1:, arg2:, arg3:)
arg1 + arg2 + arg3
end
p order_irrelevant_key_args(arg1:1, arg3: 2, arg2: 3)
p order_irrelevant_key_args(arg3: 2, arg2: 3, arg1:3,)
def order_irrelevant_optional_key_args(arg1:, arg2:1, arg3:2)
arg1 + arg2 + arg3
end
p order_irrelevant_optional_key_args(arg1:1)
p order_irrelevant_optional_key_args(arg1:1, arg3:5)
## Parameterization - arguments
def order_matters(arg1, arg2, arg3)
arg1 + arg2 + arg3
end
p order_matters(1,2,3)
def order_matters_optional(arg1 = 1, arg2 = 2, arg3)
arg1 + arg2 + arg3
end
p order_matters_optional(3)
## Parameterization - optional (as arr)
## Logically, this order of args is required
## Standard args occur first (and ordering matters)
## It also looks for any key args
## The remainder are optional and specified by * (... in JS)
def optional(arg1, *opt, arg2:)
total = arg1 + arg2
opt.each{|x| total = total + x }
total
end
p optional(1,2,3,4, arg2: 5)
Note: official Ruby documentation refers to all Functions as Methods (and uses these interchangeably):
- https://ruby-doc.com/docs/ProgrammingRuby/html/tut_methods.html
- https://docs.ruby-lang.org/en/2.0.0/syntax/methods_rdoc.html
Resources and Links
- https://docs.ruby-lang.org/en/2.0.0/syntax/methods_rdoc.html
- https://ruby-doc.com/docs/ProgrammingRuby/html/tut_methods.html
Code samples:
Ruby: Hashes and Arrays
Hash
A Hash is a dict / array (since they are integer-indexed collections) / map equivalent.
Named Key - Value (e.g. - Objects in JS, dicts in Python).
# hash_example = {}
# hash_example = Hash.new
hash_example = Hash[]
hash_example['a'] = n
hash_example[:b] = 100
Array
Array - an Array proper (e.g. - Key by index Value).
Expands in size - ArrayList like (Java).
arr_example = Array.new()
arr_example = []
arr_example.push(1)
arr_example.push(2)
arr_example.push(3)
p arr_example.first
p arr_example.last
Ruby Arrays are used to implement (or as backing Data Structures for) most other commonly encountered Data Structures (Set, Heap, Stack, Queue, etc.).
Unlike many other languages, in Ruby there are only the two primary container-type Data Structures (Array and Hash). Data Structures such as Set aren't inherent to the core language itself and are added through standalone, core, libraries (in a manner somewhat akin to the Java EE and Collections APIs, JavaScripts Web APIs, etc.).
In Ruby, Array length and size() accomplish the same:
size()(the method) is an alias for the fieldlength.- Added for uniformity since many languages use
length(Java, JavaScript) while C++ usessize(). - Refer to the Documentation.
Resources and Links
- https://docs.ruby-lang.org/en/3.4/Hash.html
- https://docs.ruby-lang.org/en/3.4/Array.html
- https://docs.ruby-lang.org/en/3.4/Set.html
Code samples:
Ruby: Interceptors
before_action- provides the same functionality as a Java Interceptors, executes a function prior to other actions being performed within an HTTP handler.
Ruby: Object Oriented Design
Inheritance
class Animal
attr_accessor :name
def initialize(name)
@name = name
end
def speak
"Hello!"
end
end
class GoodDog < Animal
def initialize(name, color)
super(name)
@color = color
end
def speak
super + " from GoodDog class"
end
end
Resources and Links
Code samples:
Ruby: Exception Handling
- Use
failinstead ofraisein circumstances where an Exception will not be handled and rethrown as something more specific. - Use
raiseotherwise. rescueis the relevant "catch" keyword.- When defining customized Exceptions inherit from
StdErr.
Examples
begin
p hash_example['a']['b']
rescue Exception # Should generally prefer to check for StdErr
p "hash_example['a']['b'] throws an exception"
end
begin
p hash_example['a']['b']
rescue Exception # Should generally prefer to check for StdErr
p "hash_example['a']['b'] throws an exception"
ensure
p "I'm a finally block"
end
Resources and Links
Ruby: Techniques
Strings and Symbols
# Ruby symbols and strings
## Comparison - these are not the same
a = 'hello'
b = :hello
p a == b
## Hash - different keys
x = Hash[]
x['a'] = 1
x[:a] = 2
p x['a'] == x[:a]
p x
## Via try
y = Hash[]
y['b'] = { 'c' => 100 }
p y
p y['b']['c']
p y[:b]&.try(:c)
## Convert
p :a.to_s
p 'a'.to_sym
https://github.com/Thoughtscript/rrr_2024/blob/main/ruby/17-sym_str/main.rb
Remember:
- Ruby Strings don’t work like Java's String Pool. The same String content can have two Pointers.
- Ruby Symbols that share the same content will have the same Address in memory.
- Strings are mutable and Symbols aren't.
- It's often easier and better to use Symbols (performance, overhead, immutability-wise) to access, enumerate, or work with Hashes.
Equality and Comparisons
For all intents and purposes one should generally use == ("Generic Equality"):
# At the Object level, == returns true only if obj and other are the same object.
1 == 1.0 #=> true
1.eql? 1.0 #=> false
As opposed to eql? (Hash Equality) which checks by Hash key and/or subtype (as in the case of Numeric comparisons above) or any of: ===, equal?. Generally, these should be avoided due to the specifics of their implementations and occasionally lengthy research into their side-effects.
If required use the other existing comparison operators or Methods:
b.is_a? A
b.kind_of? A
b.instance_of? A
## check identity by object_id
x = 10;
y = 25;
z = x;
puts x.object_id
puts y.object_id
puts z.object_id == x.object_id
Consult: https://stackoverflow.com/questions/7156955/whats-the-difference-between-equal-eql-and and https://ruby-doc.org/3.2.2/Object.html#method-i-eql-3F
Helpful Loop Opertations
each_with_index:
a.each_with_index { |item, index|
p %Q(#{item} at index: #{index})
}
Resources and Links
- https://docs.google.com/presentation/d/1cqdp89_kolr4q1YAQaB-6i5GXip8MHyve8MvQ_1r6_s/edit#slide=id.g2048949e_0_38
- https://www.toptal.com/ruby/interview-questions
- https://www.turing.com/interview-questions/ruby
- https://stackoverflow.com/questions/255078/whats-the-difference-between-a-string-and-a-symbol-in-ruby
- https://stackoverflow.com/questions/7156955/whats-the-difference-between-equal-eql-and
- https://ruby-doc.org/3.2.2/Object.html#method-i-eql-3F
Code samples:
Ruby: Safe Navigation Operators
Consider the example of checking the index of an M x N Array. Specifically, where some row r may be Out of Bounds.
Or, alternatively, checking if a nested field exists on an Object.
Three ways of checking that include are given as follows.
The Overly Verbose Way
if account && account.owner && account.owner.address
# ...
# => false
# => nil
end
ActiveRecord Try
if account.try(:owner).try(:address)
# ...
# => false
# => nil
end
Safe Navigation Operator
if account&.owner&.address
# ...
# => nil
# => undefined method `address' for false:FalseClass`
end
With Hashes
Given:
hash_example = Hash[]
hash_example['a'] = n
hash_example[:b] = 100
To check for nested a > :b:
hash_example['a']&.try(:b)
hash_example['a']&.try(:[], :b)
Array#dig and Hash#dig
Consider:
address = params[:account].try(:[], :owner).try(:[], :address)
# or
address = params[:account].fetch(:owner) { {} }.fetch(:address)
address = params.dig(:account, :owner, :address)
Resources and Links
Code samples:
Ruby: Truthy Evaluations
In Ruby, only false and nil are evaluated to false.
0, [], etc. all evaluate to true. (Unlike Javascript where 0 evaluates to false.)
Resources and Links
Ruby: Visibility and Access
@@- Class Variable, defines a field that's synchronized between all instances of the Class. For example, counting the number of instantiated copies of a Class that have been created since Application start.@- fields must be set and got using getters and setters, same asself.attr_accessor- fields can be accessed directly with dot notation.private- a keyword that can be used to indicate that every Closure definition in the scope below be given the private access visibility modifier.
Example
class ExampleClass
attr_accessor :field_one
def set_and_get_field_two(arg)
## Does not have to be declared as attr_accessor
## But cannot be directly accessed in public
@field_two = arg
p @field_two
end
def get_field_two()
p @field_two
end
def get_field_one
## These are the same
p @field_one
p self.field_one
example_private(arg1: @field_one)
end
# Everything below is given the 'private' access modifier
private
def example_private(arg1:)
p arg1
end
end
## Call
### Constructor
e = ExampleClass.new
### Set attr_accessor field
e.field_one = 2
### Getter for @
e.get_field_one
Resources and Links
Code samples:
Ruby: Common Rails Commands
Start
rails db:create
rails db:migrate
rake db:seed
rails server --binding=127.0.0.1
By default, the Ruby on Rails serve will serve from: http://localhost:3000/
Migrations
bin/rails generate migration ExampleMigration
rails db:migrate
# rake db:migrate
Reset DB
# run migration and seeding
rails db:setup
# rails db:create
# rails db:migrate
# rake db:seed
rails db:reset
Create Model and Table
rails g model Dinosaur name:text
rails g model BabyDino name:text
Create Controller
rails g controller Dinosaurs
Resources and Links
Code samples:
Ruby: Active Record
Active Record FK Example
Schema
ActiveRecord::Schema[7.1].define(version: 2024_07_05_224153) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "examples", force: :cascade do |t|
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "jsonexample", id: false, force: :cascade do |t|
t.integer "id"
t.json "json_col"
t.json "json_array_col"
t.jsonb "jsonb_col"
t.jsonb "jsonb_array_col"
end
create_table "sub_examples", force: :cascade do |t|
t.bigint "example_id"
t.text "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["example_id"], name: "index_sub_examples_on_example_id"
end
end
From: https://github.com/Thoughtscript/rrr_2024/blob/main/rails/web/db/schema.rb
Models
class SubExample < ApplicationRecord
attribute :name, :string
# Does not need to be explicitly set
# attribute :id, :integer
# self.primary_key = :id
# Does not need to be explicitly set
# attribute :example_id, :integer
belongs_to :example, class_name: "Example", inverse_of: :sub_examples
validates :name, presence: true
end
From: https://github.com/Thoughtscript/rrr_2022/blob/main/_ruby/web/app/models/baby_dino.rb
class Example < ApplicationRecord
# Remember that Rails ActiveRecord uses attributes here!
# Distinct from DTO's.
attribute :name, :string
# Does not need to be explicitly set
# #attribute :id, :integer
# self.primary_key = :id
has_many :sub_examples, inverse_of: :example
validates :name, presence: true
def msg
"Test Message!"
end
end
From: https://github.com/Thoughtscript/rrr_2024/blob/main/rails/web/app/models/example.rb