Class: Rack::MatrixParams

Inherits:
Object
  • Object
show all
Defined in:
lib/sinatra/rack_matrix_params.rb

Instance Method Summary collapse

Constructor Details

#initialize(app) ⇒ MatrixParams

Returns a new instance of MatrixParams.



23
24
25
# File 'lib/sinatra/rack_matrix_params.rb', line 23

def initialize(app)
  @app = app
end

Instance Method Details

#call(env) ⇒ Object

This will allow to use 'matrix' params in requests, like:

http://example.com/library;section=nw/books;topic=money;binding=hardcover

Will result in this params matrix:

=> params['section'] = 'nw' => params['topic'] = 'money' => params['binding'] = 'hardcover'

All HTTP methods are supported, in case of POST they will be passed as a regular

parameters.



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
# File 'lib/sinatra/rack_matrix_params.rb', line 39

def call(env)

  # This ugly hack should fix the issue with Rack::Test where
  # these two variables are empty and Rack::Test will always
  # return 404.
  #
  if env['rack.test']
    env['REQUEST_URI'] = env['PATH_INFO']
    env['REQUEST_PATH'] = env['PATH_INFO']
  end

  # Split URI to components and then extract ;var=value pairs
  matrix_params = {}
  uri_components = (env['rack.test'] ? env['PATH_INFO'] : env['REQUEST_URI']).split('/')

  uri_components.each do |component|
    sub_components, value = component.split(/\;(\w+)\=/), nil
    next unless sub_components.first  # Skip subcomponent if it's empty (usually /)
    while param=sub_components.pop do
      if value
        matrix_params[sub_components.first] ||= {}
        matrix_params[sub_components.first].merge!(param => value)
        value=nil
        next
      else
        value = param.gsub(/\?.*$/, '')
      end
    end
  end

  # Two things need to happen to make matrix params work:
  #     (1) the parameters need to be appended to the 'normal' params
  #         for the request. 'Normal' really depends on the content
  #         type of the request, which does not seem accessible from
  #         Middleware, so we use the existence of
  #         rack.request.form_hash in the environment to distinguish
  #         between basic and application/x-www-form-urlencoded
  #         requests
  #     (2) the parameters need to be stripped from the appropriate
  #         path related env variables, so that request dispatching
  #         does not trip over them

  # (1) Rewrite current path by stripping all matrix params from it
  ['REQUEST_PATH', 'REQUEST_URI', 'PATH_INFO'].select { |k|
    env[k] }.each do |k|
    env[k] = env[k].remove_matrix_params
  end

  # (2) Append the matrix params to the 'normal' request params
  # FIXME: Make this work for multipart/form-data
  if env['rack.request.form_hash']
    # application/x-www-form-urlencoded, most likely a POST
    env['rack.request.form_hash'].merge!(matrix_params)
  else
    # For other methods it's more complicated
    if env['REQUEST_METHOD']!='POST' and not matrix_params.keys.empty?
      env['QUERY_STRING'] = env['QUERY_STRING'].gsub(/;([^\/]*)/, '')
      new_params = matrix_params.collect do |component, params|
        params.collect { |k,v| "#{component}[#{k}]=#{CGI::escape(v.to_s)}" }
      end.flatten
      # Add matrix params as a regular GET params
      env['QUERY_STRING'] += '&' if not env['QUERY_STRING'].empty?
      env['QUERY_STRING'] += "#{new_params.join('&')}"
    end
  end
  @app.call(env)
end