sinatra-rroute 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.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +125 -0
- data/Rakefile +14 -0
- data/lib/sinatra/rroute.rb +431 -0
- data/lib/sinatra/rroute/version.rb +5 -0
- data/rroute.gemspec +37 -0
- data/spec/rroute_spec.rb +216 -0
- data/spec/test_app.rb +193 -0
- metadata +97 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 38d10acac3a5452cbbce30a6290ea0e381b5a6c0
|
|
4
|
+
data.tar.gz: fae34365023bd7529d861cfb82f35e1eaacd6eba
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: bff81eca8b56be9a0b66d08197268040e419f5beead89550918ec961946233b9a079472b069bfb28eb39f8b7a46e96bb9de9df5af93d94272f3c225e0f0a3d34
|
|
7
|
+
data.tar.gz: f3acaf70bdf9f67240eae87693cef2535548cd835aac1a87ce289cec3c6c1d9ccad47ff1a1df4a461fb0326377ab73da36ba924d919bf13a1844228d2eaae076
|
data/.gitignore
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
*.bundle
|
|
19
|
+
*.so
|
|
20
|
+
*.o
|
|
21
|
+
*.a
|
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 nounch
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Sinatra-rroute
|
|
2
|
+
|
|
3
|
+
Sinatra-rroute provides Rails-like routes for Sinatra. Using one of the `gget`/`ppost`/`ddelete`/... methods, a route is assigned a name, is mapped to a controller-like method and has a mask to be used by the provided `path` helper function. The `path` helper takes the name of a route, a set of keyword-value mappings, applies them to the mask and gives back the route with each keyword replaced by the associated value.
|
|
4
|
+
|
|
5
|
+
Routes can also be namespaced. Namespaces can be nested. There can be multiple namespaces per app.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
1. Include `sinatra-rroute` in your Gemfile:
|
|
10
|
+
|
|
11
|
+
gem 'sinatra-rroute'
|
|
12
|
+
|
|
13
|
+
2. Require it in your Sinatra app:
|
|
14
|
+
|
|
15
|
+
require 'sinatra/rroute'
|
|
16
|
+
|
|
17
|
+
3. Register it in your app:
|
|
18
|
+
|
|
19
|
+
register Sinatra::Rroute
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Route Mapping
|
|
24
|
+
|
|
25
|
+
Mapping routes to controller-like methods works by calling one of the sinatra-rroute mapping methods like so:
|
|
26
|
+
|
|
27
|
+
# This will call Sinatra's `get' method behind the scenes.
|
|
28
|
+
gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
|
|
29
|
+
:user, :mask => '/user/:name/:age/'
|
|
30
|
+
|
|
31
|
+
# This will call Sinatra's `post' method behind the scenes.
|
|
32
|
+
ppost '/[uU]ser/new/:name/:age/?' => :create_user, :as =>
|
|
33
|
+
:new_user, :mask => '/user/new/:name/:age/'
|
|
34
|
+
|
|
35
|
+
The above examples will map GET requests for routes matching the RegEx `'/[uU]ser/:name/:age/?'` to the function `user_info` and POST requests to the route matching the RegEx `'/[uU]ser/new/:name/:age/?'` to the function :create_user. The first route can be referenced as `user`, the second one as `new_user`.
|
|
36
|
+
|
|
37
|
+
Here are all the supported mapping methods and their associated HTTP methods (all of them have the same signature, e.g. `gget('/route/regex/?' => :controller_method, :as => :route_name, :mask => '/route/mask/')`):
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
| Rroute Method | HTTP Method |
|
|
41
|
+
|---------------+-------------|
|
|
42
|
+
| gget | GET |
|
|
43
|
+
| ppost | POST |
|
|
44
|
+
| pput | PUT |
|
|
45
|
+
| ppatch | PATCH |
|
|
46
|
+
| hhead | HEAD |
|
|
47
|
+
| ddelete | DELETE |
|
|
48
|
+
| ooptions | OPTIONS |
|
|
49
|
+
| llink | LINK |
|
|
50
|
+
| uunlink | UNLINK |
|
|
51
|
+
| ttrace | TRACE |
|
|
52
|
+
| cconnect | CONNECT |
|
|
53
|
+
|
|
54
|
+
*The validity of some of those HTTP methods may be debatable, but it is nice to have them, nonetheless, in case you might need them one day.*
|
|
55
|
+
|
|
56
|
+
If you do not want to map routes to methods, but still give them names and masks, you can use the `mmap` function in combination with Sinatra's built-in `get`/`post`/`delete`/... functions:
|
|
57
|
+
|
|
58
|
+
# Signature: mmap(regex, mask, name)
|
|
59
|
+
get mmap('/user/:name/?', '/user/:name/', :user_info) do
|
|
60
|
+
# ...
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
### Namespacing
|
|
64
|
+
|
|
65
|
+
Sinatra-rroute comes with a `nnamespace` method which takes the name of a namespace and a block. Each route specified using one of the sinatra-rroute mapping functions (`gget`/`ppost`/`ddelete`/, also `mmap`) inside the block will be prefixed by the specified namespace prefix. Sinatra's built-in `get`/`post`/`delete`/... functions can be used within a namespace block, but are *not* affected by the namespacing. Namespaces can be nested. *Prefixes do have to specify leading and/or trailing slashes explicitely!*
|
|
66
|
+
|
|
67
|
+
Here is an example:
|
|
68
|
+
|
|
69
|
+
nnamespace '/api' do
|
|
70
|
+
nnamespace '/v1' do
|
|
71
|
+
gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
|
|
72
|
+
:user, :mask => '/user/:name/:age/'
|
|
73
|
+
|
|
74
|
+
ppost '/[uU]ser/new/:name/:age/?' => :create_user, :as =>
|
|
75
|
+
:new_user, :mask => '/user/new/:name/:age/'
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
gget '/documentation/?' => :api_documentation, :as =>
|
|
79
|
+
:api_docs, :mask => '/documentation/'
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
### Route Helper
|
|
84
|
+
|
|
85
|
+
Sinatra-rroute comes with a `path` helper function. It takes the name of a route and a hash of key-value mappings and returns the path for that route according to its mask. Each "token" in a mask will be replaced by the value for the specified key in the mapping.
|
|
86
|
+
|
|
87
|
+
# Specify a route mapping.
|
|
88
|
+
gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
|
|
89
|
+
:user, :mask => '/user/:name/:age/'
|
|
90
|
+
|
|
91
|
+
# Redirect to this route.
|
|
92
|
+
get '/friend/:name/:age/?' do
|
|
93
|
+
redirect to(path(:user, :name => params[:name], :age => params[:age]))
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Generate a link for the route in the view.
|
|
97
|
+
<a href="<%= path(:user, :name => 'John', :age => 32) %>">Get user info</a>
|
|
98
|
+
# This will render the following link:
|
|
99
|
+
# <a href="/user/John/32/">Get user info</a>
|
|
100
|
+
|
|
101
|
+
The helper can be used in your view files as well as in your controller/model/application. It is handy for links, redirecting etc.
|
|
102
|
+
|
|
103
|
+
### Accessing Routes
|
|
104
|
+
|
|
105
|
+
The full hash of all route-method mappings is attached to your Sinatra app's `settings` object available through the `settings.app_paths` variable.
|
|
106
|
+
|
|
107
|
+
*Note*: The full list of all routes is only available after the last call of one of the sinatra-rroute mapping methods, of course.
|
|
108
|
+
|
|
109
|
+
*Note*: When mixing sinatra-rroute mapping methods (`gget`/`ppost`/`delete`/`mmap`/...) and Sinatra's built-in `get`/`post`/`delete`/... the routes defined using the Sinatra built-ins will *not* show up in `settings.app_paths`!
|
|
110
|
+
|
|
111
|
+
## Mixing With Sinatra and Other Extensions
|
|
112
|
+
|
|
113
|
+
Sinatra's built-in `get`/`post`/`delete`/... functions as well as other route-related extensions do work fine in combination with sinatra-rroute as long as they do not
|
|
114
|
+
|
|
115
|
+
- clash with sinatra-rroute's function names,
|
|
116
|
+
- fiddle with the `settings.app_paths` variable of your Sinatra app or
|
|
117
|
+
- redefine/overload Sinatra's built-in `get`/`post`/`delete` functions without letting sinatra-rroute know.
|
|
118
|
+
|
|
119
|
+
## *What about the weird method names?*
|
|
120
|
+
|
|
121
|
+
The names are
|
|
122
|
+
|
|
123
|
+
- concise (as short as possible),
|
|
124
|
+
- consistent (same name generatiion scheme) and
|
|
125
|
+
- unobtrusive (they do not interfere with or overload Sinatra's and Rack's built-in routing methods, they also do not interfere with other Sinatra extensions).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
|
2
|
+
require 'rspec/core/rake_task'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
desc 'Run RSpec tests'
|
|
6
|
+
RSpec::Core::RakeTask.new(:test) do |t|
|
|
7
|
+
t.rspec_opts = '-c'
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
task :default do
|
|
11
|
+
puts <<-STRINGSTRINGSTRING
|
|
12
|
+
Run `rake -T' to see a list of all available tasks.
|
|
13
|
+
STRINGSTRINGSTRING
|
|
14
|
+
end
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
module Sinatra
|
|
2
|
+
module Rroute
|
|
3
|
+
private
|
|
4
|
+
def self.registered(app)
|
|
5
|
+
app.set :app_paths, {}
|
|
6
|
+
app.set :app_prefixes, []
|
|
7
|
+
app.helpers do
|
|
8
|
+
# @!visibility public
|
|
9
|
+
#
|
|
10
|
+
# Return a path for a given route mask.
|
|
11
|
+
#
|
|
12
|
+
# @param [String] name The name of a route for which a mask has
|
|
13
|
+
# been specified.
|
|
14
|
+
# @param [Hash{Symbol=>Object}] options Hash of Symbol tokens to be
|
|
15
|
+
# replaced by the associated value. The token (symbol) has to
|
|
16
|
+
# have the exact name specified in the mask for this route.
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
#
|
|
20
|
+
# # The mask for this route has been defined like this:
|
|
21
|
+
# # gget '/[uU]ser/:name/:age/?' => :user_info, :as =>
|
|
22
|
+
# # user, :mask => '/user/:name/:age/'
|
|
23
|
+
# path :user, :name => 'John', :age => 32
|
|
24
|
+
# # => '/user/John/32/'
|
|
25
|
+
#
|
|
26
|
+
#
|
|
27
|
+
# @return [String] Path for the specified route. Every token for
|
|
28
|
+
# this route is replaced by the specified value in `options'.
|
|
29
|
+
def path(name, *options)
|
|
30
|
+
keywords = options[0]
|
|
31
|
+
# Take the last string as path mask.
|
|
32
|
+
# (Important: Make a duplicate. Do not reference the original
|
|
33
|
+
# mask/regex in `settings.app_paths'!)
|
|
34
|
+
path = settings.app_paths[name.to_sym][:mask].dup ||
|
|
35
|
+
settings.app_paths[name.to_sym][:regex].dup
|
|
36
|
+
if keywords != nil
|
|
37
|
+
keywords.each do |keyword, value|
|
|
38
|
+
path.gsub! /:#{keyword.to_s}/, value.to_s
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
path
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
public
|
|
47
|
+
|
|
48
|
+
# @!visibility public
|
|
49
|
+
#
|
|
50
|
+
# Merge paths into `settings.app_paths' which holds all paths defined
|
|
51
|
+
# for the app.
|
|
52
|
+
#
|
|
53
|
+
# @param [Hash{Symbol=>Hash}] paths Paths to be merged into
|
|
54
|
+
# `settings.app_paths'. The hash has to have the structure outlined
|
|
55
|
+
# below.
|
|
56
|
+
#
|
|
57
|
+
# @example
|
|
58
|
+
#
|
|
59
|
+
# paths({:route =>
|
|
60
|
+
# {:http_method =>:get,
|
|
61
|
+
# :regex =>"/route/:name/?",
|
|
62
|
+
# :controller =>nil,
|
|
63
|
+
# :mask =>"/route/:name"},
|
|
64
|
+
# :color =>
|
|
65
|
+
# {:http_method =>:get,
|
|
66
|
+
# :regex =>"/api/new/color/:name/:value/?",
|
|
67
|
+
# :controller =>:show_color,
|
|
68
|
+
# :mask =>"/api/new/color/:name/:value"},
|
|
69
|
+
# :blue =>
|
|
70
|
+
# {:http_method =>:post,
|
|
71
|
+
# :regex =>"/important/api/blue/:name/:value/?",
|
|
72
|
+
# :controller =>:post_blue,
|
|
73
|
+
# :mask =>"/important/api/blue/:name/:value"},
|
|
74
|
+
# :something =>
|
|
75
|
+
# {:http_method =>:get,
|
|
76
|
+
# :regex =>"/something/:name/:value/?",
|
|
77
|
+
# :controller =>:api_something,
|
|
78
|
+
# :mask =>"/something/:name/:value"}})
|
|
79
|
+
#
|
|
80
|
+
# @return [nil] Nothing.
|
|
81
|
+
def paths(paths)
|
|
82
|
+
settings.app_paths.merge! paths
|
|
83
|
+
generate_paths
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# @!visibility public
|
|
88
|
+
#
|
|
89
|
+
# Define a route mapping a path to a controller with a HTTP method, a
|
|
90
|
+
# name and a mask for route generation.
|
|
91
|
+
#
|
|
92
|
+
# @param [Hash{String=>Symbol}] mapping Mapping of a route RegEx to a
|
|
93
|
+
# controller function.
|
|
94
|
+
# @param [Symbol] http_method The HTTP method used for this route
|
|
95
|
+
# (`:get', `post', `patch', ...)
|
|
96
|
+
# @param [Hash] options Optional: Options for this mapping. Allowed
|
|
97
|
+
# values: `:as => :route_name', `:mask => "/mask/for/route/:value/"'
|
|
98
|
+
#
|
|
99
|
+
# @example
|
|
100
|
+
#
|
|
101
|
+
# ppath({'/[Pp]ost?/info/:id/?' => :post_info}, :get, :as =>
|
|
102
|
+
# :post, :mask => '/post/:info/')
|
|
103
|
+
#
|
|
104
|
+
# @return [nil] Nothing.
|
|
105
|
+
def ppath(mapping, http_method, *options)
|
|
106
|
+
options = options[0]
|
|
107
|
+
prefix = settings.app_prefixes.empty? ? '' :
|
|
108
|
+
settings.app_prefixes.join
|
|
109
|
+
controller = nil
|
|
110
|
+
if mapping.values[0] != nil
|
|
111
|
+
controller = mapping.values[0].to_sym
|
|
112
|
+
end
|
|
113
|
+
if mapping.keys[0].class == Regexp
|
|
114
|
+
mapping_regex = mapping.keys[0]
|
|
115
|
+
else
|
|
116
|
+
mapping_regex = Regexp.new(mapping.keys[0])
|
|
117
|
+
end
|
|
118
|
+
settings.app_paths.merge!({options[:as].to_sym => {
|
|
119
|
+
:http_method => http_method || :get,
|
|
120
|
+
:regex => %r{#{prefix}\
|
|
121
|
+
#{mapping_regex.source}} || Regexp.new(''),
|
|
122
|
+
:controller => controller,
|
|
123
|
+
:mask => prefix + options[:mask]}}) ||
|
|
124
|
+
''
|
|
125
|
+
generate_paths
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# @!visibility public
|
|
129
|
+
# @!method gget(mapping, *options)
|
|
130
|
+
#
|
|
131
|
+
# Map a request using the HTTP GET method to a controller method.
|
|
132
|
+
#
|
|
133
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
134
|
+
# function.
|
|
135
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
136
|
+
# values: `:as => :route_name',
|
|
137
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
138
|
+
#
|
|
139
|
+
# @example
|
|
140
|
+
#
|
|
141
|
+
# gget '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
142
|
+
# :user, :mask => '/user/:name/:args/'
|
|
143
|
+
#
|
|
144
|
+
# @return [nil] Nothing.
|
|
145
|
+
|
|
146
|
+
# @!visibility public
|
|
147
|
+
# @!method ppost(mapping, *options)
|
|
148
|
+
#
|
|
149
|
+
# Map a request using the HTTP POST method to a controller method.
|
|
150
|
+
#
|
|
151
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
152
|
+
# function.
|
|
153
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
154
|
+
# values: `:as => :route_name',
|
|
155
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
156
|
+
#
|
|
157
|
+
# @example
|
|
158
|
+
#
|
|
159
|
+
# ppost '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
160
|
+
# :user, :mask => '/user/:name/:args/'
|
|
161
|
+
#
|
|
162
|
+
# @return [nil] Nothing.
|
|
163
|
+
|
|
164
|
+
# @!visibility public
|
|
165
|
+
# @!method pput(mapping, *options)
|
|
166
|
+
#
|
|
167
|
+
# Map a request using the HTTP PUT method to a controller method.
|
|
168
|
+
#
|
|
169
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
170
|
+
# function.
|
|
171
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
172
|
+
# values: `:as => :route_name',
|
|
173
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
174
|
+
#
|
|
175
|
+
# @example
|
|
176
|
+
#
|
|
177
|
+
# pput '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
178
|
+
# :user, :mask => '/user/:name/:args/'
|
|
179
|
+
#
|
|
180
|
+
# @return [nil] Nothing.
|
|
181
|
+
|
|
182
|
+
# @!visibility public
|
|
183
|
+
# @!method ppatch(mapping, *options)
|
|
184
|
+
#
|
|
185
|
+
# Map a request using the HTTP PATCH method to a controller method.
|
|
186
|
+
#
|
|
187
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
188
|
+
# function.
|
|
189
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
190
|
+
# values: `:as => :route_name',
|
|
191
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
192
|
+
#
|
|
193
|
+
# @example
|
|
194
|
+
#
|
|
195
|
+
# ppatch '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
196
|
+
# :user, :mask => '/user/:name/:args/'
|
|
197
|
+
#
|
|
198
|
+
# @return [nil] Nothing.
|
|
199
|
+
|
|
200
|
+
# @!visibility public
|
|
201
|
+
# @!method hhead(mapping, *options)
|
|
202
|
+
#
|
|
203
|
+
# Map a request using the HTTP HEAD method to a controller method.
|
|
204
|
+
#
|
|
205
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
206
|
+
# function.
|
|
207
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
208
|
+
# values: `:as => :route_name',
|
|
209
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
210
|
+
#
|
|
211
|
+
# @example
|
|
212
|
+
#
|
|
213
|
+
# hhead '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
214
|
+
# :user, :mask => '/user/:name/:args/'
|
|
215
|
+
#
|
|
216
|
+
# @return [nil] Nothing.
|
|
217
|
+
|
|
218
|
+
# @!visibility public
|
|
219
|
+
# @!method ddelete(mapping, *options)
|
|
220
|
+
#
|
|
221
|
+
# Map a request using the HTTP DELETE method to a controller method.
|
|
222
|
+
#
|
|
223
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
224
|
+
# function.
|
|
225
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
226
|
+
# values: `:as => :route_name',
|
|
227
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
228
|
+
#
|
|
229
|
+
# @example
|
|
230
|
+
#
|
|
231
|
+
# ddelete '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
232
|
+
# :user, :mask => '/user/:name/:args/'
|
|
233
|
+
#
|
|
234
|
+
# @return [nil] Nothing.
|
|
235
|
+
|
|
236
|
+
# @!visibility public
|
|
237
|
+
# @!method ooptions(mapping, *options)
|
|
238
|
+
#
|
|
239
|
+
# Map a request using the HTTP OPTIONS method to a controller method.
|
|
240
|
+
#
|
|
241
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
242
|
+
# function.
|
|
243
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
244
|
+
# values: `:as => :route_name',
|
|
245
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
246
|
+
#
|
|
247
|
+
# @example
|
|
248
|
+
#
|
|
249
|
+
# ooptions '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
250
|
+
# :user, :mask => '/user/:name/:args/'
|
|
251
|
+
#
|
|
252
|
+
# @return [nil] Nothing.
|
|
253
|
+
|
|
254
|
+
# @!visibility public
|
|
255
|
+
# @!method llink(mapping, *options)
|
|
256
|
+
#
|
|
257
|
+
# Map a request using the HTTP LINK method to a controller method.
|
|
258
|
+
#
|
|
259
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
260
|
+
# function.
|
|
261
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
262
|
+
# values: `:as => :route_name',
|
|
263
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
264
|
+
#
|
|
265
|
+
# @example
|
|
266
|
+
#
|
|
267
|
+
# llink '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
268
|
+
# :user, :mask => '/user/:name/:args/'
|
|
269
|
+
#
|
|
270
|
+
# @return [nil] Nothing.
|
|
271
|
+
|
|
272
|
+
# @!visibility public
|
|
273
|
+
# @!method uunlink(mapping, *options)
|
|
274
|
+
#
|
|
275
|
+
# Map a request using the HTTP UNLINK method to a controller method.
|
|
276
|
+
#
|
|
277
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
278
|
+
# function.
|
|
279
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
280
|
+
# values: `:as => :route_name',
|
|
281
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
282
|
+
#
|
|
283
|
+
# @example
|
|
284
|
+
#
|
|
285
|
+
# uunlink '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
286
|
+
# :user, :mask => '/user/:name/:args/'
|
|
287
|
+
#
|
|
288
|
+
# @return [nil] Nothing.
|
|
289
|
+
|
|
290
|
+
# @!visibility public
|
|
291
|
+
# @!method ttrace(mapping, *options)
|
|
292
|
+
#
|
|
293
|
+
# Map a request using the HTTP TRACE method to a controller method.
|
|
294
|
+
#
|
|
295
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
296
|
+
# function.
|
|
297
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
298
|
+
# values: `:as => :route_name',
|
|
299
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
300
|
+
#
|
|
301
|
+
# @example
|
|
302
|
+
#
|
|
303
|
+
# ttrace '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
304
|
+
# :user, :mask => '/user/:name/:args/'
|
|
305
|
+
#
|
|
306
|
+
# @return [nil] Nothing.
|
|
307
|
+
|
|
308
|
+
# @!visibility public
|
|
309
|
+
# @!method cconnect(mapping, *options)
|
|
310
|
+
#
|
|
311
|
+
# Map a request using the HTTP CONNECT method to a controller method.
|
|
312
|
+
#
|
|
313
|
+
# @param [Hash] mapping Mapping from a route RegEx to a controller
|
|
314
|
+
# function.
|
|
315
|
+
# @param [Hash] options Optional: Options for this route. Allowed
|
|
316
|
+
# values: `:as => :route_name',
|
|
317
|
+
# `:mask => "/mask/for/route/:value/"'
|
|
318
|
+
#
|
|
319
|
+
# @example
|
|
320
|
+
#
|
|
321
|
+
# cconnect '/[Uu]sers?/:name/:age/?' => :user_info, :as =>
|
|
322
|
+
# :user, :mask => '/user/:name/:args/'
|
|
323
|
+
#
|
|
324
|
+
# @return [nil] Nothing.
|
|
325
|
+
|
|
326
|
+
{:gget => :get,
|
|
327
|
+
:ppost => :post,
|
|
328
|
+
:pput => :put,
|
|
329
|
+
:ppatch => :patch,
|
|
330
|
+
:hhead => :head,
|
|
331
|
+
:ddelete => :delete,
|
|
332
|
+
:ooptions => :options,
|
|
333
|
+
:llink => :link,
|
|
334
|
+
:uunlink => :unlink,
|
|
335
|
+
:ttrace => :trace,
|
|
336
|
+
:cconnect => :connect}.each do |method_name, http_method|
|
|
337
|
+
define_method method_name do |options|
|
|
338
|
+
# Do some `magic'/hacking to get the same `get' route drawer method
|
|
339
|
+
# interface that Rails has. People are used to it/get to get used
|
|
340
|
+
# to it quickly.
|
|
341
|
+
key = (options.keys - [:as, :regex, :controller, :mask])[0]
|
|
342
|
+
ppath({key => options[key]}, http_method, options)
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# @!visibility public
|
|
347
|
+
#
|
|
348
|
+
# Map a route RegEx to a controller method with a name and a mask for
|
|
349
|
+
# path generation.
|
|
350
|
+
#
|
|
351
|
+
# Since this method is meant to be used together with Sinatra's
|
|
352
|
+
# built-in `get'/`post'/`patch'/... mapper methods, it does not to
|
|
353
|
+
# map the route to a controller function since this would bypass
|
|
354
|
+
# Sinatra's built-in mapper method.
|
|
355
|
+
#
|
|
356
|
+
# @param [String] regex Route RegEx.
|
|
357
|
+
# @param [String] mask Mask for route generation.
|
|
358
|
+
# @name [Symbol] name Name to reference this route for route generation
|
|
359
|
+
# etc.
|
|
360
|
+
#
|
|
361
|
+
# @example
|
|
362
|
+
#
|
|
363
|
+
# get mmap('/[Uu]ser/:name/:age/?', '/user/:name/:age/', :user) do
|
|
364
|
+
# # ...
|
|
365
|
+
# end
|
|
366
|
+
#
|
|
367
|
+
# @return [nil] Nothing.
|
|
368
|
+
def mmap(regex, mask, name)
|
|
369
|
+
options = {}
|
|
370
|
+
options[:regex] = regex
|
|
371
|
+
options[:mask] = mask
|
|
372
|
+
options[:as] = name
|
|
373
|
+
options[:controller] = nil
|
|
374
|
+
ppath({regex => nil}, nil, options)
|
|
375
|
+
|
|
376
|
+
# Prepend the prefix to the returned RegExp.
|
|
377
|
+
mapping_regex = regex == Regexp ? regex : Regexp.new(regex)
|
|
378
|
+
prefix = settings.app_prefixes.empty? ? '' :
|
|
379
|
+
settings.app_prefixes.join
|
|
380
|
+
output = %r{#{prefix}#{mapping_regex.source}}.source ||
|
|
381
|
+
Regexp.new('')
|
|
382
|
+
# Return the right type of object depending on the input.
|
|
383
|
+
regex.class == Regexp ? Regexp.new(output) : output
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# @!visibility public
|
|
387
|
+
#
|
|
388
|
+
# Generate all route mappings from the paths in `settings.app_paths'.
|
|
389
|
+
#
|
|
390
|
+
# @return [nil] Nothing.
|
|
391
|
+
def generate_paths
|
|
392
|
+
settings.app_paths.each do |name, value|
|
|
393
|
+
if value[:controller] != nil
|
|
394
|
+
# Paths merged using `paths' should be allowed to to be specified
|
|
395
|
+
# as strings. So they have to be converted here.
|
|
396
|
+
if value[:regex].class != Regexp
|
|
397
|
+
value[:regex] = Regexp.new(value[:regex])
|
|
398
|
+
end
|
|
399
|
+
# Adapt the RegEx representation.
|
|
400
|
+
value[:regex] = value[:regex].source
|
|
401
|
+
send(value[:http_method],
|
|
402
|
+
value[:regex]) { send value[:controller] }
|
|
403
|
+
end
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
|
|
407
|
+
# @!visibility public
|
|
408
|
+
#
|
|
409
|
+
# Namespace a all routes defined within the given block using the
|
|
410
|
+
# mapping methods `gget'/`ppost'/`ppatch'/...
|
|
411
|
+
#
|
|
412
|
+
# Sinatra's built-in mapping methods `get'/`post'/`patch' are NOT
|
|
413
|
+
# affected by the namespacing.
|
|
414
|
+
#
|
|
415
|
+
# @param [String] prefix Namespace to be used. HAS to include leading
|
|
416
|
+
# and/or trailing slashes!
|
|
417
|
+
# @yield [] Block containing route mapping method calls
|
|
418
|
+
# (`gget'/`ppost'/`ppatch'/...) and/or other relevant application
|
|
419
|
+
# code. Can also include calls to Sinatra's built-in mapping methods
|
|
420
|
+
# (`get'/`post'/`patch'/...) which will not be affected by the
|
|
421
|
+
# namespacing.
|
|
422
|
+
#
|
|
423
|
+
# @return [nil] Nothing.
|
|
424
|
+
def nnamespace(prefix, &block)
|
|
425
|
+
settings.app_prefixes << prefix
|
|
426
|
+
block.call
|
|
427
|
+
settings.app_prefixes.pop
|
|
428
|
+
end
|
|
429
|
+
end
|
|
430
|
+
register Rroute
|
|
431
|
+
end
|
data/rroute.gemspec
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'sinatra/rroute/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "sinatra-rroute"
|
|
8
|
+
spec.version = Sinatra::Rroute::VERSION
|
|
9
|
+
spec.authors = ["nounch"]
|
|
10
|
+
spec.email = ["nounch@outlook.com"]
|
|
11
|
+
spec.summary =
|
|
12
|
+
"Rails-style routes with names, namespaces and `path' helper."
|
|
13
|
+
spec.description = <<-DESCRIPTION
|
|
14
|
+
Sinatra-rraoute provides `gget'/`ppost'/`ddelete'/... methods which work
|
|
15
|
+
just like Sinatra's built-in `get'/`post'/`delete'/... methods, but which
|
|
16
|
+
map named routes to functions so that they can be referenced in redirects
|
|
17
|
+
etc.
|
|
18
|
+
|
|
19
|
+
The `path' helper will return a route for a certain route name and the
|
|
20
|
+
given values for this route and comes in handy in both, the
|
|
21
|
+
controller/model component of the application, and the view where you can
|
|
22
|
+
use it to render links, assets URLs, AJAX calls...
|
|
23
|
+
|
|
24
|
+
The nestable `nnamespace' method is useful for API versioning and does not
|
|
25
|
+
interfere with other namespace extensions for Sinatra.
|
|
26
|
+
DESCRIPTION
|
|
27
|
+
spec.homepage = ""
|
|
28
|
+
spec.license = "MIT"
|
|
29
|
+
|
|
30
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
31
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
32
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
33
|
+
spec.require_paths = ["lib"]
|
|
34
|
+
|
|
35
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
|
36
|
+
spec.add_development_dependency "rake"
|
|
37
|
+
end
|
data/spec/rroute_spec.rb
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
require 'rspec'
|
|
2
|
+
require 'rack/test'
|
|
3
|
+
require 'sinatra/base'
|
|
4
|
+
require_relative '../lib/sinatra/rroute'
|
|
5
|
+
require_relative 'test_app'
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
RSpec.configure do |config|
|
|
9
|
+
config.include Rack::Test::Methods
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
describe "Rroute" do
|
|
13
|
+
# Setup
|
|
14
|
+
|
|
15
|
+
def app
|
|
16
|
+
TestApp
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
before(:each) do
|
|
20
|
+
@user = { :name => 'John', :age => 32}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Tests
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
describe "#paths" do
|
|
28
|
+
it "merges route mappings into `settings.app_paths' uppon a GET \
|
|
29
|
+
request" do
|
|
30
|
+
get '/paths/randomname/randomdescription/'
|
|
31
|
+
paths = {:paths_test_path =>
|
|
32
|
+
{:http_method=>:get,
|
|
33
|
+
:regex =>"/paths/:name/:description/?",
|
|
34
|
+
:controller =>:paths_test,
|
|
35
|
+
:mask =>"/paths/:name/:description/"},
|
|
36
|
+
:route =>
|
|
37
|
+
{:http_method =>:get,
|
|
38
|
+
:regex =>"/route/:name/?",
|
|
39
|
+
:controller =>nil,
|
|
40
|
+
:mask =>"/route/:name"},
|
|
41
|
+
:color =>
|
|
42
|
+
{:http_method =>:get,
|
|
43
|
+
:regex =>"/api/new/color/:name/:value/?",
|
|
44
|
+
:controller =>:show_color,
|
|
45
|
+
:mask =>"/api/new/color/:name/:value"},
|
|
46
|
+
:blue =>
|
|
47
|
+
{:http_method =>:post,
|
|
48
|
+
:regex =>"/important/api/blue/:name/:value/?",
|
|
49
|
+
:controller =>:post_blue,
|
|
50
|
+
:mask =>"/important/api/blue/:name/:value"},
|
|
51
|
+
:something =>
|
|
52
|
+
{:http_method =>:get,
|
|
53
|
+
:regex =>"/something/:name/:value/?",
|
|
54
|
+
:controller =>:api_something,
|
|
55
|
+
:mask =>"/something/:name/:value"}}
|
|
56
|
+
expect(last_response).to be_ok
|
|
57
|
+
expect(last_response.body).to eq(paths.inspect)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
describe "#gget" do
|
|
63
|
+
it "successfully dispatches a GET request and defines a route mask" do
|
|
64
|
+
get "/user/#{@user[:name]}/#{@user[:age]}/"
|
|
65
|
+
expect(last_response).to be_ok
|
|
66
|
+
expect(app.settings.app_paths[:user][:mask]).to eq("/user/:name/:age\
|
|
67
|
+
/")
|
|
68
|
+
expect(last_response.body).to eq("/user/#{@user[:name]}/\
|
|
69
|
+
#{@user[:age]}/")
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
describe "#gget (2)" do
|
|
74
|
+
it "successfully dispatches a GET request and defines a route mask
|
|
75
|
+
(using a RegExp-quoted string as route)" do
|
|
76
|
+
get "/regex/#{@user[:name]}/#{@user[:age]}/"
|
|
77
|
+
expect(last_response).to be_ok
|
|
78
|
+
expect(last_response.body).to eq("/regex/#{@user[:name]}/\
|
|
79
|
+
#{@user[:age]}/")
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
describe "#ppost" do
|
|
84
|
+
it "successfully dispatches a POST request and defines a route mask" do
|
|
85
|
+
post "/user/new/#{@user[:name]}/#{@user[:age]}/"
|
|
86
|
+
expect(last_response).to be_ok
|
|
87
|
+
expect(app.settings.app_paths[:new_user][:mask]).to eq("/user/new/\
|
|
88
|
+
:name/:age/")
|
|
89
|
+
expect(last_response.body).to eq("/user/new/#{@user[:name]}/\
|
|
90
|
+
#{@user[:age]}/")
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
describe "#pput" do
|
|
95
|
+
it "successfully dispatches a PUT request and defines a route mask" do
|
|
96
|
+
put "/user/change/#{@user[:name]}/#{@user[:age]}/"
|
|
97
|
+
expect(last_response).to be_ok
|
|
98
|
+
expect(app.settings.app_paths[:change_user][:mask]).to eq("/user/\
|
|
99
|
+
change/:name/:age/")
|
|
100
|
+
expect(last_response.body).to eq("/user/change/#{@user[:name]}/\
|
|
101
|
+
#{@user[:age]}/")
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
describe "#ddelete" do
|
|
106
|
+
it "successfully dispatches a DELETE request and defines a route \
|
|
107
|
+
mask" do
|
|
108
|
+
delete "/user/delete/#{@user[:name]}/#{@user[:age]}/"
|
|
109
|
+
expect(last_response).to be_ok
|
|
110
|
+
expect(app.settings.app_paths[:delete_user][:mask]).to eq("/user/\
|
|
111
|
+
delete/:name/:age/")
|
|
112
|
+
expect(last_response.body).to eq("/user/delete/#{@user[:name]}/\
|
|
113
|
+
#{@user[:age]}/")
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe "#hhead" do
|
|
118
|
+
it "successfully dispatches a HEAD request and defines a route \
|
|
119
|
+
mask" do
|
|
120
|
+
head "/user/head/#{@user[:name]}/#{@user[:age]}/"
|
|
121
|
+
expect(last_response).to be_ok
|
|
122
|
+
expect(app.settings.app_paths[:head_user][:mask]).to eq("/user/\
|
|
123
|
+
head/:name/:age/")
|
|
124
|
+
expect(last_response.body).to be_empty
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
describe "#ppath" do
|
|
129
|
+
it "successfully dispatches a GET request and defines a route \
|
|
130
|
+
mask (using `ppath')" do
|
|
131
|
+
get "/user/moreinfo/#{@user[:name]}/#{@user[:age]}/"
|
|
132
|
+
expect(last_response).to be_ok
|
|
133
|
+
expect(app.settings.app_paths[:more_user_info][:mask]).to eq("/user/\
|
|
134
|
+
moreinfo/:name/:age/")
|
|
135
|
+
expect(last_response.body).to eq("/user/moreinfo/#{@user[:name]}/\
|
|
136
|
+
#{@user[:age]}/")
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe "#nnampespace" do
|
|
141
|
+
it "successfully dispatches a GET request and defines a route \
|
|
142
|
+
mask (using `nnamespace' and `gget')" do
|
|
143
|
+
# /api/v1/beta
|
|
144
|
+
get "/api/v1/beta/user/#{@user[:name]}/#{@user[:age]}/"
|
|
145
|
+
expect(last_response).to be_ok
|
|
146
|
+
expect(app.settings.app_paths[:api_user_beta][:mask]).to eq("/api/\
|
|
147
|
+
v1/beta/user/:name/:age/")
|
|
148
|
+
expect(last_response.body).to eq("/api/v1/beta/user/#{@user[:name]}/\
|
|
149
|
+
#{@user[:age]}/")
|
|
150
|
+
|
|
151
|
+
# /api/v1/alpha
|
|
152
|
+
get "/api/v1/alpha/user/#{@user[:name]}/#{@user[:age]}/"
|
|
153
|
+
expect(last_response).to be_ok
|
|
154
|
+
expect(app.settings.app_paths[:api_user_alpha][:mask]).to eq("/api/\
|
|
155
|
+
v1/alpha/user/:name/:age/")
|
|
156
|
+
expect(last_response.body).to eq("/api/v1/alpha/user/\
|
|
157
|
+
#{@user[:name]}/#{@user[:age]}/")
|
|
158
|
+
|
|
159
|
+
# `mmap' in a `nnamespace' block
|
|
160
|
+
get "/api/v1/overview/#{@user[:name]}/#{@user[:age]}/"
|
|
161
|
+
expect(last_response).to be_ok
|
|
162
|
+
expect(app.settings.app_paths[:api_overview][:mask]).to eq("/api/\
|
|
163
|
+
v1/overview/:name/:age/")
|
|
164
|
+
expect(last_response.body).to eq("/api/v1/overview/\
|
|
165
|
+
#{@user[:name]}/#{@user[:age]}/")
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
describe "#nnampespace (2)" do
|
|
170
|
+
it "successfully dispatches a GET request and defines a route mask \
|
|
171
|
+
(using `nnamespace' and Sinatra's `get', `post', `put' and `delete')" do
|
|
172
|
+
# GET
|
|
173
|
+
get "/user/sinatra/get/#{@user[:name]}/#{@user[:age]}/"
|
|
174
|
+
expect(last_response).to be_ok
|
|
175
|
+
expect(last_response.body).to eq('User GET')
|
|
176
|
+
# POST
|
|
177
|
+
post "/user/sinatra/post/#{@user[:name]}/#{@user[:age]}/"
|
|
178
|
+
expect(last_response).to be_ok
|
|
179
|
+
expect(last_response.body).to eq('User POST')
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
describe "#mmap" do
|
|
184
|
+
it "successfully dispatches a GET request and defines a route \
|
|
185
|
+
mask (using `mmap' and Sinatra's built-in `get')" do
|
|
186
|
+
get "/user/mmap/#{@user[:name]}/#{@user[:age]}/"
|
|
187
|
+
expect(last_response).to be_ok
|
|
188
|
+
expect(app.settings.app_paths[:mmap_user][:mask]).to eq("/user/mmap/\
|
|
189
|
+
:name/:age/")
|
|
190
|
+
expect(last_response.body).to eq("/user/mmap/#{@user[:name]}/\
|
|
191
|
+
#{@user[:age]}/")
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
describe "#mmap (2)" do
|
|
196
|
+
it "successfully dispatches a GET request and defines a route \
|
|
197
|
+
mask (using `mmap', Sinatra's built-in `get' and a quoted string as route)" do
|
|
198
|
+
get "/user/mmap/regex/#{@user[:name]}/#{@user[:age]}/"
|
|
199
|
+
expect(last_response).to be_ok
|
|
200
|
+
expect(app.settings.app_paths[:mmap_user][:mask]).to eq("/user/mmap/\
|
|
201
|
+
:name/:age/")
|
|
202
|
+
expect(last_response.body).to eq("/user/mmap/regex/#{@user[:name]}/\
|
|
203
|
+
#{@user[:age]}/")
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
describe "#generate_paths" do
|
|
208
|
+
it "successfully dispatches a GET request and generates \
|
|
209
|
+
`settings.app_paths')" do
|
|
210
|
+
get '/generate/paths/'
|
|
211
|
+
expect(last_response).to be_ok
|
|
212
|
+
expect(last_response.body).to eq 'Paths have been generated.'
|
|
213
|
+
expect(app.settings.app_paths).not_to be(nil)
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
data/spec/test_app.rb
ADDED
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
require 'sinatra'
|
|
2
|
+
require_relative '../lib/sinatra/rroute'
|
|
3
|
+
|
|
4
|
+
class TestApp < Sinatra::Base
|
|
5
|
+
register Sinatra::Rroute
|
|
6
|
+
|
|
7
|
+
# `paths'
|
|
8
|
+
|
|
9
|
+
gget '/paths/:name/:description/?' => :paths_test, :as =>
|
|
10
|
+
:paths_test_path, :mask => '/paths/:name/:description/'
|
|
11
|
+
|
|
12
|
+
paths({:route =>
|
|
13
|
+
{:http_method =>:get,
|
|
14
|
+
:regex =>"/route/:name/?",
|
|
15
|
+
:controller =>nil,
|
|
16
|
+
:mask =>"/route/:name"},
|
|
17
|
+
:color =>
|
|
18
|
+
{:http_method =>:get,
|
|
19
|
+
:regex =>"/api/new/color/:name/:value/?",
|
|
20
|
+
:controller =>:show_color,
|
|
21
|
+
:mask =>"/api/new/color/:name/:value"},
|
|
22
|
+
:blue =>
|
|
23
|
+
{:http_method =>:post,
|
|
24
|
+
:regex =>"/important/api/blue/:name/:value/?",
|
|
25
|
+
:controller =>:post_blue,
|
|
26
|
+
:mask =>"/important/api/blue/:name/:value"},
|
|
27
|
+
:something =>
|
|
28
|
+
{:http_method =>:get,
|
|
29
|
+
:regex =>"/something/:name/:value/?",
|
|
30
|
+
:controller =>:api_something,
|
|
31
|
+
:mask =>"/something/:name/:value"}})
|
|
32
|
+
@@merged_paths = settings.app_paths.dup
|
|
33
|
+
|
|
34
|
+
def paths_test
|
|
35
|
+
@@merged_paths.inspect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def show_color
|
|
39
|
+
'#FF0000'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def post_blue
|
|
43
|
+
'This is a blue post.'
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def api_something
|
|
47
|
+
'API...'
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#########################################################################
|
|
51
|
+
# NOTE
|
|
52
|
+
#
|
|
53
|
+
# For the tests to work correctly do not define more routes above this
|
|
54
|
+
# point in the file or modify `settings.app_paths' in any other way
|
|
55
|
+
# without also adjusting the `paths' variable in the `#paths' RSpec block
|
|
56
|
+
# in `rroute_integration_spec.rb'!
|
|
57
|
+
#########################################################################
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# GET
|
|
61
|
+
|
|
62
|
+
gget '/user/:name/:age/?' => :do_show_user_info, :as =>
|
|
63
|
+
:user, :mask => '/user/:name/:age/'
|
|
64
|
+
|
|
65
|
+
def do_show_user_info
|
|
66
|
+
path :user, :name => params[:name], :age => params[:age]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# GET (using a RegExp-quoted string as route)
|
|
70
|
+
|
|
71
|
+
gget %r{/regex/:name/:age/?} => :show_regex, :as =>
|
|
72
|
+
:regex, :mask => '/regex/:name/:age/'
|
|
73
|
+
|
|
74
|
+
def show_regex
|
|
75
|
+
path :regex, :name => params[:name], :age => params[:age]
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# POST
|
|
79
|
+
|
|
80
|
+
ppost '/user/new/:name/:age/?' => :do_create_new_user, :as =>
|
|
81
|
+
:new_user, :mask => '/user/new/:name/:age/'
|
|
82
|
+
|
|
83
|
+
def do_create_new_user
|
|
84
|
+
path :new_user, :name => params[:name], :age => params[:age]
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# PUT
|
|
88
|
+
|
|
89
|
+
pput '/user/change/:name/:age/?' => :do_change_user, :as =>
|
|
90
|
+
:change_user, :mask => '/user/change/:name/:age/'
|
|
91
|
+
|
|
92
|
+
def do_change_user
|
|
93
|
+
path :change_user, :name => params[:name], :age => params[:age]
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# DELETE
|
|
97
|
+
|
|
98
|
+
ddelete '/user/delete/:name/:age/?' => :do_delete_user, :as =>
|
|
99
|
+
:delete_user, :mask => '/user/delete/:name/:age/'
|
|
100
|
+
|
|
101
|
+
def do_delete_user
|
|
102
|
+
path :delete_user, :name => params[:name], :age => params[:age]
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# HEAD
|
|
106
|
+
|
|
107
|
+
hhead '/user/head/:name/:age/?' => :do_head_user, :as =>
|
|
108
|
+
:head_user, :mask => '/user/head/:name/:age/'
|
|
109
|
+
|
|
110
|
+
def do_head_user
|
|
111
|
+
# Because this is a controller method for a HEAD request, the following
|
|
112
|
+
# response should NOT be sent! This is default Sinatra behavior.
|
|
113
|
+
path :head_user, :name => params[:name], :age => params[:age]
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# GET (using `ppath')
|
|
117
|
+
|
|
118
|
+
ppath({'/user/moreinfo/:name/:age/?' =>
|
|
119
|
+
:show_more_user_info}, :get, :as => :more_user_info, :mask =>
|
|
120
|
+
'/user/moreinfo/:name/:age/')
|
|
121
|
+
|
|
122
|
+
def show_more_user_info
|
|
123
|
+
path :more_user_info, :name => params[:name], :age => params[:age]
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# GET (using `nnamespace' and `gget')
|
|
127
|
+
|
|
128
|
+
nnamespace '/api' do
|
|
129
|
+
nnamespace '/v1' do
|
|
130
|
+
nnamespace '/beta' do
|
|
131
|
+
gget '/user/:name/:age/?' => :api_do_show_user_info_beta, :as =>
|
|
132
|
+
:api_user_beta, :mask => '/user/:name/:age/'
|
|
133
|
+
|
|
134
|
+
def api_do_show_user_info_beta
|
|
135
|
+
path :api_user_beta, :name => params[:name], :age => params[:age]
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
nnamespace '/alpha' do
|
|
140
|
+
gget '/user/:name/:age/?' => :api_do_show_user_info_alpha, :as =>
|
|
141
|
+
:api_user_alpha, :mask => '/user/:name/:age/'
|
|
142
|
+
|
|
143
|
+
def api_do_show_user_info_alpha
|
|
144
|
+
path :api_user_alpha, :name => params[:name], :age =>
|
|
145
|
+
params[:age]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
get mmap('/overview/:name/:age/?', '/overview/:name/:age/',
|
|
150
|
+
:api_overview) do
|
|
151
|
+
path :api_overview, :name => params[:name], :age => params[:age]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
nnamespace '/api' do
|
|
157
|
+
nnamespace '/v1' do
|
|
158
|
+
nnamespace '/beta' do
|
|
159
|
+
get '/user/sinatra/get/:name/:age/?' do
|
|
160
|
+
'User GET'
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
post '/user/sinatra/post/:name/:age/?' do
|
|
164
|
+
'User POST'
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# GET (using `mmap' and Sinatra's built-in `get')
|
|
171
|
+
|
|
172
|
+
get mmap('/user/mmap/:name/:age/?', '/user/mmap/:name/:age/',
|
|
173
|
+
:mmap_user) do
|
|
174
|
+
path :mmap_user, :name => params[:name], :age => params[:age]
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# GET (using `mmap', Sinatra's built-in `get' and a quoted string as
|
|
178
|
+
# route)
|
|
179
|
+
|
|
180
|
+
get mmap(%r{/user/mmap/regex/([\w]+)/([\w]+)/?},
|
|
181
|
+
'/user/mmap/regex/:name/:age/', :mmap_regex_user) do |name, age|
|
|
182
|
+
path :mmap_regex_user, :name => name, :age => age
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# `generate_paths'
|
|
186
|
+
|
|
187
|
+
# Callign `mmap' invokes `generate_paths' and registers a new route.
|
|
188
|
+
# Consequence: `settings.app_paths' cannot be `nil' anymore. This is
|
|
189
|
+
# what the test can test check.
|
|
190
|
+
get mmap('/generate/paths/?', 'generate/paths/', :build_paths) do
|
|
191
|
+
'Paths have been generated.'
|
|
192
|
+
end
|
|
193
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: sinatra-rroute
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- nounch
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2014-05-25 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.6'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.6'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
description: |
|
|
42
|
+
Sinatra-rraoute provides `gget'/`ppost'/`ddelete'/... methods which work
|
|
43
|
+
just like Sinatra's built-in `get'/`post'/`delete'/... methods, but which
|
|
44
|
+
map named routes to functions so that they can be referenced in redirects
|
|
45
|
+
etc.
|
|
46
|
+
|
|
47
|
+
The `path' helper will return a route for a certain route name and the
|
|
48
|
+
given values for this route and comes in handy in both, the
|
|
49
|
+
controller/model component of the application, and the view where you can
|
|
50
|
+
use it to render links, assets URLs, AJAX calls...
|
|
51
|
+
|
|
52
|
+
The nestable `nnamespace' method is useful for API versioning and does not
|
|
53
|
+
interfere with other namespace extensions for Sinatra.
|
|
54
|
+
email:
|
|
55
|
+
- nounch@outlook.com
|
|
56
|
+
executables: []
|
|
57
|
+
extensions: []
|
|
58
|
+
extra_rdoc_files: []
|
|
59
|
+
files:
|
|
60
|
+
- ".gitignore"
|
|
61
|
+
- Gemfile
|
|
62
|
+
- LICENSE.txt
|
|
63
|
+
- README.md
|
|
64
|
+
- Rakefile
|
|
65
|
+
- lib/sinatra/rroute.rb
|
|
66
|
+
- lib/sinatra/rroute/version.rb
|
|
67
|
+
- rroute.gemspec
|
|
68
|
+
- spec/rroute_spec.rb
|
|
69
|
+
- spec/test_app.rb
|
|
70
|
+
homepage: ''
|
|
71
|
+
licenses:
|
|
72
|
+
- MIT
|
|
73
|
+
metadata: {}
|
|
74
|
+
post_install_message:
|
|
75
|
+
rdoc_options: []
|
|
76
|
+
require_paths:
|
|
77
|
+
- lib
|
|
78
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - ">="
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '0'
|
|
88
|
+
requirements: []
|
|
89
|
+
rubyforge_project:
|
|
90
|
+
rubygems_version: 2.2.2
|
|
91
|
+
signing_key:
|
|
92
|
+
specification_version: 4
|
|
93
|
+
summary: Rails-style routes with names, namespaces and `path' helper.
|
|
94
|
+
test_files:
|
|
95
|
+
- spec/rroute_spec.rb
|
|
96
|
+
- spec/test_app.rb
|
|
97
|
+
has_rdoc:
|