Class: ServerSide::Router

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

Overview

The Router class defines a kind of connection that can route requests to 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 in 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]+))/

Constants inherited from HTTP::Request

HTTP::Request::AMPERSAND, HTTP::Request::CLOSE, HTTP::Request::CONNECTION, 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_CACHE_FILE_SIZE, Static::RHTML, Static::TEXT_HTML, Static::TEXT_PLAIN

Constants included from HTTP::Caching

HTTP::Caching::CACHE_CONTROL, HTTP::Caching::DEFAULT_MAX_AGE, HTTP::Caching::ETAG, HTTP::Caching::ETAG_WILDCARD, HTTP::Caching::IF_MODIFIED_SINCE, HTTP::Caching::IF_NONE_MATCH, HTTP::Caching::LAST_MODIFIED, HTTP::Caching::MAX_AGE, HTTP::Caching::NOT_MODIFIED_CLOSE, HTTP::Caching::NOT_MODIFIED_PERSIST

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_response, #set_cookie, #stream

Methods included from Static

#serve_dir, #serve_file, #serve_static, #serve_template

Methods included from HTTP::Caching

#send_not_modified, #valid_client_cache?, #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



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

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.



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

def self.compile_rules
  @@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.



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

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

.define_proc(&block) ⇒ Object

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



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

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

.has_routes?Boolean

Returns true if routes were defined.

Returns:

  • (Boolean)


26
27
28
# File 'lib/serverside/routing.rb', line 26

def self.has_routes?
  @@rules && !@@rules.empty? rescue false
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')}


38
39
40
41
42
43
# File 'lib/serverside/routing.rb', line 38

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

.route_default(&block) ⇒ Object

Sets the default handler for incoming requests.



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

def self.route_default(&block)
  define_method(:default_handler, &block)
  compile_rules
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.



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

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
  "return #{proc_tag} if #{cond}\n"
end

Instance Method Details

#unhandledObject Also known as: default_handler

Generic responder for unhandled requests.



125
126
127
# File 'lib/serverside/routing.rb', line 125

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