module ActiveRecord module ValidatesWordCount def self.included(base) base.extend(ClassMethods) setup_default_error_messages end def self.setup_default_error_messages ActiveRecord::Errors.default_error_messages[:should_not_exceed_word_count] = "should not have more than %d words" ActiveRecord::Errors.default_error_messages[:should_exceed_word_count] = "should have more than %d words" ActiveRecord::Errors.default_error_messages[:word_count_should_be_in_range] = "word count should be between %d and %d" end module ClassMethods def validates_word_count(*attr_names) configuration = { :message => '', :on => :save, :with => nil } configuration.update(attr_names.extract_options!) get_message = Proc.new do |key| configuration[:message].empty? ? ActiveRecord::Errors.default_error_messages[key] : configuration[:message] end proc = nil if configuration[:maximum] configuration[:message] = get_message.call(:should_not_exceed_word_count) % configuration[:maximum].to_i proc = Proc.new { |words| words <= configuration[:maximum].to_i } elsif configuration[:minimum] configuration[:message] = get_message.call(:should_exceed_word_count) % configuration[:minimum].to_i proc = Proc.new { |words| words >= configuration[:minimum].to_i } elsif configuration[:in] && configuration[:in].is_a?(Range) configuration[:message] = get_message.call(:word_count_should_be_in_range) % [configuration[:in].begin, configuration[:in].end] range = configuration[:in] proc = Proc.new do |words| range_end_condition = range.exclude_end? ? (words < range.end) : (words <= range.end) (words >= range.begin && range_end_condition) end else raise(ArgumentError, ":maximum => int, :minimum => int, or :in => Range argument must be supplied!") end validates_each(attr_names, configuration) do |record, attr_name, value| next if configuration[:allow_nil] and value.nil? record.errors.add(attr_name, configuration[:message]) unless proc.call(value.to_s.words) end end end end end