File: splitting.rb

package info (click to toggle)
ruby-rack-mount 0.8.3-2
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 424 kB
  • ctags: 569
  • sloc: ruby: 4,100; yacc: 28; makefile: 3
file content (159 lines) | stat: -rw-r--r-- 4,672 bytes parent folder | download | duplicates (2)
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
72
73
74
75
76
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
require 'rack/mount/utils'

module Rack::Mount
  module Analysis
    class Splitting
      NULL = "\0"

      class Key < Struct.new(:method, :index, :separators)
        def self.split(value, separator_pattern)
          keys = value.split(separator_pattern)
          keys.shift if keys[0] == ''
          keys << NULL
          keys
        end

        def call(cache, obj)
          (cache[method] ||= self.class.split(obj.send(method), separators))[index]
        end

        def call_source(cache, obj)
          "(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]"
        end

        def inspect
          "#{method}[#{index}].split(#{separators.inspect})"
        end
      end

      def initialize(*keys)
        clear
        keys.each { |key| self << key }
      end

      def clear
        @raw_keys = []
        @key_frequency = Analysis::Histogram.new
        self
      end

      def <<(key)
        raise ArgumentError unless key.is_a?(Hash)
        @raw_keys << key
        nil
      end

      def possible_keys
        @possible_keys ||= begin
          @raw_keys.map do |key|
            key.inject({}) { |requirements, (method, requirement)|
              process_key(requirements, method, requirement)
              requirements
            }
          end
        end
      end

      def report
        @report ||= begin
          possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
          return [] if @key_frequency.count <= 1
          @key_frequency.keys_in_upper_quartile
        end
      end

      def expire!
        @possible_keys = @report = nil
      end

      def process_key(requirements, method, requirement)
        separators = separators(method)
        if requirement.is_a?(Regexp) && separators.any?
          generate_split_keys(requirement, separators).each_with_index do |value, index|
            requirements[Key.new(method, index, Regexp.union(*separators))] = value
          end
        else
          if requirement.is_a?(Regexp)
            expression = Utils.parse_regexp(requirement)

            if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
              expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
              return requirements[method] = expression.to_s if expression.literal?
            end
          end

          requirements[method] = requirement
        end
      end

      private
        def separators(key)
          key == :path_info ? ["/", "."] : []
        end

        def generate_split_keys(regexp, separators) #:nodoc:
          segments = []
          buf = nil
          parts = Utils.parse_regexp(regexp)
          parts.each_with_index do |part, index|
            case part
            when Regin::Anchor
              if part.value == '$' || part.value == '\Z'
                segments << join_buffer(buf, regexp) if buf
                segments << NULL
                buf = nil
                break
              end
            when Regin::CharacterClass
              break if separators.any? { |s| part.include?(s) }
              buf = nil
              segments << part.to_regexp(true)
            when Regin::Character
              if separators.any? { |s| part.include?(s) }
                segments << join_buffer(buf, regexp) if buf
                peek = parts[index+1]
                if peek.is_a?(Regin::Character) && separators.include?(peek.value)
                  segments << ''
                end
                buf = nil
              else
                buf ||= Regin::Expression.new([])
                buf += [part]
              end
            when Regin::Group
              if part.quantifier == '?'
                value = part.expression.first
                if separators.any? { |s| value.include?(s) }
                  segments << join_buffer(buf, regexp) if buf
                  buf = nil
                end
                break
              elsif part.quantifier == nil
                break if separators.any? { |s| part.include?(s) }
                buf = nil
                segments << part.to_regexp(true)
              else
                break
              end
            else
              break
            end
          end

          while segments.length > 0 && (segments.last.nil? || segments.last == '')
            segments.pop
          end

          segments
        end

        def join_buffer(parts, regexp)
          if parts.literal?
            parts.to_s
          else
            parts.to_regexp(true)
          end
        end
    end
  end
end