module Uniqueness module Model # :nodoc: def self.included(base) base.extend ClassMethods # Track all uniqueness arguments base.class_attribute :uniqueness_options end module ClassMethods # Adds random field support to Rails models # # Examples: # To auto-generate a new random string for field +foo+ # has_unique_field :foo # # You can customize the generated string by # passing an options hash. The following keys are supported: # # +:length+ number of characters, defaults to 32 # # +:case_sensitive+ defaults to true # # +:type+ type of string, defaults to :auto # can be one of: :human, :auto # # +:blacklist+ characters to exclude when generating the random # string, defaults to [] # # +:scope+ defines the `ActiveRecord` `scope` applied before # calculating the `position` field value. # defaults to [] def has_unique_field(name, options = {}) self.uniqueness_options ||= {} self.uniqueness_options[name] = Uniqueness.uniqueness_default_options.merge(options) before_validation :uniqueness_generate validate :uniqueness_validation define_method("regenerate_#{name}") { update_attributes(name => Uniqueness.generate(self.uniqueness_options[name])) } end end # Generates a new code based on given options def uniqueness_generate self.uniqueness_options.each do |field, options| value = send(field) unless value.present? value = Uniqueness.generate(options) self.send("#{field}=", value) end end end def uniqueness_validation self.class.uniqueness_options.each do |field, options| next unless new_record? || self.changes.has_key?(field) value = send(field) if value.nil? errors.add(field, 'should not be empty') else conditions = {} options[:scope].each do |s| conditions[s] = send(s) end conditions[field] = value query = self.class.where(conditions) errors.add(field, 'should be unique') if query.any? end end end end end