Jason Earl
Personal Website
BLOG
Overlooked Rails Best Practises Pt.3
In this post I want to cover a number of very Rails specific ways to make code more robust. Many of these are small defensive coding techniques, but some also help tie down security issues.
Use of Active Record Bang Methods
Bang methods are methods that end with an exclamation mark. Their root is from impure functional languages such as LISP where this was used to denote side effects (mutability, IO etc). In ActiveRecord this is used to force exceptions to be raised if an action fails to act on the database due to validations, or failing callbacks.
1 2 3 |
ModelName.create!
some_record.save!
some_other_record.update_attributes! |
I always had a tendency to avoid using these methods initially when I started off programming in Rails. Probably because I have a bit of a hatred for exceptions. This probably stems from the annoyance they cause when I was forced to do some university work in Java (I hate the fact you have to bloat up trivial code with catch clauses, and putting throws in method prototypes). Likewise in C++ exceptions are considered a bit of an evil because they don’t play nicely with raw pointers.
However in recent Rails projects I found that the except based Active Record methods can be a great aid during development. One area particular is during migrations. During development I have a tendency to break migrations into the following three main initial files:
- Schema Creation
- Data Loading
- Development Data Loading
During the data loading, I find it handy to use code to create basic model instances, such as initial users etc. By forcing exceptions you can be assured that all of the actions you execute will happen and there will be no cases where data will silently fail to save back to the database.
Another case where these methods become useful is during transactions, as exceptions will force transactions to rollback. It is often a good idea to use transactions in databases to ensure actions are atomic, not to mention in some databases the use of transactions can aid the speed up operations by reducing the communication between the DB server and client libraries.
Use of Closures or Symbols for Meta-Programming
Due to the event driven model of Rails programming, there are many methods such as before_filter
, after_filter
, before_save
, after_validation
, etc that can be defined either by overriding the function, referencing a function through a symbol, or passing a block code.
Unless there is a critical requirement for speed (in which case you are probably using the wrong language) then one should always use symbols or ‘proc’ objects. The issue when you override methods can make it very easy if you are using inheritance to block calls to callbacks defined in the parent class. This can be an issue when developing projects in teams as it means other developers will need to check parent classes when ever they want to use a filter.
Use of ‘attr_protected’
I would guess most people are educated enough to know about SQL injection security issues. However it is very easy to overlook sensitive fields like password hashes, salts, and other credential-orientated. Without attr_protected it is very easy to exploit mass assignment in Rails for one who knows or has a good idea how the database schema works.
Indexes on Tables
Again, this is a common mistake, and I often make this mistake. While pre-mature optimisation is considered to be an evil in programming, indexes are a very passive / non-intrusive form of optimisation. Typically one should look at placing indexes on any fields that are heavily used in comparison criteria. While ad-hoc guesses at optimisation can be bad, this will generally avoid your site from suddenly crawling in production mode due to drive seeks and heavy memory usage.
Understand Your Database
Clearly ad-hoc optimisation should not be substitute to a proper database tune, but it will quite often your intuition will be helpful. Likewise one should take time to clearly understand how your database uses indexes.
Common MySQL Example
MySQL and PostgreSQL have very different techniques and features. For instance MyISAM tables have great text indexing support, but lack any form of atomic safety for transactions. Clever DBA’s have exploited this by using InnoDB for most data, and have used replication to mirror text-search content to a slave database that uses MyISAM to provide fast text searches.
GIS and Spatial Data
MySQL has some very basic spatial data type support. PostgreSQL has even better support. Spatial data such as locations can not use your typical linear B+ tree based index, because the data works in several dimensions. Typically most DB systems will use bounding box based systems using structures such as R-Trees. While it’s not important knowing exactly how these algorithms use, a good programmer / DBA needs to be aware how to make the best use of them.
Avoid Using Direct SQL
Active Record is a very powerful abstraction for SQL. You rarely need to use SQL directly if you use it properly. However, I still see a number of Rails projects with significant chunks of SQL code.
Complex Queries
Even in cases where queries can get potentially very complex, such as search systems with large permutations of criteria, the use of scopes, and/or external libraries such as ThoughtBot’s Squirrel can still allow you to avoid this.
DB Specific Features
In cases where you do find you need to use direct SQL, it should generally only be when you need to exploit DB specific features or speed. In the case of speed, one should look at alternative indexing strategies first. Failing that, I have found it helps to hide each vendor’s SQL specific code behind modules that can be included to Active Record on the fly on start up. This way all vendor specific code it clearly isolated.
Don’t Develop With MySQL
Surely you would have seen the rant on MySQL I made. MySQL is like VB for databases. It encourages some really poor practices. If you use PostgreSQL for development and SQLite (where possible) during development, you can pretty much guarantee it your code will work on MySQL in production (and you can always run tests using MySQL occasionally to ensure this is the case). Many apps coded with MySQL often have silly syntax issues when switching to another database system. To be honest, in most cases PostgreSQL and SQLite are far superior alternatives for a fair amount of projects.
Conclusion
I think this wraps up some fairly trivial, yet handy ways to write more robust Rails code. It always helps to keep an eye on blogs such as “The Rails Way” and the “Ryan Baits” blogs. “The Rails Way” promotes a number of ways to improve code using existing projects as examples. Ryan Baits will keep you updated with all the progress in edge Rails which often contains improvements to help DRY up code.
Listening to Techno / Progressive:
Snyper
- Hybrid