I was frustrated with the amount of duplicated code sprinkled in my models that are using the attachment_fu plugin to deal with file uploads. It’s not like I need to write a plugin or anything fancy – I just want to be a little more DRY.
So, how does one simply share code between ActiveRecord models? It’s actually quite easy.
First, drop some code into a module in a .rb file in your lib directory. For example:
# lib/attachment_fu_extensions.rb module AttachmentFuExtensions # include some standard attachment_fu stuff def self.included(klass) klass.send :validates_as_attachment klass.send :validates_uniqueness_of, :filename klass.send :validates_presence_of, :user_id klass.send :attr_protected, :id, :parent_id, :user_id, :created_at, :updated_at end # upload into a single directory (e.g. public/uploads) instead of subdirectories based on the id def full_filename(thumbnail = nil) file_system_path = (thumbnail ? thumbnail_class : self).attachment_options[:path_prefix].to_s File.join(RAILS_ROOT, file_system_path, thumbnail_name_for(thumbnail)) end # prevent users from uploading index files that would be served instead of the index template/action def validate errors.add("filename", "is invalid") if filename? && %w(index.html index.htm).include?(filename.downcase) end end
That code will automatically be loaded when your application starts, provided that the filename and module follow the standard Rails naming conventions.
Then, simply include the relevant module in the relevant models. For example:
# app/models/upload.rb class Upload :file_system, :path_prefix => 'public/uploads', :max_size => 10.megabytes include AttachmentFuExtensions # ... end
That’s it. Extending ActiveRecord models and DRYing out your app is pretty easy, eh?
Update: Walter pointed out that I should put modules like this into lib/ instead of config/initializers/, so I’ve updated this post to reflect that. I’m not sure why that didn’t occur to me. He also told me about the trick for sharing calls to validates_as_attachment and so forth – I didn’t even know that was possible!