Slide 1

Slide 1 text

Refactor Like A Boss a few techniques for everyday Ruby hacking

Slide 2

Slide 2 text

Refactoring The process of changing code without modifying behavior

Slide 3

Slide 3 text

Ungoals of refactoring • Brevity for the sake of brevity • To demonstrate mastery of Ruby or design patterns

Slide 4

Slide 4 text

Goals of refactoring • Improve readability • Improve maintainability • Improve extensibility • Promote an expressive API • Reduce complexity

Slide 5

Slide 5 text

Ruby freebies

Slide 6

Slide 6 text

if 1 > 0 @foo = 'bar' else @foo = 'baz' end ! @foo # => 'bar' DRY Assignment

Slide 7

Slide 7 text

@foo = if 1 > 0 'bar' else 'baz' end ! @foo # => 'bar' DRY Assignment

Slide 8

Slide 8 text

@foo = case 1 when 0..1 'bar' else 'baz' end ! @foo # => 'bar' DRY Assignment

Slide 9

Slide 9 text

@foo = 1 > 0 ? 'bar' : 'qux' ! @foo # => 'bar' Ternary operator

Slide 10

Slide 10 text

def postive?(number) number > 0 ? 'yes' : 'no' end ! positive?(100) # => 'yes' Ternary operator

Slide 11

Slide 11 text

def foo? if @foo true else false end end Bang bang

Slide 12

Slide 12 text

def foo? @foo ? true : false end Bang bang

Slide 13

Slide 13 text

def foo? !!@foo end Bang bang

Slide 14

Slide 14 text

if not @foo @foo = 'bar' end Conditional assignment

Slide 15

Slide 15 text

unless @foo @foo = 'bar' end Conditional assignment

Slide 16

Slide 16 text

@foo = 'bar' unless @foo Conditional assignment

Slide 17

Slide 17 text

@foo ||= 'bar' Conditional assignment

Slide 18

Slide 18 text

Parallel assignment @foo = 'baz' @bar = 'qux' # => "baz" # => "qux"

Slide 19

Slide 19 text

Parallel assignment @foo, @bar = 'baz', 'qux' # => ["baz", "qux"] ! @foo # => "baz"

Slide 20

Slide 20 text

Multiple return def get_with_benchmark(uri) res = nil bench = Benchmark.measure do res = Net::HTTP.get_response(uri) end return res, bench.real end ! @response, @benchmark = get_with_benchmark(@uri) # => [#, 0.123]

Slide 21

Slide 21 text

def my_safe_method begin do_something_dangerous() true rescue false end end Implied begin

Slide 22

Slide 22 text

def my_safe_method do_something_dangerous() true rescue false end Implied begin

Slide 23

Slide 23 text

def self.likelihood_of_rain Hpricot::XML(weather_xml)/'probability-of-rain' rescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError return false end ! def self.likelihood_of_snow Hpricot::XML(weather_xml)/'probability-of-snow' rescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError return false end Exception lists

Slide 24

Slide 24 text

NET_EXCEPTIONS = [ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError ] ! def self.likelihood_of_rain Hpricot::XML(weather_xml)/'probability-of-rain' rescue NET_EXCEPTIONS return false end ! def self.likelihood_of_snow Hpricot::XML(weather_xml)/'probability-of-snow' rescue NET_EXCEPTIONS return false end Exception lists

Slide 25

Slide 25 text

(1..5).map{|number| number.to_s } # => ["1", "2", "3", "4", "5"] Symbol to Proc

Slide 26

Slide 26 text

(1..5).map(&:to_s) # => ["1", "2", "3", "4", "5"] Symbol to Proc

Slide 27

Slide 27 text

def fibonacci_sum sum = 0 [1,1,2,3,5,8,13].each{|int| sum += int } sum end ! fibonacci_sum() # => 33 MapReduce

Slide 28

Slide 28 text

def fibonacci_sum [1,1,2,3,5,8,13].reduce(0){|sum, int| sum + int } end ! fibonacci_sum() # => 33 MapReduce

Slide 29

Slide 29 text

{:foo => 'bar'}.inject({}) do |memo, (key, value)| memo[value] = key memo end ! # => {"bar" => :foo} MapReduce

Slide 30

Slide 30 text

match_data = 'my lil string'.match(/my (\w+) (\w+)/) match_data.captures[0] # => "lil" match_data.captures[1] # => "string" match_data.captures[2] # => nil Regex captures

Slide 31

Slide 31 text

'my lil string'.match(/my (\w+) (\w+)/) $1 # => "lil" $2 # => "string" $3 # => nil Regex captures

Slide 32

Slide 32 text

'my lil string' =~ /my (\w+) (\w+)/ $1 # => "lil" $2 # => "string" $3 # => nil Regex captures

Slide 33

Slide 33 text

def Resource.create resource = Resource.new resource.save resource end # => # tap

Slide 34

Slide 34 text

def Resource.create Resource.new.tap{|resource| resource.save } end # => # tap

Slide 35

Slide 35 text

sprintf("%d as hexadecimal: %04x", 123, 123) # => "123 as hexadecimal: 007b" ! "%d as hexadecimal: %04x" % [123, 123] # => "123 as hexadecimal: 007b" ! "%s string" % ['my'] # => "my string" sprintf

Slide 36

Slide 36 text

def cerealize(val) if val.is_a?(Numeric) || val.is_a?(String) val elsif val.is_a?(Enumerable) val.to_json else val.to_s end end case equality

Slide 37

Slide 37 text

def cerealize(val) case val when Numeric, String val when Enumerable val.to_json else val.to_s end end case equality

Slide 38

Slide 38 text

if command =~ /sudo/ raise 'Danger!' elsif command =~ /^rm / puts 'Are you sure?' else puts 'Run it.' end case equality

Slide 39

Slide 39 text

case command when /sudo/ raise 'Danger!' when /^rm / puts 'Are you sure?' else puts 'Run it.' end case equality

Slide 40

Slide 40 text

Splat Array def shopping_list(ingredients) unless ingredients.is_a?(Array) ingredients = [ingredients] end ingredients.join(", ") end ! shopping_list("eggs") # => "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon"

Slide 41

Slide 41 text

Splat Array def shopping_list(ingredients) [ingredients].flatten.join(", ") end ! shopping_list("eggs") # => "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon" ! !

Slide 42

Slide 42 text

Splat Array def shopping_list(ingredients) [*ingredients].join(", ") end ! shopping_list("eggs") # => "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon" ! !

Slide 43

Slide 43 text

Splat args def shopping_list(*ingredients) ingredients.join(", ") end ! shopping_list("eggs") # => "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon" ! shopping_list("eggs", "bacon") # => "eggs, bacon"

Slide 44

Slide 44 text

Rails freebies

Slide 45

Slide 45 text

if @user.name and !@user.name.empty? puts @user.name else puts "no name" end blank?

Slide 46

Slide 46 text

if @user.name.blank? puts "no name" else puts @user.name end blank?

Slide 47

Slide 47 text

if @user.name.present? puts @user.name else puts "no name" end present?

Slide 48

Slide 48 text

puts @user.name.presence || "no name" presence

Slide 49

Slide 49 text

truncate opening = "A long time ago in a galaxy far, far away" if opening.size > 20 opening[0..16] + "..." end # => "A long time ago i..."

Slide 50

Slide 50 text

truncate opening = "A long time ago in a galaxy far, far away" opening.truncate(20) # => "A long time ago i..." !

Slide 51

Slide 51 text

truncate opening = "A long time ago in a galaxy far, far away" opening.truncate(20, :separator => ' ') # => "A long time ago..." !

Slide 52

Slide 52 text

@existing = User.find_by_email(@new.email) @existing.destroy if @existing try

Slide 53

Slide 53 text

User.find_by_email(@new.email).try(:destroy) try

Slide 54

Slide 54 text

in? if admin_roles.include? @user.role puts "Hi Admin!" end

Slide 55

Slide 55 text

in? if @user.role.in? admin_roles puts "Hi Admin!" end

Slide 56

Slide 56 text

class User ! has_one :account ! def balance self.account.balance end ! def balance=(amount) self.account.balance=(amount) end ! end Delegation

Slide 57

Slide 57 text

class User ! has_one :account ! delegate :balance, :balance=, :to => :account ! end Delegation

Slide 58

Slide 58 text

class Avatar ! def file_size if @file_size return @file_size else result = some_expensive_calculation result += more_expensive_calculation @file_size = result end end ! end Memoization

Slide 59

Slide 59 text

class Avatar ! extend ActiveSupport::Memoizable ! def file_size result = some_expensive_calculation result += more_expensive_calculation end memoize :file_size ! end Memoization

Slide 60

Slide 60 text

! alias_method :translate_without_log, :translate ! def translate_with_log(*args) result = translate_without_log(*args) Rails.logger.info result result end ! alias_method :translate, :translate_with_log alias_method_chain

Slide 61

Slide 61 text

def translate_with_log(*args) result = translate_without_log(*args) Rails.logger.info result result end ! alias_method_chain :translate, :log alias_method_chain

Slide 62

Slide 62 text

class Resource class < self ! def host=(name) @host = hame end def host @host end ! end end class_attribute

Slide 63

Slide 63 text

class Resource class < self ! attr_accessor :host ! end end class_attribute

Slide 64

Slide 64 text

class Resource ! class_attribute :host ! end class_attribute

Slide 65

Slide 65 text

Hash#symbolize_keys my_hash = { 'foo' => 123 }.symbolize_keys my_hash['foo'] # => nil my_hash[:foo] # => 123

Slide 66

Slide 66 text

Hash#stringify_keys my_hash = { :foo => 123 }.stringify_keys my_hash['foo'] # => 123 my_hash[:foo] # => nil

Slide 67

Slide 67 text

HashWithIndifferentAccess my_hash = { :foo => 123 } my_hash['foo'] # => nil my_hash[:foo] # => 123

Slide 68

Slide 68 text

HashWithIndifferentAccess my_hash = { :foo => 123 }.with_indifferent_access my_hash['foo'] # => 123 my_hash[:foo] # => 123

Slide 69

Slide 69 text

forty_two my_array = [] my_array[41] = "the answer" ! my_array[41] # => "the answer"

Slide 70

Slide 70 text

forty_two my_array = [] my_array[41] = "the answer" ! my_array.forty_two # => "the answer"

Slide 71

Slide 71 text

slideshare github irc
 gsterndale gsterndale sternicus