acts_as_similar – A basic similarity activerecord plugin

I had a need to find items that were similar to an item. I looked at acts_as_recommendable, and while this was very nice plugin, it was too slow when working with large data sets. I also decided that the results I needed didn’t need to be scientifically accurate, just a rough match. So I created this plugin to allow for a very elementary way to find items that are similar to the object you are working with. This works best when used with a has_many :through relationship.
Basically all it is doing is looking for other objects of the same class, that have some related value.

Git it here: http://github.com/randy-girard/acts_as_similar/tree/master

Example 1

Look for similar playlists, using the videos as what defines similarity.
This will look for all playlists that have a similar video as the playlist you are looking against.

class Playlist
  has_many :playlists_videos
  has_many :videos, :through => :playlists_videos
  acts_as_similar :videos
end

Example 2

Look for similar playlists, using the title of the playlist as the similarity item.
This will look for all playlists that have a similar title as the playlist you are looking against.

class Playlist
  has_many :playlists_videos
  has_many :videos, :through => :playlists_videos
  acts_as_similar :field => :title
end

Example 3

Look for similar playlists, using the video_id of the playlists_videos as the similarity item.
This will look for all playlists that have a similar video_id as the playlist you are looking against.

class Playlist
  has_many :playlists_videos
  has_many :videos, :through => :playlists_videos
  acts_as_similar :playlists_videos, :field => :video_id
end

Execute

@playlist = Playlist.first
@playlist.similar

Note: This is a work in progress.

ActiveRecord – Making :include and :select play nice!

So I have been using a nice plug-in that will allow me to using :select when using :include, and not have it pull the entire data set. You can add the plug-in to your app like this:

script/plugin install git://github.com/blythedunham/eload-select.git

Here are some ways to use the plug-in:

Employee.find :all,
  :select => "addresses.city, address.state, employees.*",
  :include => :address

Employee.find :first,
  :select => "now() as current_time, addresses.city, DATE(addresses.created_at) as addresses.created_at, employee.*",
  :include => :address

Employee.find :all,
  :select => "addresses.city, employees.name, employees.start_date",
  :include => :address

Examples taken from:

http://www.snowgiraffe.com/tech/329/eager-loading-select-plugin-when-select-plays-nice-with-include/

Some Possibly less know ActiveRecord configuration options

I am going to list from the guides.rubyonrails.com site, some possibly less know, but useful configuration options for ruby on rails. I didn’t know these existed until I had to figure out an elegant way to prefix tables with something, and I didn’t want to just type it into the migrations and models. Here is how you set the variables. In your config/environments/development.rb (or production.rb, or whatever.rb) do the following:

config.active_record.table_name_prefix = "whatever_"

You can also select any of the following options instead of table_name_prefix to configure active record globally. Here is the list:

ActiveRecord::Base includes a variety of configuration options:

  • logger accepts a logger conforming to the interface of Log4r or the default Ruby 1.8.x Logger class, which is then passed on to any new database connections made. You can retrieve this logger by calling logger on either an ActiveRecord model class or an ActiveRecord model instance. Set to nil to disable logging.
  • primary_key_prefix_type lets you adjust the naming for primary key columns. By default, Rails assumes that primary key columns are named id (and this configuration option doesn’t need to be set.) There are two other choices:
    • :table_name would make the primary key for the Customer class customer_id
    • :table_name_with_underscore would make the primary key for the Customer class customer_id
  • table_name_prefix lets you set a global string to be prepended to table names. If you set this to northwest_, then the Customer class will look for northwest_customers as its table. The default is an empty string.
  • table_name_suffix lets you set a global string to be appended to table names. If you set this to _northwest, then the Customer class will look for customers_northwest as its table. The default is an empty string.
  • pluralize_table_names specifies whether Rails will look for singular or plural table names in the database. If set to true (the default), then the Customer class will use the customers table. If set to false, then the Customers class will use the customer table.
  • colorize_logging (true by default) specifies whether or not to use ANSI color codes when logging information from ActiveRecord.
  • default_timezone determines whether to use Time.local (if set to :local) or Time.utc (if set to :utc) when pulling dates and times from the database. The default is :local.
  • schema_format controls the format for dumping the database schema to a file. The options are :ruby (the default) for a database-independent version that depends on migrations, or :sql for a set of (potentially database-dependent) SQL statements.
  • timestamped_migrations controls whether migrations are numbered with serial integers or with timestamps. The default is true, to use timestamps, which are preferred if there are multiple developers working on the same application.
  • lock_optimistically controls whether ActiveRecord will use optimistic locking. By default this is true.

ActiveRecord and Transactions!

If you ever have to do multiple actions with activerecord, you should group it into a transaction like so:

Post.transaction do
  posts.each do |post|
    post = Post.new(:body=>'Whatever')
    post.save!
  end
end

This will rollback too if the save fails.

Bulk update using ActiveRecord without SQL!

Here is a cool way you can update columns in a table without writing any SQL using ActiveRecord. Suppose you have a form with checkboxes that are for deleting posts for the ones that are checked:

<input name="posts[]" type="checkbox" value="<%= post.id %>" />

Now you can update this in the method of your controller that this form posts to:

Post.update_all({:removed=>true}, {:id=>params[:posts]})

ActiveRecord will take of the fact that you want to update 1 record or multiple!

ActiveRecord counting with associations

The other day I had to display the number of children items an object had so I decided to do the following:

# Controller
@customers = Customer.find(:all, :include=>[:receive_payments])

#View
<%= customer.receive_payments.count %>

While this worked for what I needed, it executed the following sql every iteration:

SELECT count(*) AS count_all FROM "receive_payments" WHERE ("receive_payments".customer_ref_list_id = E'850000-1071531366')

I thought this wasn’t right considering I used an association, however when you use count, it forces the use of count(*) on the database. This is where size comes in!!!!

<%= customer.receive_payments.size %>

Now we get the same results without the extra database counts!

Instant Rails Scaling through Asynchronous(Non-blocking) ActiveRecord

The guys over at NeverBlock have released a database adapter for Rails application that will severely increase the performance of ActiveRecord. Its also really easy to integrate into your application. Heres how: Add a line to environment.rb for mongrel or thin servers:

require 'never_block/servers/thin'

or

require 'never_block/servers/mongrel'

Change the adapter in database.yml:

adapter: neverblock_postgresql

or

adapter: neverblock_mysql

You can also specify the number of connections (default of 4):

connections: 12

More information, along with benchmarks, can be found here.

ActiveRecord conditions with association from hash

I’m sure you all know how to use the :conditions attribute when using ActiveRecord:

User.find(:all, :conditions=>['active = ?', true])

And you may even use associations this way:

User.find(
  :all,
  :include=>[:photos],
  :conditions=>['photos.removed = ? and users.active = ?', false, true]
)

But did you know that you can do this easier through hashes?

User.find(:all, :conditions=>{:active=>true})
User.find(
  :all,
  :include=>[:photos],
  :conditions=>{'photos.removed'=>false, 'users.active'=>true}
)

Nothing special there, but I thought it was pretty cool. One thing you have to remember when using associations, is to include that model.

Override default find conditions for model

Here is a little trick I use when I want to override a find method for a model, instead of adding the conditions option to my association. While I don’t think you should avoid using the conditions options in your associations, this will provide an alternative:

class ModelName < ActiveRecord::Base
  def self.find(*args)
    with_scope(:find=>{ :conditions=>LIMIT_CONDITION }) do
      super(*args)
    end
  end
end

Basically what is happening, is that you are overriding the default find function for a model, and wrapping its own find method with a with_scope call. So now every time you call Model.find(:all) or whatever options you want, it will execute it under that scope, with the conditions you specify.