In episode #10 of the Giant Robots Smashing into other Giant Robots podcast, Ben Orenstein is joined by Joe Ferris and Mike Burns.
They start off with some recommendations for awesome programming books and then dive right in to questions about not following “Tell don’t ask” in the view, how MVC and the Single Responsibility Principle may be at odds with “Tell don’t ask” in the view, and what a more object oriented approach may look like. They also discuss “Class-oriented programming”, what it is, why it is bad, how Rails does it, and how to avoid it. They take a quick trip through Mike’s experiments in Ruby and Smalltalk in creating his own programming language. The three codecateers then take on the really important topic of method order and code organization, and finally they reflect on how their code has changed over the years, how no solution is foolproof, and how to move to the next level as a programmer. These topics and more, in this installment of the GIANT ROBOTS SMASHING INTO OTHER GIANT ROBOTS podcast!
Email your questions to info@thoughtbot.com or tweet to us @thoughtbot.
Follow @thoughtbot, @r00k, @joeferris, @mikeburns on twitter.
And now, a friendly message from your local Tell, don’t ask Department.
Not so good:
<% if current_user.admin? %>
<%= current_user.admin_welcome_message %>
<% else %>
<%= current_user.user_welcome_message %>
<% end %>
Better:
<%= current_user.welcome_message %>
Not so good:
def check_for_overheating(system_monitor)
if system_monitor.temperature > 100
system_monitor.sound_alarms
end
end
Better:
system_monitor.check_for_overheating
class SystemMonitor
def check_for_overheating
if temperature > 100
sound_alarms
end
end
end
Not so good:
class Post
def send_to_feed
if user.is_a?(TwitterUser)
user.send_to_feed(contents)
end
end
end
Better:
class Post
def send_to_feed
user.send_to_feed(contents)
end
end
class TwitterUser
def send_to_feed(contents)
twitter_client.post_to_feed(contents)
end
end
class EmailUser
def send_to_feed(contents)
# no-op.
end
end
Not so good:
def street_name(user)
if user.address
user.address.street_name
else
'No street name on file'
end
end
Better:
def street_name(user)
user.address.street_name
end
class User
def address
@address || NullAddress.new
end
end
class NullAddress
def street_name
'No street name on file'
end
end
Good OOP is about telling objects what you want done, not querying an object and acting on its behalf. Data and operations that depend on that data belong in the same object.
Tell, don’t ask!
Detect emerging problems in your codebase with Ruby Science. We’ll deliver solutions for fixing them, and demonstrate techniques for building a Ruby on Rails application that will be fun to work on for years to come.
Over the past few months of slinging Ruby here at Thoughtbot, I’ve picked up quite a few stupid ruby tricks smart ruby techniques that really help out your code. If you’ve got your own, feel free to leave a comment.
def touch_down
yield [3, 7]
puts "touchdown!"
end
touch_down do |(first_down, second_down)|
puts "#{first_down} yards on the run"
puts "#{second_down} yards passed"
end
=> "3 yards on the run"
=> "7 yards passed"
=> "touchdown!"
At first glance, this barely looks like valid Ruby. But somehow, it just makes sense: it splits up the array. If you’re going to pull out the values of the array inside of the block, why not just do it when you’re defining the block-level variables? This doesn’t seem to work nicely (in 1.8.7 at least) for Hashes, though.
>> args = [1, 2, 3]
>> first, *rest = args
>> first
=> 1
>> rest
=> [2, 3]
I knew about splitting up arrays before into individual arguments, but I didn’t know that you could easily get an array of the rest. Perhaps this is Lisp inspired?
>> items = { :apples => 2, :oranges => 3 }
=> items = {:apples=>2, :oranges=>3}
>> items.fetch(:apples)
=> 2
>> items.fetch(:bananas) { |key| "We don't carry #{key}!"}
=> We don't carry bananas!
This is just a nice little way to provide some default behavior that might be nicer than checking if the value exists in the hash first.
>> smash = Hash.new { |hash, key| hash[key] = "a #{key} just got SMASHED!" }
=> {}
>> smash[:plum] = "cannot smash."
=> {:plum=>"cannot smash."}
>> smash[:watermelon]
=> {:plum=>"cannot smash.", :watermelon=>"a watermelon just got SMASHED!"}
This is a really neat way to cache unknown values for Hashes (read: memoization!) I also heard it’s awesome for implementing a Fibonacci sequence.
>> cars = %w[beetle volt camry]
=> ["beetle", "volt", "camry"]
>> cars.sort_by { |car| car.size }
=> ["volt", "camry", "beetle"]
So, Array#sort_by sorts based on the return value of the block. It’s like a built in #map and #sort that rules even more with some Symbol#to_proc magic.
>> "brain".present?
=> true
>> "".present?
=> false
I’m sure most Rails developers know about blank? from ActiveSupport, but what about present?. Yeah, it blew my mind too. I like being as positive as possible in conditionals, so toss out those !something.blank? calls today and start using this.
I’ve always had a hard time separating work and home. The problem with being paid to think is that you don’t need your tools in front of you to keep working; you have your brain, you’re good to go. The problem comes in where you can’t stop thinking about a particular difficulty in your job.
I attended Mike’s 30th birthday party on Friday, but was completely unable to get a problem out of my head that had been there since the beginning of November or so (and before you ask, no I didn’t work it out Friday either). Unfortunately, the inability to stop thinking (which is one of my big personal faults), plus the fact that I didn’t know anyone else there, plus the fact that I’m a habitual pacer meant that for the first hour or so I was just walking around a room of people I don’t know, not talking to anyone. I must have looked like a complete loon!
And to think, this could have been avoided if Java would have allowed multiple inheritance. But that’s a post for another time.