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:

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 <form> 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