Please stop throwing blank? at everything.
There’s a very cool principle I’d like to introduce you to: ‘Code as documentation’.
If you have any favourite programming blogs, you’ll probably have read enough decent code snippets to have figured this out yourself. In a nutshell: I should know what your code does by reading through it.
I’ve found that many of the code snippets I’ve seen toted as examples of ‘good style’ read very easily.
Good code should convey as much information as possible while putting as little strain on the reader as possible. Easy reading is damn hard writing.
In the end, it comes down to simplicity and effective communication.
ActiveSupport added the #blank? method to various core classes to simplify your life. Let’s see what #blank? can do for you:
some_array = nil some_array.blank? #=> true some_array = [] some_array.blank? #=> true some_array = [nil] some_array.blank? #=> false
Pretty cool! You say. If an array/hash/string is nil or empty ([], {}, ” “, respectively) we can test for both cases with #blank?.
I like #blank? because it allows us to get closer to the principle of Code as Documentation. No more cumbersome members.nil? or members == ""!
However, it’s easy to fall into the trap of writing members.blank? instead of members.nil?. Why do I call this a trap?
Let me explain. When reading through source code when jumping on board with an existing project, you’re building connections between the various parts of the system. Now read the following and tell me which is easier:
def method_one(param_1)
param_1.map do |e|
e.some_calculation
end unless param_1.blank?
end
Let’s look at that method. We receive a parameter (param_1). We then call #map on it, meaning it is enumerable. Since only one block argument is bound, we assume it’s not a Hash and we finally conclude that it’s probably an Array.
So method_one maps over some array we pass to it, collecting some_calculation and returning that unless the array we pass it is blank?.
WOAH! So that method means:
def method_one(param_1)
return nil if param_1.nil? or param_1.empty?
param_1.map do |e|
e.some_calculation
end
end
The first thing that bugs me about this method is that as I’m still reading through the codebase, I don’t know all the places method_one gets called. I have to assume that the author meant ‘sometimes I want to pass nil to method_one and sometimes I’ll pass []. OK, in my mental picture of the codebase and how everything fits together, I remember that method_one sometimes takes nil arguments and other times gets passed [] as argument.
This is all fine if the author really meant that method_one should accept both types of argument (Array and Nil) and if param_1 is either [] or nil we should return specifically nil.
However! Far too often these criteria don’t hold:
- It turns out we never pass nil to
method_one. This results in the following simplification:
def method_one_1(param_1)
return nil if param_1.empty?
param_1.map do |e|
e.some_calculation
end
end
(Besides, we should being returning false here or :failed to convey even more information to both the calling method and the reader!)
- And it’s even more rare to return
nilwhen the caller expects the method to map over an array! Returning[]is probably perfectly OK from the caller’s perspective:
def method_one_1(param_1)
param_1.map do |e|
e.some_calculation
end
end
With two reasonable assumptions that I’ve found hold 90% of the time when working with #blank? we’ve massively simplified the reader’s life.
In summary:
if some_var.empty? do_something! else do_something_else! end
Tells me that if some_var is [] or {} when the program flows past this point, handle that fact, otherwise proceed as planned. From the context it should be apparent whether some_var is an Array or a Hash. As such, someone reading the code immediately knows that “ok, I know some_var is a list of Some objects” and proceed with that assumptions, reading the rest of the method.
if some_var.blank? do_something! else do_something_else! end
Says that at this point, some_var is one of nil, [], {}, " " and if that’s the case we need to do_something! otherwise we can proceed as planned… We still don’t have a clue what some_var is and every time it gets used in the rest of the method we need to try to figure out what type of object it’s supposed to be.
It should seem obvious that using #nil? or #empty? says a lot more in the same amount of calls as #blank? does.
So please, when you mean #nil?, use it! When you mean #empty? use that! And if you mean some_var.nil? or some_var.empty? or some_var.nil? or some_var == "" use #blank?!!! :)
Monitoring delayed_job with bluepill
Bluepill is a pure-ruby process monitoring library similar to god/monit. Unlike god, bluepill doesn’t leak memory (according to the bluepill authors anyway :] .)
To set up delayed_job, please have a look at this asciicast (note that you should use the collectiveidea-delayed_job fork for this!)
Once you’ve got your delayed_job set up you’ll want to ensure your workers don’t die.
Now to configure bluepill
Follow these steps:
$ sudo gem sources -a http://gemcutter.org $ sudo gem install bluepill $ sudo mkdir /var/bluepill # The /var/bluepill directory is for bluepill to store its PID files in # For more details like configuring logging, see the installation section on github: http://github.com/arya/bluepill
Now that you have bluepill installed, we need to configure it for our app.
# A sample configuration which we can store in the rails ./config directory as
# RAILS_ROOT/config/bluepill.pill:
Bluepill.application("my_complex_app") do |app|
app.process("delayed_job") do |process|
process.start_command = "/path/to/rails_root/script/delayed_job -e production start"
process.stop_command = "/path/to/rails_root/script/delayed_job -e production stop"
process.pid_file = "/path/to/rails_root/tmp/pids/delayed_job.pid"
process.uid = "my_complex_app_user"
process.gid = "my_complex_app_group"
process.checks :cpu_usage, :every => 10.seconds, :below => 5, :times => 3
process.checks :mem_usage, :every => 10.seconds, :below => 100.megabytes, :times => [3,5]
end
end
To start monitoring, we execute:
$ sudo bluepill load /path/to/rails_root/config/bluepill.pill # Check it with $ sudo bluepill status delayed_job(pid:29710): up
Sweet!
For more on the command line interface to bluepill, check out the CLI section of the readme. I’ll let the respective readme’s tell you about the more detailed config options available.
Interesting Questions asked on Stack Overflow Part 1
Here are some questions I saw on Stack Overflow today that bears repeating.
First Question: I have a long-running task in my controller. It locks up the entire site for 5 minutes while it crunches some numbers.
This is a shockingly important question that everyone writing non-trivial applications should know: pass the task to an external process and immediately respond to the user saying “We’re processing that in the background! Feel free to continue with whatever you were doing…”
A brilliant candidate for managing background tasks is delayed_job. See the Readme and the delayed_job Railscast for more information on using delayed_job to run your long-running tasks in the background.
Second Question: I have a date_select control in my form, how do I get the date from the params hash without assigning it to an ActiveRecord model?
Step 1: replace date_select with select_date! The reason is that date_select uses an unfriendly naming convention for the year/month/day parameters, eg.
# if you do this in your view:
<%= date_select('range', 'start_date')%>
# you need to do this in your controller
@start_date = Date.civil(params[:range][:"start_date(1i)"].to_i,params[:range][:"start_date(2i)"].to_i,params[:range][:"start_date(3i)"].to_i)
# however, if you do this in your view:
<%= select_date :prefix => "range" %>
# you can do this in your controller
@start_date = Date.civil(params[:range][:year], params[:range][:month], params[:range][:day])
# Or even better, IMHO
@start_date = Date.civil *params[:range].slice(%w[year month day])
That’s how I’d do it. :)
That’s it for now! The next post is on monitoring delayed_job with bluepill.
Cool books and what I'm up to
In which I let you know about some cool books and one technology I’ve been busy with that are well worth checking out.
I read a wonderful book recently: Shantaram. Check it out, it’s definitely worth it!
The second book I recommend you read (or study!) is called Ideas: A History from Fire to Freud. I’ve never encountered a book with such a high signal to noise ratio! It feels like pretty much every page lists a couple of ideas that completely changes the way I think about the history of many of our most fundamental ideas. This is a really interesting book.
The past three months I’ve had the pleasure of coding with Tom Locke (author of Hobo) on a very cool project of his. I’m learning a lot: half of what we’ve done is research cool ideas and the other half is hacking the good ones together in clever ways! :)
I’m writing the first of my final exams on Tuesday! Consequently, the past few weeks have been spent doing a lot more studying than hacking, but in a month’s time my exams will be over and the balance will be restored. Alongside my university studies I’ve started playing around with Scala again. Oddly, one of the things I like most about Scala is the fact that grok’ing its type system and other oddities is quite difficult. I find that this forces me to improve as programmer. I suspect that the better I understand Scala, the better programmer I’ll be.
I wanted to code a little app to help me get comfortable with coding in Scala so I’ve decided to build a little simulation of bots fighting each other. The plan is to design the class structure in a very modular fashion so I can plug in a complex graphical interface (read scala <-> CrystalSpace+blender, another fun project I’m looking forward to ;] ) or decision making model (sniper bots, assault bots, sneaky back-stabbing bots, etc.) in future.
A final ‘do check out’ would have to be LaTeX for generating documents containing mathematical equations. If you’re a teacher / student and you have to generate documents (eg. exam papers, assigment solutions, etc.) please do yourself a HUGE favour and check it out!
Useful things you should know about Mysql
I discovered a useful article by Ian Gilfillan on my disk earlier today. After scanning it I realized that pretty much everything he said was important if you’re doing anything with Mysql.
Here’s a tip:
Index your tables
Appropriate indices drastically speed up your Mysql select queries. Adding an index to an existing table is made easy with Ruby on Rails migrations. First we generate the migration:
$ ./script/generate migration add_indices_to_the_posts_table
exists db/migrate
create db/migrate/20090717134840_add_indices_table_posts.rb
Next we add our code:
class AddIndicesTablePosts < ActiveRecord::Migration
def self.up
add_index :posts, :member_id
add_index :posts, :permalink
end
def self.down
remove_index :posts, :member_id
remove_index :posts, :permalink
end
end
Indices are useful whenever SELECT queries contain an ORDER BY or WHERE clause. This means that if nothing else, indexing your foreign keys is very important (eg. posts.member_id above.)
Everyone loves a human-readable URL so direct links to our posts are of the form http://mydomain.com/posts/hello-world instead of the Rails-default http://mydomain.com/posts/1. As such we’ll be using Post.find_by_permalink(params[:permalink]) quite a lot so we added another index to the permalink field.
Mysql EXPLAIN
Sure this makes sense but this kind of reasoning will only take you so far. If you suspect a slow query is the reason for a bottleneck in your application you should use the Mysql EXPLAIN keyword to find out exactly how Mysql is going about finding your records.
I’ve adapted the ‘query-analyzer rails plugin’ by Bob Silva to make this really easy. First install the explain-query gem by issuing the following commands:
$ gem sources -a http://gems.github.com/ $ gem install g00k-explain-query
Then start up your Rails console session and start EXPLAINing your queries:
$ ./script/console
Loading development environment (Rails 2.3.2)
>> require 'explain-query'
>> Post.explain do
>> Post.find_by_permalink("first-post")
>> end
SELECT * FROM `posts` WHERE (`posts`.`permalink` = 'first-post') LIMIT 1
select_type | key_len | table | id | possible_keys | type | Extra | rows | ref | key
------------------------------------------------------------------------------------------
SIMPLE | | posts | 1 | | ALL | Using where | 6 | |
As you can see my posts table isn’t indexed yet so Mysql checks ‘ALL’ reocrds in the posts table. After running the migration posted above, this is how the same query looks:
>> Post.explain do
?> Post.find_by_permalink("first-post")
>> end
SELECT * FROM `posts` WHERE (`posts`.`permalink` = 'first-post') LIMIT 1
select_type | key_len | table | id | possible_keys | type | Extra | rows | ref | key
----------------------------------------------------------------------------------------------------------------------------
SIMPLE | 768 | posts | 1 | index_posts_on_permalink | ref | Using where | 1 | const | index_posts_on_permalink
That’s much better! The index on our posts table’s permalink field allows Mysql to jump straight to the row we’re curious about.
There’s a lot to know about indexing your tables and using those indices effectively. You should definitely check out the following article by Ian Gilfillan as well as the 2 follow-up articles for more on optimizing Mysql: Optimizing Mysql: Queries and Indexes.
Check out PhilosophersNotes.com!
The past year I’ve been working with Brian Johsnon creating PhilosophersNotes.com.
Do check it out! We have some very cool features lined up for the near future so be sure to drop in every once in a while. I’ll also be blogging about them here!