aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch/middleware/stack.rb
blob: 5c5362ce4a8ec7852f0ed95b3640cecd7e16d786 (plain) (blame)
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
require "active_support/inflector/methods"

module ActionDispatch
  class MiddlewareStack < Array
    class Middleware
      def self.new(klass, *args, &block)
        if klass.is_a?(self)
          klass
        else
          super
        end
      end

      attr_reader :args, :block

      def initialize(klass, *args, &block)
        @klass = klass

        options = args.extract_options!
        if options.has_key?(:if)
          @conditional = options.delete(:if)
        else
          @conditional = true
        end
        args << options unless options.empty?

        @args = args
        @block = block
      end

      def klass
        if @klass.respond_to?(:new)
          @klass
        elsif @klass.respond_to?(:call)
          @klass.call
        else
          ActiveSupport::Inflector.constantize(@klass.to_s)
        end
      end

      def active?
        return false unless klass

        if @conditional.respond_to?(:call)
          @conditional.call
        else
          @conditional
        end
      end

      def ==(middleware)
        case middleware
        when Middleware
          klass == middleware.klass
        when Class
          klass == middleware
        else
          if lazy_compare?(@klass) && lazy_compare?(middleware)
            normalize(@klass) == normalize(middleware)
          else
            klass.name == middleware.to_s
          end
        end
      end

      def inspect
        klass.to_s
      end

      def build(app)
        if block
          klass.new(app, *build_args, &block)
        else
          klass.new(app, *build_args)
        end
      end

      private
        def lazy_compare?(object)
          object.is_a?(String) || object.is_a?(Symbol)
        end

        def normalize(object)
          object.to_s.strip.sub(/^::/, '')
        end

        def build_args
          Array(args).map { |arg| arg.respond_to?(:call) ? arg.call : arg }
        end
    end

    def initialize(*args, &block)
      super(*args)
      block.call(self) if block_given?
    end

    def insert(index, *args, &block)
      index = self.index(index) unless index.is_a?(Integer)
      middleware = Middleware.new(*args, &block)
      super(index, middleware)
    end

    alias_method :insert_before, :insert

    def insert_after(index, *args, &block)
      i = index.is_a?(Integer) ? index : self.index(index)
      raise "No such middleware to insert after: #{index.inspect}" unless i
      insert(i + 1, *args, &block)
    end

    def swap(target, *args, &block)
      insert_before(target, *args, &block)
      delete(target)
    end

    def use(*args, &block)
      middleware = Middleware.new(*args, &block)
      push(middleware)
    end

    def active
      find_all { |middleware| middleware.active? }
    end

    def build(app = nil, &blk)
      app ||= blk

      raise "MiddlewareStack#build requires an app" unless app

      active.reverse.inject(app) { |a, e| e.build(a) }
    end
  end
end