Ruby Nuances— Ruby Deep Dive [19]

Bhavyansh @ DiversePixel
6 min readAug 23, 2024

--

#frozen_string_literal: true

Provides better memory management and prevents unintended modifications, along with thread safety (concurrent programming)

# frozen_string_literal: true

message = "Hello"
message << ", World!" # This line would raise a RuntimeError

<%= form_with(… , local: false …

The local: false option is used in forms to specify that the form should be submitted via AJAX (Asynchronous JavaScript and XML) rather than a standard HTTP request. When local: false is set, the form submission is handled by JavaScript, allowing the page to update dynamically without a full page reload.

%i and %w

%i creates symbols, while %w creates strings.

symbols = %i[edit update destroy]
#Output: [:edit, :update, :destroy]
%w[pending confirmed in_progress]
#Output: ['pending', 'confirmed', 'in_progress']

Method with **options

def example_method(required_param, optional_param = nil, **options)
puts "Required: #{required_param}"
puts "Optional: #{optional_param}"
puts "Options: #{options}"
end
example_method("Hello", "World", foo: "bar", baz: 123)
# Output
Required: Hello
Optional: World
Options: {:foo=>"bar", :baz=>123}

More with splat operators (* and **)

def print_args(*args)
p args
end
print_args(1, 2, 3) # => [1, 2, 3]

hash = { a: 1, b: 2 }
{ **hash, c: 3 } # => { a: 1, b: 2, c: 3 }
# Destructuring Assignment

a, b, *rest = [1, 2, 3, 4, 5]
# a = 1, b = 2, rest = [3, 4, 5]

Exploring a Ruby Gem: Devise

Devise is used for user authentication in rails

Lets locate it to find out more about it

gem which devise

We have all the controllers in this directory structure

Float::INFINITY in Ruby

In Ruby, Float::INFINITY represents an infinite value. This is useful in various scenarios, such as initializing a variable to an extremely large or small value that can be compared against real numbers during computations.

How it is used:

puts Float::INFINITY   # Outputs: Infinity
puts -Float::INFINITY # Outputs: -Infinity
# Example usage in a comparison
if 1000 < Float::INFINITY
puts "1000 is less than infinity"
end

Storage:

Float::INFINITY is a special floating-point value that adheres to the IEEE 754 standard for floating-point arithmetic. This standard defines special values for positive and negative infinity, which are used in computations to handle cases where results exceed the largest representable finite number.

# Lazy enumerable usage
(1..Float::INFINITY).lazy.select(&:even?).first(5) # => [2, 4, 6, 8, 10]

Garbage Collection in Ruby

Garbage collection (GC) in Ruby is the process of automatically reclaiming memory by destroying objects that are no longer in use. Ruby uses a mark-and-sweep garbage collection algorithm with some enhancements. Here’s a high-level overview:

  1. Mark Phase: Ruby’s garbage collector traverses all the objects starting from root objects (such as global variables, stack variables, etc.) and marks all reachable objects.
  2. Sweep Phase: Once the marking phase is complete, the garbage collector sweeps through the memory and collects all the unmarked objects, reclaiming their memory for future use.

Ruby 2.1 and later versions introduced a generational garbage collector to improve performance by categorizing objects into different generations based on their lifespan:

  • Young Generation: Newly created objects. Most objects die young, so the garbage collector frequently collects young objects.
  • Old Generation: Objects that survive several garbage collection cycles are promoted to the old generation. These objects are collected less frequently since they are assumed to be long-lived.
  • Remembered Set: To keep track of references from old to young objects, optimizing the collection process.

Advanced Features

  1. Incremental Garbage Collection: This feature, introduced in Ruby 2.2, allows the garbage collector to perform its work in smaller steps, reducing the pause times for garbage collection and improving the application’s responsiveness.
  2. Compacting Garbage Collection: Introduced in Ruby 2.7, this feature compacts the heap by moving live objects together, which can help reduce memory fragmentation and improve performance.

Key Points:

  • Ruby’s garbage collector helps manage memory automatically, allowing developers to focus on writing code without worrying about manual memory management.
  • The generational garbage collection strategy improves performance by optimizing how frequently objects are collected based on their age.
  • Advanced features like incremental and compacting garbage collection further enhance performance and memory management.

The `self` keyword

Using self for methods allows us to call them on the class itself rather than on an instance of class.

The self keyword indicates that reverse_words is a class method:

& symbol

Symbol to proc shorthand

char_count.values.all?(&:zero?) does this:

Checks if all the values in a hash called char_count are zero. &:zero? is a shorthand for { |value| value.zero? }. It's using Ruby's symbol to proc syntax to check if each value is zero.

&:zero? == { |value| value.zero? }

More examples:

[1, 2, 3, 4].map(&:to_s)  # => ["1", "2", "3", "4"]
[1, 2, 3, 4].select(&:even?) # => [2, 4]

Safe Navigation Operator

user&.name  # Returns nil if user is nil, otherwise returns user.name

Debugging using ‘tap’

tap is a method that yields the receiver (the object it's called on) to a block, and then returns the receiver. It's useful for performing operations on an object within a method chain without affecting the return value of the chain.

Here’s a simple example:

(1..5).tap { |x| puts "Original: #{x}" }
.to_a
.tap { |x| puts "Array: #{x}" }
.select(&:even?)
.tap { |x| puts "Evens: #{x}" }

This will output:

Original: 1..5
Array: [1, 2, 3, 4, 5]
Evens: [2, 4]

And return [2, 4].

Cool Hash-ing

The problem with solution:

# Given an array of strings strs, group the anagrams together.
# You can return the answer in any order.

def group_anagrams(strs)
anagrams = Hash.new{|hash, key| hash[key] = []}
strs.each do |str|
sorted_str = str.chars.sort.join
anagrams[sorted_str] << str
end
anagrams.values
end
  1. Creating the Hash
anagrams = Hash.new{|hash, key| hash[key] = []}

This line creates a new hash with a default value. The block {|hash, key| hash[key] = []} is called when you try to access a key that doesn't exist in the hash. It automatically creates an empty array as the value for that key.

2. Adding to the Hash:

anagrams[sorted_str] << str

The << operator is used to append an element to an array. In this case, it's adding str to the array that's the value for the key sorted_str.

Here’s how it works step by step:

  1. When you access anagrams[sorted_str], if the key doesn't exist, the default block creates an empty array [] as the value.
  2. Then << str appends the current string to this array.
  3. If the key already exists, it simply appends to the existing array.

HashSet / Set

Set is implemented using Hash under the hood

  1. Efficient lookups
  2. Keeps unique elements
  3. Set operations and membership testing come handy
fruit_set = Set.new(['apple', 'banana', 'orange'])
puts fruit_set.include?('banana') # Output: true
puts fruit_set.include?('grape') # Output: false

Practical example:

# LONGEST CONSECUTIVE SUBSEQUENCE
# Given an unsorted array of integers nums,
# return the length of the longest consecutive elements sequence.

class SequenceUtil
def longest_consecutive(nums)
return 0 if nums.empty?

num_set = nums.to_set # for O(1) lookup time
longest_streak = 0

num_set.each do |num|
if !num_set.include?(num - 1) # found a potential start of sequence
current_num = num
current_streak = 1

while num_set.include?(current_num + 1)
current_num += 1
current_streak += 1
end
longest_streak = [longest_streak, current_streak].max
end
end
longest_streak
end
end

# Usage
nums = [0,3,7,2,5,8,4,6,0,1]

util = SequenceUtil.new
longest_streak = util.longest_consecutive(nums)
puts longest_streak

The params hash in rails

Users send data to your Rails application in three main ways:

  1. Query Parameters: Data sent via the URL, e.g., example.com/?q=bacon.
  2. Form Submissions: Data sent through form fields.
  3. URL Segments: Data embedded within the URL, e.g., /books/1/author.

The params hash stores them all.

Array#sort

Ruby’s Array#sort method is stable. A stable sort preserves the relative order of equal elements in the array.

The Array#sort method uses a variation of the quicksort algorithm called “introsort.” Introsort is a hybrid sorting algorithm that begins with quicksort and switches to heapsort when the recursion depth exceeds a certain level, thus ensuring optimal worst-case performance. This combination provides the average-case speed of quicksort and the worst-case speed of heapsort.

--

--

Bhavyansh @ DiversePixel

Hey I write about Tech. Join me as I share my tech learnings and insights. 🚀