class ActiveRecord::CustomAttributes::CustomAttributeList def initialize(record) @record = record @defined_attributes = @record.class.defined_custom_attributes @defined_validations = @record.class.defined_custom_validations @extra_attribute_types = @record.class.defined_custom_field_types define_attribute_methods end def set_post_data(post_data) loaded_attributes.each(&:mark_for_destruction) post_data.each do |attribute_type, values| attribute_type = attribute_type.to_sym if supported_attribute_types.keys.include? attribute_type values.each do |key, attributes| removed = attributes["_destroy"].to_s == "true" add attribute_type, attributes["label"], attributes["value"] unless key == "%nr%" or removed end end end end def add(type, label, value) type = type.to_sym return if label.blank? and value.respond_to? :blank? and value.blank? internal_label = convert_to_internal_label(type, label) attribute = get_attribute(type, internal_label || label, true) attribute.value = value end def update_cache defined_attributes.each do |type, attributes| attributes.each do |name, options| if cache_attribute = options[:on_model] if attribute = get_attribute(type, name) record.send("#{cache_attribute}=", attribute.value) else record.send("#{cache_attribute}=", nil) end end end end end def save loaded_attributes.each(&:save) end def validate(errors) loaded_attributes.each do |a| a.validate unless a.valid? a.errors.each do |attribute, error_message| errors.add(:custom_attributes, error_message) end end end end def get_custom_validations_for(type, name) [defined_validations[type], (defined_attributes[type][name] || {})[:validate_with]].compact end def defined_attribute_types (extra_attribute_types.keys + defined_attributes.keys).uniq end def defined_labels_for type results = [] (defined_attributes[type.to_sym] || {}).each do |key, options| results << human_label_for(type, key) end results end def supported_attribute_types return @supported_attribute_types if @supported_attribute_types standard_attribute_types = {} ActiveRecord::CustomAttributes::CUSTOM_ATTRIBUTE_TYPES.each { |t| standard_attribute_types[t.to_sym] = t.to_sym } @supported_attribute_types = (standard_attribute_types.merge extra_attribute_types) end def rename_label_of attribute, new_name internal_label = convert_to_internal_label(attribute.type, new_name) attribute.label = new_name attribute.internal_label = internal_label end def attributes_of_type(type) loaded_attributes.select { |i| i.type == type.to_sym and !i.marked_for_destruction? } end private attr_reader :defined_attributes, :extra_attribute_types, :defined_validations, :record def get_attribute(type, internal_label, auto_create = false) if internal_label.is_a? Symbol attribute = loaded_attributes.find { |a| a.type == type and a.internal_label.to_s == internal_label.to_s } else attribute = loaded_attributes.find { |a| a.type == type and a.label == internal_label } end return attribute if attribute return nil unless auto_create new_attribute = ActiveRecord::CustomAttributes::CustomAttribute.new(self, record, nil) new_attribute.type = type.to_sym if internal_label.is_a? Symbol new_attribute.internal_label = internal_label new_attribute.label = human_label_for(type, internal_label) else new_attribute.internal_label = convert_to_internal_label(type, internal_label) new_attribute.label = internal_label end loaded_attributes << new_attribute new_attribute end def convert_to_internal_label(type, label) (defined_attributes[type] || {}).each do |name, options| return name if label == human_label_for(type, name) end nil end def human_label_for(type, internal_name) translate_scope = [:activerecord, :custom_attributes, record.class.name.underscore.to_sym, type.to_sym] defaults = [internal_name.to_s.underscore.gsub("_", " ").capitalize] I18n.t(internal_name, :scope => translate_scope, :default => defaults) end def get_value_of(type, internal_label) found = get_attribute(type, internal_label) return nil if found and found.marked_for_destruction? found.value if found end def loaded_attributes @loaded_attributes ||= record.external_custom_attributes.collect do |attribute_record| ActiveRecord::CustomAttributes::CustomAttribute.new(self, record, attribute_record) end end def define_attribute_methods supported_attribute_types.each do |key, value| class_eval do define_method "add_#{key}" do |label, value| add(key.to_sym, label, value) end define_method "#{key}_attributes" do attributes_of_type(key.to_sym) end define_method "#{key}_value_of" do |internal_name| get_value_of(key.to_sym, internal_name) end end end end end