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.
Avatar for Vinícius Alonso Vinícius Alonso
February 01, 2026
11

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

Avatar for Vinícius Alonso

Vinícius Alonso

February 01, 2026
Tweet

Transcript

  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