Ruby Nuances— Ruby Deep Dive [19]
#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:
- 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.
- 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
- 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.
- 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
- 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:
- When you access
anagrams[sorted_str]
, if the key doesn't exist, the default block creates an empty array[]
as the value. - Then
<< str
appends the current string to this array. - If the key already exists, it simply appends to the existing array.
HashSet / Set
Set is implemented using Hash under the hood
- Efficient lookups
- Keeps unique elements
- 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:
- Query Parameters: Data sent via the URL, e.g.,
example.com/?q=bacon
. - Form Submissions: Data sent through form fields.
- 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.