Skip to content

Instantly share code, notes, and snippets.

@aiaio
Created June 13, 2009 23:28
Show Gist options
  • Save aiaio/129471 to your computer and use it in GitHub Desktop.
Save aiaio/129471 to your computer and use it in GitHub Desktop.
# Adds behavior for getting, setting, and validating
# images for any given model. Relies on RAILS_ROOT/config/image_definitions.rb
module ImageHandling
def self.included(base)
base.class_eval do
# Defines accessor names: if the class is Book,
# names will be #book_images and #book_images=
image_accessor_name = self.to_s.underscore + "_images"
# Define a getter method for all images.
define_method(image_accessor_name) do
self.images
end
# Define a setter methods for images.
define_method(image_accessor_name + "=") do |attrs|
attrs.each do |attr_set|
if attr_set['id'].blank?
self.images.create(attr_set)
else
image = self.images.find(attr_set['id'])
image.update_attributes(attr_set)
end
end
end
# Note: Uses the conditional validation plugin to determine whether
# to run the validation.
validation_check_name = "should_validate_#{image_accessor_name}?".to_sym
validate :validate_required_images, :if => validation_check_name
after_validation :filter_image_errors
end
base.extend(ClassMethods)
base.send(:include, InstanceMethods)
end
module ClassMethods
# Gets the image definitions from RAILS_ROOT/config/image_definitions.rb.
# Expects a constant named class_name + ImageDefinition.
# If you only want an array of one of the definitions' attributes,
# pass in the attribute's name as a symbol (e.g., :label).
def image_definitions(attr=nil)
image_definition_const = const_get(self.to_s + "ImageDefinition")
return image_definition_const if attr.nil?
image_definition_const.map {|image| image[attr]}
end
end
module InstanceMethods
# Return available images by label.
# If no label is given, return all available images.
def available_images(label=nil)
if label.nil?
all_images = self.images
self.class.image_definitions(:label).map do |label|
image_with_label = all_images.detect {|image| image.label == label}
image_with_label || self.images.new(:label => label)
end
else
self.images.find(:first, :conditions => {:label => label})
end
end
private
# Validates the presence of all images marked as
# required in the class's image definition.
def validate_required_images
missing_images = []
self.class.image_definitions.each do |definition|
if definition[:required]
if (image = self.available_images(definition[:label])).nil? || image.new_record?
missing_images << definition[:label]
end
end
end
return true if missing_images.blank?
self.errors.add(:base, "The following required images are missing: #{missing_images.to_sentence}")
end
# HACK: Problem here is that ActiveRecord
# automatically invokes a validates_associated callback
# on new has_many relations, which produces some ugly and
# unhelpful error messages. This filter removes those messages.
# TODO: Remove this when a better fix is discovered.
def filter_image_errors
copy = self.errors.map do |attr, message|
[attr, message]
end
self.errors.clear
copy.each do |error|
unless error.first =~ /image/i
error.last.each do |e|
self.errors.add(error.first, e)
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment