Un profondo punto di vista sul perché Ruby on Rails ha rivoluzionato lo sviluppo web.
Questo talk focalizzerà la sua attenzione sul “Golden Path” di Rails, sui motivi del suo successo, sui problemi più comuni, e su come le sue API possano essere migliorate.
Impareremo a trarre beneficio da uno strumento tanto potente quanto pericoloso, di come mitigare le implicazioni architetturali, di design e testabilità delle vostre applicazioni, migliorando la qualità del codice..
6. Why Rails
has revolutionized web
development?
Convention Over
Config uration
(IMHO)
:)
7. Why Rails
has revolutionized web
development?
Dynamic and innovative
Convention Over
Config uration
ecosystem
(IMHO)
:)
8. Why Rails
has revolutionized web
development?
Dynamic and innovative
Convention Over
Productivity and
Developer uration
Config Happiness
ecosystem
(IMHO)
:)
15. Active
Record
Encapsulation violations
1 if article.statearticle.published?
1 unless != 'published'
2 article.update_attribute(state: 'published')
2 article.publish!
3 end 3 end
4 # not completely right...
16. Active
Record
Encapsulation violations
1 if article.statearticle.published?
1 unless != 'published'
2 article.update_attribute(state: 'published')
2 article.publish!
3 end 3 end
4 # not completely right...
17. Active
Record
Encapsulation violations
1 if article.statearticle.published?
1 unless != 'published'
2 article.update_attribute(state: 'published')
2 article.publish!
3 end 3 end
4 # not completely right...
18. Active
Record
Tell, Don’t Ask
1
2 violations
article.publish!
unless article.published?
# article.publish!
push the implementation
3 end
# into the model
4 # not completely right...
19. Active
Record
Tell, Don’t Ask
violations
1 article.publish!
unless article.published?
2 # article.publish!
push the implementation
3 end
# into the model
4 # not completely right...
20. Active
Record
Tell, Don’t Ask
violations
1 article.publish!
unless article.published?
2 # article.publish!
push the implementation
3 end
# into the model
4 # not completely right...
21. Active
Record
2
Implicit vs Explicit API
1 class Post < ActiveRecord::Base
1 Post.where(state:'published').
1 Post.most_recent_published
def self.most_recent_published(limit = 5)
3 2published.recent(limit).order('created_at DESC')
order('created_at DESC').
4 end
5 3 limit(5)
6 private
7 scope :published, ->() { where(state: 'published') }
8 scope :recent, ->(n) { limit(n) }
9 end
22. Active
Record
Implicit vs Explicit API
1 class Post < ActiveRecord::Base
2 1 Post.where(state:'published').
1 Post.most_recent_published
def self.most_recent_published(limit = 5)
3 2published.recent(limit).order('created_at DESC')
order('created_at DESC').
4 end
5 3 limit(5)
6 private
7 scope :published, ->() { where(state: 'published') }
8 scope :recent, ->(n) { limit(n) }
9 end
23. Active
Record
Implicit vs Explicit API
1 class Post < ActiveRecord::Base
2 1 Post.where(state:'published').
1 Post.most_recent_published
def self.most_recent_published(limit = 5)
3 2published.recent(limit).order('created_at DESC')
order('created_at DESC').
4 end
5 3 limit(5)
6 private
7 scope :published, ->() { where(state: 'published') }
8 scope :recent, ->(n) { limit(n) }
9 end
24. Active
Record
Implicit vs Explicit API
1 class Post < ActiveRecord::Base
2 1 Post.where(state:'published').
1 Post.most_recent_published
def self.most_recent_published(limit = 5)
3 2published.recent(limit).order('created_at DESC')
order('created_at DESC').
4 end
5 3 limit(5)
6 private
7 scope :published, ->() { where(state: 'published') }
8 scope :recent, ->(n) { limit(n) }
9 end
25. Active
Record
Callbacks abuse
Non-persistence logic is
tight to the persistence
life cycle.
Eg. Sending emails
26. Active
Record
Testability issues
Micheal Feathers
27. Active
Record
A test is not a unit test if it talks
Testability issues
to a database.
Micheal Feathers
28. Action
Controller
Action
Controller
It doesn’t affect too much your
architecture, but it has strange
OOP design.
29. Action
Controller
1 class PostsController < ApplicationController
2 before_filter :authenticate
Frankenstein Controllers
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
30. Action
Controller
Frankenstein Controllers
1 class PostsController < ApplicationController
2 before_filter :authenticate
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
31. Action
Controller
Frankenstein Controllers
1 class PostsController < ApplicationController
2 before_filter :authenticate
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
32. Action
Controller
Frankenstein Controllers
1 class PostsController < ApplicationController
2 before_filter :authenticate
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
33. Action
Controller
Frankenstein Controllers
1 class PostsController < ApplicationController
2 before_filter :authenticate
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
34. Action
Controller
Frankenstein Controllers
1 class PostsController < ApplicationController
2 before_filter :authenticate
3
4 def new
5 end
6
7 def create
8 @post = Post.new(params[:post])
9
10 if @post.save
11 redirect_to post_url(@post), notice: 'Yay!'
12 else
13 render :new
14 end
15 end
16 end
35. Action
Controller
1 class PostsController < ApplicationController
2 # ...
Odd classes
3
4 def create
5 @post = Post.new(params[:post])
6
7 if @post.save
8 # ...
9 else
10 # ...
11 end
12 end
13 end
36. Action
Controller
Odd classes
1 class PostsController < ApplicationController
2 # ...
3
4 def create
5 @post = Post.new(params[:post])
6
7 if @post.save
8 # ...
9 else
10 # ...
11 end
12 end
13 end
37. Action
Controller
Odd classes
1 class PostsController < ApplicationController
2 # ...
3
4 def create
5 @post = Post.new(params[:post])
6
7 if @post.save
8 # ...
9 else
10 # ...
11 end
12 end
13 end
38. Action
Controller
Odd classes
1 class PostsController < ApplicationController
2 # ...
3
4 def create
5 @post = Post.new(params[:post])
6
7 if @post.save
8 # ...
9 else
10 # ...
11 end
12 end
13 end
39. Action
Controller
3
4
Encapsulation violations
1 class PostsController < ApplicationController
2 # ...
def create
5 @post = Post.new(params[:post])
6 # ...
7 end
8 end
40. Action
Controller
Encapsulation violations
1 class PostsController < ApplicationController
2 # ...
3
4 def create
5 @post = Post.new(params[:post])
6 # ...
7 end
8 end
41. Action
Controller
1 describe PostsController do
Testability issues
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
42. Action
Controller
Testability issues
1 describe PostsController do
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
43. Action
Controller
Testability issues
1 describe PostsController do
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
44. Action
Controller
Testability issues
1 describe PostsController do
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
45. Action
Controller
Testability issues
1 describe PostsController do
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
46. Action
Controller
Testability issues
1 describe PostsController do
2 it 'assigns @posts' do
3 Post.should_receive(:most_recent_published).
4 and_return(posts = [mock])
5 get :index
6
7 expect(assigns(:posts)).to eq(posts)
8 end
9 end
48. Action
View
Views aren’t views
(but templates with logic)
Without “real” views (or In an ideal world we
presenters), we’re tempted to shouldn’t test our
push presentational methods templates.
into the models.
49. Action
View
Helpers are
functional programming
1 def user_full_name(user)
2 [ user.first_name, user.last_name ].join(' ')
3 end
4
5 url_for
6 # vs Half-assed way to solve
7 Url.for presentational problems in
8 ActionView.
9 posts_url
50. Action
View
Helpers are
functional programming
1 def user_full_name(user)
2 [ user.first_name, user.last_name ].join(' ')
3 end
4
5 url_for
6 # vs Half-assed way to solve
7 Url.for presentational problems in
8 ActionView.
9 posts_url
58. Solutions
Keep methods and
accessors private as
much as possible.
Public APIs are hard to
maintain as the codebase
and the team grows.
59. Solutions
Skinny controllers
and
skinny models.
Use ser vice objects or DCI, they
are easier and faster to test.
60. Solutions
Don’t be afraid to extract
ad-hoc classes for
specific responsibilities.
Inner classes are your friends,
they help you with details, and
aren’t part of your public API.
65. me@lucag uidi.com
@jodosha
https://speakerdeck.com/jodosha/a-rails-criticism
Except where otherwise noted, this work is licensed under:
http://creativecommons.org/licenses/by-nc-sa/3.0/