Code Reuse in Ruby
by Sava Virtosu
Code reuse aims to save time and resources and reduce redundancy by taking advantage of assets that have already been created in some form within the software product development process.
1. Inheritance
class Car
def drive
p 'Drive'
end
def color
p 'Black'
end
end
class RedCar < Car
def color
p 'Red'
end
end
class YellowCar < Car
def color
p 'Yellow'
end
end
car = Car.new
car.color
redCar = RedCar.new
redCar.color
ApplicationController becomes a dumping ground of random methods that are needed by “a lot” of controllers - it’s effectively a global scope for code that doesn’t have an obvious place to go.
Framework (like rails) love Inheritance
developers don't :)
David Bryant Copeland
require 'rails_helper'
describe SomeModel do
# code
end
require 'some_module'
class DummyObject
include SomeModule
end
describe "SomeModule" do
# code
end
~ 7 sec
~ 0.2 sec
Framework overhead
2. Mixins
module BlackColor
def color
p "Black"
end
end
module RedColor
def color
p "Red"
end
end
module YellowColor
def color
p "Yellow"
end
end
class Car
include BlackColor
def drive
p "Drive"
end
end
class RedCar
include RedColor
def drive
p "Drive"
end
end
simpleCar = Car.new
simpleCar.color
simpleRedCar = RedCar.new
simpleRedCar.color
Simple Mixin
DCI (Data Context Interaction)
class Car
attr_reader :color_module
def initialize(color_module=:black)
@color_module = color_module
include_modules
end
def drive
p "Drive"
end
def include_modules
case @color_module
when :red
class << self
include RedColor
end
when :yellow
class << self
include YellowColor
end
when :black
class << self
include BlackColor
end
else
raise UnsupportedModule.new
end
end
end
Dynamic Mixin
blackCar = Car.new
blackCar.color
redCar = Car.new :red
redCar.color
Dynamic Mixin
class Car
def drive
p "Drive"
end
end
class ColorModuleMapping
class UnsupportedModule < StandardError; end
def self.include_color_in(color=:black, object)
case color
when :red
class << object
include RedColor
end
when :yellow
class << object
include YellowColor
end
when :black
class << object
include BlackColor
end
else
raise UnsupportedModule.new
end
end
end
Dynamic Module Mapping Mixin
car = Car.new
ColorModuleMapping.include_color_in car
car.color
car = Car.new
ColorModuleMapping.include_color_in :red, car
car.color
Dynamic Module Mapping Mixin
car = Car.new
ColorModuleMapping.include_color_in car unless car.respond_to? :color
car.color
This is cool, because ruby is cool ^_^
↓
3. Composition
“composition” == “call methods on a private object”
Code reuse results in dependency on the component being reused.
class Car
attr_reader :color_obj
def initialize(color_obj)
@color_obj = color_obj
end
def color
color_obj.get_color
end
def drive
p "Drive"
end
end
class CarColor
attr_reader :color
def initialize(color=:black)
@color = color
end
def get_color
case @color
when :red
p 'Red'
when :yellow
p 'Yellow'
when :black
p 'Black'
end
end
end
carColor = CarColor.new
car = Car.new carColor
car.color
carColor = CarColor.new :red
car = Car.new carColor
car.color
Dependency Injection! (DHH will kill you XD)
class Car
attr_reader :color_obj
def build(color)
@color_obj = CarColor.new color
end
def initialize(color=:black)
build(color)
end
def color
color_obj.get_color
end
def drive
p "Drive"
end
end
blackCar = Car.new
blackCar.color
redCar = Car.new :red
redCar.color
Conclusion: Hard to test
The Memory!

require 'memory_profiler'
report = MemoryProfiler.report do
# run your code here
end
report.pretty_print

class Car
def drive
p 'Drive'
end
def color
p 'Black'
end
end
class RedCar < Car
def color
p 'Red'
end
end
class YellowCar < Car
def color
p 'Yellow'
end
end
car = Car.new
car.color
redCar = RedCar.new
redCar.color
class Car
attr_reader :color_obj
def initialize(color_obj)
@color_obj = color_obj
end
def color
color_obj.get_color
end
def drive
p "Drive"
end
end
class CarColor
attr_reader :color
def initialize(color=:black)
@color = color
end
def get_color
case @color
when :red
p 'Red'
when :yellow
p 'Yellow'
when :black
p 'Black'
end
end
end

class Car
include BlackColor
def drive
p "Drive"
end
end
class RedCar
include RedColor
def drive
p "Drive"
end
end
simpleCar = Car.new
simpleCar.color
simpleRedCar = RedCar.new
simpleRedCar.color
module BlackColor
def color
p "Black"
end
end
module RedColor
def color
p "Red"
end
end
module YellowColor
def color
p "Yellow"
end
end

class Car
attr_reader :color_module
def initialize(color_module=:black)
@color_module = color_module
include_modules
end
def drive
p "Drive"
end
def include_modules
case @color_module
when :red
class << self
include RedColor
end
when :yellow
class << self
include YellowColor
end
when :black
class << self
include BlackColor
end
else
raise UnsupportedModule.new
end
end
end

class Car
def drive
p "Drive"
end
end
class ColorModuleMapping
class UnsupportedModule < StandardError; end
def self.include_color_in(color=:black, object)
case color
when :red
class << object
include RedColor
end
when :yellow
class << object
include YellowColor
end
when :black
class << object
include BlackColor
end
else
raise UnsupportedModule.new
end
end
end






4. Copy & Paste
"A little copying is better than a little dependency"
Rob Pike
"You can actually make your app harder to change by “drying up” code that really isn’t the same by design."
David Bryant Copeland
Conclusion
What to use?
- Will you use this functionaloty across the system - use Mixins
- Is this your first ruby/rails project - DON'T use Mixins, Use Inheritance :)
- Is your functionality a customization of an existing type - Use Inheritance
- Can’t figure out what to do? Use composition
- Don''t use Copy & Paste - it was a joke
Thanks
Code Reuse in Ruby
By Sava Virtosu
Code Reuse in Ruby
- 417