Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Real-world functional Ruby
Search
Tim Riley
October 06, 2017
Technology
1.2k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Real-world functional Ruby
Tim Riley
October 06, 2017
More Decks by Tim Riley
See All by Tim Riley
A tour of dry-schema and dry-validation 1.0
timriley
1
430
Views, from the top
timriley
0
1.9k
Real-world Functional Ruby
timriley
0
580
Reinvesting in Ruby
timriley
1
150
Next-generation Ruby web apps with dry-rb, rom-rb, and Roda
timriley
5
5.4k
Functional Ruby, Rodakase, and another world of Ruby web applications
timriley
7
1.2k
Introduction to RubyMotion
timriley
6
3.6k
Other Decks in Technology
See All in Technology
今さら理解するウェブフォントver.2026
takanorip
2
190
AI Made Us Faster at Solving the Wrong Problems
marceloancelmo
0
190
Codex概要
ymiya55
0
460
オブザーバビリティを高める AI エージェント体験を考える / Designing AI Agent Experiences That Enhance Observability
aoto
PRO
2
320
契約書画像からの情報抽出に特化した視覚接地モデルを育てる話
sansantech
PRO
1
150
データ品質を壊しながらSnowflakeのAIに分析させてみた
kawanago
0
540
DWH の限界突破! AI エージェント向け爆速リアルタイムデータ基盤 ClickHouse
jozono
1
280
「事実」を「計測」して、「解釈」する 〜AWS Lambdaのメモリ最適解の見つけ方〜
maimyyym
0
110
生成AIと進める探索的データ分析(2026.10.03 第122回Tokyo.R勉強会)
tatamiya
2
1.4k
生成AIを使って「人が」考える技術 ― AI時代の人機共想と実践ノウハウ|UNITT AC2026
ishiirikie
0
560
OpenSharing について熱く語る〜AI アセットの共有について〜
kameitomohiro
0
240
あなたの知らないAmazon VPC Route Server/Amazon VPC Route Server you don't know about
masakiokuda
2
230
Featured
See All Featured
RailsConf & Balkan Ruby 2019: The Past, Present, and Future of Rails at GitHub
eileencodes
141
35k
How to audit for AI Accessibility on your Front & Back End
davetheseo
0
580
The B2B funnel & how to create a winning content strategy
katarinadahlin
PRO
1
550
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
470
Bootstrapping a Software Product
garrettdimon
PRO
306
120k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
16k
Un-Boring Meetings
codingconduct
0
440
Jamie Indigo - Trashchat’s Guide to Black Boxes: Technical SEO Tactics for LLMs
techseoconnect
PRO
0
690
A Modern Web Designer's Workflow
chriscoyier
699
190k
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
400
Rails Girls Zürich Keynote
gr2m
96
14k
Conquering PDFs: document understanding beyond plain text
inesmontani
PRO
4
3.2k
Transcript
Real-world Functional Ruby Southeast Ruby 2017
@timriley
None
@icelab
!
None
@dry_rb @rom_rb
None
None
PODCASTERS
“TBA”
None
None
“Programmers at work maintaining a Ruby on Rails application” Eero
Järnefelt Oil on canvas, 1893
None
None
user.rb after_save accepts_nested_attributes_for
⏱ DESIGN STAMINA HYPOTHESIS
⏱ DESIGN STAMINA HYPOTHESIS
⏱ DESIGN STAMINA HYPOTHESIS
⏱ DESIGN STAMINA HYPOTHESIS No design
⏱ DESIGN STAMINA HYPOTHESIS No design
⏱ DESIGN STAMINA HYPOTHESIS No design
⏱ DESIGN STAMINA HYPOTHESIS No design Good design
⏱ DESIGN STAMINA HYPOTHESIS No design Good design
⏱ DESIGN STAMINA HYPOTHESIS No design Good design Design payoff
line
Object-oriented design
The gap.
None
None
Haskell
Haskell
Haskell
Real-world Functional Ruby Southeast Ruby 2017 Tim Riley // @timriley
FP is hot AF
None
App as series of transformations
Request → [*] → [*] → Response
App as series of transformations
App as series of functions
Functional objects
class ImportProducts def call(feed) import_from(feed) end end
class ImportProducts def call(feed) import_from(feed) end end
class ImportProducts def call(feed) import_from(feed) end end
class ImportProducts def call(feed) import_from(feed) end end
Separate data from behaviour
Immutable
class ImportProducts def call(feed) end end records = download_feed.(feed) records.each
do |attrs| products_repo.create(attrs) end
class ImportProducts def call(feed) records = download_feed.(feed) records.each do |attrs|
products_repo.create(attrs) end end end
class ImportProducts def call(feed) records = download_feed.(feed) records.each do |attrs|
product_repo.create(attrs) end end end
download_feed: product_repo:
class ImportProducts attr_reader :download_feed attr_reader :products_repo def initialize(download_feed:, product_repo:) @download_feed
= download_feed @products_repo = products_repo end def call(feed) # ... end end
class ImportProducts attr_reader :download_feed attr_reader :product_repo def initialize(download_feed:, product_repo:) @download_feed
= download_feed @product_repo = product_repo end def call(feed) # ... end end
# Initialize once import = ImportProducts.new( download_feed: download, product_repo: repo,
) # Reuse many times import.(:books_feed) import.(:dvds_feed)
# Initialize once import = ImportProducts.new( download_feed: download, product_repo: repo,
) # Call many times import.(book_feed) import.(film_feed)
Feeds::Create Feeds::Update Feeds::Delete Products::Update
Behaviour
Data
Types
Values
class Product attr_reader :title, :isbn def initialize(**attrs) @title = attrs[:title]
@isbn = attrs[:isbn] end def slug "#{to_slug(title)}-#{isbn}" end end
class Product attr_reader :title, :isbn def initialize(**attrs) @title = attrs[:title]
@isbn = attrs[:isbn] end def slug "#{to_slug(title)}-#{isbn}" end end
Values objects are first-class
Values & Functions
Values Functions Hold data Operate on data
Values Functions Hold data Operate on data
Values Functions Hold data Operate on data Inert Active
Values Functions Hold data Operate on data Inert Active Content
Pipeline
Functional design
Functional, object-oriented design
Functional Ruby yields better object-oriented Ruby!
RSpec.describe ImportProducts
download_feed product_repo
let(:download) { double(:download) } let(:repo) { double(:repo) }
subject(:import) { ImportProducts.new( download_feed: download, product_repo: repo, ) }
let(:feed) { Feed.new(…) } before do allow(download_feed) .to receive(:call) .with(feed)
.and_return(books_data) end
let(:feed) { Feed.new(…) } before do allow(download) .to receive(:call) .with(feed)
.and_return(BOOKS_DATA) end
it "creates the products" do expect(repo) .to receive(:create).with(title: "…") import.(feed)
end
Import Products download_feed product_repo
Dependencies
App as graph
None
None
Good design makes change easier
Functional design makes change easier
Real developers hate him! He’s idealistic He’s making too many
.rb files He found a #method to build better apps. Learn the one WEIRD trick to his stunning results! GET FUNCTIONAL NOW
None
A real-world functional Ruby ecosystem
dry-rb
None
None
Dependency management
dry-system & dry-auto_inject
# lib/my_app/import_products.rb MyApp::Container[“import_products"] #<MyApp::ImportProducts>
# lib/my_app/import_products.rb MyApp::Container["import_products"] #<MyApp::ImportProducts>
class ImportProducts include MyApp::Import[ "products.download_feed", "product_repo", ] def call(feed) #
... end end
# lib/my_app/import_products.rb MyApp::Container["import_products"] #<MyApp::ImportProducts>
# lib/my_app/import_products.rb MyApp::Container["import_products"] #<MyApp::ImportProducts download_feed=#<DownloadFeed> product_repo=#<ProductRepo>>
ImportProducts.new( download_feed: double(:download) ) #<ImportProducts download_feed=#<Double :download> product_repo=#<ProductRepo>>
ImportProducts.new( download_feed: double(:download) ) #<ImportProducts download_feed=#<Double :download> product_repo=#<ProductRepo>>
Result handling
dry-monads & dry-matcher
# age, or nil Maybe(age) # successful import results Right(imported_products)
# age, or nil Maybe(age) Maybe(33) => Some(33) # successful
import results Right(imported_products)
# age, or nil Maybe(age) Maybe(33) => Some(33) Maybe(nil) =>
None
# Successful import result Right(imported_products)
import_products.() do |m| m.success do |imported_products| # … end m.failure
do |error| # … end end
Data & types
dry-validation
Dry::Validation.Schema do required(:name).filled required(:url).filled(format?: /…/) required(:active).filled(:bool?) end
dry-types & dry-struct
ISBN = Strict::String .constrained(format: /…/)
class Product < Dry::Struct attribute :title, Types::Strict::String attribute :isbn, Types::ISBN
end
View rendering
dry-view
expose :product do |slug:| product_repo.by_slug(slug) end
HTTP & routing
dry-web-roda
$ dry-web-roda new my_app
. ├── Gemfile ├── README.md ├── Rakefile ├── apps │
└── main │ ├── lib │ │ └── main │ │ └── views │ │ └── welcome.rb │ ├── system │ │ ├── boot │ │ │ └── view.rb │ │ ├── boot.rb │ │ └── main │ │ ├── application.rb │ │ ├── container.rb │ │ ├── import.rb │ │ ├── transactions.rb │ │ ├── view_context.rb │ │ └── view_controller.rb
│ │ └── relations │ └── types.rb ├── log ├──
spec │ ├── app_helper.rb │ ├── db_helper.rb │ ├── spec_helper.rb │ └── support │ ├── db │ │ └── test_factories.rb │ └── test_helpers.rb └── system ├── blog │ ├── application.rb │ ├── container.rb │ ├── import.rb │ └── settings.rb ├── boot │ └── rom.rb └── boot.rb 28 directories, 37 files
Persistence
rom-rb
Dependency management Result handling Data validation & types Views Routing
& HTTP Database persistence
None
A functional Ruby movement
None
None
hanami
RailsClub 2017 // @jodosha Functional Web with Hanami
Ruby is ready
There is nothing more powerful than an idea whose time
has come. — Victor Hugo
Bridging the gap for Ruby
Real-world Functional Ruby
FP + OO = Win!
FP + OO + Community = Win!
Functional Ruby is supported
Functional Ruby is practical
Functional Ruby is worth a try!
dry-rb/dry-web-blog
Thank you! @timriley dry-rb/dry-web-blog bit.ly/se-functional-ruby