File: route.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 (126 lines) | stat: -rw-r--r-- 3,681 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
require 'rack/mount/generatable_regexp'
require 'rack/mount/regexp_with_named_groups'
require 'rack/mount/utils'

module Rack::Mount
  # Route is an internal class used to wrap a single route attributes.
  #
  # Plugins should not depend on any method on this class or instantiate
  # new Route objects. Instead use the factory method, RouteSet#add_route
  # to create new routes and add them to the set.
  class Route
    # Valid rack application to call if conditions are met
    attr_reader :app

    # A hash of conditions to match against. Conditions may be expressed
    # as strings or regexps to match against.
    attr_reader :conditions

    # A hash of values that always gets merged into the parameters hash
    attr_reader :defaults

    # Symbol identifier for the route used with named route generations
    attr_reader :name

    attr_reader :named_captures

    def initialize(app, conditions, defaults, name)
      unless app.respond_to?(:call)
        raise ArgumentError, 'app must be a valid rack application' \
          ' and respond to call'
      end
      @app = app

      @name = name ? name.to_sym : nil
      @defaults = (defaults || {}).freeze

      @conditions = {}

      conditions.each do |method, pattern|
        next unless method && pattern

        pattern = Regexp.compile("\\A#{Regexp.escape(pattern)}\\Z") if pattern.is_a?(String)

        if pattern.is_a?(Regexp)
          pattern = Utils.normalize_extended_expression(pattern)
          pattern = RegexpWithNamedGroups.new(pattern)
          pattern.extend(GeneratableRegexp::InstanceMethods)
          pattern.defaults = @defaults
        end

        @conditions[method] = pattern.freeze
      end

      @named_captures = {}
      @conditions.map { |method, condition|
        next unless condition.respond_to?(:named_captures)
        @named_captures[method] = Hash[condition.named_captures.map { |k, v|
          [k.to_sym, v.last - 1]
        }].freeze
      }
      @named_captures.freeze

      @has_significant_params = @conditions.any? { |method, condition|
        (condition.respond_to?(:required_params) && condition.required_params.any?) ||
          (condition.respond_to?(:required_defaults) && condition.required_defaults.any?)
      }

      if @conditions.has_key?(:path_info) &&
          !Utils.regexp_anchored?(@conditions[:path_info])
        @prefix = true
        @app = Prefix.new(@app)
      else
        @prefix = false
      end

      @conditions.freeze
    end

    def prefix?
      @prefix
    end


    def generation_keys
      @conditions.inject({}) { |keys, (_, condition)|
        if condition.respond_to?(:required_defaults)
          keys.merge!(condition.required_defaults)
        else
          keys
        end
      }
    end

    def significant_params?
      @has_significant_params
    end

    def generate(method, params = {}, recall = {}, options = {})
      if method.nil?
        result = Hash[@conditions.map { |m, condition|
          [m, condition.generate(params, recall, options)] if condition.respond_to?(:generate)
        }.compact]
        return nil if result.values.compact.empty?
      else
        if condition = @conditions[method]
          if condition.respond_to?(:generate)
            result = condition.generate(params, recall, options)
          end
        end
      end

      if result
        @defaults.each do |key, value|
          params.delete(key) if params[key] == value
        end
      end

      result
    end


    def inspect #:nodoc:
      "#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>"
    end
  end
end