public
Fork of caring/acts_as_url_param
Description: Pretty url support for Ruby on Rails applications
Homepage: http://www.caring.com
Clone URL: git://github.com/joshuabates/acts_as_url_param.git
acts_as_url_param / lib / url_utils.rb
100644 71 lines (67 sloc) 3.347 kb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
require 'generator'
 
module Caring
  module Utilities
    module UrlUtils
      # Makes a string safe for urls
      # Options:
      # :replacements - a Hash of a replacement string to a regex that should match for replacement
      # :char - when :replacements is not provided, this is the string that will be used to replace unsafe characters. defaults to '-'
      # :collapse - set to false if multiple, consecutive unsafe characters should not be replaced with only a single instance of :char.
      # defaults to true. only matters if you don't provide :replacements. If you do, you'll need to write your regex such
      # that it matches multiple characters.
      # :strip_endings - remove replacement characters from the beginning and end of the safe string. Defaults to true.
      # :downcase - make the input string lower case. Defautls to true.
      def url_safe(s, options = {})
        return s if s.blank?
        s = s.downcase if options.fetch(:downcase, true)
        collapse = options.fetch(:collapse, true)
        default_regex = /[^'a-zA-Z0-9]#{"+" if collapse}/
        s.gsub! /(&)|&/, 'and'
        replacements = options[:replacements] || { options.fetch(:char,"-") => default_regex, "" => /'#{"+" if collapse}/}
        replacements.each do |replacement, regex|
          s = s.gsub(regex,replacement)
        end
        if options.fetch(:strip_endings,true)
          replacement_strings = replacements.keys.map{|k| "(#{Regexp.escape(k)})" unless k.blank?}.compact.join("|")
          s = s.gsub(/(^#{replacement_strings})|(#{replacement_strings}$)/,"")
        end
        return s
      end
      
      # Generate integers
      # Options:
      # :start => 1, The integer to start with
      # :end => nil, the last integer to generate, when nil this becomes an infinite sequence
      # :increment => 1, the amount to add for each iteration
      def int_generator(options = {})
        start = options.fetch(:start,1)
        last = options[:end]
        increment = options.fetch(:increment, 1)
        raise ArgumentError if increment == 0
        raise ArgumentError if last && (increment > 0 && start > last) || (increment < 0 && start < last)
        Generator.new do |g|
          i = start
          loop do
            g.yield i
            return if !last.nil? && (increment > 0 && i >= last) || (increment < 0 && i <= last)
            i = i + increment
          end
        end
      end
      
      # accepts a block that will be passed a candidate string and should return true if it is unique.
      # Options:
      # => :separator => "-", a string that will be injected between the base string and the uniqifier
      # => :endings => generator, a Generator that provides endings to be placed at the end of the base.
      # defaults to the set of positive integers starting with two.
      def uniquify(base, options = {})
        sep = options.fetch(:separator, "-")
        endings = options[:endings] || int_generator(:start => 2)
        return base if yield base
        while endings.next? do
          candidate = base+sep+endings.next.to_s
          return candidate if yield candidate
        end
        raise ArgumentError.new("No unique construction found for \"#{base}\"")
      end
    end
  end
end