Module: Rack::Mount::Utils

Defined in:
lib/rack/mount/utils.rb

Overview

:nodoc:

Defined Under Namespace

Classes: Capture

Constant Summary collapse

GLOB_REGEXP =
/\/\\\*(\w+)$/
OPTIONAL_SEGMENT_REGEXP =
/\\\((.+)\\\)/
SEGMENT_REGEXP =
/(:([a-z](_?[a-z0-9])*))/
NAMED_CAPTURE_REGEXP =
/\?:<([^>]+)>/.freeze

Class Method Summary collapse

Class Method Details

.convert_segment_string_to_regexp(str, requirements = {}, separators = []) ⇒ Object

Raises:

  • (ArgumentError)


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
# File 'lib/rack/mount/utils.rb', line 10

def convert_segment_string_to_regexp(str, requirements = {}, separators = [])
  raise ArgumentError unless str.is_a?(String)

  str = Regexp.escape(str.dup)
  requirements = requirements || {}
  str.replace("/#{str}") unless str =~ /^\//

  re = ''

  while m = (str.match(SEGMENT_REGEXP))
    re << m.pre_match unless m.pre_match.empty?
    if requirement = requirements[$2.to_sym]
      re << Const::REGEXP_NAMED_CAPTURE % [$2, requirement.source]
    else
      re << Const::REGEXP_NAMED_CAPTURE % [$2, "[^#{separators.join}]+"]
    end
    str = m.post_match
  end

  re << str unless str.empty?

  if m = re.match(GLOB_REGEXP)
    re.sub!(GLOB_REGEXP, "/#{Const::REGEXP_NAMED_CAPTURE % [$1, '.*']}")
  end

  while re =~ OPTIONAL_SEGMENT_REGEXP
    re.gsub!(OPTIONAL_SEGMENT_REGEXP, '(\1)?')
  end

  RegexpWithNamedGroups.new("^#{re}$")
end

.extract_named_captures(regexp) ⇒ Object



131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/rack/mount/utils.rb', line 131

def extract_named_captures(regexp)
  source = Regexp.compile(regexp).source
  names, scanner = [], StringScanner.new(source)

  while scanner.skip_until(/\(/)
    if scanner.scan(NAMED_CAPTURE_REGEXP)
      names << scanner[1]
    else
      names << nil
    end
  end

  source.gsub!(NAMED_CAPTURE_REGEXP, '')
  return Regexp.compile(source), names
end

.extract_regexp_parts(regexp) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/rack/mount/utils.rb', line 77

def extract_regexp_parts(regexp)
  unless regexp.is_a?(RegexpWithNamedGroups)
    regexp = RegexpWithNamedGroups.new(regexp)
  end

  if regexp.source =~ /\?<([^>]+)>/
    regexp, names = extract_named_captures(regexp)
  else
    names = regexp.names
  end
  source = regexp.source

  source =~ /^\^/ ? source.gsub!(/^\^/, '') :
    raise(ArgumentError, "#{source} needs to match the start of the string")
  source.gsub!(/\$$/, '')

  scanner = StringScanner.new(source)
  stack = [[]]

  capture_index = 0
  until scanner.eos?
    char = scanner.getch
    cur  = stack.last

    if char == '('
      name = names[capture_index]
      capture = Capture.new(:name => name)
      capture_index += 1
      cur.push(capture)
      stack.push(capture)
    elsif char == ')'
      capture = stack.pop
      if scanner.peek(1) == '?'
        scanner.pos += 1
        capture.optionalize!
      end
    else
      cur.push('') unless cur.last.is_a?(String)
      cur.last << char
    end
  end

  result = stack.pop
  result.each { |e| e.freeze }
  result
end