SlideShare a Scribd company logo
1 of 62
Download to read offline
Rails Antipattern 
Hong
 ChulJu
Speaker 
Hong ChulJu 
• http://blog.fegs.kr 
• https://github.com/FeGs 
• Rails Newbie 
• SW Maestro 5th
RAILS ANTIPATTERN 
mainly about code refactoring
Index 
• Monolithic Controllers 
• Fat Controller 
• PHPitis 
• Voyeuristic Models 
• Spaghetti SQL 
• Fat Model 
• Duplicate Code Duplication 
• Fixture Blues 
• Messy Migration
Monolithic Controllers 
• User Authentication 
class UsersController  ApplicationController 
def action 
operation = params[:operation] 
# ... 
end 
end
Monolithic Controllers 
• Our projects 
resources :users, only: [] do 
collection do 
get 'show' 
get 'sign_in', to: 'users#sign_in' 
get 'sign_up', to: 'users#new' 
post 'sign_up', to: 'users#create' 
get 'email_sent', to: 'users#email_sent' 
get 'verify/:code', to: 'users#verify' 
end 
end 
powerful user
Monolithic Controllers 
• Our projects 
class UsersController  ApplicationController 
def new 
end 
def create 
end 
def show 
end 
?? 
def sign_in 
end 
def sign_out 
end 
def email_sent 
end 
def verify 
end 
end
• UsersController#new 
• UsersController#create 
• UsersController#verify 
• UsersController#show 
• UsersController#sign_in 
• UsersController#sign_out 
• UsersController#email_sent 
• - 
break apart controllers 
ActivationsController 
[:new, :create, :show] 
SessionsController 
[:new, :destroy]
Fat Controller 
class RailsController  ApplicationController 
def create 
# ... 
# transaction, association 
# service logic, etc 
# Suppose that this method contains 100+ lines of code. 
end 
end
Fat Controller 
class RailsController  ApplicationController 
def create 
active record callback, 
build object 
# ... 
# transaction, association 
# service logic, etc 
# Suppose that this method contains 100+ lines of code. 
end 
end 
service objects, lib
Controller + lib 
class ReservationsController  ApplicationController 
def create 
reservation = Reservation.new 
ticket = Ticket.new 
# ticket code generation 
# ... 
ticket.code = # ... 
reservation.transaction do 
ticket.save! 
reservation.ticket = ticket 
reservation.save! 
end 
end 
end 
1) to lib?
Controller + lib 
class TicketsController  ApplicationController 
def create 
ticket = Ticket.new 
code_generator = CodeGenerator.new 
ticket.code = code_generator.generate 
# ... 
end 
end 
ticket need to be coupling with code 
may miss it?
Model + lib 
class Ticket  ActiveRecord::Base 
# has a code column 
before_save :generate_code 
private 
def generate_code 
active record callback 
code_generator = CodeGenerator.new 
self.code ||= code_generator.generate 
end 
end 
# TicketsController#create 
ticket = Ticket.create! 
profit!
internal transaction 
class ReservationsController  ApplicationController 
def create 
reservation = Reservation.new 
reservation.transaction do 
reservation.ticket = Ticket.create! 
reservation.save! 
end 
end 
end 
2) Remove transaction
internal transaction 
class ReservationsController  ApplicationController 
def create 
reservation = Reservation.new 
reservation.ticket.build 
reservation.save! 
end 
end association
Service Object 
class ReservationsController  ApplicationController 
def create 
result = CreateReservationService.new.execute 
end 
end 
ServiceObject
Service Object 
https://github.com/gitlabhq/gitlabhq/tree/master/app/services
PHPitis 
• Do you know PHP? 
% if current_user  
(current_user == @post.user || 
@post.editors.include?(current_user))  
@post.editable?  
@post.user.active? % 
%= link_to 'Edit this post', edit_post_url(@post) % 
% end %
Useful accessors to model 
• Post#editable_by? (not a helper method) 
% if @post.editable_by?(current_user) % 
%= link_to 'Edit this post', edit_post_url(@post) % 
% end %
Useful accessors to model 
• Our project 
module Admin::UsersHelper 
def pretty_phone_number(phone_number) 
return  unless phone_number 
# prettify logic 
prettified 
end 
def pretty_rails # ... 
end 
%= pretty_phone_number(user.phone_number) %
Useful accessors to model 
• Decorate a user 
class User  ActiveRecord::Base 
# recommend to use draper 
def display_phone_number 
return  unless phone_number 
# prettify logic 
prettified 
end 
end 
%= user.display_phone_number %
content_for? 
• named yield block 
html 
head 
%= yield :head % 
/head 
body 
%= yield % 
/body 
/html 
% content_for :head do % 
titleA simple page/title 
% end % 
pHello, Rails!/p
Extract into Custom Helpers 
• Markup Helpers 
def rss_link(project = nil) 
link_to Subscribe to these #{project.name if project} alerts., 
alerts_rss_url(project), :class = feed_link 
end 
div class=feed 
%= rss_link(@project) % 
/div
Extract into Custom Helpers 
• Our project 
def nav_link_to (text, link) 
active = active if current_page?(link) 
content_tag :li, class: active do 
link_to text, link 
end 
end 
ul class=nav nav-pills nav-stacked col-md-3 pull-left 
%= nav_link_to Unread, notifications_path % 
%= nav_link_to All Notifications, notifications_all_path % 
/ul
Voyeuristic Models 
• Situation 
class Invoice  ActiveRecord::Base 
belongs_to :customer 
end 
class Customer  ActiveRecord::Base 
has_one :address 
has_many :invoice 
end 
class Address  ActiveRecord::Base 
belongs_to :customer 
end 
%= @invoice.customer.address.city %
Voyeuristic Models 
• Law of Demeter 
• No method chaining (Down coupling) 
• Basic refactoring of OOP (why getter, setter?) 
• Not only for rails
Voyeuristic Models 
• General way 
class Invoice  ActiveRecord::Base 
# ... 
def customer_city 
customer.city 
end 
end 
class Customer  ActiveRecord::Base 
# ... 
def city 
address.city 
end 
end 
%= @invoice.customer_city %
Voyeuristic Models 
class Customer  ActiveRecord::Base 
def city 
address.city 
end 
def street 
address.street 
end 
def state 
address.state 
end 
# many fields below 
end 
??
Voyeuristic Models 
• Refactoring using delegate (Rails way) 
class Customer  ActiveRecord::Base 
# ... 
delegate :street, :city, :state, to: :address 
end 
class Invoice  ActiveRecord::Base 
# ... 
delegate :city, to: :customer, prefix: true 
end 
%= @invoice.customer_city %
Voyeuristic Models 
• Furthermore 
• http://blog.revathskumar.com/2013/08/rails-use- 
delegates-to-avoid-long-method-chains. 
html 
• http://simonecarletti.com/blog/2009/12/inside-ruby- 
on-rails-delegate/ 
• http://blog.aliencube.org/ko/2013/12/06/law-of-demeter- 
explained/
Spaghetti SQL 
class RemoteProcess  ActiveRecord::Base 
def self.find_top_running_processes(limit = 5) 
find(:all, 
:conditions = state = 'Running', 
:order = percent_cpu desc, 
:limit = limit) 
end 
end 
Reusability?
Spaghetti SQL 
class RemoteProcess  ActiveRecord::Base 
scope :running, where(:state = 'Running') 
scope :system, where(:owner = ['root', 'mysql']) scope :sorted, 
order(percent_cpu desc) 
scope :top, lambda {|l| limit(l) } 
end 
RemoteProcess.running.sorted.top(5) 
RemoteProcess.running.system.sorted.top(5) 
Reusability!
Spaghetti SQL 
class RemoteProcess  ActiveRecord::Base 
scope :running, where(:state = 'Running') 
scope :system, where(:owner = ['root', 'mysql']) scope :sorted, 
order(percent_cpu desc) 
scope :top, lambda {|l| limit(l) } 
# Shortcut 
def self.find_top_running_processes(limit = 5) 
running.sorted.top(limit) 
end 
end
Scope vs Class method 
• Almost same, but scopes are always chainable 
class Post  ActiveRecord::Base 
def self.status(status) 
where(status: status) if status.present? 
end 
def self.recent 
limit(10) 
end 
end 
Post.status('active').recent 
Post.status('').recent 
Post.status(nil).recent 
nil
Scope vs Class method 
• Almost same, but scopes are always chainable 
class Post  ActiveRecord::Base 
scope :status, - status { where(status: status) if 
status.present? } 
scope :recent, limit(10) 
end 
Post.status('active').recent 
Post.status('').recent 
Post.status(nil).recent 
just ignored
Spaghetti SQL 
• Further reading 
• http://blog.plataformatec.com.br/2013/02/ 
active-record-scopes-vs-class-methods/
Fat Model 
• Use extend, include module 
• example: too many scope, finder, etc.
Fat Model 
• ledermann/unread 
module Unread 
module Readable 
module Scopes 
def join_read_marks(user) 
# ... 
end 
def unread_by(user) 
# ... 
end 
# ... 
end 
end 
end 
class SomeReadable  ActiveRecord::Base 
# ... 
extend Unread::Readable::Scopes 
end
Fat Model 
• Do you prefer composition to inheritance?
Fat Model 
• Further Reading 
• http://blog.codeclimate.com/blog/ 
2012/10/17/7-ways-to-decompose-fat-activerecord- 
models/
Duplicate Code Duplication 
• Basic of refactoring 
• Extract into modules 
• included, extended 
• using metaprogramming
Extract into modules 
class Car  ActiveRecord::Base 
validates :direction, :presence = true 
validates :speed, :presence = true 
def turn(new_direction) 
self.direction = new_direction 
end 
def brake 
self.speed = 0 
end 
def accelerate 
self.speed = [speed + 10, 100].min 
end 
# Other, car-related activities... 
end 
class Bicycle  ActiveRecord::Base 
validates :direction, :presence = true 
validates :speed, :presence = true 
def turn(new_direction) 
self.direction = new_direction 
end 
def brake 
self.speed = 0 
end 
def accelerate 
self.speed = [speed + 1, 20].min 
end 
end
Extract into modules 
module Drivable 
extend ActiveSupport::Concern 
included do 
validates :direction, :presence = true 
validates :speed, :presence = true 
end 
def turn(new_direction) 
self.direction = new_direction 
end 
def brake 
self.speed = 0 
end 
def accelerate 
self.speed = [speed + acceleration, top_speed].min 
end 
end
Write a your gem! (plugin) 
ex) https://github.com/FeGs/read_activity 
module Drivable 
extend ActiveSupport::Concern 
included do 
validates :direction, :presence = true 
validates :speed, :presence = true 
end 
def turn(new_direction) 
self.direction = new_direction 
end 
def brake 
self.speed = 0 
end 
def accelerate 
self.speed = [speed + acceleration, top_speed].min 
end 
end 
‘drivable’ gem
Write a your gem! (plugin) 
module DrivableGem 
def self.included(base) 
base.extend(Module) 
end 
module Module 
def act_as_drivable 
include Drivable 
end 
end 
end 
ActiveRecord::Base.send(:include, DrivableGem)
Write a your gem! (plugin) 
class Car  ActiveRecord::Base 
act_as_drivable 
end
How about Metaprogramming? 
class Purchase  ActiveRecord::Base 
validates :status, presence: true, 
inclusion: { in: %w(in_progress submitted ...) } 
# Status Finders 
scope :all_in_progress, where(status: in_progress) 
# ... 
# Status 
def in_progress? 
status == in_progress 
end 
# ... 
end
How about Metaprogramming? 
class Purchase  ActiveRecord::Base 
STATUSES = %w(in_progress submitted ...) 
validates :status, presence: true, 
inclusion: { in: STATUSES } 
STATUSES.each do |status_name| 
scope all_#{status_name}, where(status: status_name) 
define_method #{status_name}? do 
status == status_name 
end 
end 
end 
How to improve reusability?
How about Metaprogramming? 
class ActiveRecord::Base 
def self.has_statuses(*status_names) 
validates :status, presence: true, 
inclusion: { in: status_names } 
status_names.each do |status_name| 
scope all_#{status_name}, where(status: status_name) 
define_method #{status_name}? do 
status == status_name 
end 
end 
end 
end 
Use extension! 
class Purchase  ActiveRecord::Base 
has_statuses :in_progress, :submitted, # ... 
end
Fixture Blues 
• Rails fixture has many problems: 
• No validation 
• Not following model lifecycle 
• No context 
• …
Make Use of Factories 
• Rails fixture has many problems: 
• No validation 
• Not following model lifecycle 
• No context 
• …
Make Use of Factories 
module Factory 
class  self 
def create_published_post 
post = Post.create!({ 
body: lorem ipsum, 
title: published post title, 
published: true 
}) 
end 
def create_unpublished_post 
# ... 
end 
end 
end
Make Use of Factories: 
FactoryGirl 
Factory.sequence :title do |n| 
Title #{n} 
end 
Factory.define :post do |post| 
post.body lorem ipsum 
post.title { Factory.next(:title) } 
post.association :author, :factory = :user 
post.published true 
end 
Factory(:post) 
Factory(:post, :published = false)
Make Use of Factories 
• Rails fixture has many problems: 
• No validation 
• Not following model lifecycle 
• No context 
• …
Refactor into Contexts 
context A dog do 
setup do 
@dog = Dog.new 
end 
should bark when sent #talk do 
assert_equal bark, @dog.talk 
end 
context with fleas do 
setup do 
@dog.fleas  Flea.new 
@dog.fleas  Flea.new 
end 
should scratch when idle do 
@dog.idle! 
assert @dog.scratching? 
end
Refactor into Contexts: 
rspec 
• context is alias of describe 
describe #bark do 
before(:each) do 
@dog = Dog.new 
end 
context sick dog do 
before(:each) do 
@dog.status = :sick 
end 
# ... 
end 
end
Messy Migrations 
• You should ensure that your migrations never 
irreconcilably messy. 
• Never Modify the up Method on a Committed 
Migration : obviously 
• Always Provide a down Method in Migrations
Never Use External Code in a Migration 
class AddJobsCountToUser  ActiveRecord::Migration 
def self.up 
add_column :users, :jobs_count, :integer, :default = 0 
Users.all.each do |user| 
user.jobs_count = user.jobs.size 
user.save 
end 
end 
If No User, No Job? 
def self.down 
remove_column :users, :jobs_count 
end 
end
Never Use External Code in a Migration 
class AddJobsCountToUser  ActiveRecord::Migration 
def self.up 
add_column :users, :jobs_count, :integer, :default = 0 
update(-SQL) 
UPDATE users SET jobs_count = ( 
SELECT count(*) FROM jobs 
WHERE jobs.user_id = users.id 
) 
SQL 
end 
def self.down 
remove_column :users, :jobs_count 
end 
end 
No dependancy
Never Use External Code in a Migration 
class AddJobsCountToUser  ActiveRecord::Migration 
class Job  ActiveRecord::Base 
end 
class User  ActiveRecord::Base 
has_many :jobs 
end 
def self.up 
add_column :users, :jobs_count, :integer, :default = 0 
User.reset_column_information 
Users.all.each do |user| 
Provide definition internally 
Alternative to raw SQL 
user.jobs_count = user.jobs.size 
user.save 
end 
end 
# ... 
end
Never Use External Code in a Migration 
• Further Reading 
• http://railsguides.net/change-data-in-migrations- 
like-a-boss/ 
• https://github.com/ajvargo/data-migrate 
• https://github.com/ka8725/migration_data

More Related Content

What's hot

Rails engines in large apps
Rails engines in large appsRails engines in large apps
Rails engines in large appsEnrico Teotti
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkBo-Yi Wu
 
Outside-in Development with Cucumber and Rspec
Outside-in Development with Cucumber and RspecOutside-in Development with Cucumber and Rspec
Outside-in Development with Cucumber and RspecJoseph Wilk
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsKonstantin Kudryashov
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Eliran Eliassy
 
Rails Engines as a way to Micro services
Rails Engines as a way to Micro servicesRails Engines as a way to Micro services
Rails Engines as a way to Micro servicesLucas Alencar
 
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu CódigoWhere Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu CódigoGuilherme
 
Coffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSCoffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSDeepu S Nath
 
Styling recipes for Angular components
Styling recipes for Angular componentsStyling recipes for Angular components
Styling recipes for Angular componentsNir Kaufman
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperfNew Relic
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Fwdays
 
Introduction to Sightly
Introduction to SightlyIntroduction to Sightly
Introduction to SightlyAnkit Gubrani
 
How to build customizable multitenant web applications - PHPBNL11
How to build customizable multitenant web applications - PHPBNL11How to build customizable multitenant web applications - PHPBNL11
How to build customizable multitenant web applications - PHPBNL11Stephan Hochdörfer
 
Introduction to Sightly and Sling Models
Introduction to Sightly and Sling ModelsIntroduction to Sightly and Sling Models
Introduction to Sightly and Sling ModelsStefano Celentano
 
Telling Stories With RSpec
Telling Stories With RSpecTelling Stories With RSpec
Telling Stories With RSpecrahoulb
 
Understanding JSP -Servlets
Understanding JSP -ServletsUnderstanding JSP -Servlets
Understanding JSP -ServletsGagandeep Singh
 

What's hot (19)

Angularjs
AngularjsAngularjs
Angularjs
 
Rails engines in large apps
Rails engines in large appsRails engines in large apps
Rails engines in large apps
 
RESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP FrameworkRESTful API Design & Implementation with CodeIgniter PHP Framework
RESTful API Design & Implementation with CodeIgniter PHP Framework
 
Rails Engines
Rails EnginesRails Engines
Rails Engines
 
Outside-in Development with Cucumber and Rspec
Outside-in Development with Cucumber and RspecOutside-in Development with Cucumber and Rspec
Outside-in Development with Cucumber and Rspec
 
Enabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projectsEnabling agile devliery through enabling BDD in PHP projects
Enabling agile devliery through enabling BDD in PHP projects
 
Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics Angular server side rendering - Strategies & Technics
Angular server side rendering - Strategies & Technics
 
Rails Engines as a way to Micro services
Rails Engines as a way to Micro servicesRails Engines as a way to Micro services
Rails Engines as a way to Micro services
 
ParisJS #10 : RequireJS
ParisJS #10 : RequireJSParisJS #10 : RequireJS
ParisJS #10 : RequireJS
 
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu CódigoWhere Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
Where Does the Fat Goes? Utilizando Form Objects Para Simplificar seu Código
 
Coffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JSCoffee@DBG - Exploring Angular JS
Coffee@DBG - Exploring Angular JS
 
Styling recipes for Angular components
Styling recipes for Angular componentsStyling recipes for Angular components
Styling recipes for Angular components
 
Intro to-rails-webperf
Intro to-rails-webperfIntro to-rails-webperf
Intro to-rails-webperf
 
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
Maciej Treder "Server-side rendering with Angular—be faster and more SEO, CDN...
 
Introduction to Sightly
Introduction to SightlyIntroduction to Sightly
Introduction to Sightly
 
How to build customizable multitenant web applications - PHPBNL11
How to build customizable multitenant web applications - PHPBNL11How to build customizable multitenant web applications - PHPBNL11
How to build customizable multitenant web applications - PHPBNL11
 
Introduction to Sightly and Sling Models
Introduction to Sightly and Sling ModelsIntroduction to Sightly and Sling Models
Introduction to Sightly and Sling Models
 
Telling Stories With RSpec
Telling Stories With RSpecTelling Stories With RSpec
Telling Stories With RSpec
 
Understanding JSP -Servlets
Understanding JSP -ServletsUnderstanding JSP -Servlets
Understanding JSP -Servlets
 

Viewers also liked

Viewers also liked (6)

Pp 2007
Pp 2007Pp 2007
Pp 2007
 
Copyright1.4
Copyright1.4Copyright1.4
Copyright1.4
 
Copyright1.2
Copyright1.2Copyright1.2
Copyright1.2
 
Nochildleftbehindppt
NochildleftbehindpptNochildleftbehindppt
Nochildleftbehindppt
 
안드로이드 세미나
안드로이드 세미나안드로이드 세미나
안드로이드 세미나
 
Follow the Red Line
Follow the Red LineFollow the Red Line
Follow the Red Line
 

Similar to Rails antipatterns

OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialYi-Ting Cheng
 
Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Plataformatec
 
Namespace less engine
Namespace less engineNamespace less engine
Namespace less engineshaokun
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsJim Jeffers
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Yasuko Ohba
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiPivorak MeetUp
 
Ruby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapMarcio Marinho
 
Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2A.K.M. Ahsrafuzzaman
 
Documenting from the Trenches
Documenting from the TrenchesDocumenting from the Trenches
Documenting from the TrenchesXavier Noria
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on RailsMark Menard
 
Action Controller Overview, Season 2
Action Controller Overview, Season 2Action Controller Overview, Season 2
Action Controller Overview, Season 2RORLAB
 
Panmind at Ruby Social Club Milano
Panmind at Ruby Social Club MilanoPanmind at Ruby Social Club Milano
Panmind at Ruby Social Club MilanoPanmind
 
RoR 101: Session 5
RoR 101: Session 5RoR 101: Session 5
RoR 101: Session 5Rory Gianni
 
walkmod: An open source tool for coding conventions
walkmod: An open source tool for coding conventionswalkmod: An open source tool for coding conventions
walkmod: An open source tool for coding conventionswalkmod
 
Rails for Beginners - Le Wagon
Rails for Beginners - Le WagonRails for Beginners - Le Wagon
Rails for Beginners - Le WagonAlex Benoit
 

Similar to Rails antipatterns (20)

OSDC 2009 Rails Turtorial
OSDC 2009 Rails TurtorialOSDC 2009 Rails Turtorial
OSDC 2009 Rails Turtorial
 
Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010Código Saudável => Programador Feliz - Rs on Rails 2010
Código Saudável => Programador Feliz - Rs on Rails 2010
 
Namespace less engine
Namespace less engineNamespace less engine
Namespace less engine
 
Building Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in RailsBuilding Mobile Friendly APIs in Rails
Building Mobile Friendly APIs in Rails
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
 
Rails MVC by Sergiy Koshovyi
Rails MVC by Sergiy KoshovyiRails MVC by Sergiy Koshovyi
Rails MVC by Sergiy Koshovyi
 
Ruby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter BootstrapRuby on Rails + AngularJS + Twitter Bootstrap
Ruby on Rails + AngularJS + Twitter Bootstrap
 
Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2Ride on the Fast Track of Web with Ruby on Rails- Part 2
Ride on the Fast Track of Web with Ruby on Rails- Part 2
 
Documenting from the Trenches
Documenting from the TrenchesDocumenting from the Trenches
Documenting from the Trenches
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on Rails
 
Introduction to AngularJs
Introduction to AngularJsIntroduction to AngularJs
Introduction to AngularJs
 
Ruby For Startups
Ruby For StartupsRuby For Startups
Ruby For Startups
 
Action Controller Overview, Season 2
Action Controller Overview, Season 2Action Controller Overview, Season 2
Action Controller Overview, Season 2
 
Panmind at Ruby Social Club Milano
Panmind at Ruby Social Club MilanoPanmind at Ruby Social Club Milano
Panmind at Ruby Social Club Milano
 
Codegnitorppt
CodegnitorpptCodegnitorppt
Codegnitorppt
 
RoR 101: Session 5
RoR 101: Session 5RoR 101: Session 5
RoR 101: Session 5
 
walkmod: An open source tool for coding conventions
walkmod: An open source tool for coding conventionswalkmod: An open source tool for coding conventions
walkmod: An open source tool for coding conventions
 
mean stack
mean stackmean stack
mean stack
 
Rails for Beginners - Le Wagon
Rails for Beginners - Le WagonRails for Beginners - Le Wagon
Rails for Beginners - Le Wagon
 
Rails 4.0
Rails 4.0Rails 4.0
Rails 4.0
 

More from Chul Ju Hong

Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-publicChul Ju Hong
 
게임 클론하기
게임 클론하기게임 클론하기
게임 클론하기Chul Ju Hong
 
안드로이드 세미나 2
안드로이드 세미나 2안드로이드 세미나 2
안드로이드 세미나 2Chul Ju Hong
 
협업 툴 사용법
협업 툴 사용법협업 툴 사용법
협업 툴 사용법Chul Ju Hong
 

More from Chul Ju Hong (8)

Mixture model
Mixture modelMixture model
Mixture model
 
Digit recognizer
Digit recognizerDigit recognizer
Digit recognizer
 
Naive ML Overview
Naive ML OverviewNaive ML Overview
Naive ML Overview
 
Rails antipattern-public
Rails antipattern-publicRails antipattern-public
Rails antipattern-public
 
Node.js intro
Node.js introNode.js intro
Node.js intro
 
게임 클론하기
게임 클론하기게임 클론하기
게임 클론하기
 
안드로이드 세미나 2
안드로이드 세미나 2안드로이드 세미나 2
안드로이드 세미나 2
 
협업 툴 사용법
협업 툴 사용법협업 툴 사용법
협업 툴 사용법
 

Recently uploaded

Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...confluent
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Cizo Technology Services
 
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
 
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
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWave PLM
 
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
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Mater
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureDinusha Kumarasiri
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Matt Ray
 
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
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...Technogeeks
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityNeo4j
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtimeandrehoraa
 
Best Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfBest Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfIdiosysTechnologies1
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noidabntitsolutionsrishis
 
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
 
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
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprisepreethippts
 
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
 

Recently uploaded (20)

Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
Catch the Wave: SAP Event-Driven and Data Streaming for the Intelligence Ente...
 
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
Global Identity Enrolment and Verification Pro Solution - Cizo Technology Ser...
 
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...
 
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
 
What is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need ItWhat is Fashion PLM and Why Do You Need It
What is Fashion PLM and Why Do You Need It
 
Cloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEECloud Data Center Network Construction - IEEE
Cloud Data Center Network Construction - IEEE
 
Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)Ahmed Motair CV April 2024 (Senior SW Developer)
Ahmed Motair CV April 2024 (Senior SW Developer)
 
Implementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with AzureImplementing Zero Trust strategy with Azure
Implementing Zero Trust strategy with Azure
 
2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva2.pdf Ejercicios de programación competitiva
2.pdf Ejercicios de programación competitiva
 
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
Open Source Summit NA 2024: Open Source Cloud Costs - OpenCost's Impact on En...
 
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...
 
What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...What is Advanced Excel and what are some best practices for designing and cre...
What is Advanced Excel and what are some best practices for designing and cre...
 
EY_Graph Database Powered Sustainability
EY_Graph Database Powered SustainabilityEY_Graph Database Powered Sustainability
EY_Graph Database Powered Sustainability
 
SpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at RuntimeSpotFlow: Tracking Method Calls and States at Runtime
SpotFlow: Tracking Method Calls and States at Runtime
 
Best Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdfBest Web Development Agency- Idiosys USA.pdf
Best Web Development Agency- Idiosys USA.pdf
 
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in NoidaBuds n Tech IT Solutions: Top-Notch Web Services in Noida
Buds n Tech IT Solutions: Top-Notch Web Services in Noida
 
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
 
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
 
Odoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 EnterpriseOdoo 14 - eLearning Module In Odoo 14 Enterprise
Odoo 14 - eLearning Module In Odoo 14 Enterprise
 
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....
 

Rails antipatterns

  • 3. Speaker Hong ChulJu • http://blog.fegs.kr • https://github.com/FeGs • Rails Newbie • SW Maestro 5th
  • 4. RAILS ANTIPATTERN mainly about code refactoring
  • 5. Index • Monolithic Controllers • Fat Controller • PHPitis • Voyeuristic Models • Spaghetti SQL • Fat Model • Duplicate Code Duplication • Fixture Blues • Messy Migration
  • 6. Monolithic Controllers • User Authentication class UsersController ApplicationController def action operation = params[:operation] # ... end end
  • 7. Monolithic Controllers • Our projects resources :users, only: [] do collection do get 'show' get 'sign_in', to: 'users#sign_in' get 'sign_up', to: 'users#new' post 'sign_up', to: 'users#create' get 'email_sent', to: 'users#email_sent' get 'verify/:code', to: 'users#verify' end end powerful user
  • 8. Monolithic Controllers • Our projects class UsersController ApplicationController def new end def create end def show end ?? def sign_in end def sign_out end def email_sent end def verify end end
  • 9. • UsersController#new • UsersController#create • UsersController#verify • UsersController#show • UsersController#sign_in • UsersController#sign_out • UsersController#email_sent • - break apart controllers ActivationsController [:new, :create, :show] SessionsController [:new, :destroy]
  • 10. Fat Controller class RailsController ApplicationController def create # ... # transaction, association # service logic, etc # Suppose that this method contains 100+ lines of code. end end
  • 11. Fat Controller class RailsController ApplicationController def create active record callback, build object # ... # transaction, association # service logic, etc # Suppose that this method contains 100+ lines of code. end end service objects, lib
  • 12. Controller + lib class ReservationsController ApplicationController def create reservation = Reservation.new ticket = Ticket.new # ticket code generation # ... ticket.code = # ... reservation.transaction do ticket.save! reservation.ticket = ticket reservation.save! end end end 1) to lib?
  • 13. Controller + lib class TicketsController ApplicationController def create ticket = Ticket.new code_generator = CodeGenerator.new ticket.code = code_generator.generate # ... end end ticket need to be coupling with code may miss it?
  • 14. Model + lib class Ticket ActiveRecord::Base # has a code column before_save :generate_code private def generate_code active record callback code_generator = CodeGenerator.new self.code ||= code_generator.generate end end # TicketsController#create ticket = Ticket.create! profit!
  • 15. internal transaction class ReservationsController ApplicationController def create reservation = Reservation.new reservation.transaction do reservation.ticket = Ticket.create! reservation.save! end end end 2) Remove transaction
  • 16. internal transaction class ReservationsController ApplicationController def create reservation = Reservation.new reservation.ticket.build reservation.save! end end association
  • 17. Service Object class ReservationsController ApplicationController def create result = CreateReservationService.new.execute end end ServiceObject
  • 19. PHPitis • Do you know PHP? % if current_user (current_user == @post.user || @post.editors.include?(current_user)) @post.editable? @post.user.active? % %= link_to 'Edit this post', edit_post_url(@post) % % end %
  • 20. Useful accessors to model • Post#editable_by? (not a helper method) % if @post.editable_by?(current_user) % %= link_to 'Edit this post', edit_post_url(@post) % % end %
  • 21. Useful accessors to model • Our project module Admin::UsersHelper def pretty_phone_number(phone_number) return unless phone_number # prettify logic prettified end def pretty_rails # ... end %= pretty_phone_number(user.phone_number) %
  • 22. Useful accessors to model • Decorate a user class User ActiveRecord::Base # recommend to use draper def display_phone_number return unless phone_number # prettify logic prettified end end %= user.display_phone_number %
  • 23. content_for? • named yield block html head %= yield :head % /head body %= yield % /body /html % content_for :head do % titleA simple page/title % end % pHello, Rails!/p
  • 24. Extract into Custom Helpers • Markup Helpers def rss_link(project = nil) link_to Subscribe to these #{project.name if project} alerts., alerts_rss_url(project), :class = feed_link end div class=feed %= rss_link(@project) % /div
  • 25. Extract into Custom Helpers • Our project def nav_link_to (text, link) active = active if current_page?(link) content_tag :li, class: active do link_to text, link end end ul class=nav nav-pills nav-stacked col-md-3 pull-left %= nav_link_to Unread, notifications_path % %= nav_link_to All Notifications, notifications_all_path % /ul
  • 26. Voyeuristic Models • Situation class Invoice ActiveRecord::Base belongs_to :customer end class Customer ActiveRecord::Base has_one :address has_many :invoice end class Address ActiveRecord::Base belongs_to :customer end %= @invoice.customer.address.city %
  • 27. Voyeuristic Models • Law of Demeter • No method chaining (Down coupling) • Basic refactoring of OOP (why getter, setter?) • Not only for rails
  • 28. Voyeuristic Models • General way class Invoice ActiveRecord::Base # ... def customer_city customer.city end end class Customer ActiveRecord::Base # ... def city address.city end end %= @invoice.customer_city %
  • 29. Voyeuristic Models class Customer ActiveRecord::Base def city address.city end def street address.street end def state address.state end # many fields below end ??
  • 30. Voyeuristic Models • Refactoring using delegate (Rails way) class Customer ActiveRecord::Base # ... delegate :street, :city, :state, to: :address end class Invoice ActiveRecord::Base # ... delegate :city, to: :customer, prefix: true end %= @invoice.customer_city %
  • 31. Voyeuristic Models • Furthermore • http://blog.revathskumar.com/2013/08/rails-use- delegates-to-avoid-long-method-chains. html • http://simonecarletti.com/blog/2009/12/inside-ruby- on-rails-delegate/ • http://blog.aliencube.org/ko/2013/12/06/law-of-demeter- explained/
  • 32. Spaghetti SQL class RemoteProcess ActiveRecord::Base def self.find_top_running_processes(limit = 5) find(:all, :conditions = state = 'Running', :order = percent_cpu desc, :limit = limit) end end Reusability?
  • 33. Spaghetti SQL class RemoteProcess ActiveRecord::Base scope :running, where(:state = 'Running') scope :system, where(:owner = ['root', 'mysql']) scope :sorted, order(percent_cpu desc) scope :top, lambda {|l| limit(l) } end RemoteProcess.running.sorted.top(5) RemoteProcess.running.system.sorted.top(5) Reusability!
  • 34. Spaghetti SQL class RemoteProcess ActiveRecord::Base scope :running, where(:state = 'Running') scope :system, where(:owner = ['root', 'mysql']) scope :sorted, order(percent_cpu desc) scope :top, lambda {|l| limit(l) } # Shortcut def self.find_top_running_processes(limit = 5) running.sorted.top(limit) end end
  • 35. Scope vs Class method • Almost same, but scopes are always chainable class Post ActiveRecord::Base def self.status(status) where(status: status) if status.present? end def self.recent limit(10) end end Post.status('active').recent Post.status('').recent Post.status(nil).recent nil
  • 36. Scope vs Class method • Almost same, but scopes are always chainable class Post ActiveRecord::Base scope :status, - status { where(status: status) if status.present? } scope :recent, limit(10) end Post.status('active').recent Post.status('').recent Post.status(nil).recent just ignored
  • 37. Spaghetti SQL • Further reading • http://blog.plataformatec.com.br/2013/02/ active-record-scopes-vs-class-methods/
  • 38. Fat Model • Use extend, include module • example: too many scope, finder, etc.
  • 39. Fat Model • ledermann/unread module Unread module Readable module Scopes def join_read_marks(user) # ... end def unread_by(user) # ... end # ... end end end class SomeReadable ActiveRecord::Base # ... extend Unread::Readable::Scopes end
  • 40. Fat Model • Do you prefer composition to inheritance?
  • 41. Fat Model • Further Reading • http://blog.codeclimate.com/blog/ 2012/10/17/7-ways-to-decompose-fat-activerecord- models/
  • 42. Duplicate Code Duplication • Basic of refactoring • Extract into modules • included, extended • using metaprogramming
  • 43. Extract into modules class Car ActiveRecord::Base validates :direction, :presence = true validates :speed, :presence = true def turn(new_direction) self.direction = new_direction end def brake self.speed = 0 end def accelerate self.speed = [speed + 10, 100].min end # Other, car-related activities... end class Bicycle ActiveRecord::Base validates :direction, :presence = true validates :speed, :presence = true def turn(new_direction) self.direction = new_direction end def brake self.speed = 0 end def accelerate self.speed = [speed + 1, 20].min end end
  • 44. Extract into modules module Drivable extend ActiveSupport::Concern included do validates :direction, :presence = true validates :speed, :presence = true end def turn(new_direction) self.direction = new_direction end def brake self.speed = 0 end def accelerate self.speed = [speed + acceleration, top_speed].min end end
  • 45. Write a your gem! (plugin) ex) https://github.com/FeGs/read_activity module Drivable extend ActiveSupport::Concern included do validates :direction, :presence = true validates :speed, :presence = true end def turn(new_direction) self.direction = new_direction end def brake self.speed = 0 end def accelerate self.speed = [speed + acceleration, top_speed].min end end ‘drivable’ gem
  • 46. Write a your gem! (plugin) module DrivableGem def self.included(base) base.extend(Module) end module Module def act_as_drivable include Drivable end end end ActiveRecord::Base.send(:include, DrivableGem)
  • 47. Write a your gem! (plugin) class Car ActiveRecord::Base act_as_drivable end
  • 48. How about Metaprogramming? class Purchase ActiveRecord::Base validates :status, presence: true, inclusion: { in: %w(in_progress submitted ...) } # Status Finders scope :all_in_progress, where(status: in_progress) # ... # Status def in_progress? status == in_progress end # ... end
  • 49. How about Metaprogramming? class Purchase ActiveRecord::Base STATUSES = %w(in_progress submitted ...) validates :status, presence: true, inclusion: { in: STATUSES } STATUSES.each do |status_name| scope all_#{status_name}, where(status: status_name) define_method #{status_name}? do status == status_name end end end How to improve reusability?
  • 50. How about Metaprogramming? class ActiveRecord::Base def self.has_statuses(*status_names) validates :status, presence: true, inclusion: { in: status_names } status_names.each do |status_name| scope all_#{status_name}, where(status: status_name) define_method #{status_name}? do status == status_name end end end end Use extension! class Purchase ActiveRecord::Base has_statuses :in_progress, :submitted, # ... end
  • 51. Fixture Blues • Rails fixture has many problems: • No validation • Not following model lifecycle • No context • …
  • 52. Make Use of Factories • Rails fixture has many problems: • No validation • Not following model lifecycle • No context • …
  • 53. Make Use of Factories module Factory class self def create_published_post post = Post.create!({ body: lorem ipsum, title: published post title, published: true }) end def create_unpublished_post # ... end end end
  • 54. Make Use of Factories: FactoryGirl Factory.sequence :title do |n| Title #{n} end Factory.define :post do |post| post.body lorem ipsum post.title { Factory.next(:title) } post.association :author, :factory = :user post.published true end Factory(:post) Factory(:post, :published = false)
  • 55. Make Use of Factories • Rails fixture has many problems: • No validation • Not following model lifecycle • No context • …
  • 56. Refactor into Contexts context A dog do setup do @dog = Dog.new end should bark when sent #talk do assert_equal bark, @dog.talk end context with fleas do setup do @dog.fleas Flea.new @dog.fleas Flea.new end should scratch when idle do @dog.idle! assert @dog.scratching? end
  • 57. Refactor into Contexts: rspec • context is alias of describe describe #bark do before(:each) do @dog = Dog.new end context sick dog do before(:each) do @dog.status = :sick end # ... end end
  • 58. Messy Migrations • You should ensure that your migrations never irreconcilably messy. • Never Modify the up Method on a Committed Migration : obviously • Always Provide a down Method in Migrations
  • 59. Never Use External Code in a Migration class AddJobsCountToUser ActiveRecord::Migration def self.up add_column :users, :jobs_count, :integer, :default = 0 Users.all.each do |user| user.jobs_count = user.jobs.size user.save end end If No User, No Job? def self.down remove_column :users, :jobs_count end end
  • 60. Never Use External Code in a Migration class AddJobsCountToUser ActiveRecord::Migration def self.up add_column :users, :jobs_count, :integer, :default = 0 update(-SQL) UPDATE users SET jobs_count = ( SELECT count(*) FROM jobs WHERE jobs.user_id = users.id ) SQL end def self.down remove_column :users, :jobs_count end end No dependancy
  • 61. Never Use External Code in a Migration class AddJobsCountToUser ActiveRecord::Migration class Job ActiveRecord::Base end class User ActiveRecord::Base has_many :jobs end def self.up add_column :users, :jobs_count, :integer, :default = 0 User.reset_column_information Users.all.each do |user| Provide definition internally Alternative to raw SQL user.jobs_count = user.jobs.size user.save end end # ... end
  • 62. Never Use External Code in a Migration • Further Reading • http://railsguides.net/change-data-in-migrations- like-a-boss/ • https://github.com/ajvargo/data-migrate • https://github.com/ka8725/migration_data
  • 63. EOF