sidewalk 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/sidewalk/app_uri.rb +33 -0
- data/lib/sidewalk/application.rb +146 -0
- data/lib/sidewalk/config.rb +160 -0
- data/lib/sidewalk/controller.rb +141 -0
- data/lib/sidewalk/controller_mixins/view_templates.rb +94 -0
- data/lib/sidewalk/errors.rb +71 -0
- data/lib/sidewalk/redirect.rb +78 -0
- data/lib/sidewalk/regexp.rb +32 -5
- data/lib/sidewalk/relative_uri.rb +38 -0
- data/lib/sidewalk/request.rb +116 -13
- data/lib/sidewalk/rooted_uri.rb +32 -0
- data/lib/sidewalk/template_handlers/base.rb +30 -0
- data/lib/sidewalk/template_handlers/base_delegate.rb +39 -0
- data/lib/sidewalk/template_handlers/erb_handler.rb +40 -0
- data/lib/sidewalk/template_handlers/haml_handler.rb +19 -0
- data/lib/sidewalk/template_handlers/rxhp_handler.rb +32 -0
- data/lib/sidewalk/uri.rb +2 -0
- data/lib/sidewalk/uri_mapper.rb +89 -2
- data/lib/sidewalk/uri_match.rb +17 -1
- data/lib/sidewalk.rb +2 -0
- metadata +32 -4
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'sidewalk/errors'
|
2
|
+
|
3
|
+
module Sidewalk
|
4
|
+
# Base class for HTTP-level redirections.
|
5
|
+
#
|
6
|
+
# This, and its' subclasses are expected to be +raise+d.
|
7
|
+
class Redirect < HttpError
|
8
|
+
# Where to redirect to.
|
9
|
+
attr_reader :url
|
10
|
+
|
11
|
+
# Initialize a Redirect.
|
12
|
+
#
|
13
|
+
# You probably don't want to use this directly - use
|
14
|
+
# {PermanentRedirect} or {SeeOtherRedirect} instead.
|
15
|
+
#
|
16
|
+
# @param [String, URI] url is where to redirect to
|
17
|
+
# @param [Fixnum] status is a numeric HTTP status code
|
18
|
+
# @param [String] description is a short description of the status
|
19
|
+
# code, such as 'Moved Permanently'
|
20
|
+
def initialize url, status, description
|
21
|
+
@url = url.to_s
|
22
|
+
super status, description
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Use if a resource has permanently moved.
|
27
|
+
#
|
28
|
+
# Does not change POST into GET.
|
29
|
+
#
|
30
|
+
# Uses a 301 Moved Permanently response.
|
31
|
+
class PermanentRedirect < Redirect
|
32
|
+
def initialize url
|
33
|
+
super url, 301, 'Moved Permanently'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Use if the resource hasn't moved, but you want to redirect anyway.
|
38
|
+
#
|
39
|
+
# The new request will be a GET request.
|
40
|
+
#
|
41
|
+
# Standard usage is to redirect after a POST, to make the browser's
|
42
|
+
# Refresh and back/forward buttons work as expected.
|
43
|
+
#
|
44
|
+
# Defaults to a '303 See Other', but if you need to support pre-HTTP/1.1
|
45
|
+
# browsers, you might want to change this.
|
46
|
+
#
|
47
|
+
# @example Supporting Legacy Browsers
|
48
|
+
class SeeOtherRedirect < Redirect
|
49
|
+
def initialize url
|
50
|
+
super url, nil, nil
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return an appropriate status code.
|
54
|
+
#
|
55
|
+
# @return +303+ for HTTP/1.1 clients
|
56
|
+
# @return +302+ for HTTP/1.0 clients
|
57
|
+
def status request
|
58
|
+
if request.http_version == '1.1'
|
59
|
+
303
|
60
|
+
else
|
61
|
+
302
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Return an appropriate description.
|
66
|
+
#
|
67
|
+
# @return 'See Other' for HTTP/1.1 clients
|
68
|
+
# @return 'Found' for HTTP/1.0 clients
|
69
|
+
def description request
|
70
|
+
case status(request)
|
71
|
+
when 303
|
72
|
+
'See Other'
|
73
|
+
when 302
|
74
|
+
'Found'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
data/lib/sidewalk/regexp.rb
CHANGED
@@ -11,13 +11,40 @@ module Sidewalk
|
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
if
|
14
|
+
REGEXP_USING_ONIGURUMA = RUBY_VERSION.start_with?('1.8.')
|
15
|
+
if REGEXP_USING_ONIGURUMA
|
16
16
|
require 'oniguruma'
|
17
|
-
|
18
|
-
|
17
|
+
REGEXP_BASE = ::Oniguruma::ORegexp
|
19
18
|
::MatchData.send(:include, Sidewalk::Oniguruma::MatchData)
|
20
19
|
else
|
21
|
-
|
20
|
+
REGEXP_BASE = ::Regexp
|
21
|
+
end
|
22
|
+
|
23
|
+
# A Regexp class that supports named captures.
|
24
|
+
#
|
25
|
+
# This class exists just to give a portable class name to refer to.
|
26
|
+
#
|
27
|
+
# * On Ruby 1.9, this inherits +::Regexp+.
|
28
|
+
# * On Ruby 1.8, this inherits +Oniguruma::ORegexp+ (which is basically
|
29
|
+
# the same library that Ruby 1.9 uses for +::Regexp+)
|
30
|
+
#
|
31
|
+
# @example Using named captures
|
32
|
+
# regexp = Sidewalk::Regexp.new('(?<foo>bar)')
|
33
|
+
# match = regexp.match(regexp)
|
34
|
+
# match['foo'].should == 'bar'
|
35
|
+
class Regexp < REGEXP_BASE
|
36
|
+
# Whether we're using +Onigirumua::ORegexp+.
|
37
|
+
#
|
38
|
+
# Inverse of {#native?}.
|
39
|
+
def self.oniguruma?
|
40
|
+
Sidewalk::REGEXP_USING_ONIGURUMA
|
41
|
+
end
|
42
|
+
|
43
|
+
# Whether we're using +::Regexp+
|
44
|
+
#
|
45
|
+
# Inverse of {#native?}.
|
46
|
+
def self.native?
|
47
|
+
!self.oniguruma?
|
48
|
+
end
|
22
49
|
end
|
23
50
|
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'sidewalk/controller'
|
2
|
+
require 'sidewalk/rooted_uri'
|
3
|
+
require 'sidewalk/request'
|
4
|
+
|
5
|
+
module Sidewalk
|
6
|
+
# URI relative to the current request.
|
7
|
+
#
|
8
|
+
# For example, if your app lives at 'http://www.example.com/foo',
|
9
|
+
# the current request is to '/foo/bar', +RelativeUri.new('/baz').to_s+
|
10
|
+
# will return 'http://www.example.com/foo/bar/baz', whereas {AppUri}
|
11
|
+
# would give you 'http://www.example.com/foo/baz'.
|
12
|
+
#
|
13
|
+
# Existing query data is discarded.
|
14
|
+
#
|
15
|
+
# Not a real class as the +URI+ hierarchy doesn't lend itself to
|
16
|
+
# subclassing.
|
17
|
+
module RelativeUri
|
18
|
+
# Create a URI relative to the current request.
|
19
|
+
#
|
20
|
+
# If this is called, it _must_ have {Controller#call} in the call stack
|
21
|
+
# so that {Controller#current} works - otherwise it does not have
|
22
|
+
# enough information to construct the URI.
|
23
|
+
#
|
24
|
+
# Query string data is discarded.
|
25
|
+
#
|
26
|
+
# @param [String] path is the path relative to the current request.
|
27
|
+
# @param [Hash] query is a +Hash+ of key-value query data.
|
28
|
+
def self.new path, query = {}
|
29
|
+
context = Sidewalk::Controller.current
|
30
|
+
unless context
|
31
|
+
raise ScriptError.new("Only valid when called by a controller")
|
32
|
+
end
|
33
|
+
uri = context.request.uri
|
34
|
+
|
35
|
+
Sidewalk::RootedUri.new(uri, path, query)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/sidewalk/request.rb
CHANGED
@@ -4,38 +4,135 @@ require 'rack/request'
|
|
4
4
|
require 'uri'
|
5
5
|
|
6
6
|
module Sidewalk
|
7
|
+
# An object representing an HTTP request.
|
8
|
+
#
|
9
|
+
# This is meant to provide convenient, high-level functions.
|
10
|
+
# You do have access to #{rack_environment} and #{rack_request} for
|
11
|
+
# lower-level information, but it's best avoided.
|
12
|
+
#
|
13
|
+
#
|
14
|
+
# Instances of this class are created by the {Application}.
|
7
15
|
class Request
|
8
|
-
|
16
|
+
# The environment array provided by Rack.
|
17
|
+
#
|
18
|
+
# Please don't use this directly unless you're sure you need to.
|
19
|
+
attr_reader :rack_environment
|
20
|
+
# An instance of +Rack::Request+.
|
21
|
+
#
|
22
|
+
# This is a lower-level wrapper around {#rack_environment}
|
23
|
+
#
|
24
|
+
# Please don't use this directly unless you're sure you need to.
|
25
|
+
attr_reader :rack_request
|
26
|
+
|
27
|
+
# Create a new instance from a Rack environment array
|
28
|
+
#
|
29
|
+
# @param [Array] env is the environment data provided by Rack.
|
9
30
|
def initialize env
|
10
|
-
@
|
31
|
+
@rack_environment = env
|
11
32
|
@rack_version = env['rack.version']
|
12
33
|
|
13
34
|
sanity_check!
|
14
|
-
@rack_request = Rack::Request.new(
|
35
|
+
@rack_request = Rack::Request.new(rack_environment)
|
15
36
|
|
16
37
|
initialize_uris
|
17
38
|
end
|
18
39
|
|
40
|
+
# The HTTP headers.
|
41
|
+
#
|
42
|
+
# This does not include Rack/CGI variables - just real HTTP headers.
|
43
|
+
def headers
|
44
|
+
@headers ||= rack_environment.select{|k,v| k.start_with? 'HTTP_'}
|
45
|
+
end
|
46
|
+
|
47
|
+
# Cookies provided by the client.
|
48
|
+
#
|
49
|
+
# @returns [Hash]
|
50
|
+
def cookies
|
51
|
+
rack_request.cookies
|
52
|
+
end
|
53
|
+
|
54
|
+
# What version of HTTP the client is using.
|
55
|
+
#
|
56
|
+
# @return '1.1'
|
57
|
+
# @return '1.0'
|
58
|
+
# @return nil
|
59
|
+
def http_version
|
60
|
+
@http_version ||= [
|
61
|
+
'HTTP_VERSION',
|
62
|
+
'SERVER_PROTOCOL',
|
63
|
+
].map{ |x| rack_environment[x] }.find.first.to_s.split('/').last
|
64
|
+
end
|
65
|
+
|
66
|
+
# The root URI of the application.
|
67
|
+
#
|
68
|
+
# If you're looking at this to construct an URI to another page, take a
|
69
|
+
# look at {Sidewalk::AppUri}.
|
70
|
+
#
|
71
|
+
# If you want to find out where the current request was to, see {#uri}.
|
72
|
+
#
|
73
|
+
# @return [URI::Common]
|
74
|
+
def root_uri
|
75
|
+
@root_uri.dup
|
76
|
+
end
|
77
|
+
|
78
|
+
# The URI of the current request.
|
79
|
+
#
|
80
|
+
# If you're looking at this to construct a relative URI, take a look at
|
81
|
+
# {Sidewalk::RelativeUri}.
|
82
|
+
#
|
83
|
+
# If you want to find out what the URI for the application is, see
|
84
|
+
# {#root_uri}.
|
85
|
+
#
|
86
|
+
# @return [URI::Common]
|
87
|
+
def uri
|
88
|
+
@request_uri.dup
|
89
|
+
end
|
90
|
+
|
91
|
+
# Whether or not this request came via HTTPS.
|
92
|
+
#
|
93
|
+
# @return [true, false]
|
19
94
|
def secure?
|
20
95
|
@secure
|
21
96
|
end
|
22
97
|
|
98
|
+
# Parameters provided via the query string.
|
99
|
+
#
|
100
|
+
# Consider using {#params} instead.
|
101
|
+
#
|
102
|
+
# @return [Hash]
|
23
103
|
def get_params
|
24
104
|
@get_params ||= @rack_request.GET
|
25
105
|
end
|
26
106
|
|
107
|
+
# The {UriMatch} created by {UriMapper}.
|
27
108
|
def uri_match
|
28
|
-
@uri_match ||=
|
109
|
+
@uri_match ||= rack_environment['sidewalk.urimatch']
|
29
110
|
end
|
30
111
|
|
112
|
+
# Paramters provided via POST.
|
113
|
+
#
|
114
|
+
# Consider using {#params} instead.
|
115
|
+
#
|
116
|
+
# @return [Hash]
|
31
117
|
def post_params
|
32
118
|
@post_params ||= @rack_request.POST
|
33
119
|
end
|
34
120
|
|
121
|
+
# Paramters provided via named captures in the URL.
|
122
|
+
#
|
123
|
+
# Consider using {#params} instead.
|
124
|
+
#
|
125
|
+
# @return [Hash]
|
35
126
|
def uri_params
|
36
127
|
uri_match.parameters
|
37
128
|
end
|
38
129
|
|
130
|
+
# Parameters provided by any means.
|
131
|
+
#
|
132
|
+
# Precedence:
|
133
|
+
# * URL captures first
|
134
|
+
# * Then query string parameters
|
135
|
+
# * Then POST parameters
|
39
136
|
def params
|
40
137
|
# URI parameters take precendence
|
41
138
|
@params ||= post_params.merge(get_params.merge(uri_params))
|
@@ -44,7 +141,7 @@ module Sidewalk
|
|
44
141
|
private
|
45
142
|
|
46
143
|
def initialize_uris
|
47
|
-
case
|
144
|
+
case rack_environment['rack.url_scheme']
|
48
145
|
when 'http'
|
49
146
|
@secure = false
|
50
147
|
uri_class = URI::HTTP
|
@@ -52,36 +149,42 @@ module Sidewalk
|
|
52
149
|
@secure = true
|
53
150
|
uri_class = URI::HTTPS
|
54
151
|
else
|
55
|
-
raise
|
152
|
+
raise ArgumentError.new(
|
153
|
+
"Unknown URL scheme: #{rack_environment['rack.url_scheme'].inspect}"
|
154
|
+
)
|
56
155
|
end
|
57
156
|
|
58
|
-
root =
|
157
|
+
root = rack_environment['SCRIPT_NAME']
|
59
158
|
unless root.start_with? '/'
|
60
159
|
root[0,0] = '/' # no prepend on 1.8
|
61
160
|
end
|
62
161
|
|
63
162
|
@root_uri = uri_class.build(
|
64
|
-
:host =>
|
65
|
-
:port =>
|
163
|
+
:host => rack_environment['SERVER_NAME'],
|
164
|
+
:port => rack_environment['SERVER_PORT'].to_i,
|
66
165
|
:path => root
|
67
166
|
).freeze
|
68
|
-
path_info =
|
167
|
+
path_info = rack_environment['PATH_INFO']
|
69
168
|
if root.end_with? '/' and path_info.start_with? '/'
|
70
169
|
path_info = path_info[1..-1]
|
71
170
|
end
|
72
171
|
@request_uri = @root_uri.dup
|
73
172
|
@request_uri.path += path_info
|
74
|
-
@request_uri.query =
|
173
|
+
@request_uri.query = rack_environment['QUERY_STRING']
|
75
174
|
@request_uri.freeze
|
76
175
|
end
|
77
176
|
|
78
177
|
def sanity_check!
|
79
178
|
# Sanity checks
|
80
179
|
unless @rack_version
|
81
|
-
raise ArgumentError.new
|
180
|
+
raise ArgumentError.new(
|
181
|
+
"env doesn't specify a Rack version"
|
182
|
+
)
|
82
183
|
end
|
83
184
|
if @rack_version != [1, 1]
|
84
|
-
raise
|
185
|
+
raise ArgumentError.new(
|
186
|
+
"Expected Rack version [1, 1], got #{@rack_version}"
|
187
|
+
)
|
85
188
|
end
|
86
189
|
end
|
87
190
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'rack/utils'
|
2
|
+
|
3
|
+
module Sidewalk
|
4
|
+
# A URI relative to a specified root URI.
|
5
|
+
#
|
6
|
+
# You probably don't want to use this directly - see {AppUri} and
|
7
|
+
# {RelativeUri} for examples.
|
8
|
+
module RootedUri
|
9
|
+
# Create a new URI, relative to the specified root.
|
10
|
+
#
|
11
|
+
# @param [URI::Common] root is the uri that you want to use as the base
|
12
|
+
# @param [String] path is the sub-path to add
|
13
|
+
# @param [Hash] query is a +Hash+ of key-values to add to the query
|
14
|
+
# string.
|
15
|
+
def self.new root, path, query = {}
|
16
|
+
uri = root.dup
|
17
|
+
root_path = uri.path
|
18
|
+
root_path = root_path[0..-2] if root_path.end_with? '/'
|
19
|
+
uri.path = root_path + path
|
20
|
+
|
21
|
+
query_string = query.map do |k,v|
|
22
|
+
'%s=%s' % [
|
23
|
+
Rack::Utils.escape(k.to_s),
|
24
|
+
Rack::Utils.escape(v.to_s),
|
25
|
+
]
|
26
|
+
end.join('&')
|
27
|
+
uri.query = query_string unless query.empty?
|
28
|
+
|
29
|
+
uri
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Sidewalk
|
2
|
+
module TemplateHandlers
|
3
|
+
# Base class for TemplateHandlers.
|
4
|
+
#
|
5
|
+
# This currently just defines an interface - see {ErbHandler} for an
|
6
|
+
# example.
|
7
|
+
#
|
8
|
+
# Instances of this class may be re-used between requests. Anything
|
9
|
+
# request-specific needs to go in the {#render} method.
|
10
|
+
class Base
|
11
|
+
# Actually render a template.
|
12
|
+
#
|
13
|
+
# @param {Controller} controller is the controller that wants the
|
14
|
+
# template. Instance variables should be available to the template,
|
15
|
+
# and the handler should not change the scope. See {BaseDelegate}
|
16
|
+
# and {ErbHandler::Delegate} for an approach to this.
|
17
|
+
#
|
18
|
+
# @return [String]
|
19
|
+
def render controller
|
20
|
+
raise NotImplementedError.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# A new instance of the controller.
|
24
|
+
#
|
25
|
+
# @param [String] path a full path to a template source file.
|
26
|
+
def initialize path
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Sidewalk
|
2
|
+
module TemplateHandlers
|
3
|
+
# A delegate scope to run templates in.
|
4
|
+
#
|
5
|
+
# Used to provide templates access to the controller's instance
|
6
|
+
# variables, and extra helper functions, without polluting the
|
7
|
+
# controller with them. For example, {ErbHandler::Delegate} will also
|
8
|
+
# include the standard +ERB::Util+ helper functions like +#h+ (aka
|
9
|
+
# +#html_escape+)
|
10
|
+
class BaseDelegate
|
11
|
+
# A new instance of delegate.
|
12
|
+
#
|
13
|
+
# Copies all instance variables from controller to object.
|
14
|
+
#
|
15
|
+
# @param [Controller] controller
|
16
|
+
def initialize controller
|
17
|
+
controller.instance_variables.each do |name|
|
18
|
+
self.instance_variable_set(
|
19
|
+
name,
|
20
|
+
controller.instance_variable_get(name)
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Render the template in the scope.
|
26
|
+
#
|
27
|
+
# The default implementation raises a NotImplementedError.
|
28
|
+
#
|
29
|
+
# @example {ErbDelegate#render}
|
30
|
+
# def render
|
31
|
+
# # @template is set by {ErbDelegate}'s constructor.
|
32
|
+
# @template.result binding
|
33
|
+
# end
|
34
|
+
def render
|
35
|
+
raise NotImplementedError.new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'sidewalk/template_handlers/base'
|
2
|
+
require 'sidewalk/template_handlers/base_delegate'
|
3
|
+
|
4
|
+
require 'erb'
|
5
|
+
|
6
|
+
module Sidewalk
|
7
|
+
module TemplateHandlers
|
8
|
+
# Handler for ERB Templates.
|
9
|
+
class ErbHandler < Base
|
10
|
+
def initialize path
|
11
|
+
super path
|
12
|
+
@template = ERB::new(File.read(path))
|
13
|
+
@template.filename = path
|
14
|
+
end
|
15
|
+
|
16
|
+
def render controller
|
17
|
+
Delegate.new(@template, controller).render
|
18
|
+
end
|
19
|
+
|
20
|
+
# Class representing the controller to ERB.
|
21
|
+
#
|
22
|
+
# Using a delegate so we can add extra methods to the binding
|
23
|
+
# without polluting the class - for example, most people expect an
|
24
|
+
# ERB template to have access to ERB::Util#h
|
25
|
+
class Delegate < BaseDelegate
|
26
|
+
# Pull in '#html_escape' aka '#h
|
27
|
+
include ERB::Util
|
28
|
+
|
29
|
+
def initialize template, controller
|
30
|
+
@template = template
|
31
|
+
super controller
|
32
|
+
end
|
33
|
+
|
34
|
+
def render
|
35
|
+
@template.result binding
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'sidewalk/template_handlers/base'
|
2
|
+
require 'sidewalk/template_handlers/base_delegate'
|
3
|
+
|
4
|
+
require 'haml'
|
5
|
+
|
6
|
+
module Sidewalk
|
7
|
+
module TemplateHandlers
|
8
|
+
class HamlHandler < Base
|
9
|
+
def initialize path
|
10
|
+
super path
|
11
|
+
@engine = Haml::Engine.new(File.read(path))
|
12
|
+
end
|
13
|
+
|
14
|
+
def render controller
|
15
|
+
@engine.render(BaseDelegate.new(controller))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'sidewalk/template_handlers/base'
|
2
|
+
require 'sidewalk/template_handlers/base_delegate'
|
3
|
+
|
4
|
+
require 'rxhp'
|
5
|
+
require 'rxhp/html'
|
6
|
+
|
7
|
+
module Sidewalk
|
8
|
+
module TemplateHandlers
|
9
|
+
class RxhpHandler < Base
|
10
|
+
def initialize path
|
11
|
+
@path = path
|
12
|
+
@template = File.read(path)
|
13
|
+
end
|
14
|
+
|
15
|
+
def render controller
|
16
|
+
Delegate.new(@path, @template, controller).render
|
17
|
+
end
|
18
|
+
|
19
|
+
class Delegate < BaseDelegate
|
20
|
+
include Rxhp::Html
|
21
|
+
def initialize path, template, controller
|
22
|
+
@path, @template, @controller = path, template, controller
|
23
|
+
super controller
|
24
|
+
end
|
25
|
+
|
26
|
+
def render
|
27
|
+
eval(@template, binding, @path).render
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/sidewalk/uri.rb
ADDED
data/lib/sidewalk/uri_mapper.rb
CHANGED
@@ -1,16 +1,83 @@
|
|
1
1
|
require 'sidewalk/regexp'
|
2
2
|
require 'sidewalk/uri_match'
|
3
3
|
|
4
|
+
require 'active_support/inflector'
|
5
|
+
|
4
6
|
module Sidewalk
|
7
|
+
autoload :Application, 'sidewalk/application'
|
8
|
+
|
9
|
+
# Maps URIs to Controllers.
|
10
|
+
#
|
11
|
+
# Used to decide how to respond to a given HTTP request.
|
5
12
|
class UriMapper
|
6
|
-
|
13
|
+
# Initialize a UriMapper.
|
14
|
+
#
|
15
|
+
# This converts a convenient-to-write map into a fast-to-lookup map.
|
16
|
+
#
|
17
|
+
# The input hash takes +String+ regular expression patterns as keys,
|
18
|
+
# and values can either be another map, a {Controller} class (not an
|
19
|
+
# instance), a +Symbol+ or +String+ that is the name of a {Controller}
|
20
|
+
# class, or a +Proc+.
|
21
|
+
#
|
22
|
+
# If the value is a +String+, it will:
|
23
|
+
# * see if a +Class+ with the same name exists; if so, use that.
|
24
|
+
# * try to +require+ +foo_bar_controller.rb+ for 'FooBarController'
|
25
|
+
# * see if a +Class+ now exists with the same name
|
26
|
+
# * if so, done; if not, bail out.
|
27
|
+
#
|
28
|
+
# It looks for controller files in
|
29
|
+
# {Application#local_root}+/controllers+
|
30
|
+
#
|
31
|
+
# You usually won't interact with this class directly - instead you'll
|
32
|
+
# usually pass the input hash to {Sidewalk::Application}.
|
33
|
+
#
|
34
|
+
# Keys are required to be +String+s instead of +Regexp+s because they
|
35
|
+
# are converted to a +Sidewalk::+{Regexp} behind the scenes; this is
|
36
|
+
# is the same thing as a +::Regexp+ on Ruby 1.9, but on Ruby 1.8 it
|
37
|
+
# adds several additional features such as named captures.
|
38
|
+
#
|
39
|
+
# @example A Simple Map
|
40
|
+
# urimap = {
|
41
|
+
# '$' => :IndexController,
|
42
|
+
# 'hello$' => :HelloController,
|
43
|
+
# }
|
44
|
+
# run Sidewalk::Application.new(urimap)
|
45
|
+
#
|
46
|
+
# @example A Nested Map
|
47
|
+
# urimap = {
|
48
|
+
# '$' => :IndexController,
|
49
|
+
# 'foo/' => {
|
50
|
+
# '$' => :FooController,
|
51
|
+
# 'bar$' => :FooBarController,
|
52
|
+
# },
|
53
|
+
# }
|
54
|
+
# run Sidewalk::Application.new(urimap)
|
55
|
+
#
|
56
|
+
# @example A Map With Named Captures
|
57
|
+
# urimap = {
|
58
|
+
# '$' => :IndexController,
|
59
|
+
# 'foo/$' => :FooController,
|
60
|
+
# '~(?<username>[^/]+)/$' => :UserController
|
61
|
+
# }
|
62
|
+
# run Sidewalk::Application.new(urimap)
|
63
|
+
#
|
64
|
+
# @param [Hash] uri_map
|
7
65
|
def initialize uri_map = {}
|
8
66
|
unless uri_map.is_a? Hash
|
9
67
|
raise ArgumentError.new('URI map must be a Hash')
|
10
68
|
end
|
69
|
+
$LOAD_PATH.push File.join(
|
70
|
+
Sidewalk::Application.local_root,
|
71
|
+
'controllers'
|
72
|
+
)
|
11
73
|
@uri_map = Sidewalk::UriMapper.convert_map(uri_map)
|
74
|
+
$LOAD_PATH.pop
|
12
75
|
end
|
13
76
|
|
77
|
+
# Given a path, find what should respond to it.
|
78
|
+
#
|
79
|
+
# @return [UriMatch] if something was found.
|
80
|
+
# @return [nil] if there is no match.
|
14
81
|
def map path
|
15
82
|
Sidewalk::UriMapper.map(
|
16
83
|
[], #stack
|
@@ -19,7 +86,17 @@ module Sidewalk
|
|
19
86
|
)
|
20
87
|
end
|
21
88
|
|
22
|
-
#
|
89
|
+
# The normalized URI map.
|
90
|
+
#
|
91
|
+
# @return [Hash<Regexp,(Controller|Proc)>]
|
92
|
+
attr_reader :uri_map
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
# Convert uri_map from easy-to-write to fast-to-use.
|
97
|
+
#
|
98
|
+
# - Replace string keys with Sidewalk::Regexp instances
|
99
|
+
# - Attempt to load classes for symbols
|
23
100
|
def self.convert_map uri_map
|
24
101
|
out = {}
|
25
102
|
uri_map.each do |key, value|
|
@@ -36,6 +113,16 @@ module Sidewalk
|
|
36
113
|
key = Sidewalk::Regexp.new(key)
|
37
114
|
if value.is_a? Hash
|
38
115
|
out[key] = convert_map(value)
|
116
|
+
elsif value.is_a? Class
|
117
|
+
out[key] = value
|
118
|
+
elsif value.to_s.end_with? 'Controller'
|
119
|
+
# Attempt to load the class
|
120
|
+
begin
|
121
|
+
out[key] = value.to_s.constantize
|
122
|
+
rescue NameError
|
123
|
+
require value.to_s.underscore
|
124
|
+
retry
|
125
|
+
end
|
39
126
|
else
|
40
127
|
out[key] = value
|
41
128
|
end
|