SlideShare a Scribd company logo
1 of 62
Chris Oliver
Powerful TypeScript Rails
Features You Might Not know
Learn Your Tools
ActiveRecord Excluding
User.where.not(id: users.map(&:id))
# SELECT "users".* FROM "users"
WHERE "users"."id" NOT IN (1,2)
User.all.excluding(users)
# SELECT "users".* FROM "users"
WHERE "users"."id" NOT IN (1,2)
ActiveRecord Strict Loading
class Project < ApplicationRecord
has_many :comments, strict_loading: true
end
project = Project.first
project.comments
ActiveRecord::StrictLoadingViolationError
`Project` is marked as strict_loading. The Comment association named
`:comments` cannot be lazily loaded.
ActiveRecord Strict Loading
project = Project.includes(:comments).first
Project Load (0.3ms) SELECT "projects".* FROM "projects" ORDER BY
"projects"."id" ASC LIMIT ? [["LIMIT", 1]]
Comment Load (0.1ms) SELECT "comments".* FROM "comments" WHERE
"comments"."project_id" = ? [["project_id", 1]]
=> #<Project:0x00000001054af780 id: 1>
project.comments
=> [#<Comment:0x00000001054aed80 id: 1, project_id: 1, body: "Hello
RailsWorld">]
Generated Columns
class AddNameVirtualColumnToUsers < ActiveRecord::Migration[7.0]
def change
add_column :users, :full_name, :virtual,
type: :string,
as: "first_name || ' ' || last_name",
stored: true
end
end
attr_readonly
class User < ApplicationRecord
attr_readonly :super_admin
end
with_options
class Account < ActiveRecord::Base
with_options dependent: :destroy do
has_many :customers
has_many :products
has_many :invoices
has_many :expenses
end
end
with_options
I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
subject i18n.t :subject
body i18n.t :body, user_name: user.name
end
Try
method_name if respond_to?(:method_name)
try(:method_name)
try(:method_name) || default
(method_name if respond_to?(:method_name)) || default
ActionText Embeds
Searching Users
json.array! @users do |user|
json.sgid user.attachable_sgid
json.content render(
partial: "users/user",
locals: {user: user},
formats: [:html]
)
end
Signed GlobalIDs
User.first.attachable_sgid.to_s
"eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDluYVdRNkx5OXFkVz
F3YzNSaGNuUXRZWEJ3TDFWelpYSXZNUVk2QmtWVSIsImV4cCI
6IjIwMjMtMTAtMjhUMTY6MDY6MTEuMjEyWiIsInB1ciI6ImRlZmF1bH
QifX0=--217284b31bc4e28f6e2cf2890ccee87ca7d3ea2d"
ActionText Embeds
ActionText Embeds
import Trix from "trix"
let attachment = new Trix.Attachment({
content: "<div><img src=“…”> Chris Oliver</div>",
sgid: "BAh7CEkiCG…"
})
element.editor.insertAttachment(attachment)
ActionText Embeds
<p>Hey <action-text-attachment sgid=“BAh7CEkiCG…">
</action-text-attachment>!</p>
Signed GlobalIDs
Base64.decode64("eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaEpJaDluYVdRNkx5OXFkV
zF3YzNSaGNuUXRZWEJ3TDFWelpYSXZNUVk2QmtWVSIsImV4cCI6IjIwMjMtMTAtMjh
UMTY6MD
Y6MTEuMjEyWiIsInB1ciI6ImRlZmF1bHQifX0=“)
=>
“{"_rails":{"message":"BAhJIh9naWQ6Ly9qdW1wc3RhcnQtYXBwL1VzZXIvMQY6
BkVU","exp":"2023-10-28T16:06:11.212Z","pur":"attachable"}}"
Signed GlobalIDs
Base64.decode64(“BAhJIh9naWQ6Ly9qdW1wc3RhcnQtYXBwL1VzZXIvMQ
Y6BkVU")
=> “x04bI"x1Fgid://jumpstart-pro/User/1x06:x06ET"
ActionText Embeds
Serialize Coders
module ActionText
class RichText < Record
serialize :body, coder: ActionText::Content
end
end
Serialize Coders
class ActionText::Content
def self.load(content)
new(content) if content
end
def self.dump(content)
case content
when nil
nil
when self
content.to_html
end
end
end
ActionMailbox Inbound Emails
ActionMailbox Inbound Emails
# app/mailboxes/application_mailbox.rb
class ApplicationMailbox < ActionMailbox::Base
routing /@replies./i => :replies
end
class RepliesMailbox < ApplicationMailbox
MATCHER = /^reply-(d+)@replies./
def process
conversation.posts.create!(
author: author,
body: body,
message_id: mail.message_id
)
end
private
def conversation
Conversation.find(conversation_id)
end
def conversation_id
mail.recipients.find { |recipient| MATCHER.match?(recipient) }[MATCHER, 1]
end
end
ActionMailbox Inbound Emails
Routing constraints
constraints subdomain: :app do
root "dashboard#show"
end
root "homepage#show"
Routing constraints
authenticated :user, -> { _1.admin? } do
resource :admin
end
authenticated :user do
root "dashboard#show"
end
root "homepage#show"
Routing constraints
Draw Routes
Rails.application.routes.draw do
draw :api
# ...
end
# config/routes/api.rb
namespace :api, defaults: {format: :json} do
namespace :v1 do
resources :accounts
end
end
Custom Generators
$ bin/rails g api_client OpenAI
--base-url https://api.openai.com
Custom Generators
$ bin/rails generate generator ApiClient
creates an ApiClient generator:
lib/generators/api_client/
lib/generators/api_client/api_client_generator.rb
lib/generators/api_client/USAGE
lib/generators/api_client/templates/
test/lib/generators/api_client_generator_test.rb
Custom Turbo Stream Actions
Custom Turbo Stream Actions
<turbo-stream action="notification" title="Hello world">
</turbo-stream>
Custom Turbo Stream Actions
# create.turbo_stream.erb
<%= turbo_stream_action_tag “notification”,
title: “Hello world” %>
Custom Turbo Stream Actions
import "@hotwired/turbo-rails"
Turbo.StreamActions.notification = function() {
Notification.requestPermission(function(status) {
if (status == "granted") {
new Notification(this.getAttribute("title"))
}
})
}
Custom Turbo Stream Actions
truncate_words
content = 'And they found that many people were sleeping better.'
content.truncate_words(5, omission: '... (continued)')
# => "And they found that many... (continued)"
starts_at > Time.current
starts_at.after?(Time.current)
starts_at.future?
Time Helpers
starts_at < Time.current
starts_at.before?(Time.current)
starts_at.past?
Time.current.all_day
#=> Fri, 06 Oct 2023 00:00:00 UTC +00:00..
Fri, 06 Oct 2023 23:59:59 UTC +00:00
Time.current.all_week
#=> Mon, 02 Oct 2023 00:00:00 UTC +00:00..
Sun, 08 Oct 2023 23:59:59 UTC +00:00
Time.current.all_month
#=> Sun, 01 Oct 2023 00:00:00 UTC +00:00..
Tue, 31 Oct 2023 23:59:59 UTC +00:00
Time Helpers
123 -> 123
1234 -> 1,234
10512 -> 10.5K
2300123 -> 2.3M
Abbreviated numbers
Abbreviated numbers
number_to_human(123) # => "123"
number_to_human(12345) # => "12.3 Thousand"
number_to_human(1234567) # => "1.23 Million"
number_to_human(489939, precision: 2) # => "490 Thousand"
number_to_human(489939, precision: 4) # => "489.9 Thousand"
number_to_human(1234567, precision: 4,
significant: false)# => "1.2346 Million"
def number_to_social(number)
return number_with_delimiter(number) if number < 10_000
number_to_human(number,
precision: 1,
round_mode: :down,
significant: false,
format: "%n%u",
units: {thousand: "K", million: "M", billion: "B"}
)
end
Abbreviated numbers
Abbreviated numbers
number_to_social(123) #=> 123
number_to_social(1_234) #=> 1,234
number_to_social(10_512) #=> 10.5K
number_to_social(2_300_123) #=> 2.3M
Rails.env.local?
Rails.env.development? || Rails.env.test?
Rails.env.local?
ActiveSupport Inquiry
"production".inquiry.production? # => true
"active".inquiry.inactive? # => false
Unused routes
$ rails routes --unused
Found 4 unused routes:
Prefix Verb URI Pattern Controller#Action
edit_comment GET /comments/:id/edit(.:format) comments#edit
PATCH /comments/:id(.:format) comments#update
PUT /comments/:id(.:format) comments#update
DELETE /comments/:id(.:format) comments#destroy
Template strict locals
<%# locals: (message:) -%>
<%= tag.div id: dom_id(message) do %>
<%= message %>
<% end %>
<%= render partial: “message” %>
ArgumentError: missing local: :message
<%= render partial: “message”,
locals: {message: @message} %>
Template strict locals
<%# locals: (message: “Hello”) -%>
<div>
<%= message %>
</div>
<%= render partial: “message” %>
<%= render partial: “message”,
locals: {message: “Hey”} %>
Template strict locals
<%# locals: () -%>
<div>
Hello RailsWorld!
</div>
<%= render partial: “message” %>
normalizes
class User < ApplicationRecord
end
def email=(value)
super value&.strip&.downcase
end
normalizes
class User < ApplicationRecord
end
normalizes :email, with: ->(email) { email.strip.downcase }
normalizes :email, with: ->{ _1.strip.downcase }
has_secure_password
authenticate_by & password_challenge
authenticate_by
class User < ApplicationRecord
has_secure_password
end
User.find_by(email: "chris@gorails.com")
&.authenticate(“railsworld2023")
#=> false or user
User.authenticate_by(
email: "chris@gorails.com",
password: "railsworld2023"
) #=> user or nil
password_challenge
Current.user.update(password_params)
def password_params
params.require(:user).permit(
:password,
:password_confirmation,
:password_challenge
).with_defaults(password_challenge: "")
end
generates_token_for
generates_token_for
class User < ApplicationRecord
generates_token_for :password_reset, expires_in: 15.minutes do
# BCrypt salt changes when password is updated
BCrypt::Password.new(password_digest).salt[-10..]
end
end
generates_token_for
User.find_by_token_for(:password_reset, params[:token])
#=> User or nil
user.generate_token_for(:password_reset)
“eyJfcmFpbHMiOnsiZGF0YSI6WzkwMjU0MTYzNSwiaC9oTkRJck9uL…”
ActiveStorage Variants
Named variants
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumbnail,
resize_to_limit: [200, 200]
end
end
Preprocessed variants
class User < ApplicationRecord
has_one_attached :avatar do |attachable|
attachable.variant :thumbnail,
resize_to_limit: [200, 200],
preprocessed: true
end
end
if @project.file.previewable?
@project.file.preview(:thumbnail)
elsif @project.file.variable?
@project.file.variant(:thumbnail)
end
# Render a preview or variant
@project.file.represenation(:thumbnail)
Representations
Fact
L Learn

More Related Content

Similar to Rails World 2023: Powerful Rails Features You Might Not Know

GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant TrainingAidIQ
 
Rails best practices_slides
Rails best practices_slidesRails best practices_slides
Rails best practices_slidesCao Van An
 
Debugging on Rails. Mykhaylo Sorochan
Debugging on Rails. Mykhaylo SorochanDebugging on Rails. Mykhaylo Sorochan
Debugging on Rails. Mykhaylo SorochanSphere Consulting Inc
 
Advanced Internationalization with Rails
Advanced Internationalization with RailsAdvanced Internationalization with Rails
Advanced Internationalization with RailsClinton Dreisbach
 
Phoenix for Rails Devs
Phoenix for Rails DevsPhoenix for Rails Devs
Phoenix for Rails DevsDiacode
 
Application Security from the Inside - OWASP
Application Security from the Inside - OWASPApplication Security from the Inside - OWASP
Application Security from the Inside - OWASPSqreen
 
Building Web-API without Rails, Registration or SMS
Building Web-API without Rails, Registration or SMSBuilding Web-API without Rails, Registration or SMS
Building Web-API without Rails, Registration or SMSPivorak MeetUp
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperfNew Relic
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelpauldix
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelpauldix
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overviewYehuda Katz
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Coupa Software
 
Emberjs as a rails_developer
Emberjs as a rails_developer Emberjs as a rails_developer
Emberjs as a rails_developer Sameera Gayan
 
Migration from Rails2 to Rails3
Migration from Rails2 to Rails3Migration from Rails2 to Rails3
Migration from Rails2 to Rails3Umair Amjad
 
Rails web api 开发
Rails web api 开发Rails web api 开发
Rails web api 开发shaokun
 
Timothy N. Tsvetkov, Rails 3.1
Timothy N. Tsvetkov, Rails 3.1Timothy N. Tsvetkov, Rails 3.1
Timothy N. Tsvetkov, Rails 3.1Evil Martians
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
How to disassemble one monster app into an ecosystem of 30
How to disassemble one monster app into an ecosystem of 30How to disassemble one monster app into an ecosystem of 30
How to disassemble one monster app into an ecosystem of 30fiyuer
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications Juliana Lucena
 

Similar to Rails World 2023: Powerful Rails Features You Might Not Know (20)

GHC Participant Training
GHC Participant TrainingGHC Participant Training
GHC Participant Training
 
Rails best practices_slides
Rails best practices_slidesRails best practices_slides
Rails best practices_slides
 
Debugging on rails
Debugging on railsDebugging on rails
Debugging on rails
 
Debugging on Rails. Mykhaylo Sorochan
Debugging on Rails. Mykhaylo SorochanDebugging on Rails. Mykhaylo Sorochan
Debugging on Rails. Mykhaylo Sorochan
 
Advanced Internationalization with Rails
Advanced Internationalization with RailsAdvanced Internationalization with Rails
Advanced Internationalization with Rails
 
Phoenix for Rails Devs
Phoenix for Rails DevsPhoenix for Rails Devs
Phoenix for Rails Devs
 
Application Security from the Inside - OWASP
Application Security from the Inside - OWASPApplication Security from the Inside - OWASP
Application Security from the Inside - OWASP
 
Building Web-API without Rails, Registration or SMS
Building Web-API without Rails, Registration or SMSBuilding Web-API without Rails, Registration or SMS
Building Web-API without Rails, Registration or SMS
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModel
 
Building Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModelBuilding Web Service Clients with ActiveModel
Building Web Service Clients with ActiveModel
 
Rails 3 overview
Rails 3 overviewRails 3 overview
Rails 3 overview
 
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
Staying railsy - while scaling complexity or Ruby on Rails in Enterprise Soft...
 
Emberjs as a rails_developer
Emberjs as a rails_developer Emberjs as a rails_developer
Emberjs as a rails_developer
 
Migration from Rails2 to Rails3
Migration from Rails2 to Rails3Migration from Rails2 to Rails3
Migration from Rails2 to Rails3
 
Rails web api 开发
Rails web api 开发Rails web api 开发
Rails web api 开发
 
Timothy N. Tsvetkov, Rails 3.1
Timothy N. Tsvetkov, Rails 3.1Timothy N. Tsvetkov, Rails 3.1
Timothy N. Tsvetkov, Rails 3.1
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
How to disassemble one monster app into an ecosystem of 30
How to disassemble one monster app into an ecosystem of 30How to disassemble one monster app into an ecosystem of 30
How to disassemble one monster app into an ecosystem of 30
 
Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications  Ember.js - A JavaScript framework for creating ambitious web applications
Ember.js - A JavaScript framework for creating ambitious web applications
 

Recently uploaded

Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEEVICTOR MAESTRE RAMIREZ
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...OnePlan Solutions
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaHanief Utama
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptkotipi9215
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...stazi3110
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsAhmed Mohamed
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Hr365.us smith
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesŁukasz Chruściel
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....kzayra69
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Velvetech LLC
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEOrtus Solutions, Corp
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Andreas Granig
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - InfographicHr365.us smith
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...OnePlan Solutions
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software DevelopersVinodh Ram
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfAlina Yurenko
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataBradBedford3
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmSujith Sukumaran
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...Christina Lin
 

Recently uploaded (20)

Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...Advancing Engineering with AI through the Next Generation of Strategic Projec...
Advancing Engineering with AI through the Next Generation of Strategic Projec...
 
React Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief UtamaReact Server Component in Next.js by Hanief Utama
React Server Component in Next.js by Hanief Utama
 
chapter--4-software-project-planning.ppt
chapter--4-software-project-planning.pptchapter--4-software-project-planning.ppt
chapter--4-software-project-planning.ppt
 
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
Building a General PDE Solving Framework with Symbolic-Numeric Scientific Mac...
 
Unveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML DiagramsUnveiling Design Patterns: A Visual Guide with UML Diagrams
Unveiling Design Patterns: A Visual Guide with UML Diagrams
 
Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)Recruitment Management Software Benefits (Infographic)
Recruitment Management Software Benefits (Infographic)
 
Unveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New FeaturesUnveiling the Future: Sylius 2.0 New Features
Unveiling the Future: Sylius 2.0 New Features
 
What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....What are the key points to focus on before starting to learn ETL Development....
What are the key points to focus on before starting to learn ETL Development....
 
Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...Software Project Health Check: Best Practices and Techniques for Your Product...
Software Project Health Check: Best Practices and Techniques for Your Product...
 
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASEBATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
BATTLEFIELD ORM: TIPS, TACTICS AND STRATEGIES FOR CONQUERING YOUR DATABASE
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024Automate your Kamailio Test Calls - Kamailio World 2024
Automate your Kamailio Test Calls - Kamailio World 2024
 
Asset Management Software - Infographic
Asset Management Software - InfographicAsset Management Software - Infographic
Asset Management Software - Infographic
 
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
Maximizing Efficiency and Profitability with OnePlan’s Professional Service A...
 
Professional Resume Template for Software Developers
Professional Resume Template for Software DevelopersProfessional Resume Template for Software Developers
Professional Resume Template for Software Developers
 
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdfGOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
GOING AOT WITH GRAALVM – DEVOXX GREECE.pdf
 
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer DataAdobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
Adobe Marketo Engage Deep Dives: Using Webhooks to Transfer Data
 
Intelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalmIntelligent Home Wi-Fi Solutions | ThinkPalm
Intelligent Home Wi-Fi Solutions | ThinkPalm
 
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
ODSC - Batch to Stream workshop - integration of Apache Spark, Cassandra, Pos...
 

Rails World 2023: Powerful Rails Features You Might Not Know

Editor's Notes

  1. When all you’ve got is a hammer, everything looks like a nail. You have a task and end up in the weeds to figure out a solution. Next time you come across a similar problem, you jump straight into the weeds remembering your solution from last time even though there may be better tools available to you this time around. So the goal of this talk is a reminder to learn and master the tools you have available.
  2. Let’s start off with a simple example. Say you’re sending notifications for a conversation thread. Something like a GitHub issue. Rails 7 added the excluding method to ActiveRecord. By using excluding, we can think about the problem at a higher level instead of thinking on a SQL level.
  3. Another example of this is strict loading. This was introduced in Rails 6.1 and helps prevent N+1 issues without any external dependencies. ActiveRecord queries will now raise a Strict Loading error when you query an association that’s marked as strict_loading.
  4. To do this properly, we can grab the project but eager load comments using includes(:comments). This will load the project and the comments into memory And when we ask for the project comments, we get the list of comments with no error this time.
  5. Generated columns allow you to create a column computed from other columns in your database. For example, we can make a “full name” column that is a combination of first and last name. Or calculate price_in_cents from a decimal price (or vice versa). By using the “stored”, the column is computed when it’s written and is saved in storage like a normal column. When stored: false, it’s a virtual column that’s computed when read. Generated columns were added to the PostgreSQL adapter in Rails 7 and has been supported in MySQL for a while and SQLite support is coming.
  6. attr_readonly is another handy feature in ActiveRecord. We can still create a new user with super_admin set to true or false, but after that ActiveRecord cannot change the super_admin attribute. Even update_column which skips validations and callbacks still verifies the readonly attributes.
  7. with_options is a feature of ActiveSupport that lets you define options you want to include in every method call in the block. Here we have 4 associations and all of them need dependent: :destroy, so we can use with_options to add that for every association.
  8. You can also use with_options like this where we call it on the I18n class. The i18n translate calls here get the locale and scope applied automatically.
  9. The safe navigation operator in Ruby is amazing, but it doesn’t help in every situation. Let’s say you want to call a method if an object responds to it. This gets more complicated when we want to have a fallback. Try lets us do this cleanly and it reads extremely well.
  10. Another undervalued feature of Rails is ActionText. Sure, it format text, but the real magic is in embedding records from your database. Here, you can see ActionText with a User record mentioned in it. When my name or avatar changes, the text renders with the new content. You could also use this to embed YouTube videos, Twitter and Instagram posts.
  11. The first step for embeds is asking the server for the embed data. The server will need to return a Signed GlobalID (SGID) and HTML preview for each user suggested.
  12. A signed global ID looks like this. It’s a base64 encoded string with a signature at the end after the double hyphens. We’ll explore this more in a second.
  13. When the user selects one of these entries, we can take the SGID and HTML preview on the client side and insert it into the Trix editor. If I select myself here…
  14. The JavaScript creates a new Trix Attachment using the HTML content and SGID and then inserts it into the editor.
  15. Rails saves this to the database and it looks like this. We have an action-text-attachment element, but you’ll notice that it’s an empty tag. The HTML content is missing. That’s because every time Rails renders this, it will look up the attachable (in this case, a User) and re-render the HTML content. This is what gets you the latest avatar and name every time it’s rendered.
  16. When rendering, Rails looks at that Signed Global ID and finds the associated record. Since these are Base64 encoded, we can decode them and you’ll see it’s a simple JSON message. It contains an expiration and purpose. The message itself is also Base64 encoded.
  17. If we decode the message, we’ll see that it is a regular Global ID that references a User model with the ID of 1 in our Jumpstart Pro app. These signed Global IDs can also be extremely handy for building a polymorphic select box that allows you to choose a record from any model.
  18. So here’s what our final result looks like. When we render our ActionText body, it finds each attachment and renders the partial for each associated record. When my name or avatar changes, the next render will include the latest data.
  19. A cool trick that ActionText uses internally is serializing with a custom coder. Normally, we use Serialize to convert a Ruby hash to and from JSON or YAML. But you can use this to load and dump any object like ActionText does here.
  20. To build a custom coder, all you need are the load and dump class methods. Here’s a simplified version of what ActionText does. It simply creates a new Content object with the HTML and dumps the HTML back to the database while handling nil appropriately.
  21. ActionMailbox is another favorite feature of mine that doesn’t get enough love. Have you ever noticed the “via email” on GitHub issues and PRs? You can implement something like this in Rails using ActionMailbox.
  22. To do this, you’ll need to wire up ActionMailbox to your email provider. They will convert incoming emails to webhooks that are sent to your Rails application. Inside ApplicationMailbox, you’ll define routings that match against the sender’s email address. Here, we’re taking anything @replies.domain.com and sending it to the replies inbox.
  23. The replies inbox implements a process method that takes the email and creates a new post for the conversation using the sender’s email address and body of the email. We can look up the right conversation by embedding the ID in the email address and parsing it out with a little regex.
  24. Routing constraints let you define routes that are available depending on details in the request. For example, this constraint says to send the root route to the dashboard controller if the user is on the app subdomain. Otherwise, we’ll use the marketing site homepage for requests with say www or no subdomain.
  25. Routing constraints have access to the request and the session so you can do some cool things with it. Devise provides some custom routing helpers too. Authenticated is a helper method that checks if the user is authenticated and then draws routes accordingly. If you’re logged in and have the admin flag, you can access /admin If you’re logged in, your root path will be the dashboard. And if you’re not logged in, we’ll show you the homepage.
  26. Fun fact, 11 years ago I contributed part of this feature to Devise and was one of my first open source contributions.
  27. Another handy routing feature is “draw”. Draw lets you organize routes into separate files. If we say “draw :api”, the config/routes/api.rb file will be evaluated. This is super handy to organize API or Admin routes separately. Or if you’re building a marketplace that has a separate storefront and backend areas.
  28. Rails also lets you build your own generators. In Jumpstart Pro applications, we provide an API client generator. Instead of adding another dependency, you can generate an API client by running rails g api_client and give it a name.
  29. Of course, Rails provides a generator for generating generators so you can run “rails generate generator name” and it will generate a generator. Templates get evaluated with Erb, so you can use Ruby to generate a Ruby file dynamically just like how the Scaffold generator creates controllers, models, and views.
  30. On the fronted, we can customize Turbo by adding our own Turbo Stream Actions. Let’s say you want to send a browser notification from Rails.
  31. Turbo Streams are a simple HTML element that have an action attribute that maps to a JavaScript function on the client side. The server just needs to tell it which action to perform and provide any other attributes or HTML to render.
  32. We can use the turbo_stream_action_tag helper to generate this HTML element. This can be in a response from an HTTP request or broadcasted through Redis from a background job.
  33. Once the element is inserted into the body of the page, the JavaScript finds the matching Turbo StreamAction and executes it. To send our notification, we can ask the browser if we have permission to send a notification and, if granted, we can create a new notification with the title from the attribute.
  34. And then you’ll see a fancy notification in your browser.
  35. There are countless methods in ActiveSupport, but truncating text is another common thing we need. I learned about “truncate” years ago and have used it ever since, but truncate works by character count. The problem is this will often stop in the middle of a word and isn’t as nice as a user. Only recently did I learn that truncate words exists. We can specify how many words we want to display and even customize the “omission” parameter to say “dot dot dot continued”.
  36. It always takes me a second to process time comparisons when I read them. starts_at is less than Time.current, so that means we’re asking if starts at before the current time. Rails 6 added before? And after? Helpers that alias less and greater than for making time comparisons easier to read at a glance. You can also use past? And future? To make this even quicker to read at a glance.
  37. ActiveSupport also provides time helpers for generating ranges. all_day will take the given date and return a range from the beginning of the day to the end of the day. all_week does the same but for the week the date is in. It also can be configured for weeks to start on Sunday or Monday. This example shows weeks starting on Mondays. all_month does the same but for the month
  38. Matt Swanson posted this on Twitter a little while back and I thought his solution was another great example of taking advantage of the tools Rails gives you. In the case of small numbers, we want to display them with commas. For numbers larger than ten thousand, we want to abbreviate them to the nearest scale like thousands, millions, billions. In the past, I would jump right into implementing this from scratch, but as we’ve learned…
  39. Rails ships with the number_to_human helper. It takes a number and outputs the number with the human scale at the end. The actual implementation is in ActiveSupport, but exposed as an ActionView Helper to use in your Rails views. This is almost what we want, but we need to figure out how to abbreviate this. Precision lets us control how rounding works and significant: false tells it to use the precision number for fractional digits instead of significant digits.
  40. So here’s how we can implement abbreviated numbers using the tools Rails gives us Return the number with delimiter if it’s less than ten thousand For larger numbers, we want number to human version with some configuration If we set precision to 1 and significant: false, we get 1 number after the decimal place. We want to round down so we don’t accidentally inflate our numbers Setting the format allows us to remove the default space between the number and the unit And last but not least, we can just replace the long unit names with abbreviations like K, M, and B. All that without any custom logic, rounding or anything!
  41. So here’s how we can implement abbreviated numbers using the tools Rails gives us Return the number with delimiter if it’s less than ten thousand For larger numbers, we want number to human version with some configuration If we set precision to 1 and significant: false, we get 1 number after the decimal place. We want to round down so we don’t accidentally inflate our numbers Setting the format allows us to remove the default space between the number and the unit And last but not least, we can just replace the long unit names with abbreviations like K, M, and B. All that without any custom logic, rounding or anything!
  42. Rails 7.1 also adds strict locals. If you add this magic comment to the top of your templates, we can require the “message” local to be passed in a s a local.
  43. Default values are also supported, so here we can set “Hello” as our default message.
  44. You can also define a partial that takes no locals by using empty parentheses.
  45. Normalizes is another feature I’m excited about. When users input something like an email address, we need to clean it up by removing whitespace and downcasing it. The best way to do this until now has been overriding the setter method so that it’s normalized as soon as the value is assigned. We also have to safely handle nil values and it has always felt a bit messy.
  46. In Rails 7.1, we can replace that with a call to normalizes. By default, nil values will be skipped so we can simply call strip and downcase without any safe navigation. This reads a little redundant so we can also use Ruby’s numbered parameters to simplify this using _1.
  47. Authenticate_by allows you to authenticate a user while preventing timing attacks. It will query the database for the User and load it into memory if it exists. If it doesn’t, your HTTP request may return measurably faster allowing attackers to figure out if you have an account with this email or not. Authenticate_by mitigates this by instantiating a User in memory if one is not found so the timing in either case is virtually the same.
  48. Password_challenge has been added as well. This is useful for verifying the current password before changing it to a new password. with_defaults is another ActiveSupport goodie that is an alias for reverse_merge that lets you set default values in a hash. This makes sure that password_challenge is always set to a value, even if a crafty user tries submitting the form without a password_challenge.
  49. generates_token_for lets you generate tokens that don’t need to be stored in your database. These are signed using ActiveSupport MessageVerifier and can include an expiration and embedded data. If you embed data using a block, this will be matched when the token is used and allows you to make one-time use tokens. If we successfully update our password, the salt changes and attempting to use the token again will fail because the salts no longer match.
  50. To generate a token, we get the “generate_token_for” method which takes the token name. To use the token, we can call find_by_token_for and give it the name and token value.
  51. I’ve you’ve used ActiveStorage, you’ve probably added variant code all over your views and/or helpers. Rails 7.0 introduced named variants which allow you to replace these resize and other calls with a name (just like you would do with scopes!)
  52. Rails 7.1 introduces preprocessed variants. If we slap proprocessed: true on our named variant, a TransformJob will run automatically on after_create_commit