Tag Archives: tips

Rails 3: Storing Model Metadata Attributes with ActiveRecord::Store

I recently discovered the excellent ActiveRecord store method whilst researching the best-practice for storing optional, flexible metadata against a record. store lets you keep simple key/value data into a single text column on your model.

By declaring stored attributes on your model, ActiveRecord will automatically generate the appropriate setter/accessor methods, and validations work just as you'd expect.

# db/migrate/create_cars.rb
# ...
create_table do |t|
  t.references :model
  t.references :manufacturer
  t.text :metadata # Note metadata is just a text column
 
  t.timestamps
end
 
# app/models/car.rb
class Car < ActiveRecord::Base
  belongs_to :model
  belongs_to :manufacturer
 
  # Manufacturer and Model are 'real', database-backed attributes
  attr_accessor :model_id, :manufacturer_id, :colour, :size, :notes, :product_url
 
  store :metadata, :accessors => [:colour, :size, :notes, :product_url]
 
  # Database-backed attributes
  validates :model, :presence, :presence => true
  validates :manufacturer, :presence => true
 
  # Metadata stored attributes
  validates :colour, :presence => true
  validates :size, :presence => true, :inclusion => { :in => %w(small medium large) }
  validates :product_url, :format => { :with => /^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?$/ }
end

Note that the metadata is just a text column. Rails will automatically serialise the hash into this column.

With our Car model defined, and its metadata attributes declared with store, we can use them in just the same way as normal, database-backed attributes:

car = Car.new
car.model = CarModel.first
car.manufacturer = Manufacturer.first
car.colour = "Red"
car.size = "medium"
car.product_url = "http://www.example.com/"
 
car.save
# => true
 
car.colour
# => "red"
 
car.size
# => "medium"

Pros and Cons

The biggest advantage to this type is store is it provides a flexible data schema. This is perfect for storing non-indexed metadata about a model, and a use-case that often crops up when building apps. It provides a compromise between common relational databases (such as MySQL and PostgreSQL) and the flexibility of NoSQL databases such as MongoDB and CouchDB. In practical use, it means attributes can quickly be added to a model without the need to perform any migrations on the database schema.

A possible disadvantage is that stored attributes can not be indexed or used in queries (as they have no corresponding database column). For example, you could not call Car.where(:colour => "red"). However, as Garry Tan points out in his post, if this becomes a requirement in the future, you could always add a database-column to the schema at a later date, and just move the data at that time.

Helpful post? Let me know on Twitter.

References

  1. http://axonflux.com/one-of-my-favorite-additions-to-rails-3-activ

Android: How to test Local Web Apps on an Emulated Device (AVD)

Android AVD running local Web App

Editing your /etc/hosts file is great for running and testing web apps as they are under development. For a project I’m working on, I needed to be able to access one of my local Rails apps through an Android device, which meant editing the device’s /etc/hosts file. As I haven’t rooted my Nexus-S, so opted to use an emulated Android Virtual Device (AVD), as they are configured by default to allow root access.

I started by creating a simple hosts file on my desktop. It’s this file that I would push to the AVD

cd Desktop
vim hosts # or your favourite text editor....

Use your text editor to create a simple hosts file pointing to your local web app. Remember to also include a localhost entry:

  # hosts
  127.0.0.1       localhost 
  192.168.1.10    rails-app

Next, copy your new hosts file to the AVD:

adb push hosts /etc/hosts

Access Denied

Despite having root access, though, adb wouldn’t let me push the new hosts file because the system partition is mounted as read-only. You can remount the system partition in read-write mode using adb remount, and then try pushing the hosts file up to the device:

adb remount
abd push hosts /etc/hosts

I kept getting an Out of Memory error, which seems to be a common problem. The solution (gleaned from scattered forum posts) is to use the emulator’s -partition-size option.

Using this option means you won’t be able to launch the AVD directly from Eclipse, but instead need to use the command line. Close down any running AVDs, and then run, and then re-launch the AVD passing in the -partition-size option with a reasonable value:

emulator -avd Samsung_Galaxy-Tab -partition-size 128
 
# ...wait the avd to boot up...
 
adb remount
abd push hosts /etc/hosts

The hosts file should now successfully be copied to your AVD. If you launch the AVD’s browser and enter the local URL from your hosts file (e.g. http://rails-app:3000/), the AVD will connect to the local IP address you specified.

Note: You’ll need to keep hold of your hosts file as your settings will be wiped when you shut down the AVD. You can reinstall the file next time by performing the adb remount; adb push hosts /etc/hosts script each time you boot the AVD.

References

  1. Google Groups Thread
  2. Cute Android Tips: Failed to Copy File to System

Rails 3: How to Autoload and Autorequire your Custom Library Code

Every time I start a new Rails 3 project, I’m always caught out by its autoloading behaviour. Rails 3 will only require (and so autoload) a module when it is first encountered within the application code, for example by a call to include or require.

Whilst the reasoning behind this decision is sound, I usually just want to load some common functionality into, for example ActiveRecord, and have it available to all of my models.

So, as a reminder to myself and to help anyone else caught out by this, here is how to autoload (and autorequire) your own library code in Rails 3:

Ensure that your library code’s path is set in config/application.rb. By default, Rails 3 autoloads from a /extras folder, but I conventionally keep custom library code in a /lib folder:

# config/application.rb
config.autoload_paths += %W(#{config.root}/extras #{config.root}/lib)

Next, we need to tell Rails to require our library code (so it is available to the application). Create a new initializer called application.rb in config/initializers and require your library modules:

# config/initializers/application.rb
require 'my_modules'
require 'my_modules/active_record/active_record_extensions'
# ... require any other custom modules your application uses

All that’s left to do is restart your Rails server to load your custom modules into the application. Remember that if you change your custom code, you’ll need to restart the server again to reload the changes.

Ubuntu 11.04 Beta – How to test Unity in VirtualBox 4

Ubuntu 11.04 Beta ScreenshotIf you like to test out the latest and greatest builds of Ubuntu, you might like to try the latest 11.04 beta in VirtualBox.

Unfortunately, I’ve found the standard VirtualBox guest additions don’t offer the graphics support to run compiz on Ubuntu 11.04, so testing Unity didn’t seem possible. Thankfully, I stumbled across this post (Portugese) which shows you can install guest additions directly from the Ubuntu repositories:

With a freshly installed copy of Ubuntu 11.04 Beta running in VirtualBox, open a terminal window and type the following:

sudo apt-get update; sudo apt-get install virtualbox-ose-guest-utils -y; sudo reboot

Update: Also make sure you’ve enabled 3D acceleration in the guest machine’s settings.

This line will install the up-to-date VirtualBox guest utilities, and reboot the virtual machine. You should now find the new – and very impressive – Unity interface ready to try out.

References

Ubuntu 11.04 in VirtualBox with Natty Narwhal Unity

Rails 3: How to Dynamically Build Complex Queries with Composed Scopes

In previous posts, I wrote how to build complex search queries in Rails 2 using the ez_where plugin. In Rails 3, such plugins and long-winded code is completely unnecessary thanks to the powerful ActiveRelation query interface.

In a project I’m working on, I needed to be able to build just such a complex search so that users could find a customer whose name or email matched one or several search terms. The search terms would be supplied as a space-separated string. For example, to find all the customers whose name (first_name or last_name) or email contained ‘joe bloggs’, the SQL fragment would look something like:

SELECT * FROM customers WHERE (customers.first_name ILIKE '%term_1%' OR customers.last_name ILIKE '%term_1%' OR customers.email ILIKE '%term_1%') AND (customers.first_name ILIKE '%term_2%' OR customers.last_name ILIKE '%term_2%' OR customers.email ILIKE '%term_2%');

Achieving this in ActiveRelation is deceptively simple, but most tutorials I’ve read only discuss how to build simple named scopes that can be chained together. I needed to be able to dynamically build the scope based on any number of search terms the user might enter.

Reading the API docs, I discovered ActiveRecord::Base.scoped which returns an empty scope object upon which you can build in your code. Here’s an example that generates the SQL fragment above:

class Customer < ActiveRecord::Base
  scope :terms, lambda { |terms|
    return if terms.blank?
 
    composed_scope = self.scoped
 
    terms.split(' ').each do |term|
      term = '%' << term << '%' # Wrap the search term in % to achieve 'fuzzy' searching
      composed_scope = composed_scope.where('first_name ILIKE ? OR last_name ILIKE ?' OR email ILIKE ?', term, term, term)
    end
 
    composed_scope
  }
end

With this scope, you can perform complex searches that span multiple attributes (in this case first_name, last_name, and email) with ease. What’s more, because the returned composed_scope is an ActiveRelation, you can continue the chain in your code, for example:

Customer.terms('joe bloggs').order('first_name').where('active = ?', true).all