Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Real-world Functional Ruby
Search
Tim Riley
October 12, 2017
Technology
570
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Real-world Functional Ruby
RubyConf Malaysia 2017
Tim Riley
October 12, 2017
More Decks by Tim Riley
See All by Tim Riley
A tour of dry-schema and dry-validation 1.0
timriley
1
420
Views, from the top
timriley
0
1.9k
Real-world functional Ruby
timriley
0
1.2k
Reinvesting in Ruby
timriley
1
140
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
SO-101×VLAによる3色キューブのピック&プレース
abeja
0
220
関東Kaggler会発表資料
takoi
1
260
Head First モブプログラミング / Head First Mobprogramming
takaking22
10
12k
ブラウザ研修 2026
recruitengineers
PRO
7
1.2k
FORENSIA: ローカルLLMフォレンジックハーネス
sumeshi
2
420
20260804_Q4AzureUpdateBite_FabricDataAgentの精度を高める設計.pdf
matayuuu
1
150
モバイルアプリ開発概論2026
recruitengineers
PRO
6
660
[ChatGPT Work LT]事務作業が苦手な人のための バックオフィスの「半」自動化
chimaki_iot
0
320
認知負荷をGemini で溶かす — GKE 基盤「Orbit」における AI エージェントの実践
sansantech
PRO
1
320
攻撃と防御で学ぶAI時代のプロダクトセキュリティ演習
recruitengineers
PRO
9
3.1k
MulticaとPi Coding Agentで、小規模OSSを30本同時運用した流れ
eiei114
0
130
カートの信頼性を担保するWireMockを使ったe2eテスト
ykagano
0
460
Featured
See All Featured
Producing Creativity
orderedlist
PRO
348
40k
What the history of the web can teach us about the future of AI
inesmontani
PRO
1
660
The Psychology of Web Performance [Beyond Tellerrand 2023]
tammyeverts
49
3.5k
sira's awesome portfolio website redesign presentation
elsirapls
0
330
Joys of Absence: A Defence of Solitary Play
codingconduct
1
440
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
3k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
670
The Hidden Cost of Media on the Web [PixelPalooza 2025]
tammyeverts
2
470
Exploring anti-patterns in Rails
aemeredith
3
470
Bash Introduction
62gerente
615
220k
ラッコキーワード サービス紹介資料
rakko
1
4.4M
Heart Work Chapter 1 - Part 1
lfama
PRO
8
36k
Transcript
Real-world Functional Ruby RubyConf Malaysia 2017
@timriley
None
@icelab
!
None
@dry_rb @rom_rb
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 RubyConf Malaysia 2017 Tim Riley // @timriley
Functional Ruby, can!
Functional Ruby, can!
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
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
# Successful result Right(imported_products) # Failed result Left(error)
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/rubyconfmy-fp-ruby