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
Rails & SOLID by José Valim - RailsConf 2011
Search
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
Plataformatec
March 08, 2012
Technology
5
5.4k
Rails & SOLID by José Valim - RailsConf 2011
Plataformatec
March 08, 2012
Tweet
Share
More Decks by Plataformatec
See All by Plataformatec
O case da Plataformatec com o Elixir - Como uma empresa brasileira criou uma linguagem que é usada no mundo inteiro @ Elixir Brasil 2019
plataformatec
5
1.2k
O case da Plataformatec com o Elixir - Como uma empresa brasileira criou uma linguagem que é usada no mundo inteiro @ QCon SP 2018
plataformatec
1
240
Elixir @ iMasters Intercon 2016
plataformatec
1
270
GenStage and Flow by @josevalim at ElixirConf
plataformatec
17
2.9k
Elixir: Programação Funcional e Pragmática @ 2º Tech Day Curitiba
plataformatec
2
320
Elixir: Programação Funcional e Pragmática @ Encontro Locaweb 2016
plataformatec
4
310
What's ahead for Elixir: v1.2 and GenRouter
plataformatec
15
2.2k
Arquiteturas Comuns de Apps Rails @ RubyConf BR 2015
plataformatec
6
400
Pirâmide de testes, escrevendo testes com qualidade @ RubyConf 2015
plataformatec
10
2.5k
Other Decks in Technology
See All in Technology
SREチームをどう作り、どう育てるか ― Findy横断SREのマネジメント
rvirus0817
0
320
AI駆動開発を事業のコアに置く
tasukuonizawa
1
280
20260204_Midosuji_Tech
takuyay0ne
1
160
Claude_CodeでSEOを最適化する_AI_Ops_Community_Vol.2__マーケティングx_AIはここまで進化した.pdf
riku_423
2
600
ランサムウェア対策としてのpnpm導入のススメ
ishikawa_satoru
0
190
Oracle AI Database移行・アップグレード勉強会 - RAT活用編
oracle4engineer
PRO
0
100
茨城の思い出を振り返る ~CDKのセキュリティを添えて~ / 20260201 Mitsutoshi Matsuo
shift_evolve
PRO
1
340
usermode linux without MMU - fosdem2026 kernel devroom
thehajime
0
240
Red Hat OpenStack Services on OpenShift
tamemiya
0
120
StrandsとNeptuneを使ってナレッジグラフを構築する
yakumo
1
120
コスト削減から「セキュリティと利便性」を担うプラットフォームへ
sansantech
PRO
3
1.5k
超初心者からでも大丈夫!オープンソース半導体の楽しみ方〜今こそ!オレオレチップをつくろう〜
keropiyo
0
110
Featured
See All Featured
Being A Developer After 40
akosma
91
590k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.4k
Leveraging Curiosity to Care for An Aging Population
cassininazir
1
160
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
1
110
Mind Mapping
helmedeiros
PRO
0
88
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
350
SEO for Brand Visibility & Recognition
aleyda
0
4.2k
Evolving SEO for Evolving Search Engines
ryanjones
0
130
We Analyzed 250 Million AI Search Results: Here's What I Found
joshbly
1
740
Bash Introduction
62gerente
615
210k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
60
42k
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
0
150
Transcript
Design Principles behind
I am José Valim @josevalim
I work at blog.plataformatec.co
Core Team Member
Rails 3
SOLID Design
None
•Single Responsibility Principle •Open/closed Principle •Liskov Substitution
Single Responsibility Principle
“A class should have one, and only one, reason to
change” - Uncle Bob
Example: Views Refactoring
0.x ActionView::Base tracking details finding templates compiling templates rendering templates
rendering context Controller Render Contents
1.0 ActionView::Base tracking details finding templates rendering templates rendering context
Controller Render Contents Template Template handler compiling templates
builder, rjs, haml...
2.2 ActionView::Base tracking details rendering templates rendering context Controller Render
Contents Template Template handler compiling templates View paths hold filesystem paths finding templates
Rails Engines!
3.0 ActionView::Base tracking details rendering templates rendering context Controller Render
Contents Template Template handler compiling templates View paths hold resolvers Resolvers finding templates
The resolver object no longer restricts templates to the filesystem
class BasicController < ActionController::Base self.view_paths = [ ActionView::FixtureResolver.new( "basic/hello_world.html.erb" =>
"Hello world!" ) ] def hello_world render :action => "hello_world" end end
You can create your own abstractions, allowing you to read
templates from the database!
3.0 ActionView::Base tracking details rendering templates rendering context Controller Render
Contents Template Template handler compiling templates View paths hold resolvers Resolvers finding templates
3.0 ActionView::Base rendering templates rendering context Controller Render Contents Template
Template handler compiling templates View paths hold resolvers Resolvers finding templates Lookup context tracking details
3.1 Controller Template Template handler compiling templates View paths hold
resolvers Resolvers finding templates Lookup context tracking details AV::Renderer rendering templates ActionView::Base rendering context
def lookup_context ActionView::LookupContext.new(self.class._view_paths) end def view_renderer ActionView::Renderer.new(lookup_context) end def _render_template(options)
view_renderer.render(view_context, options) end
def view_context ActionView::Base.new(view_renderer, view_assigns, self) end
Maybe templates could be rendered in the controller context?
class BasicController < ActionController::Base include ActionView::Context # STEP 1 before_filter
:_prepare_context # STEP 2 def hello_world @value = "Hello" end protected def view_context # STEP 3 self end def __controller_method__ "controller context!" end end
# app/views/basic/hello_world.html.erb <%= @value %> from <%= __controller_method__ %> #
would return... Hello from controller context!
3.1 Controller Template Template handler compiling templates View paths hold
resolvers Resolvers finding templates Lookup context tracking details AV::Renderer rendering templates ActionView::Base rendering context
Top-down comprehension Worse Maintainability Better Focused comprehension Much Better Extensibility
POSSIBLE!
Open/closed Principle
“You should be able to extend a class behavior without
modifying it” - Uncle Bob
Open for extension, closed for modification
class ApplicationController < ActionController::Base end
Easy to follow because Ruby classes are all open for
extensions...
... but easy to violate because they are not closed
for modification.
# In your initializer ... config.active_record.table_name_prefix = “foo” # Then
... ActiveRecord::Base.table_name_prefix #=> “foo”
class ApplicationModel < ActiveRecord::Base end
class MyApp::ApplicationModel < ActiveRecord::Base end
Dependency Inversion
“Depend on abstractions, not on concretions” - Uncle Bob
def initialize(app:
def initialize(app)
@rack_app.call
1) Define
match “/foo”, to:
match “/foo”, to: match “/foo”, to: PostsController.action
3.1 Controller Template Template handler compiling templates View paths hold
resolvers Resolvers finding templates Lookup context tracking details AV::Renderer rendering templates ActionView::Base rendering context
@resolver.find_all( name, prefix, partial, details, key, locals
@haml_handler.call
2) Remove hardcoded dependencies
class PostsController < ApplicationController use ActiveRecord::IdentityMap::Middleware, :only => :index #
... end # And this builds the middleware stack ... PostsController.action(:index)
def self.action(name) middleware_stack.build(name.to_s) do |env| new.dispatch(name, ActionDispatch::Request.new(env)) end end
def self.action(name, klass = ActionDispatch::Request) middleware_stack.build(name.to_s) do |env| new.dispatch(name, klass.new(env))
end end
3) Define hooks
1) Load console on sandbox rails console --sandbox 2) Internally...
require “rails/console/ sandbox”
ActiveRecord::Base.connection.increment_open_transactions ActiveRecord::Base.connection.begin_db_transaction at_exit do ActiveRecord::Base.connection.rollback_db_transaction ActiveRecord::Base.connection.decrement_open_transactions end
Instead provide a hook...
class ActiveRecord::Railtie < Rails::Railtie console do |sandbox| if sandbox require
"active_record/railties/console_sandbox" end ActiveRecord::Base.logger = Logger.new(STDERR) end end
Liskov Substitution Principle
“Derived classes must be substitutable for their base classes” -
Uncle Bob
“Derived classes must be substitutable for their base classes” -
Uncle Bob
@rack_app.call •Receives a hash •Returns an array with status code,
headers and an object that responds
Define substitutable but also don’t violate the principle
Datamapper x Active Record
Active Model User.model_name user.persisted? user.valid? user.errors user.to_key user.to_param
How do we ensure substitutability?
ActiveModel::Lint::Tests
class LintTest < ActiveSupport::TestCase include ActiveModel::Lint::Tests def setup @model =
SomeDatamapperModel.new end end
Interface Segregation Principle
“Make fine grained interfaces that are client specific” - Uncle
Bob
“Clients should depend on as narrow protocol as possible” -
Jim Weirich
Active Model User.model_name user.persisted? user.valid? user.errors user.to_key user.to_param
How do we ensure a narrow protocol?
class MyPost < ActiveRecord::Base end my_post = MyPost.new assert_equal "my_posts/1",
url_for(my_post)
my_post = Mock.new my_post.stubs(:model_name).returns("MyPost") my_post.stubs(:to_param).returns(2) assert_equal "my_posts/1", url_for(my_post)
Code to well- defined and narrow protocols
None
None
Questions? José Valim @josevalim blog.plataformatec.com
Questions? blog twitter ID José Valim @josevalim blog.plataformatec.com