module WulffeldSlug module SlugInclude def self.included(base) base.class_eval do if self.slug_config[:mongo] field :slug end validates_presence_of :slug before_validation :on => :create do cb_make_slug end end end def to_param slug end # Can override this. def slug_unique?(slug) !self.class.where(:slug => slug).first end def cb_make_slug return unless slug.blank? a = [*send(slug_config[:fields])].reject {|f| f.blank? }.map {|f| f.is_a?(String) ? f : send(f) }.flatten return if a.blank? self.slug = to_unique_slug(a.join('-')) end def to_unique_slug(s, n=nil) str_part = prepare_string(s) loop do slug = [str_part, n].compact.join('-') return slug if slug_unique?(slug) # First loop we do some optimistic searching. if n.nil? b = 512 last_b = nil loop do slug = [str_part, b].join('-') if slug_unique?(slug) || b < 1 last_b = b break if b < 1 else n = last_b break end b /= 2 end end n ||= 0 n += 1 end end # Use dashes. # http://www.mattcutts.com/blog/dashes-vs-underscores/ def prepare_string(s) s.strip! # Blow away apostrophes s.gsub! /['`]/,"" orig = s # @ --> at, and & --> and s.gsub! /\s*@\s*/, " at " s.gsub! /\s*&\s*/, " and " [ [/[+]/, 'plus'], [/[ÀÁÂÃÅĀĂĄǍ]/, 'A'], [/[āăąàáâãǎ]/, 'a'], [/[ĆĈĊČÇ]/, 'C'], [/[ćĉċčç]/, 'c'], [/[ĎĐ]/, 'D'], [/[ďđ]/, 'd'], [/[ĒĔĖĘĚÈÉÊË]/, 'E'], [/[èéêëēĕėęě]/, 'e'], [/[ĜĞĠĢǤǦǴ]/, 'G'], [/[ĝğġģǥǧ]/, 'g'], [/[Ĥ]/, 'H'], [/[ĥĦħ]/, 'h'], [/[ĨĪĬĮİǏÌÍÎÏ]/, 'I'], [/[ĩīĭįıǐìíîï]/, 'i'], [/[Ĵ]/, 'J'], [/[ĵ]/, 'j'], [/[ĶǨК]/, 'K'], [/[ķĸǩ]/, 'k'], [/[ĹĻĽĿŁ]/, 'L'], [/[ĺļľŀł]/, 'l'], [/[ŃŅŇŊÑǸП]/, 'N'], [/[ńņňʼnŋñǹ]/, 'n'], [/[ŌŎŐǑÒÓÔÕÖ]/, 'O'], [/[ōŏőǒðòóôõ]/, 'o'], [/[ŔŖŘ]/, 'R'], [/[ŕŗř]/, 'r'], [/[ŚŜŞŠ]/, 'S'], [/[śŝşš]/, 's'], [/[ŢŤŦ]/, 'T'], [/[ţťŧ]/, 't'], [/[ŨŪŬŮŰŲÙÚÛÜ]/, 'U'], [/[ũūŭůűųùúûü]/, 'u'], [/[Ŵ]/, 'W'], [/[ŵ]/, 'w'], [/[ŶŸÝ]/, 'Y'], [/[ýÿŷ]/, 'y'], [/[ŹŻŽ]/, 'Z'], [/[źżž]/, 'z'], [/[ǓǕǗǙǛ]/, 'U'], [/[ǔǖǘǚǜ]/, 'u'], [/[ǢÆǼ]/, 'AE'], [/[ǣäæǽ]/, 'ae'], [/[ØǾ]/, 'OE'], [/[øǿö]/, 'oe'], [/[ÄÅ]/, 'AA'], [/[å]/, 'aa'], [/[ǪǬ]/, 'O'], [/[ǫǭ]/, 'o'], [/[^a-zA-Z0-9]+/i, '-'] ].each do |regex, ch| s.gsub! regex, ch end s.strip! s = s.split('-').each { |word| word.sub!(/^[a-z]/) {|m| slug_config[:capitalize] ? m.tr('a-z', 'A-Z') : m} }.join('-') new_slug = s.blank? ? orig : s if new_slug.length > 240 new_slug = new_slug[0..239] else new_slug end end end end