Refactor like a boss

1,142 views
1,076 views

Published on

A few techniques for everyday Ruby hacking

Touching on the following topics:

DRY Assignment
Ternary operator
Bang bang
Conditional assignment
Parallel assignment
Multiple return
Implied begin
Exception lists
Symbol to Proc
MapReduce
Regex captures
tap
sprintf
case equality
Splat Array
Splat args
blank?
present?
presence
truncate
try
in?
Delegation
delegate
Memoization
memoize
alias_method_chain
class_attribute
HashWithIndifferentAccess

Published in: Technology, Self Improvement
0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,142
On SlideShare
0
From Embeds
0
Number of Embeds
17
Actions
Shares
0
Downloads
1
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide
  • for clean, DRY, expressive code.\n\n
  • Code refactoring is the process of changing a computer program's source code without modifying its external functional behavior in order to improve some of the nonfunctional attributes of the software.\n\nIt can be as simple as renaming a method or a class or making cosmetic changes (e.g. adding whitespace, aligning indentation or breaking long lines of code). Though that’s a worthy topic, I am not going to focus on cosmetic style choices today. Instead I’m going to share some examples of simple recurring code patterns that are candidates for refactoring.\n\nDisclaimer: You may find that some of the following techniques convolute logic. If so, don’t use them. Work with your team to establish what is acceptable and develop your own style guide. Also, if performance is a concern, always profile.\n
  • The goal is to improve readability, not impede it.\n\nWe’re not compressing javascript files here.\n\nIn fact, refactoring may be removing unnecessarily complex implementations or use of design patterns.\n\n
  • Advantages include \nimproved code readability and reduced complexity to improve the maintainability of the source code,\nas well as a more expressive internal architecture or object model to improve extensibility.\n
  • \n
  • You’ll see that I have a title for each of these patterns. Some came from the internets, some from people I’ve worked with, and some I just made up.\n
  • \n
  • \n
  • \n
  • \n
  • Forces a boolean return value. True unless @foo is nil or false.\n\nTwo logical NOT operators\n
  • Using the ternary operator\n
  • Forces a boolean return value. True unless @foo is nil or false.\n\nTwo logical NOT operators\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • http://blog.hasmanythrough.com/2006/3/7/symbol-to-proc-shorthand\n
  • This may have a negative effect on performance!\n\nhttp://blog.hasmanythrough.com/2006/3/7/symbol-to-proc-shorthand\n
  • \n
  • reduce AKA inject\n
  • Significance == (key, value)\n
  • \n
  • \n
  • \n
  • \n
  • tap always returns the object it’s called on, even if the block returns some other result\n\n
  • Uses the String as a format specification, and returns the result of applying it to the Array\n
  • uses the === operator\n
  • note the two conditions separated by a comma\n
  • \n
  • \n
  • \n
  • This will flatten ingredients to one dimension if it’s a multidimensional Array!\n
  • \n
  • Note that we can now call shopping_list with multiple arguments and they will be bunched into an Array\n
  • \n
  • \n
  • \n
  • \n
  • Ruby allows you to define any character you want as a string delimiter simply by prefixing the desired character with a %\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Chaining try()s is a code smell.\n\nDon’t overuse this.\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • first, second, third, forth, fifth and forty_two\n
  • \n
  • See also #extract\n
  • \n
  • Refactor like a boss

    1. 1. Refactor Like A Bossa few techniques for everyday Ruby hacking
    2. 2. RefactoringThe process of changing code without modifying behavior
    3. 3. Ungoals of refactoring• Brevity for the sake of brevity• To demonstrate mastery of Ruby or design patterns
    4. 4. Goals of refactoring• Improve readability• Improve maintainability• Improve extensibility• Promote an expressive API• Reduce complexity
    5. 5. Ruby freebies
    6. 6. DRY Assignmentif 1 > 0 @foo = barelse @foo = bazend@foo # => bar
    7. 7. DRY Assignment@foo = if 1 > 0 bar else baz end@foo # => bar
    8. 8. DRY Assignment@foo = case 1 when 0..1 bar else baz end@foo # => bar
    9. 9. Ternary operator@foo = 1 > 0 ? bar : qux@foo # => bar
    10. 10. Ternary operatordef postive?(number) number > 0 ? yes : noendpositive?(100) # => yes
    11. 11. Bang bangdef foo? if @foo true else false endend
    12. 12. Bang bangdef foo? @foo ? true : falseend
    13. 13. Bang bangdef foo? !!@fooend
    14. 14. Conditional assignmentif not @foo @foo = barend
    15. 15. Conditional assignmentunless @foo @foo = barend
    16. 16. Conditional assignment@foo = bar unless @foo
    17. 17. Conditional assignment@foo ||= bar
    18. 18. Parallel assignment@foo = baz@bar = qux# => "baz"# => "qux"
    19. 19. Parallel assignment@foo, @bar = baz, qux# => ["baz", "qux"]@foo # => "baz"
    20. 20. Multiple returndef get_with_benchmark(uri) res = nil bench = Benchmark.measure do res = Net::HTTP.get_response(uri) end return res, bench.realend@response, @benchmark = get_with_benchmark(@uri)# => [#<Net::HTTPOK 200 OK>, 0.123]
    21. 21. Implied begindef my_safe_method begin do_something_dangerous() true rescue false endend
    22. 22. Implied begindef my_safe_method do_something_dangerous() truerescue falseend
    23. 23. Exception listsdef self.likelihood_of_rain Hpricot::XML(weather_xml)/probability-of-rainrescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError return falseenddef self.likelihood_of_snow Hpricot::XML(weather_xml)/probability-of-snowrescue Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError return falseend
    24. 24. Exception listsNET_EXCEPTIONS = [ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError ]def self.likelihood_of_rain Hpricot::XML(weather_xml)/probability-of-rainrescue NET_EXCEPTIONS return falseenddef self.likelihood_of_snow Hpricot::XML(weather_xml)/probability-of-snowrescue NET_EXCEPTIONS return falseend
    25. 25. Symbol to Proc(1..5).map{|number| number.to_s }# => ["1", "2", "3", "4", "5"]
    26. 26. Symbol to Proc(1..5).map(&:to_s)# => ["1", "2", "3", "4", "5"]
    27. 27. MapReducedef fibonacci_sum sum = 0 [1,1,2,3,5,8,13].each{|int| sum += int } sumendfibonacci_sum() # => 33
    28. 28. MapReducedef fibonacci_sum [1,1,2,3,5,8,13].reduce(0){|sum, int| sum + int }endfibonacci_sum() # => 33
    29. 29. MapReduce{:foo => bar}.inject({}) do |memo, (key, value)| memo[value] = key memoend# => {"bar" => :foo}
    30. 30. Regex capturesmatch_data = my lil string.match(/my (w+) (w+)/)match_data.captures[0] # => "lil"match_data.captures[1] # => "string"match_data.captures[2] # => nil
    31. 31. Regex capturesmy lil string.match(/my (w+) (w+)/)$1 # => "lil"$2 # => "string"$3 # => nil
    32. 32. Regex capturesmy lil string =~ /my (w+) (w+)/$1 # => "lil"$2 # => "string"$3 # => nil
    33. 33. tapdef Resource.create resource = Resource.new resource.save resourceend# => #<Resource:0xffffff>
    34. 34. tapdef Resource.create Resource.new.tap{|resource| resource.save }end# => #<Resource:0xffffff>
    35. 35. sprintfsprintf("%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"
    36. 36. case equalitydef cerealize(val) if val.is_a?(Numeric) || val.is_a?(String) val elsif val.is_a?(Enumerable) val.to_json else val.to_s endend
    37. 37. case equalitydef cerealize(val) case val when Numeric, String val when Enumerable val.to_json else val.to_s endend
    38. 38. case equalityif command =~ /sudo/ raise Danger!elsif command =~ /^rm / puts Are you sure?else puts Run it.end
    39. 39. case equalitycase commandwhen /sudo/ raise Danger!when /^rm / puts Are you sure?else puts Run it.end
    40. 40. Splat Arraydef shopping_list(ingredients) unless ingredients.is_a?(Array) ingredients = [ingredients] end ingredients.join(", ")endshopping_list("eggs")# => "eggs"shopping_list(["eggs", "bacon"])# => "eggs, bacon"
    41. 41. Splat Arraydef shopping_list(ingredients) [ingredients].flatten.join(", ")endshopping_list("eggs")# => "eggs"shopping_list(["eggs", "bacon"])# => "eggs, bacon"
    42. 42. Splat Arraydef shopping_list(ingredients) [*ingredients].join(", ")endshopping_list("eggs")# => "eggs"shopping_list(["eggs", "bacon"])# => "eggs, bacon"
    43. 43. Splat argsdef shopping_list(*ingredients) ingredients.join(", ")endshopping_list("eggs")# => "eggs"shopping_list(["eggs", "bacon"])# => "eggs, bacon"shopping_list("eggs", "bacon")# => "eggs, bacon"
    44. 44. Rails freebies
    45. 45. blank?if @user.name and !@user.name.empty? puts @user.nameelse puts "no name"end
    46. 46. blank?if @user.name.blank? puts "no name"else puts @user.nameend
    47. 47. present?if @user.name.present? puts @user.nameelse puts "no name"end
    48. 48. presenceputs @user.name.presence || "no name"
    49. 49. truncateopening = "A long time ago in a galaxy far, far away"if opening.size > 20 opening[0..16] + "..."end# => "A long time ago i..."
    50. 50. truncateopening = "A long time ago in a galaxy far, far away"opening.truncate(20)# => "A long time ago i..."
    51. 51. truncateopening = "A long time ago in a galaxy far, far away"opening.truncate(20, :separator => )# => "A long time ago..."
    52. 52. try@existing = User.find_by_email(@new.email)@existing.destroy if @existing
    53. 53. tryUser.find_by_email(@new.email).try(:destroy)
    54. 54. in?if admin_roles.include? @user.role puts "Hi Admin!"end
    55. 55. in?if @user.role.in? admin_roles puts "Hi Admin!"end
    56. 56. Delegationclass User has_one :account def balance self.account.balance end def balance=(amount) self.account.balance=(amount) endend
    57. 57. Delegationclass User has_one :account delegate :balance, :balance=, :to => :accountend
    58. 58. Memoizationclass Avatar def file_size if @file_size return @file_size else result = some_expensive_calculation result += more_expensive_calculation @file_size = result end endend
    59. 59. Memoizationclass Avatar extend ActiveSupport::Memoizable def file_size result = some_expensive_calculation result += more_expensive_calculation end memoize :file_sizeend
    60. 60. alias_method_chainalias_method :translate_without_log, :translatedef translate_with_log(*args) result = translate_without_log(*args) Rails.logger.info result resultendalias_method :translate, :translate_with_log
    61. 61. alias_method_chaindef translate_with_log(*args) result = translate_without_log(*args) Rails.logger.info result resultendalias_method_chain :translate, :log
    62. 62. class_attributeclass Resource class < self def host=(name) @host = hame end def host @host end endend
    63. 63. class_attributeclass Resource class < self attr_accessor :host endend
    64. 64. class_attributeclass Resource class_attribute :hostend
    65. 65. Hash#symbolize_keysmy_hash = { foo => 123 }.symbolize_keysmy_hash[foo]# => nilmy_hash[:foo]# => 123
    66. 66. Hash#stringify_keysmy_hash = { :foo => 123 }.stringify_keysmy_hash[foo]# => 123my_hash[:foo]# => nil
    67. 67. HashWithIndifferentAccessmy_hash = { :foo => 123 }my_hash[foo]# => nilmy_hash[:foo]# => 123
    68. 68. HashWithIndifferentAccessmy_hash = { :foo => 123 }.with_indifferent_accessmy_hash[foo]# => 123my_hash[:foo]# => 123
    69. 69. forty_twomy_array = []my_array[41] = "the answer"my_array[41]# => "the answer"
    70. 70. forty_twomy_array = []my_array[41] = "the answer"my_array.forty_two# => "the answer"
    71. 71. slideshare gsterndale github gsterndale irc sternicus

    ×