Last active
December 26, 2015 16:09
-
-
Save jmondo/7177323 to your computer and use it in GitHub Desktop.
simple delegator example from sfrails oct 2013
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# it's all part of ruby! | |
require 'ostruct' | |
require 'delegate' | |
require 'forwardable' | |
require 'minitest/autorun' # can run rspec style tests with this, part of ruby | |
# --------------------------------------------------------------------------------------------------------- | |
# the classes | |
# using simple delegator with the country object /after/ this app has been made with bad dependency mgmt. | |
# we swap the country string 'FIN' out for a country object with Country.new('FIN') | |
# the parts of the app that expect a string still get a string and the parts that want a first-class object | |
# with its own methods and logic get what they want too. | |
class Account | |
extend Forwardable | |
attr_accessor :country, :name | |
def initialize(options) | |
@country = options[:country] | |
@name = options[:name] | |
end | |
def country | |
Country.new(@country) #=> 'FIN' | |
end | |
# works just like `delegate to:` in rails (with forwardable included) | |
def_delegators :country, :in_europe?, :in_united_states?, :country_name | |
end | |
class Country < SimpleDelegator | |
COUNTRIES = { | |
'FIN' => 'Finland' | |
} | |
def in_europe? | |
self == 'FIN' | |
end | |
def in_united_states? | |
self == 'USA' | |
end | |
def country_name | |
COUNTRIES[self] | |
end | |
end | |
class BillingInfo | |
attr_accessor :account | |
def initialize(options) | |
@account = options[:account] | |
end | |
# the super awesome OO app has tons of these | |
def gets_10_percent_off? | |
account.country == 'FIN' | |
end | |
end | |
# ----------------------------------------------------------------------- | |
# the tests (just run ruby simple_delegator_example.rb and they will run) | |
describe Account do | |
let(:account) { Account.new(name: 'Fincorp inc, intl.', country: 'FIN') } | |
it "has a country code" do | |
assert_equal 'FIN', account.country | |
end | |
it "is in europe" do | |
assert account.in_europe? | |
end | |
it "is finland" do | |
assert_equal 'Finland', account.country_name | |
end | |
end | |
describe BillingInfo do | |
let(:account) { Account.new(name: 'Finnish co, inc, intl', country: 'FIN') } | |
let(:billing_info) { BillingInfo.new(account: account)} | |
it "gives 10% off for being in finland" do | |
assert billing_info.gets_10_percent_off? | |
end | |
it "gives full price for USA customer" do | |
account.country = 'USA' | |
refute billing_info.gets_10_percent_off? | |
end | |
end | |
# -------------------------------------------------------------------------------------------------------- | |
# Can also be used to futureproof your code. Take basic objects like integers and wrap them in a Class.new | |
# so that down the line, you can add methods to the class. For now, they just act like their integer value. | |
class Money < SimpleDelegator | |
# add this method down the line | |
def in_dollars | |
self / 100.0 | |
end | |
end | |
price = Money.new(100_00) | |
p price.in_dollars #=> 100.00 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I really like the example of using this to improve a bad design and letting different parts of the app get a bare string or full object behavior. Makes it much easier and less risky to improve the code gradually.