Antonio Cangiano July 4th, 2007
- Ruby
and Rails
books keep popping up on the (virtual and real) shelves, which means that it may be slightly puzzling for newcomers to decide which books to spend their hard earned cash on. In the spirit of providing guidance in this process, I’ve prepared the Recommended Books for Ruby and Rails page. The recommendations are organized by skill level and should provide the reader with a nice logical sequence of increasingly more challenging reading material, making the task of identifying valid books easier for the new developer or student.
- The more I become acquainted with Textmate, the more I’m impressed by such a nice little editor. I’ve had next to no time to play with it, and yet I’ve already added new functionalities to some of the existing bundles. In particular, I’ve committed a patch that extends the Textile bundle features, and I’ve become the maintainer of that bundle, with SVN access. Textmate bundles are very easy to customize and extend, and this flexibility is really appreciated from a development standpoint.
ri is an indispensable tool for the Ruby programmer, however you may have noticed that it’s a bit sluggish. If you haven’t done it already, do yourself a big favor and install the FastRi gem. FastRi provides various enhancements and advanced functionalities over the standard ri tool. But even when it’s used simply as a replacement for ri locally, it is significantly faster, and in my opinion more practical. Also, if you look for String#o for example, ri provides you with all the methods of the class String, that contain the letter ‘o’. While FastRi outputs only the methods starting with the letter ‘o’, which is what you actually want in most cases. Install with: $ sudo gem install fastri and $sudo fastri-server -b . Then use it in this way (qri is used for stand-alone mode, fri to connect to a remote server): $ qri String#scan.
Antonio Cangiano February 10th, 2007
The performance of Ruby on Rails is influenced by many factors, particularly the configuration of your deployment server(s). However the application code can make a big difference and determine whether your site is slow or highly responsive. This short article is about some of the tips and best coding practices to improve performances in Rails only, and won’t attempt to cover the server configuration improvements for the various deployments options.
- Optimize your Ruby code: this may seem obvious, but a Rails application is essentially ruby code that will have to be run. Make sure your code is efficient from a Ruby standpoint. Take a look at your code and ask yourself if some refactoring is in order, keeping in mind performance considerations and algorithmic efficiency. Profiling tools are, of course, very helpful in identifying slow code, but the following are some general considerations (some of them may appear admittedly obvious to you):
- When available use the built-in classes and methods, rather than rolling your own;
- Use Regular Expressions rather than costly loops, when you need to parse and process all but the smallest text;
- Use Libxml rather than the slower REXML if you are processing XML documents;
- Sometimes you may want to trade off just a bit of elegance and abstraction for speed (e.g. define_method and yield can be costly);
- The best way to resolve slow loops, is to remove them if possible. Not always, but in a few cases you can avoid loops by restructuring your code;
- Simplify and reduce nested if/unless as much as you can and remember that the operator ||= is your friend;
- Hashes are expensive data structures. Consider storing the value for a given key in a local variable if you need to recall the value a few times. More in general, it’s a good idea to store in a variable (local, instance or class variable) any frequently accessed data structure.
- Caching is good: caching can significantly speed up your application. In particular:
- Use your database to the full extent of the law
: don’t be afraid of using the cool features provided by your database, even if they are not directly supported by Rails and doing so means bypassing ActiveRecord. For example define stored procedures and functions, knowing that you can use them by communicating directly with the database through driver calls, rather than ActiveRecord high level methods. This can hugely improve the performance of a data bound Rails application.
- Finders are great but be careful: finders are very pleasant to use, enable you to write readable code and they don’t require in-depth SQL knowledge. But the nice high level abstraction come with a computational cost. Follow these rules of thumb:
- Retrieve only the information that you need. A lot of execution time can be wasted by running selects for data that is not really needed. When using the various finders make sure to provide the right options to select only the fields required (:select), and if you only need a numbered subset of records from the resultset, opportunely specify a limit (with the :limit and :offset options).
- Don’t kill your database with too many queries, use eager loading of associations through the include option:
for post in Post.find(:all,
:include => [ :author, :comments ])
end
- Avoid dynamic finders like MyModel.find_by_*. While using something like User.find_by_username is very readable and easy, it also can cost you a lot. In fact, ActiveRecord dynamically generates these methods within method_missing and this can be quite slow. In fact, once the method is defined and invoked, the mapping with the model attribute (username in our example) is ultimately achieved through a select query which is built before being sent to the database. Using MyModel.find_by_sql directly, or even MyModel.find, is much more efficient;
- Be sure to use MyModel.find_by_sql whenever you need to run an optimized SQL query. Needless to say, even if the final SQL statement ends up being the same, find_by_sql is more efficient than the equivalent find (no need to build the actual SQL string from the various option passed to the method). If you are building a plugin that needs to be cross-platform though, verify that the SQL queries will run on all Rails supported databases, or just use find instead. In general, using find is more readable and leads to better maintainable code, so before starting to fill your application with find_by_sql, do some profiling and individuate slow queries which may need to be customized and optimized manually.
- Group operations in a transaction: ActiveRecord wraps the creation or update of a record in a single transaction. Multiple inserts will then generate many transactions (one for each insert). Grouping multiple inserts in one single transaction will speed things up.
Insead of:
my_collection.each do |q|
Quote.create({:phrase => q})
end
Use:
Quote.transaction do
my_collection.each do |q|
Quote.create({:phrase => q})
end
end
or for rolling back the whole transaction if any insert fails, use:
Quote.transaction do
my_collection.each do |q|
quote = Quote.new({:phrase => q})
quote.save!
end
end
- Control your controllers: filters are expensive, don’t abuse them. Also, don’t overuse too many instance variables that are not actually required by your views (they are not light).
- Use HTML for your views: in your view templates don’t overuse helpers. Every time you use form helpers you are introducing an extra step. Do you really need a helper to write the HTML for a link, a textbox or a form for you? (You may even make your designer, who doesn’t know Ruby, happy!)
- Logging: configure your applications so that they log only the information that is absolutely vital to you. Logging is an expensive operation and an inappropriate level (e.g. Logger::DEBUG) can cripple your production application.
- Patch the GC: OK, not really a coding issue, but patching Ruby’s Garbage Collection is strongly advised and will improve the speed of your Ruby and Rails applications significantly.
- A final note:I don’t advocate premature optimization, but if you can, work on your code with these principles in mind (but don’t overdo it either). Last minute changes and tweaks are possible but less desirable than a “performance aware” style of coding. Profile your applications, benchmark them
and have fun experimenting.
Antonio Cangiano January 7th, 2007
This is a tip that I’m writing in the hopes of helping Typo users who will google for the following error messages uninitialized constant ActionController::TestRequest or undefined method `publish!’ for nil:NilClass.
While publishing an article on db2onrails.com, I’ve selected a future publishing date for the post (just to round it off to the half an hour mark). I’ve performed this operation in the past without encountering any problems, but for some bizarre reason (most likely a Typo 4.0.3 bug) it didn’t work this time. The article wouldn’t show up and the /admin section would raise the typical Application Error (Rails). There was obviously some kind of issue with the application and the only reason why the homepage was working fine is because its content was cached.
At this point, I restarted fcgi, reloaded the homepage and boom: uninitialized constant ActionController::TestRequest. If you go to your PhpMyAdmin or access your database by any other means, you should drop the ‘offending’ article that you intended to publish (you can find it in the contents table). If you reload your application now, you should see another error: undefined method `publish!’ for nil:NilClass. This is due to the fact that in the table triggers there is a publishing action waiting to be executed on an article that no longer exists.
Please follow the instructions below as a quick fix to the problem, if you encountered these errors in Typo just after you did a post dated in the future:
- Drop the problematic article from the contents table. You should first take note of its id and make a copy of its body if you don’t have it already (as you are going to publish it again in a few moments).
- Drop the corresponding record from the triggers table. You’ll recognize the raw by the pending_item_id (which is the same number as the id for the record you just deleted in contents) and the trigger_method field set for publishing.
Your blog should be back up and you’ll be able to access the /admin section as well. You can now repost the initial message, just make sure not to set a future date and time again.