Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Refactor Like A Boss: A Few Techniques for Ever...
Search
PromptWorks
February 21, 2014
Programming
0
2.3k
Refactor Like A Boss: A Few Techniques for Everyday Ruby Hacking
Presented by Greg Sterndale.
http://promptworks.com
PromptWorks
February 21, 2014
Tweet
Share
More Decks by PromptWorks
See All by PromptWorks
PromptWorks Talk Tuesdays: Yvonne Chen 1/24/17 "Agile-Retros"
promptworks
0
68
PromptWorks Talk Tuesdays: Ray Zane 1/17/17 "Elixir Is Cool"
promptworks
0
85
PromptWorks Talk Tuesdays: Ray Zane 9/13/16 "Elixir Processes"
promptworks
0
3.4k
PromptWorks Talk Tuesdays: Dustin Ingram 8/30/16 "What is an ADR?"
promptworks
1
120
PromptWorks Talk Tuesdays: Dustin Ingram 8/23/16 "Detecting Asteroids with Neural Networks in TensorFlow"
promptworks
1
120
Getting to know Arel: Active Record's nerdy little brother
promptworks
1
89
Writing DSLs with Parslet - NYC.rb
promptworks
0
2.4k
Straight Up Rspec: A Neat Ruby BDD Tool
promptworks
0
23
Double Trouble: Clarity on Test Doubles
promptworks
0
2.3k
Other Decks in Programming
See All in Programming
見えないメモリを観測する: PHP 8.4 `pg_result_memory_size()` とSQL結果のメモリ管理
kentaroutakeda
0
940
ASP.NET Core の OpenAPIサポート
h455h1
0
120
Scaling your build logic
antalmonori
1
100
DevinとCursorから学ぶAIエージェントメモリーの設計とMoatの考え方
itarutomy
0
150
盆栽転じて家具となる / Bonsai and Furnitures
aereal
0
1.9k
Оптимизируем производительность блока Казначейство
lamodatech
0
960
Amazon Nova Reelの可能性
hideg
0
200
AWS re:Invent 2024個人的まとめ
satoshi256kbyte
0
100
非ブラウザランタイムとWeb標準 / Non-Browser Runtimes and Web Standards
petamoriken
0
430
情報漏洩させないための設計
kubotak
5
1.3k
カンファレンス動画鑑賞会のススメ / Osaka.swift #1
hironytic
0
170
為你自己學 Python
eddie
0
520
Featured
See All Featured
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
113
50k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
365
25k
Statistics for Hackers
jakevdp
797
220k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
44
7k
jQuery: Nuts, Bolts and Bling
dougneiner
62
7.6k
StorybookのUI Testing Handbookを読んだ
zakiyama
28
5.4k
What’s in a name? Adding method to the madness
productmarketing
PRO
22
3.2k
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
28
4.5k
Build your cross-platform service in a week with App Engine
jlugia
229
18k
Speed Design
sergeychernyshev
25
740
Optimizing for Happiness
mojombo
376
70k
Product Roadmaps are Hard
iamctodd
PRO
50
11k
Transcript
Refactor Like A Boss a few techniques for everyday Ruby
hacking
Refactoring The process of changing code without modifying behavior
Ungoals of refactoring • Brevity for the sake of brevity
• To demonstrate mastery of Ruby or design patterns
Goals of refactoring • Improve readability • Improve maintainability •
Improve extensibility • Promote an expressive API • Reduce complexity
Ruby freebies
if 1 > 0 @foo = 'bar' else @foo =
'baz' end ! @foo # => 'bar' DRY Assignment
@foo = if 1 > 0 'bar' else 'baz' end
! @foo # => 'bar' DRY Assignment
@foo = case 1 when 0..1 'bar' else 'baz' end
! @foo # => 'bar' DRY Assignment
@foo = 1 > 0 ? 'bar' : 'qux' !
@foo # => 'bar' Ternary operator
def postive?(number) number > 0 ? 'yes' : 'no' end
! positive?(100) # => 'yes' Ternary operator
def foo? if @foo true else false end end Bang
bang
def foo? @foo ? true : false end Bang bang
def foo? !!@foo end Bang bang
if not @foo @foo = 'bar' end Conditional assignment
unless @foo @foo = 'bar' end Conditional assignment
@foo = 'bar' unless @foo Conditional assignment
@foo ||= 'bar' Conditional assignment
Parallel assignment @foo = 'baz' @bar = 'qux' # =>
"baz" # => "qux"
Parallel assignment @foo, @bar = 'baz', 'qux' # => ["baz",
"qux"] ! @foo # => "baz"
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) # => [#<Net::HTTPOK 200 OK>, 0.123]
def my_safe_method begin do_something_dangerous() true rescue false end end Implied
begin
def my_safe_method do_something_dangerous() true rescue false end Implied begin
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
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
(1..5).map{|number| number.to_s } # => ["1", "2", "3", "4", "5"]
Symbol to Proc
(1..5).map(&:to_s) # => ["1", "2", "3", "4", "5"] Symbol to
Proc
def fibonacci_sum sum = 0 [1,1,2,3,5,8,13].each{|int| sum += int }
sum end ! fibonacci_sum() # => 33 MapReduce
def fibonacci_sum [1,1,2,3,5,8,13].reduce(0){|sum, int| sum + int } end !
fibonacci_sum() # => 33 MapReduce
{:foo => 'bar'}.inject({}) do |memo, (key, value)| memo[value] = key
memo end ! # => {"bar" => :foo} MapReduce
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
'my lil string'.match(/my (\w+) (\w+)/) $1 # => "lil" $2
# => "string" $3 # => nil Regex captures
'my lil string' =~ /my (\w+) (\w+)/ $1 # =>
"lil" $2 # => "string" $3 # => nil Regex captures
def Resource.create resource = Resource.new resource.save resource end # =>
#<Resource:0xffffff> tap
def Resource.create Resource.new.tap{|resource| resource.save } end # => #<Resource:0xffffff> tap
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
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
def cerealize(val) case val when Numeric, String val when Enumerable
val.to_json else val.to_s end end case equality
if command =~ /sudo/ raise 'Danger!' elsif command =~ /^rm
/ puts 'Are you sure?' else puts 'Run it.' end case equality
case command when /sudo/ raise 'Danger!' when /^rm / puts
'Are you sure?' else puts 'Run it.' end case equality
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"
Splat Array def shopping_list(ingredients) [ingredients].flatten.join(", ") end ! shopping_list("eggs") #
=> "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon" ! !
Splat Array def shopping_list(ingredients) [*ingredients].join(", ") end ! shopping_list("eggs") #
=> "eggs" shopping_list(["eggs", "bacon"]) # => "eggs, bacon" ! !
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"
Rails freebies
if @user.name and
[email protected]
? puts @user.name else puts "no name"
end blank?
if @user.name.blank? puts "no name" else puts @user.name end blank?
if @user.name.present? puts @user.name else puts "no name" end present?
puts @user.name.presence || "no name" presence
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..."
truncate opening = "A long time ago in a galaxy
far, far away" opening.truncate(20) # => "A long time ago i..." !
truncate opening = "A long time ago in a galaxy
far, far away" opening.truncate(20, :separator => ' ') # => "A long time ago..." !
@existing = User.find_by_email(@new.email) @existing.destroy if @existing try
User.find_by_email(@new.email).try(:destroy) try
in? if admin_roles.include? @user.role puts "Hi Admin!" end
in? if @user.role.in? admin_roles puts "Hi Admin!" end
class User ! has_one :account ! def balance self.account.balance end
! def balance=(amount) self.account.balance=(amount) end ! end Delegation
class User ! has_one :account ! delegate :balance, :balance=, :to
=> :account ! end Delegation
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
class Avatar ! extend ActiveSupport::Memoizable ! def file_size result =
some_expensive_calculation result += more_expensive_calculation end memoize :file_size ! end Memoization
! 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
def translate_with_log(*args) result = translate_without_log(*args) Rails.logger.info result result end !
alias_method_chain :translate, :log alias_method_chain
class Resource class < self ! def host=(name) @host =
hame end def host @host end ! end end class_attribute
class Resource class < self ! attr_accessor :host ! end
end class_attribute
class Resource ! class_attribute :host ! end class_attribute
Hash#symbolize_keys my_hash = { 'foo' => 123 }.symbolize_keys my_hash['foo'] #
=> nil my_hash[:foo] # => 123
Hash#stringify_keys my_hash = { :foo => 123 }.stringify_keys my_hash['foo'] #
=> 123 my_hash[:foo] # => nil
HashWithIndifferentAccess my_hash = { :foo => 123 } my_hash['foo'] #
=> nil my_hash[:foo] # => 123
HashWithIndifferentAccess my_hash = { :foo => 123 }.with_indifferent_access my_hash['foo'] #
=> 123 my_hash[:foo] # => 123
forty_two my_array = [] my_array[41] = "the answer" ! my_array[41]
# => "the answer"
forty_two my_array = [] my_array[41] = "the answer" ! my_array.forty_two
# => "the answer"
slideshare github irc gsterndale gsterndale sternicus