For whatever reason you need or choose to store blobs in databases. Yet, you do not wish to mix blob columns in the same table as your regular attributes. BinaryColumnTable helps you store ALL blobs in a separate table, managed transparently by an ActiveRecord model. Optionally, it helps you record the content-type of the blob.
This plugin does nothing else.
In your existing model, e.g. Member, declare your binary columns (note: these columns does not exist in your “members” table)
class Member < ActiveRecord::Base has_binary_columns :photo, :resume, :secret_zip end
For that to work, generate a model called BinaryColumn to contain these blobs.
rails generate model BinaryColumn original_table_type:string original_table_id:integer name:string content_type:string original_filename:string content:binary
You may wish to edit the generated migration file to add a custom index to the bottom of the “self.up” method,
class CreateBinaryColumns < ActiveRecord::Migration def self.up create_table :binary_columns do |t| t.string :original_table_type t.integer :original_table_id t.string :name t.string :content_type t.string :original_filename t.binary :content t.timestamps end add_index :binary_columns, [:original_table_id, :original_table_type, :name], :name => :by_owner_field_name # add this line end def self.down drop_table :binary_columns end end
Then apply the schema change,
rake db:migrate
Finally, modify the generated app/models/binary_column.rb to have a polymorphic “belongs_to”
class BinaryColumn < ActiveRecord::Base belongs_to :original_table, :polymorphic => true def file_extension self.original_filename.to_s.split('.').last end end
The usage is simple and straight-forward as-if the columns are in the original table.
m = Member.new m.name = "Michael" m.photo = IO.read("avatar.png") #=> binary string m.resume = File.open("resume.doc") #=> IO object (e.g. when uploading via http form) m.save! #=> creates a record in "members" table, saving "Michael" into the "name" column #=> creates a record in "binary_columns" table, saving "avatar.png" binary into "content" column #=> creates a record in "binary_columns" table, saving "resume.doc" binary into "content" column m = Member.last m.name #=> "Michael" m.photo_attributes #=> light-weight loading of BinaryColumn object (excludes BLOB) m.photo_attributes.file_extension #=> "png" m.photo #=> binary content of the "avatar.png" file m.resume #=> binary content of the "resume.doc"
Note: If you MUST have your binary column table called something other than “binary_columns”, just edit ActiveRecord::Extensions::BinaryColumnTable::CLASS_NAME
Note: Population of the “content_type” attribute requires “file” command on OS, i.e. most Unix otherwise, remove “content_type” column from table definition to skip content_type extraction
Note: Accessing the content_type attribute is not sugar coated, add a “_binary_column.content_type” suffix. i.e. from our earlier example
m = Member.last m.photo #=> binary content of the "avatar.png" file m.photo_binary_column.content_type #=> "image/png; charset=binary"
You may wish to generate index for that table as well
./script/generate migration AddIndexesToBinaryColumns
Edit the generated db/migrate/*_add_indexes_to_binary_columns.rb to look like this
class AddIndexesToBinaryColumns < ActiveRecord::Migration def self.up add_index :binary_columns, [:original_table_id, :original_table_type, :name], :name => 'by_original_table_name' end def self.down remove_index :binary_columns, :name => 'by_original_table_name' end end
Then run migration to commit the schema change
rake db:migrate
Copyright © 2009 Chew Choon Keat, released under the MIT license