Wednesday, November 21, 2012

Ruby/Rails Tips

A few Ruby/Rails tips:

  • Avoid references to other Rails classes outside of methods if the class is autoloaded by Rails if that might cause all of the web of classes to be loaded at once. Instead refer to those classes with strings, symbols, etc. E.g. though you can use class_name: SomeModel in a Rails association, use class_name: "SomeModel".
  • Within methods, use symbols or classes/constants rather than strings if possible, unless it is something dynamic, since defining a string will usually create a new instance of it whereas symbols and constants stay forever (well... kind of). Also, constants and symbols also compare really nicely. Note that you may have problems sorting on constants/classes, so it is better to use symbols or strings if need to sort.
  • For incoming user-defined data, don't convert the strings into symbols (e.g. don't convert the value of a request param to a symbol). Symbols stay for the life of the application, so that can allow people to make your application run out of memory by sending in a lot of requests with varying large request param values.
  • Be careful when using param values in ActiveRecord queries. See CVE-2013-1854 which affects Rails 3.2.x, 3.1.x, 2.3.x prior to 3.2.13, 3.1.12, 2.3.18.
  • If you have a string that shouldn't be changed, you probably want to freeze it. Note that Ruby will freeze certain things automatically like hash keys. Strings stored in constants may be a good thing to freeze, e.g. SOME_CONSTANT='something'.freeze. Some other tips here:
  • You must use double quotes around string definitions that have #{…} in them. Otherwise, I tend to use single quotes because it looks cleaner, but there is no performance difference, in Ruby 1.8.7 and 1.9.3 at least:
  • When in Rails, put modules and classes in a directory path that corresponds to the fully-qualified path under the autoloaded directory (app/models, app/controllers, etc.), because when Rails autoloads and reloads, that is where it is looking for it.
  • Often class variables (@@) being altered by instance methods are a bad thing, just like static variables in object instance methods in Java.
  • Global variables ($) are usually not a good idea. E.g, you cannot easily differentiate between a global boolean variable that is false and one that hasn't been set.
  • In Rails, you can use class_attribute instead of class variables to put attributes on a class. Rails convention is to use an underscore in front for internal variables not meant to be set outside of that gem, framework, etc. but you still must try to avoid conflicts. Be aware that Rails class_attribute values belong to the class or module, so if you put a class_attribute on a module and include that modules in other classes, they may be sharing a hash/array stored in a class_attribute of the module. This can be a problem, obviously.
  • Often uppercase-and-underscored constants in Ruby don't provide the value that they would in language that do compile-time checking like Java. Consider using symbols in Ruby instead, which are lighter weight, unless you need to store a value.
  • Unless you want loading/autoloading to fail when trying to load a class or module if the parent module was not defined, don't define modules or classes like: module Parent::Child or class Parent::Child. Instead use: module Parent; module Child or module Parent; class Child.

No comments: