100 Ruby & Ruby on Rails Practice Programs (Full Detailed)
This PDF contains 100 practice programs covering Core Ruby and Ruby on Rails topics. Each entry includes: Problem
statement, 1–3 implementation approaches (where applicable), example input/output, explanation, and Rails-specific
notes if relevant. If you want expanded runnable examples or more variants for specific items, tell me the program
numbers.
1. Reverse a String
Approach 1:
str = 'Rails'
puts str.reverse
Approach 2:
puts str.each_char.to_a.reverse.join
Input / Output: Input: 'Rails' → Output: 'sliaR'
Explanation: Built-in String#reverse is simplest; second approach shows manual reversal by converting to array of
chars.
2. Palindrome Check (ignore non-alphanum & case)
Approach 1:
def palindrome?(s)
s2 = s.downcase.gsub(/[^a-z0-9]/,'')
s2 == s2.reverse
end
Approach 2:
def palindrome?(s)
chars = s.downcase.scan(/[a-z0-9]/)
chars == chars.reverse
end
Input / Output: Input: 'Madam, I'm Adam' → true
Explanation: Normalize string then compare with reverse; second uses scan for only allowed chars.
3. Factorial (iterative & recursive)
Approach 1:
def fact_iter(n)
(1..n).reduce(1,:*)
end
Approach 2:
def fact_rec(n)
return 1 if n<=1
n * fact_rec(n-1)
end
Input / Output: Input: 5 → 120
Explanation: Iterative uses Enumerable#reduce; recursive shows mathematical definition (may overflow for large n).
4. Fibonacci (recursive, iterative, memoized)
Approach 1:
def fib_rec(n)
return n if n<2
fib_rec(n-1) + fib_rec(n-2)
end
Approach 2:
def fib_iter(n)
a,b=0,1
n.times{a,b=b,a+b}
a
end
Approach 3:
def fib_memo(n, memo={})
return n if n<2
memo[n] ||= fib_memo(n-1,memo)+fib_memo(n-2,memo)
end
Input / Output: Input: 6 → 8
Explanation: Recursive is exponential; iterative is O(n); memoized recursive is O(n) using caching.
5. Find duplicates in array
Approach 1:
arr = [1,2,2,3,4,4]
dups = arr.group_by{|x| x}.select{|k,v| v.size>1}.keys
Approach 2:
dups = arr.select.with_index{|x,i| arr.index(x)!=i}.uniq
Input / Output: Input: [1,2,2,3,4,4] → [2,4]
Explanation: Group_by approach counts occurrences; select-with-index identifies repeated items.
6. Unique elements
Approach 1:
arr.uniq
Approach 2:
Hash[arr.map{|e| [e,true]}].keys # alternative using hash keys to dedupe
Input / Output: Input: [1,2,2] → [1,2]
Explanation: Array#uniq is idiomatic and preserves order of first occurrences.
7. Sum of array (reduce vs sum)
Approach 1:
arr.inject(0,:+)
Approach 2:
arr.sum # Ruby 2.4+
Input / Output: Input: [1,2,3] → 6
Explanation: Use Enumerable#sum when available; inject is universal.
8. Prime check (trial division and optimized)
Approach 1:
def prime?(n)
return false if n<2
(2..Math.sqrt(n)).none?{|i| n % i == 0}
end
Approach 2:
def prime_opt?(n)
return false if n<2
return true if [2,3].include?(n)
return false if n%2==0 || n%3==0
i=5
while i*i<=n
return false if n%i==0 || n%(i+2)==0
i += 6
end
true
end
Input / Output: Input: 13 → true
Explanation: Optimized checks reduce iterations using 6k±1 rule.
9. Sort hash by value
Approach 1:
h = {a:3,b:1,c:2}
sorted = h.sort_by{|k,v| v}.to_h
Approach 2:
sorted_pairs = h.to_a.sort_by{|k,v| v}; Hash[sorted_pairs]
Input / Output: Result: {b:1,c:2,a:3}
Explanation: Use sort_by then convert back to hash; original order preserved in Ruby 1.9+ hashes.
10. Count words in string
Approach 1:
s.split(/\s+/).reject(&:empty?).size
Approach 2:
s.scan(/\w+/).size
Input / Output: Input: 'hello world' → 2
Explanation: split on whitespace or scan word characters; scan ignores punctuation.
11. Anagram check
Approach 1:
def anagram?(a,b)
a.downcase.chars.sort == b.downcase.chars.sort
end
Approach 2:
def anagram?(a,b)
a.downcase.chars.tally == b.downcase.chars.tally
end
Input / Output: Input: 'listen','silent' → true
Explanation: Sorting compares canonical forms; tally (Ruby 2.7+) compares counts without sorting.
12. Reverse words in sentence
Approach 1:
'hello world'.split.reverse.join(' ')
Approach 2:
s.split(' ').reverse.join(' ') # preserves simple cases
Input / Output: → 'world hello'
Explanation: Split words into array, reverse, then join.
13. Swap two variables without temp
Approach 1:
a,b = b,a
Approach 2:
a ^= b; b ^= a; a ^= b # works for integers only
Input / Output: Swaps values of a and b
Explanation: Multiple assignment is idiomatic Ruby; XOR trick is only for integers and less readable.
14. Map to squares and select
Approach 1:
[1,2,3].map{|n| n*n}
Approach 2:
arr.map(&->(n){ n*n }) # using lambda to demonstrate passing
Input / Output: → [1,4,9]
Explanation: Use map for functional transformation.
15. Flatten nested array (with levels)
Approach 1:
[1,[2,3],[4,[5]]].flatten # full flatten
Approach 2:
[1,[2,[3]]].flatten(1) # flatten one level
Input / Output: → [1,2,3,4,5]
Explanation: Array#flatten accepts level argument to control depth.
16. Array intersection & difference
Approach 1:
[1,2,3] & [2,3,4] # intersection
Approach 2:
[1,2,3] - [2] # difference
Input / Output: Intersection → [2,3]; Difference → [1,3]
Explanation: Operators & and - provide set-like operations on arrays.
17. Rotate string by n (Caesar cipher)
Approach 1:
def caesar(s,n)
s.chars.map{|c|
if ('a'..'z').include?(c)
(((c.ord - 97 + n)%26)+97).chr
elsif ('A'..'Z').include?(c)
(((c.ord - 65 + n)%26)+65).chr
else c end}.join
end
Approach 2:
Using tr: s.tr('A-Za-z', shifted_range) # build mapping with ranges
Input / Output: 'abc',2 → 'cde'
Explanation: Manual ord/chr math or use tr for mapping ranges.
18. Merge two sorted arrays (merge step)
Approach 1:
def merge(a,b)
i=j=0; out=[]
while i<a.size && j<b.size
out << (a[i] <= b[j] ? a[i++] : b[j++])
end
out + a[i..-1].to_a + b[j..-1].to_a
end
Approach 2:
(a+b).sort # simpler but less efficient (O((m+n)log(m+n)))
Input / Output: Merges two sorted arrays into a sorted array
Explanation: Merge step is O(m+n); concatenation+sort is simpler but less optimal.
19. Count character frequency
Approach 1:
s.chars.tally # Ruby 2.7+
Approach 2:
s.each_char.with_object(Hash.new(0)){|c,h| h[c]+=1}
Input / Output: 'aabbc' → {'a'=>2,'b'=>2,'c'=>1}
Explanation: tally is concise; manual hash accumulation works in older Ruby versions.
20. Find second largest element
Approach 1:
arr.uniq.sort[-2]
Approach 2:
arr.uniq.max(2).last # uses Enumerable#max
Input / Output: Input: [1,3,4,4] → 3
Explanation: uniq to avoid duplicate largest affecting result; max(2) returns two largest values.
21. Binary search (iterative)
Approach 1:
def bsearch(a, x)
l=0; r=a.size-1
while l<=r
m=(l+r)/2
return m if a[m]==x
a[m] < x ? l = m+1 : r = m-1
end
nil
end
Approach 2:
# Ruby's Array#bsearch can be used for monotonic functions: a.bsearch{|v| v>=x}
Input / Output: Returns index of x or nil
Explanation: Classic binary search implementation; Ruby provides bsearch for convenience.
22. Rotate array by k
Approach 1:
def rotate(a,k)
k = k % a.size
a[-k..-1] + a[0...-k]
end
Approach 2:
a.rotate(k) # built-in Array#rotate
Input / Output: [1,2,3,4],2 → [3,4,1,2]
Explanation: Array#rotate is simplest; slice+concat shows manual behavior.
23. Check power of two (bit trick)
Approach 1:
def power_of_two?(n)
n>0 && (n & (n-1)) == 0
end
Approach 2:
def power_of_two?(n)
(1..Math.log2(n).to_i).include?(n)
end # less ideal
Input / Output: 8 → true
Explanation: Bitwise trick is constant time and idiomatic for integers.
24. Find missing number in 1..n
Approach 1:
def missing(a)
n = a.size+1
(1..n).sum - a.sum
end
Approach 2:
Use XOR trick for constant space: xor all 1..n and array elements and result is missing number
Input / Output: [1,2,4] → 3
Explanation: Sum formula uses arithmetic series; XOR avoids large sums and overflow.
25. Remove nils and blanks from array
Approach 1:
arr.compact.reject{|x| x.to_s.strip==''}
Approach 2:
arr.reject(&:blank?) # Rails active_support provides blank?
Input / Output: Removes nil and empty strings
Explanation: Use compact then reject empty strings; Rails' blank? simplifies this.
Rails note: In Rails use Object#blank? provided by ActiveSupport.
26. Transpose matrix
Approach 1:
matrix.transpose
Approach 2:
Manual: (0...cols).map{|j| (0...rows).map{|i| matrix[i][j]}}
Input / Output: [[1,2],[3,4]] → [[1,3],[2,4]]
Explanation: Array#transpose requires rectangular matrix; manual method demonstrates logic.
27. Depth of nested arrays (recursive)
Approach 1:
def depth(x)
return 0 unless x.is_a?(Array)
1 + x.map{|e| depth(e)}.max.to_i
end
Approach 2:
# Alternative using stack BFS/DFS iterative approach for large depths
Input / Output: [[1,[2]]] → depth 3
Explanation: Recursively compute depth; iterative approach can avoid recursion limits.
28. String compression (run-length encoding)
Approach 1:
def compress(s)
s.gsub(/(.)\1+/){|m| m[0] + m.length.to_s}
end
Approach 2:
Manual: iterate and count runs accumulating into output string
Input / Output: 'aaabb' → 'a3b2'
Explanation: Regex-based approach is concise; manual iteration gives control and is language-agnostic.
29. LRU cache (conceptual using ordered hash)
Approach 1:
# Use Hash which preserves insertion order; implement eviction
class LRU
def initialize(size)
@size=size; @h={}
end
def get(k)
return nil unless @h.key?(k)
v=@h.delete(k); @h[k]=v; v
end
def set(k,v)
@h.delete(k) if @h.key?(k)
@h[k]=v
@h.shift if @h.size> @size
end
end
Approach 2:
# Use gems or deque for better perf
Input / Output: Conceptual behavior: caches limited size, evicts least recently used
Explanation: Hash insertion order lets us move accessed keys to end; shift removes oldest.
30. Generate permutations and combinations
Approach 1:
[1,2,3].permutation.to_a
Approach 2:
[1,2,3].combination(2).to_a
Input / Output: Permutations: [[1,2,3],...], Combinations: [[1,2],[1,3],[2,3]]
Explanation: Enumerable provides permutation and combination methods.
31. Sum of digits of number
Approach 1:
n.to_s.chars.map(&:to_i).sum
Approach 2:
while n>0; sum+=n%10; n/=10; end # arithmetic method
Input / Output: 123 → 6
Explanation: String method is simple; arithmetic avoids allocations.
32. GCD (Euclidean)
Approach 1:
def gcd(a,b)
b==0 ? a : gcd(b, a%b)
end
Approach 2:
Iterative version using while b!=0 swap modulo
Input / Output: gcd(12,8) → 4
Explanation: Recursive Euclid is concise and efficient.
33. LCM using GCD
Approach 1:
def lcm(a,b)
(a*b)/gcd(a,b)
end
Input / Output: lcm(4,6) → 12
Explanation: Use gcd to compute lcm safely; beware overflow for big numbers.
34. Binary to decimal and vice versa
Approach 1:
'101'.to_i(2) # => 5
Approach 2:
5.to_s(2) # => '101'
Input / Output: Conversions between bases using to_i and to_s with base argument
Explanation: Ruby provides direct base conversions via to_i(base) and to_s(base).
35. URL-safe base64 encode/decode
Approach 1:
require 'base64'
Base64.urlsafe_encode64('data')
Base64.urlsafe_decode64(s)
Input / Output: Encode/decode without '+'/'/' characters
Explanation: Useful for tokens in URLs; Ruby standard lib supports this.
36. Regex: extract capture groups
Approach 1:
m = 'item:123'.match(/item:(\d+)/)
id = m[1]
Approach 2:
'item:123'[/item:(\d+)/,1] # direct capture
Input / Output: Extract id '123' from string
Explanation: Use match object or String#[] with regex to fetch groups.
37. HTTP request (Net::HTTP and Faraday)
Approach 1:
require 'net/http'
res = Net::HTTP.get(URI('https://api.example.com'))
Approach 2:
require 'faraday'
Faraday.get('https://api.example.com').body
Input / Output: Fetch remote resource
Explanation: Faraday is higher-level and nicer for complex requests; Net::HTTP is stdlib.
38. JSON parse/serialize
Approach 1:
require 'json'
h = JSON.parse('{"a":1}')
JSON.generate(h)
Approach 2:
Use ActiveSupport::JSON in Rails for additional features
Input / Output: Parse JSON strings to Ruby hashes and back
Explanation: Use stdlib JSON or ActiveSupport extensions in Rails.
39. ActiveRecord: where with LIKE and parameterization
Approach 1:
User.where('email LIKE ?', '%@gmail.com')
Approach 2:
User.where('name ILIKE ?', '%john%') # Postgres case-insensitive
Input / Output: Find users matching pattern
Explanation: Always use parameterized queries to avoid SQL injection.
40. ActiveRecord: scopes and chaining
Approach 1:
class User < ApplicationRecord
scope :active, ->{ where(active:true) }
scope :recent, ->{ order(created_at: :desc) }
end
# usage: User.active.recent.limit(10)
Approach 2:
Use lambda scopes to compose queries
Input / Output: Compose reusable query fragments
Explanation: Scopes return Relations enabling lazy chaining and SQL composition.
41. ActiveRecord: associations basics (belongs_to, has_many)
Approach 1:
class Post < ApplicationRecord
belongs_to :user
end
class User < ApplicationRecord
has_many :posts
end
Approach 2:
# Access: user.posts, post.user
Input / Output: Model associations allow navigation and joins
Explanation: Ensure foreign keys and indices for performance.
42. ActiveRecord: eager loading to avoid N+1
Approach 1:
users = User.includes(:posts).where(active:true)
users.each{|u| u.posts.each{|p| p.title}}
Approach 2:
Use preload or eager_load depending on use-case
Input / Output: Prevent extra queries when iterating associations
Explanation: includes decides between JOIN or separate queries; analyze logs for behavior.
43. ActiveRecord: find vs find_by vs find_by!
Approach 1:
User.find(1) # raises if missing
User.find_by(id:1) # nil if missing
User.find_by!(id:1) # raises ActiveRecord::RecordNotFound
Input / Output: Choosing appropriate finder depending on error handling needs
Explanation: Use find_by! when you want exceptions handled by Rails rescue handlers.
44. Validations: presence, uniqueness, format
Approach 1:
class User < ApplicationRecord
validates :email, presence:true, uniqueness:true, format: { with: /@/ }
end
Input / Output: Model validation examples
Explanation: Uniqueness should be enforced at DB level as well (unique index).
45. Transactions and save! vs save
Approach 1:
ActiveRecord::Base.transaction do
user.save!
order.save!
end
Approach 2:
Use save to avoid exceptions and handle return values
Input / Output: Transactional operations roll back on exceptions
Explanation: Use bang methods inside transactions to ensure rollback on failure.
46. Callbacks: before_save and pitfalls
Approach 1:
before_save :normalize_name
def normalize_name; self.name = name.strip.titleize; end
Input / Output: Callbacks run during model lifecycle
Explanation: Avoid heavy logic in callbacks; prefer service objects for complex flows.
47. Serializing attributes (JSON/text)
Approach 1:
serialize :preferences, JSON # stores hash in text column
Approach 2:
Use json column type in Postgres and store as real JSON for querying
Input / Output: Store structured data in columns
Explanation: Prefer native JSON types for queryability and efficiency when DB supports it.
48. Enum usage for status fields
Approach 1:
class Order < ApplicationRecord
enum status: { pending: 0, paid: 1, shipped: 2 }
end
Approach 2:
Use integer-backed enums for efficient storage
Input / Output: Provides helper methods like order.paid? and Order.paid
Explanation: Be careful when changing enum order; use explicit mapping to integers.
49. Pluck for selecting columns
Approach 1:
User.pluck(:id, :email) # returns array of arrays or values
Approach 2:
User.select(:id,:email).map{|u| [u.id, u.email]} # loads AR objects
Input / Output: Efficient column extraction without instantiating full AR objects
Explanation: Use pluck for large datasets to minimize memory.
50. find_in_batches & find_each for large datasets
Approach 1:
User.find_in_batches(batch_size: 1000) do |batch| batch.each{|u| process(u)} end
Approach 2:
User.find_each(batch_size: 1000) {|u| process(u)} # yields single records
Input / Output: Process large tables without loading all rows
Explanation: find_each is convenient for per-record processing; find_in_batches gives batch arrays.
51. Upsert (insert or update) - rails 6+
Approach 1:
User.upsert({id:1, name:'A'}, unique_by: :id)
Approach 2:
Model.insert_all([...]) for bulk insert with conflict handling
Input / Output: Insert or update depending on existing keys
Explanation: Upsert uses DB features; behavior may vary by adapter (Postgres best supported).
52. Counter cache for association counts
Approach 1:
class Comment < ApplicationRecord
belongs_to :post, counter_cache: true
end
Approach 2:
# Add posts.comments_count integer column with default 0
Input / Output: Keeps cached count in parent record
Explanation: Avoids COUNT queries for frequently displayed counts; ensure counter updated on delete/create.
53. Polymorphic association example
Approach 1:
class Image < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
class Product < ApplicationRecord
has_many :images, as: :imageable
end
Input / Output: Single Image model belongs to different parents
Explanation: Useful for attachments; index on imageable_type and imageable_id recommended.
54. Nested attributes: accepts_nested_attributes_for
Approach 1:
class Post < ApplicationRecord
has_many :comments
accepts_nested_attributes_for :comments
end
Approach 2:
# In form: fields_for :comments to edit nested resources
Input / Output: Allows saving parent and children in one form
Explanation: Strong params must permit nested attributes (eg. comments_attributes: [...])
55. ActiveModel::Serializer / Jbuilder basics
Approach 1:
class UserSerializer < ActiveModel::Serializer
attributes :id, :name
end
Approach 2:
# Jbuilder: json.extract! @user, :id, :name in view
Input / Output: Serialize AR objects into JSON responses
Explanation: Choose serializer approach per project standards; AMS, Jbuilder, or Blueprinter are options.
56. Background jobs: ActiveJob with Sidekiq adapter
Approach 1:
class MyJob < ApplicationJob
def perform(user_id)
UserMailer.welcome(user_id).deliver_now
end
end
# enqueue: MyJob.perform_later(user.id)
Approach 2:
Use Sidekiq worker directly for performance and middleware features
Input / Output: Process async work decoupled from web requests
Explanation: Configure adapter (Sidekiq, Resque) in environment configs.
57. Send files: send_file vs send_data
Approach 1:
send_file user.avatar_path, disposition: 'attachment'
Approach 2:
send_data binary_data, filename: 'export.csv'
Input / Output: Serve files to clients from controller
Explanation: Use send_file for server files and send_data for generated content.
58. Pagination examples (kaminari/will_paginate)
Approach 1:
User.page(params[:page]).per(20) # kaminari
Approach 2:
User.paginate(page: params[:page], per_page: 20) # will_paginate
Input / Output: Return paginated AR relations
Explanation: Use gem of choice and render pagination controls in views.
59. Form helpers: form_with vs form_for
Approach 1:
<%= form_with(model: @post) do |f| %>
<%= f.text_field :title %>
<% end %>
Approach 2:
<%= form_for @post do |f| %> ... <% end %> # older style
Input / Output: Build HTML forms tied to models
Explanation: form_with is default in modern Rails (remote: true by default for AJAX).
60. Partial rendering and locals
Approach 1:
render partial: 'comment', locals: { c: comment }
Approach 2:
render 'comment', comment: comment # shorthand
Input / Output: Render reusable partial with passed variables
Explanation: Use locals to avoid relying on instance variables for clearer components.
61. Layouts and content_for
Approach 1:
# in controller: layout 'admin'
# in view: <% content_for :head do %> ... <% end %>
Approach 2:
yield :head in layout file to inject content
Input / Output: Structure application layout and inject per-view content
Explanation: content_for helps place scripts/styles in correct layout sections.
62. Flash messages usage
Approach 1:
flash[:notice] = 'Saved'
redirect_to root_path
Approach 2:
flash.now[:alert] = 'Error' # use with render
Input / Output: Transient messages displayed to users
Explanation: use flash.now when rendering same request; flash persists to next request.
63. Respond_to and format negotiation
Approach 1:
respond_to do |format|
format.html
format.json { render json: @post }
end
Input / Output: Serve different formats depending on request
Explanation: APIs often use respond_to for HTML/JSON/XML variants.
64. Cookies vs session (basic usage)
Approach 1:
session[:user_id] = user.id
cookies[:theme] = { value: 'dark', expires: 1.year.from_now }
Input / Output: Persistent and session-based storage
Explanation: Sessions default to cookie store; be mindful of size and security.
65. Rate limiting using rack-attack (basic config)
Approach 1:
# Example config in config/initializers/rack_attack.rb
Rack::Attack.throttle('req/ip', limit: 300, period: 5.minutes) do |req|
req.ip
end
Input / Output: Throttle requests per IP over time window
Explanation: Use middleware to protect against abuse at edge level.
66. CSV read/write
Approach 1:
require 'csv'
CSV.foreach('file.csv', headers:true) {|row| puts row['name'] }
Approach 2:
CSV.open('out.csv','w') {|csv| csv << ['a','b'] }
Input / Output: Read and write CSV files
Explanation: Use headers option for easier column access.
67. Image processing with MiniMagick
Approach 1:
MiniMagick::Image.open('in.jpg').resize '100x100'.write('out.jpg')
Approach 2:
Use ActiveStorage variants in Rails for on-the-fly resizing
Input / Output: Resize or transform images
Explanation: Prefer ActiveStorage variants when using Rails' built-in file management.
68. Tempfile use for safe temporary files
Approach 1:
t = Tempfile.new('prefix')
begin
t.write('data')
t.flush
ensure
t.close
t.unlink
end
Input / Output: Create temporary files safely and clean up
Explanation: Tempfile ensures unique temp filenames and auto-cleanup patterns.
69. Zip archive creation (rubyzip)
Approach 1:
require 'zip'
Zip::File.open('out.zip', Zip::File::CREATE) do |zip|
zip.add('file.txt','file.txt')
end
Input / Output: Create zip archives programmatically
Explanation: Use rubyzip gem for archive operations.
70. Detect MIME type
Approach 1:
require 'mime/types'
MIME::Types.type_for('file.png').first.content_type
Input / Output: Determine MIME types from file extensions
Explanation: Useful for uploads and setting response content-type headers.
71. Mailers: basics and preview
Approach 1:
class UserMailer < ApplicationMailer
def welcome(user)
@user = user
mail(to: @user.email, subject: 'Welcome')
end
end
Approach 2:
# Preview: UserMailerPreview in test/mailers/previews
Input / Output: Send transactional emails
Explanation: Configure delivery methods per environment (smtp, sendgrid, etc.).
72. Generate PDF via Prawn (basic)
Approach 1:
Prawn::Document.generate('file.pdf') do
text 'Hello PDF'
end
Input / Output: Create simple PDF files with Prawn
Explanation: Prawn is a Ruby PDF generator; for Rails integrate into controllers to send PDFs.
73. Excel generation with axlsx
Approach 1:
# create xlsx using axlsx gem
Axlsx::Package.new do |p|
p.workbook.add_worksheet(name: 'Sheet1') do |sheet|
sheet.add_row ['A','B']
end
p.serialize('file.xlsx')
end
Input / Output: Generate .xlsx spreadsheets
Explanation: Use gem and serve as attachment; mind memory for large sheets (streaming options exist).
74. Regex substitution and groups
Approach 1:
'abc123'.gsub(/(\d+)/, 'X') # replace digits
Approach 2:
'Name: John'.match(/Name: (\w+)/)[1] # capture group
Input / Output: Use regex to find and replace or capture parts
Explanation: Ruby's regex engine is powerful; avoid catastrophic backtracking patterns.
75. Delegate pattern
Approach 1:
class Order < ApplicationRecord
belongs_to :user
delegate :email, to: :user
end
Input / Output: Delegate methods to associated objects
Explanation: Reduces boilerplate forwarding methods; helpful in presenters.
76. Use of service object pattern (conceptual)
Approach 1:
# app/services/create_order.rb
class CreateOrder
def initialize(params); @params=params; end
def call
# validate, create order, notify
end
end
Input / Output: Encapsulate business logic outside models/controllers
Explanation: Helps keep controllers thin and models focused on persistence.
77. Testing: RSpec model validation example
Approach 1:
RSpec.describe User, type: :model do
it { should validate_presence_of(:email) }
end
Input / Output: Model test checking validations
Explanation: Use shoulda-matchers for concise validation specs.
78. Testing: controller request spec basic
Approach 1:
RSpec.describe PostsController, type: :request do
it 'returns 200' do
get posts_path
expect(response).to have_http_status(:ok)
end
end
Input / Output: Test controller endpoints return expected statuses
Explanation: Use request specs to test full Rails stack behavior.
79. Devise basics: authenticate_user! filter
Approach 1:
before_action :authenticate_user! # provided by Devise
Input / Output: Require authentication for controller actions
Explanation: Devise provides helpers and routes for auth flows.
80. ActiveStorage attach and variants
Approach 1:
@user.avatar.attach(io: File.open('a.jpg'), filename: 'a.jpg')
Approach 2:
@user.avatar.variant(resize: '100x100').processed
Input / Output: Attach files and generate variants
Explanation: ActiveStorage handles storage backends (local, S3) and direct uploads.
81. ActionCable simple broadcasting
Approach 1:
ActionCable.server.broadcast 'room_channel', message: 'hi'
Input / Output: Broadcast realtime messages to subscribers
Explanation: Use channels for websockets; scale with Redis for multiple servers.
82. Middleware insertion example
Approach 1:
# config/application.rb
config.middleware.use MyCustomMiddleware
Input / Output: Insert or configure Rack middleware
Explanation: Useful for cross-cutting concerns like logging/authentication/throttling.
83. Internationalization (I18n) basics
Approach 1:
I18n.t('hello') # uses config/locales/*.yml
Input / Output: Translate strings using YAML locale files
Explanation: Use interpolation and pluralization features for robust locales.
84. Caching fragment & action caching basics
Approach 1:
<% cache(@post) do %>
render @post
<% end %>
Approach 2:
caches_action :index # for action caching via gem
Input / Output: Cache view fragments to reduce rendering cost
Explanation: Invalidate caches on model changes via touch or expire methods.
85. Securing file uploads (validate content type)
Approach 1:
validates :avatar, content_type: ['image/png','image/jpg'] # via ActiveStorage validations gem
Input / Output: Ensure only allowed types are accepted
Explanation: Combine client-side checks and server-side validations for security.
86. Background job retry and error handling
Approach 1:
class MyJob < ApplicationJob
retry_on SomeError, attempts: 3 do |job, err|
# handle
end
end
Input / Output: Configure retries for transient failures
Explanation: ActiveJob provides retry helpers; Sidekiq offers advanced retry/exponential backoff.
87. Webhooks: verify signature
Approach 1:
sig = request.headers['X-Signature']
expected = OpenSSL::HMAC.hexdigest('sha256', secret, request.raw_post)
head :unauthorized unless ActiveSupport::SecurityUtils.secure_compare(sig, expected)
Input / Output: Verify webhook body against shared secret to avoid spoofing
Explanation: Use constant-time compare to avoid timing attacks.
88. Rate-limited API endpoints (controller-level)
Approach 1:
def index
if request.ip_rate_exceeded?
head :too_many_requests
else
render json: Model.all
end
end
Input / Output: Implement simple per-IP rate limits
Explanation: Prefer middleware (rack-attack) for production-grade throttling.
89. OAuth flow concept (OmniAuth)
Approach 1:
# configure OmniAuth and handle callback
def callback
user = User.from_omniauth(auth_hash)
end
Input / Output: Third-party authentication using OAuth providers
Explanation: OmniAuth provides middleware to handle provider callbacks and info.
90. Secure headers and Content Security Policy (CSP)
Approach 1:
# config/initializers/content_security_policy.rb
policy.default_src :self
policy.script_src :self, :https
Input / Output: Mitigate XSS and resource injection by specifying CSP
Explanation: Adjust policies carefully to allow required external resources.
91. Performance: eager loading vs lazy loading
Approach 1:
User.includes(:posts).where(active:true) # eager load posts
Approach 2:
User.where(active:true) # lazy until accessed
Input / Output: Reduce N+1 queries by eager loading associations
Explanation: Measure SQL queries and optimize accordingly.
92. SQL injection avoidance: parameterized queries
Approach 1:
User.where('email = ?', params[:email])
Approach 2:
User.find_by(email: params[:email])
Input / Output: Avoid string interpolation in SQL fragments
Explanation: ActiveRecord parameterization protects against injection.
93. Database indexing basics: migration example
Approach 1:
add_index :users, :email, unique: true
Input / Output: Add DB index for faster lookups and uniqueness
Explanation: Indexes speed reads but add write overhead; choose columns wisely.
94. Using transactions with retry semantics
Approach 1:
ApplicationRecord.transaction(requires_new: true) do
# critical section
end
Input / Output: Wrap critical DB operations atomically
Explanation: Combine with optimistic locking for certain concurrency scenarios.
95. Optimistic locking with lock_version
Approach 1:
# migration: add_column :table, :lock_version, :integer, default: 0
# AR will raise ActiveRecord::StaleObjectError on conflicts
Input / Output: Prevent lost updates when multiple writers modify same row
Explanation: Use rescue to handle stale object exceptions and retry or show message.
96. Soft delete pattern (acts_as_paranoid or custom)
Approach 1:
# add deleted_at column and scope
scope :active, ->{ where(deleted_at: nil) }
Input / Output: Mark records as deleted instead of removing
Explanation: Keep indexes and uniqueness in mind; adjust queries to exclude soft-deleted records.
97. Background migration / data migration strategies
Approach 1:
# Use runners or background jobs to migrate large datasets gradually
Input / Output: Migrate large data without blocking app or causing downtime
Explanation: Break into batches and monitor progress; use feature flags if changing behavior.
98. Command line script with Thor or Rake task
Approach 1:
# lib/tasks/import.rake
task :import => :environment do
Importer.run
end
Approach 2:
Use Thor to build CLI with commands and options.
Input / Output: Create reusable tasks or CLIs for maintenance
Explanation: Rake tasks run in app context when needed.
99. Security: parameter filtering in logs
Approach 1:
# config/initializers/filter_parameter_logging.rb
Rails.application.config.filter_parameters += [:password, :token]
Input / Output: Avoid leaking sensitive params into logs
Explanation: Filter parameters to protect secrets in logs and error reports.
100. Monitoring: integrate Sentry or similar
Approach 1:
Raven.configure do |config|
config.dsn = ENV['SENTRY_DSN']
end
Input / Output: Capture exceptions and performance data in Sentry
Explanation: Set environment-specific configs and ignore noisy exceptions.
101. Deployment basics: Capistrano or Docker approach
Approach 1:
# Capistrano deploy scripts or Dockerfile + docker-compose for containerized deploy
Input / Output: Common deployment patterns for Rails apps
Explanation: Choose based on team expertise; use CI/CD pipelines for repeatable releases.
102. Data validation: custom validator class
Approach 1:
class EmailFormatValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, 'invalid') unless value =~ /@/
end
end
# use: validates :email, email_format: true
Input / Output: Encapsulate complex validation logic in reusable validators
Explanation: Keeps models cleaner and validator can be reused across models.
103. Security: encrypt attributes using ActiveSupport::MessageEncryptor
Approach 1:
key = ActiveSupport::KeyGenerator.new('password').generate_key('salt', 32)
crypt = ActiveSupport::MessageEncryptor.new(key)
enc = crypt.encrypt_and_sign('sensitive')
crypt.decrypt_and_verify(enc)
Input / Output: Encrypt sensitive data before storing
Explanation: Rails 7 has ActiveRecord::Encryption for built-in support.
104. Refactoring: extract method and single responsibility principle
(conceptual)
Approach 1:
# Move repeated logic into private methods or service objects
Input / Output: Improve readability and testability by extracting methods
Explanation: Aim for small methods with clear responsibilities.