Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Don't rewrite your framework - Rubyconfth

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest. →
Vinícius Alonso
February 01, 2026
110

Don't rewrite your framework - Rubyconfth

Have you ever seen someone rewriting the framework during a code review? After some time studying the Ruby on Rails documentation and doing some research on Reddit, I found some interesting features that are often not known by developers.

This talk was presented at Rubyconfth 2025

Vinícius Alonso

February 01, 2026

Transcript

98 slides
  1. Who am I? • Vinícius Bail Alonso • Sr. Software

    Engineer • Solides Tecnologia [email protected] @alonsoemacao viniciusalonso Get in touch viniciusbalonso
  2. =

  3. class Customer < ApplicationRecord validates :name, :email, :password, :address_street, :address_city,

    presence: true end class Customer < ApplicationRecord validates :name, :email, :password, presence: true, on: :create validates :address_street, :address_city, presence: true, on: :account_finish end Validations in two di ff erent contexts
  4. customer = Customer.new(name: 'John', email: '[email protected]', password: '123') customer.valid? #

    => true customer.save # => true customer.valid?(:account_finish) # => false customer.save(context: :account_finish) # => false
  5. class Address attr_reader :street, :city def initialize(street, city) @street =

    street @city = city end def ==(other) @street == other.street && @city == other.city end end
  6. customer = Customer.new customer.address_street = "123 Main St" customer.address_city =

    "Anytown" customer.address #<Address:0x0000ffffb045c6c0 @city="Anytown", @street="123 Main St">
  7. customer = Customer.new customer.address_street = "123 Main St" customer.address_city =

    "Anytown" customer.address #<Address:0x0000ffffb045c6c0 @city="Anytown", @street="123 Main St"> address = Address.new("123 Main St", "Anytown") customer = Customer.new customer.address = address
  8. customer = Customer.new customer.address_street = "123 Main St" customer.address_city =

    "Anytown" customer.address #<Address:0x0000ffffb045c6c0 @city="Anytown", @street="123 Main St"> address = Address.new("123 Main St", "Anytown") customer = Customer.new customer.address = address other_address = Address.new("456 Elm St", "Othertown") customer.address == other_address # false
  9. customer = Customer.new customer.address_street = "123 Main St" customer.address_city =

    "Anytown" customer.address #<Address:0x0000ffffb045c6c0 @city="Anytown", @street="123 Main St"> address = Address.new("123 Main St", "Anytown") customer = Customer.new customer.address = address other_address = Address.new("456 Elm St", "Othertown") customer.address == other_address # false Customer.where(address: Address.new("123 Main St", "Anytown"))
  10. class Cart < ApplicationRecord has_many :items def update_total_price total_price =

    items.map(&:total_price).sum update(total_price: total_price) end end
  11. class Cart < ApplicationRecord has_many :items, after_add: :increment_total_price, after_remove: :decrement_total_price

    def increment_total_price(item) self.total_price = item.total_price + total_price end def decrement_total_price(item) self.total_price = total_price - item.total_price end end
  12. class ReportProcessorJob < ApplicationJob queue_as :default def perform(*args) Customer.all.each do

    |customer| ReportProcessor.new(customer).process end end end 2 problems here: * Full table scan * All customers being loaded in memory at same time
  13. class ReportProcessorJob < ApplicationJob queue_as :default def perform(*args) Customer.find_in_batches do

    |batch| batch.each do |customer| ReportProcessor.new(customer).process end end end end
  14. class ReportProcessorJob < ApplicationJob queue_as :default def perform(*args) Customer.find_in_batches(batch_size: 100)

    do |batch| batch.each do |customer| ReportProcessor.new(customer).process end end end end
  15. Split 200 customers into two workers customers_ids = Customer.ids #

    Worker 1 Customer.find_in_batches(finish: customers_ids[99]) do |batch| batch.each do |customer| ReportProcessor.new(customer).process end end
  16. Split 200 customers into two workers customers_ids = Customer.ids #

    Worker 1 Customer.find_in_batches(finish: customers_ids[99]) do |batch| batch.each do |customer| ReportProcessor.new(customer).process end end # Worker 2 Customer.find_in_batches(start: customers_ids[100]) do |batch| batch.each do |customer| ReportProcessor.new(customer).process end end
  17. =

  18. Customer.transaction do customer = Customer.lock.find(3) sleep(10) customer.address_city = "São Paulo"

    customer.save! end Customer.transaction do customer = Customer.lock.find(3) customer.address_city = "Curitiba" customer.save! end Process 1 Process 2
  19. class User < ApplicationRecord before_save :sanitize_fields private def sanitize_fields self.email

    = email.strip.downcase if email.present? self.phone = phone.delete("^0-9") if phone.present? end end
  20. class User < ApplicationRecord normalizes :email, with: -> email {

    email.strip.downcase } normalizes :phone, with: -> phone { phone.delete("^0-9") } end
  21. user = User.create(email: " [email protected]\n") user.email # => "[email protected]" https://api.rubyonrails.org/classes/ActiveRecord/Normalization/ClassMethods.html

    User.exists?(email: "\[email protected] ") # => true User.exists?(["email = ?", "\[email protected] "]) # => false User.normalize_value_for(:phone, "+1 (555) 867-5309") # => "15558675309"
  22. .extending module FilterByRole def only_admin where(role_name: :admin) end def only_manager

    where(role_name: :manager) end end User.extending(FilterByRole).only_admin SELECT "users".* FROM "users" WHERE "users"."role_name" = $1 [["role_name", "admin"]]
  23. .extending module Pagination def page(number) # pagination code goes here

    end end scope = Model.all.extending(Pagination) scope.page(params[:page])
  24. def index ActiveSupport::Notifications.instrument('contacts', extra: :information) do @contacts = Contact.all end

    end ActiveSupport::Notifications.subscribe('contacts') do |event| event.name # => "contacts" event.duration # => 7.537125000031665ms event.payload # => { extra: :information } event.allocations # => 556 (objects) end
  25. ActiveSupport::Notifications.subscribe "process_action.action_controller" do |event| event.name # => "process_action.action_controller" event.duration #

    => 10 (in milliseconds) event.allocations # => 1826 event.payload # => {:extra=>information} Rails.logger.info "#{event} Received!" end
  26. class Customer < ApplicationRecord set_callback :notify_customer, :after, :send_email def activate!

    run_notify_customer_callbacks do update!(active: true) end end private def send_email CustomerMailer.activation_email(self).deliver_later end end
  27. Rails.application.routes.draw do namespace :api do root "home#index" end end class

    API::HomeController < ApplicationController def index render plain: 'API Home' end end
  28. 'posts'.singularize # => "post" 'post'.pluralize # => "posts" 'x-men: the

    last stand'.titleize # => "X Men: The Last Stand" 'employee_salary'.humanize # => "Employee salary"
  29. Rails.application.routes.draw do root "landing#index" namespace :admin do root "home#index" resources

    :contacts, only: [:index, :show, :destroy] end namespace :manager do root "home#index" resources :categories resources :products resources :tables resources :users end end
  30. namespace :admin do root "home#index" resources :contacts, only: [:index, :show,

    :destroy] end namespace :manager do root "home#index" resources :categories resources :products resources :tables resources :users end con fi g/routes/admin.rb con fi g/routes/manager.rb
  31. Rails.application.routes.draw do # ... direct :docs do "https://docs.myapi.com" end direct

    :landing do { controller: 'landing', action: 'index', subdomain: 'www' } end end
  32. The documentation is your best friend Everything in programming involves

    trade-o ff s. Make smart choices In this talk, there are a lot of uncovered features