Class: ServerSide::Router

Inherits:
HTTP::Request show all
Defined in:
lib/serverside/routing.rb

Overview

The Router class is a subclass of HTTP::Request that can invoke different handlers based on rules that can be specified either by lambdas or by hashes that contain variable names corresponding to patterns.

The simplest form of a routing rule specifies a path pattern:

ServerSide.route('/static') {serve_static('.'/@path)}

But you can also check for other attributes of the request:

ServerSide.route(:path => '/static', :host => '^:subdomain\.mydomain') {
  serve_static(@parameters[:subdomain]/@path)
}

It also possible to pass a lambda as a rule:

ServerSide.route(lambda {@headers['Agent'] =~ /Moz/}) {serve_static('moz'/@path)}

Routing rules are evaluated backwards, so the rules should be ordered from the general to the specific.

Constant Summary collapse

ParamRegexp =

Pattern for finding parameters inside patterns. Parameters are parts of the pattern, which the routing pre-processor turns into sub-regexp that are used to extract parameter values from the pattern.

For example, matching ‘/controller/show’ against ‘/controller/:action’ will give us @parameters #=> “show”

/(?::([a-z]+))/
@@rules =
[]
@@default_route =
nil

Constants inherited from HTTP::Request

HTTP::Request::AMPERSAND, HTTP::Request::CLOSE, HTTP::Request::CONNECTION, HTTP::Request::CONTENT_DESCRIPTION, HTTP::Request::CONTENT_DISPOSITION, HTTP::Request::CONTENT_DISPOSITION_REGEXP, HTTP::Request::CONTENT_LENGTH, HTTP::Request::CONTENT_TYPE, HTTP::Request::CONTENT_TYPE_REGEXP, HTTP::Request::CONTENT_TYPE_URL_ENCODED, HTTP::Request::COOKIE, HTTP::Request::COOKIE_EXPIRED_TIME, HTTP::Request::COOKIE_REGEXP, HTTP::Request::COOKIE_SPLIT, HTTP::Request::EMPTY_HASH, HTTP::Request::EMPTY_STRING, HTTP::Request::EQUAL_SIGN, HTTP::Request::FIELD_ATTRIBUTE_REGEXP, HTTP::Request::HEADER, HTTP::Request::HEADER_REGEXP, HTTP::Request::LINE_BREAK, HTTP::Request::LOCATION, HTTP::Request::MULTIPART_REGEXP, HTTP::Request::PARAMETER_REGEXP, HTTP::Request::REQUEST_REGEXP, HTTP::Request::SET_COOKIE, HTTP::Request::SLASH, HTTP::Request::STATUS_CLOSE, HTTP::Request::STATUS_PERSIST, HTTP::Request::STATUS_REDIRECT, HTTP::Request::STATUS_STREAM, HTTP::Request::VERSION_1_1

Constants included from Static

Static::DIR_LISTING, Static::DIR_LISTING_START, Static::DIR_LISTING_STOP, Static::ETAG_FORMAT, Static::FILE_NOT_FOUND, Static::MAX_AGE, Static::MAX_CACHE_FILE_SIZE, Static::RHTML, Static::TEXT_HTML, Static::TEXT_PLAIN

Constants included from HTTP::Caching

HTTP::Caching::CACHE_CONTROL, HTTP::Caching::ETAG, HTTP::Caching::ETAG_QUOTE_FORMAT, HTTP::Caching::EXPIRES, HTTP::Caching::EXPIRY_ETAG_FORMAT, HTTP::Caching::EXPIRY_ETAG_REGEXP, HTTP::Caching::IF_MODIFIED_SINCE, HTTP::Caching::IF_NONE_MATCH, HTTP::Caching::IF_NONE_MATCH_REGEXP, HTTP::Caching::LAST_MODIFIED, HTTP::Caching::NOT_MODIFIED_CLOSE, HTTP::Caching::NOT_MODIFIED_PERSIST, HTTP::Caching::NO_CACHE, HTTP::Caching::VARY, HTTP::Caching::WILDCARD

Instance Attribute Summary

Attributes inherited from HTTP::Request

#body, #content_length, #content_type, #cookies, #headers, #method, #parameters, #path, #persistent, #query, #response_cookies, #response_headers, #socket, #version

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from HTTP::Request

#delete_cookie, #initialize, #parse, #parse_body, #parse_cookies, #parse_parameters, #process, #redirect, #send_file, #send_response, #set_cookie, #stream

Methods included from Static

#serve_dir, #serve_file, #serve_static, #serve_template

Methods included from HTTP::Caching

#disable_caching, #etag_validators, #expiry_etag, #send_not_modified_response, #valid_etag?, #valid_stamp?, #validate_cache

Constructor Details

This class inherits a constructor from ServerSide::HTTP::Request

Class Method Details

.cache_constant(value) ⇒ Object

Converts a value into a local constant and freezes it. Returns the constant’s tag name



113
114
115
116
117
# File 'lib/serverside/routing.rb', line 113

def self.cache_constant(value)
  tag = value.const_tag
  class_eval "#{tag} = #{value.inspect}.freeze" rescue nil
  tag
end

.compile_rulesObject

Compiles all rules into a respond method that is invoked when a request is received.



49
50
51
52
53
# File 'lib/serverside/routing.rb', line 49

def self.compile_rules
  code = @@rules.inject('lambda {') {|m, r| m << rule_to_statement(r[0], r[1])}
  code << 'default_handler}'
  define_method(:respond, &eval(code))
end

.condition_part(key, value) ⇒ Object

Returns the condition part for the key and value specified. The key is the name of an instance variable and the value is a pattern to match against. If the pattern contains parameters (for example, /controller/:action,) the method creates a lambda for extracting the parameter values.



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/serverside/routing.rb', line 85

def self.condition_part(key, value)
  p_parse, p_count = '', 0
  while (String === value) && (value =~ ParamRegexp)
    value = value.dup
    p_name = $1
    p_count += 1
    value.sub!(ParamRegexp, '(.+)')
    p_parse << "@parameters[:#{p_name}] = $#{p_count}\n"
  end
  cond = "(@#{key} =~ #{cache_constant(Regexp.new(value))})"
  if p_count == 0
    cond
  else
    tag = define_proc(&eval(
      "lambda {if #{cond}\n#{p_parse}true\nelse\nfalse\nend}"))
    "(#{tag})"
  end
end

.default_route(&block) ⇒ Object

Sets the default handler for incoming requests.



120
121
122
123
124
# File 'lib/serverside/routing.rb', line 120

def self.default_route(&block)
  @@default_route = block
  define_method(:default_handler, &block)
  compile_rules
end

.define_proc(&block) ⇒ Object

Converts a proc into a method, returning the method’s name (as a symbol)



105
106
107
108
109
# File 'lib/serverside/routing.rb', line 105

def self.define_proc(&block)
  tag = block.proc_tag
  define_method(tag.to_sym, &block) unless instance_methods.include?(tag)
  tag.to_sym
end

.route(rule, &block) ⇒ Object

Adds a routing rule. The normalized rule is a hash containing keys (acting as instance variable names) with patterns as values. If the rule is not a hash, it is normalized into a pattern checked against the request path. Pattern values can also be arrays, any member of which is checked as a pattern. The rule can also be a Proc or lambda which is run with the connection object’s binding. A contrived example:

ServerSide.route(lambda{path = 'mypage'}) {serve_static('mypage.html')}


41
42
43
44
45
# File 'lib/serverside/routing.rb', line 41

def self.route(rule, &block)
  rule = {:path => rule} unless (Hash === rule) || (Proc === rule)
  @@rules.unshift [rule, block]
  compile_rules
end

.routes_defined?Boolean

Returns true if routes were defined.

Returns:

  • (Boolean)


29
30
31
# File 'lib/serverside/routing.rb', line 29

def self.routes_defined?
  !@@rules.empty? || @@default_route
end

.rule_to_statement(rule, block) ⇒ Object

Converts a rule into an if statement. All keys in the rule are matched against their respective values.



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/serverside/routing.rb', line 57

def self.rule_to_statement(rule, block)
  proc_tag = define_proc(&block)
  if Proc === rule
    cond = define_proc(&rule).to_s
  else
    cond = rule.to_a.map {|kv|
      if Array === kv[1]
        '(' + kv[1].map {|v| condition_part(kv[0], v)}.join('||') + ')'
      else
        condition_part(kv[0], kv[1])
      end
    }.join('&&')
  end
  "if #{cond} && (r = #{proc_tag}); return r; end\n"
end

Instance Method Details

#unhandledObject Also known as: default_handler

Generic responder for unhandled requests.



127
128
129
# File 'lib/serverside/routing.rb', line 127

def unhandled
  send_response(403, 'text', 'No handler found.')
end