sinatra-trails 0.0.5 → 0.0.6
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/README.rdoc +312 -0
- data/lib/sinatra/trails/version.rb +1 -1
- data/lib/sinatra/trails.rb +9 -7
- data/spec/route_generation_spec.rb +22 -11
- metadata +18 -15
data/README.rdoc
ADDED
@@ -0,0 +1,312 @@
|
|
1
|
+
sinatra-trails
|
2
|
+
--------------
|
3
|
+
|
4
|
+
Is a very thin Rails inspired route naming DSL for using with Sinatra apps.
|
5
|
+
It doesn't monkeypatch or overrides any Sinatra::Base methods
|
6
|
+
It provides helpers for generating routes for resources and single resource, namespaces and named routes.
|
7
|
+
|
8
|
+
Instalation
|
9
|
+
----------
|
10
|
+
|
11
|
+
$ [sudo] gem install sinatra-trails
|
12
|
+
|
13
|
+
Usage
|
14
|
+
----
|
15
|
+
|
16
|
+
Basic
|
17
|
+
=====
|
18
|
+
|
19
|
+
Named routes are generated with map, not passing :to option sets the path to be the same as the name:
|
20
|
+
|
21
|
+
require 'sinatra/trails'
|
22
|
+
|
23
|
+
class MyApp < Sinatra::Base
|
24
|
+
register Sinatra::Trails
|
25
|
+
|
26
|
+
map :dashboard # => '/dashboard'
|
27
|
+
map :home, :to => '/' # => '/'
|
28
|
+
map :post, :to => '/posts/:id' # => '/posts/:id'
|
29
|
+
end
|
30
|
+
|
31
|
+
MyApp.print_routes
|
32
|
+
# prints
|
33
|
+
# dashboard => /dashboard
|
34
|
+
# home => /
|
35
|
+
# post => /posts/:id
|
36
|
+
|
37
|
+
Routes can be define beforehand:
|
38
|
+
|
39
|
+
map :dashboard
|
40
|
+
|
41
|
+
# GET '/dashboard'
|
42
|
+
get route_for(:dashboard) do
|
43
|
+
...
|
44
|
+
end
|
45
|
+
|
46
|
+
Or when defining the action:
|
47
|
+
|
48
|
+
# GET '/dashboard'
|
49
|
+
get map(:dashboard) do
|
50
|
+
...
|
51
|
+
end
|
52
|
+
|
53
|
+
All defined routes will be available in the views and action blocks:
|
54
|
+
|
55
|
+
get map(:posts, :to => '/posts/:id') do
|
56
|
+
...
|
57
|
+
end
|
58
|
+
|
59
|
+
get map(:dashboard) do
|
60
|
+
path_for(:dashboard) # => '/dashboard'
|
61
|
+
url_for(:dashboard) # => 'http://www.example.com/dashboard'
|
62
|
+
path_for(:dashboard, :option => 'hi') # => '/dashboard?option=hi'
|
63
|
+
# all required params must be meet, an object can be passed to meet :id but it must respond to #to_param
|
64
|
+
path_for(:posts, 1) # => '/posts/1'
|
65
|
+
end
|
66
|
+
|
67
|
+
|
68
|
+
Namespaces
|
69
|
+
==========
|
70
|
+
|
71
|
+
Pasing a symbol namespaces both the path and the route name:
|
72
|
+
|
73
|
+
namespace :admin do
|
74
|
+
map(:dashboard)
|
75
|
+
end
|
76
|
+
|
77
|
+
route_for(:admin_dashboard) # => '/admin/dashboard'
|
78
|
+
|
79
|
+
Passing a string only namespaces the path:
|
80
|
+
|
81
|
+
namespace 'admin' do
|
82
|
+
map(:dashboard)
|
83
|
+
end
|
84
|
+
|
85
|
+
route_for(:dashboard) # => '/admin/dashboard'
|
86
|
+
|
87
|
+
Passing nil to namespace only sets a context:
|
88
|
+
|
89
|
+
namespace nil do
|
90
|
+
map(:dashboard)
|
91
|
+
end
|
92
|
+
|
93
|
+
route_for(:dashboard) # => '/dashboard'
|
94
|
+
|
95
|
+
|
96
|
+
Resources
|
97
|
+
=========
|
98
|
+
|
99
|
+
Restful routes for plural resources can be generated as follows, inside the resource
|
100
|
+
definition block a route can be accessed by its name as a method call or using the path_for method:
|
101
|
+
|
102
|
+
resources :users do
|
103
|
+
# GET /users
|
104
|
+
get users do
|
105
|
+
...
|
106
|
+
end
|
107
|
+
|
108
|
+
# POST /users
|
109
|
+
post users do
|
110
|
+
...
|
111
|
+
end
|
112
|
+
|
113
|
+
# GET /users/new
|
114
|
+
get new_user do
|
115
|
+
...
|
116
|
+
end
|
117
|
+
|
118
|
+
# GET /users/:id
|
119
|
+
get user do
|
120
|
+
...
|
121
|
+
end
|
122
|
+
|
123
|
+
# GET /users/:id/edit
|
124
|
+
get edit_user do
|
125
|
+
...
|
126
|
+
end
|
127
|
+
|
128
|
+
# PUT /users/:id
|
129
|
+
put user do
|
130
|
+
...
|
131
|
+
end
|
132
|
+
|
133
|
+
# DELETE /users/:id
|
134
|
+
delete user do
|
135
|
+
...
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
It is important to note that the order in wich the sinatra action blocks are defined is important.
|
140
|
+
in this case `get(new_user)` must be defined before `get(user)`
|
141
|
+
|
142
|
+
As with previous examples routes can be defined beforehand:
|
143
|
+
|
144
|
+
resources :users
|
145
|
+
# GET '/users'
|
146
|
+
get path_for(:users) do
|
147
|
+
...
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
Nested Resources
|
152
|
+
================
|
153
|
+
|
154
|
+
Resources can be nested in a similar way as with Rails nested resources:
|
155
|
+
|
156
|
+
resources :users do
|
157
|
+
...
|
158
|
+
resources :comments do
|
159
|
+
# GET /users/:user_id/comments
|
160
|
+
get user_comments do
|
161
|
+
...
|
162
|
+
end
|
163
|
+
|
164
|
+
# GET /users/:user_id/comments/:id
|
165
|
+
get user_comment do
|
166
|
+
...
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
print_routes
|
172
|
+
# users => /users
|
173
|
+
# new_user => /users/new
|
174
|
+
# user => /users/:id
|
175
|
+
# edit_user => /users/:id/edit
|
176
|
+
# user_comments => /users/:user_id/comments
|
177
|
+
# new_user_comment => /users/:user_id/comments/new
|
178
|
+
# user_comment => /users/:user_id/comments/:id
|
179
|
+
# edit_user_comment => /users/:user_id/comments/:id/edit
|
180
|
+
|
181
|
+
|
182
|
+
And for actions that don't need to load the parent resource the route generation can be shallow:
|
183
|
+
|
184
|
+
resources :users, :shallow => true do
|
185
|
+
resources :comments
|
186
|
+
end
|
187
|
+
|
188
|
+
print_routes
|
189
|
+
# users => /users
|
190
|
+
# new_user => /users/new
|
191
|
+
# user => /users/:id
|
192
|
+
# edit_user => /users/:id/edit
|
193
|
+
# user_comments => /users/:user_id/comments
|
194
|
+
# new_user_comment => /users/:user_id/comments/new
|
195
|
+
# comment => /comments/:id
|
196
|
+
# edit_comment => /comments/:id/edit
|
197
|
+
|
198
|
+
There's an alternative way of generating routes for nested resources:
|
199
|
+
|
200
|
+
resources :users => {:posts => :comments}, :shallow => true do
|
201
|
+
...
|
202
|
+
end
|
203
|
+
print_routes
|
204
|
+
# users => /users
|
205
|
+
# new_user => /users/new
|
206
|
+
# user => /users/:id
|
207
|
+
# edit_user => /users/:id/edit
|
208
|
+
# user_posts => /users/:user_id/posts
|
209
|
+
# new_user_post => /users/:user_id/posts/new
|
210
|
+
# post => /posts/:id
|
211
|
+
# edit_post => /posts/:id/edit
|
212
|
+
# post_comments => /posts/:post_id/comments
|
213
|
+
# new_post_comment => /posts/:post_id/comments/new
|
214
|
+
# comment => /comments/:id
|
215
|
+
# edit_comment => /comments/:id/edit
|
216
|
+
|
217
|
+
Some keys to the params hash are added when routes are defined using sinatra-trails:
|
218
|
+
|
219
|
+
params[:resource] # the name of the resource in REST actions
|
220
|
+
params[:action] # the name of the action in REST actions
|
221
|
+
params[:namespace] # the name of the current namespace
|
222
|
+
|
223
|
+
|
224
|
+
Singleton Resource
|
225
|
+
=================
|
226
|
+
|
227
|
+
Same principles apply for singleton resource:
|
228
|
+
|
229
|
+
resource :user
|
230
|
+
print_routes
|
231
|
+
# user => /user
|
232
|
+
# new_user => /user/new
|
233
|
+
# edit_user => /user/edit
|
234
|
+
|
235
|
+
|
236
|
+
Before and After Filters
|
237
|
+
========================
|
238
|
+
|
239
|
+
Defining a filter within a context (namespace, resources, resource) without passing any path will execute that filter for
|
240
|
+
all actions defined in that context:
|
241
|
+
|
242
|
+
namespace :admin do
|
243
|
+
before do
|
244
|
+
@admin = true
|
245
|
+
end
|
246
|
+
|
247
|
+
get map(:dashboard) do
|
248
|
+
@admin # => true
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
get map(:home, :to => '/') do
|
253
|
+
@admin # => nil
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
Within a context named routes, strings or regexps can be used as arguments for before and after filters:
|
258
|
+
|
259
|
+
resources :users do
|
260
|
+
before new_user, edit_user do
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
A symbol can be passed to the filter definition and it will be lazily evaluated against the routes within the context:
|
265
|
+
|
266
|
+
namespace :admin do
|
267
|
+
before :dashboard do
|
268
|
+
...
|
269
|
+
end
|
270
|
+
|
271
|
+
get map(:dashboard) do
|
272
|
+
...
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
|
277
|
+
Accessing Routes from Outside the App
|
278
|
+
=====================================
|
279
|
+
|
280
|
+
On registering Sinatra::Trails a dynamic module for the Sinatra app is created and it is assigned to the constant `Routes`
|
281
|
+
including that module in another class gives that class access to the app's paths, a single class can access paths for
|
282
|
+
several Sinatra apps:
|
283
|
+
|
284
|
+
class MyApp < Sinatra::Base
|
285
|
+
register Sinatra::Trails
|
286
|
+
|
287
|
+
get map(:users) do
|
288
|
+
...
|
289
|
+
end
|
290
|
+
|
291
|
+
get map(:user, :to => '/users/:id') do
|
292
|
+
...
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
class OtherApp < Sinatra::Base
|
297
|
+
include MyApp::Routes
|
298
|
+
|
299
|
+
get 'index' do
|
300
|
+
redirect to path_for(:users)
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
class User < Sequel::Model
|
305
|
+
include MyApp::Routes
|
306
|
+
|
307
|
+
def to_param() id end
|
308
|
+
|
309
|
+
def route
|
310
|
+
path_for(:user, self) # => '/users/1'
|
311
|
+
end
|
312
|
+
end
|
data/lib/sinatra/trails.rb
CHANGED
@@ -89,10 +89,7 @@ module Sinatra
|
|
89
89
|
@actual_keys ||= routes.map{ |m| m.keys }.flatten
|
90
90
|
end
|
91
91
|
|
92
|
-
def keys
|
93
|
-
self
|
94
|
-
end
|
95
|
-
|
92
|
+
def keys() self end
|
96
93
|
def any?() actual_keys.any? end
|
97
94
|
def zip(arr) actual_keys.zip(arr) end
|
98
95
|
end
|
@@ -108,8 +105,8 @@ module Sinatra
|
|
108
105
|
end
|
109
106
|
|
110
107
|
def map name, opts = {}, &block
|
111
|
-
path
|
112
|
-
route = Route.new(path, name, [*ancestors, self], self)
|
108
|
+
path = opts.delete(:to) || name
|
109
|
+
route = Route.new(path, name.to_sym, [*ancestors, self], self)
|
113
110
|
instance_eval &block if block_given?
|
114
111
|
route
|
115
112
|
end
|
@@ -131,6 +128,12 @@ module Sinatra
|
|
131
128
|
@sinatra_app.before ScopeMatcher.new(self, args), opts, &block
|
132
129
|
end
|
133
130
|
|
131
|
+
def after *args, &block
|
132
|
+
# TODO: no specs
|
133
|
+
opts = Hash === args.last ? args.pop : {}
|
134
|
+
@sinatra_app.after ScopeMatcher.new(self, args), opts, &block
|
135
|
+
end
|
136
|
+
|
134
137
|
def generate_routes! &block
|
135
138
|
instance_eval &block if block_given?
|
136
139
|
@routes
|
@@ -242,7 +245,6 @@ module Sinatra
|
|
242
245
|
|
243
246
|
def map action
|
244
247
|
ancestors = [*self.ancestors, plural_name, action]
|
245
|
-
|
246
248
|
ancestors[0, ancestors.size - 2] = ancestors[0..-3].reject{ |ancestor| Resource === ancestor } if opts[:shallow]
|
247
249
|
Route.new([plural_name, action], action.to_s, ancestors, self)
|
248
250
|
end
|
@@ -13,7 +13,7 @@ describe 'trails' do
|
|
13
13
|
describe 'basic' do
|
14
14
|
before :all do
|
15
15
|
app.map :home, :to => '/'
|
16
|
-
app.map
|
16
|
+
app.map 'dashboard'
|
17
17
|
app.map :edit_user, :to => '/users/:id/edit'
|
18
18
|
end
|
19
19
|
|
@@ -166,7 +166,6 @@ describe 'trails' do
|
|
166
166
|
app.resources :users do
|
167
167
|
map(:confirm)
|
168
168
|
end
|
169
|
-
puts app.print_routes
|
170
169
|
end
|
171
170
|
it_should_behave_like 'generates routes for users'
|
172
171
|
it { app.route_for(:users_confirm).should == '/users/confirm' }
|
@@ -314,17 +313,29 @@ describe 'trails' do
|
|
314
313
|
end
|
315
314
|
|
316
315
|
describe 'single resource' do
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
316
|
+
describe 'basic' do
|
317
|
+
before :all do
|
318
|
+
app.resource :user => :profile
|
319
|
+
end
|
320
|
+
it { app.route_for(:user).should == '/user' }
|
321
|
+
it { app.route_for(:new_user).should == '/user/new' }
|
322
|
+
it { app.route_for(:edit_user).should == '/user/edit' }
|
323
|
+
it { app.route_for(:user_profile).should == '/user/profile' }
|
324
|
+
it { app.route_for(:new_user_profile).should == '/user/profile/new' }
|
325
|
+
it { app.route_for(:edit_user_profile).should == '/user/profile/edit' }
|
326
|
+
end
|
327
|
+
|
328
|
+
describe 'as namespace' do
|
329
|
+
before :all do
|
330
|
+
app.resource :user do
|
331
|
+
map(:confirm)
|
332
|
+
end
|
333
|
+
end
|
334
|
+
it { app.route_for(:user_confirm).should == '/user/confirm' }
|
335
|
+
end
|
326
336
|
end
|
327
337
|
|
338
|
+
|
328
339
|
describe 'finding route for scope' do
|
329
340
|
before :all do
|
330
341
|
@scope = Sinatra::Trails::Scope.new(app, :admin)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sinatra-trails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,12 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
12
|
+
date: 2011-11-29 00:00:00.000000000 -06:00
|
13
|
+
default_executable:
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: sinatra
|
16
|
-
requirement: &
|
17
|
+
requirement: &70095708882740 !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
20
|
- - ! '>='
|
@@ -21,10 +22,10 @@ dependencies:
|
|
21
22
|
version: '0'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
|
-
version_requirements: *
|
25
|
+
version_requirements: *70095708882740
|
25
26
|
- !ruby/object:Gem::Dependency
|
26
27
|
name: i18n
|
27
|
-
requirement: &
|
28
|
+
requirement: &70095708881640 !ruby/object:Gem::Requirement
|
28
29
|
none: false
|
29
30
|
requirements:
|
30
31
|
- - ! '>='
|
@@ -32,10 +33,10 @@ dependencies:
|
|
32
33
|
version: '0'
|
33
34
|
type: :runtime
|
34
35
|
prerelease: false
|
35
|
-
version_requirements: *
|
36
|
+
version_requirements: *70095708881640
|
36
37
|
- !ruby/object:Gem::Dependency
|
37
38
|
name: activesupport
|
38
|
-
requirement: &
|
39
|
+
requirement: &70095708880780 !ruby/object:Gem::Requirement
|
39
40
|
none: false
|
40
41
|
requirements:
|
41
42
|
- - ! '>='
|
@@ -43,10 +44,10 @@ dependencies:
|
|
43
44
|
version: '3.0'
|
44
45
|
type: :runtime
|
45
46
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
+
version_requirements: *70095708880780
|
47
48
|
- !ruby/object:Gem::Dependency
|
48
49
|
name: rake
|
49
|
-
requirement: &
|
50
|
+
requirement: &70095708880260 !ruby/object:Gem::Requirement
|
50
51
|
none: false
|
51
52
|
requirements:
|
52
53
|
- - ! '>='
|
@@ -54,10 +55,10 @@ dependencies:
|
|
54
55
|
version: '0'
|
55
56
|
type: :development
|
56
57
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
+
version_requirements: *70095708880260
|
58
59
|
- !ruby/object:Gem::Dependency
|
59
60
|
name: rspec
|
60
|
-
requirement: &
|
61
|
+
requirement: &70095708879500 !ruby/object:Gem::Requirement
|
61
62
|
none: false
|
62
63
|
requirements:
|
63
64
|
- - ! '>='
|
@@ -65,10 +66,10 @@ dependencies:
|
|
65
66
|
version: '0'
|
66
67
|
type: :development
|
67
68
|
prerelease: false
|
68
|
-
version_requirements: *
|
69
|
+
version_requirements: *70095708879500
|
69
70
|
- !ruby/object:Gem::Dependency
|
70
71
|
name: rack-test
|
71
|
-
requirement: &
|
72
|
+
requirement: &70095708878680 !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
74
|
requirements:
|
74
75
|
- - ! '>='
|
@@ -76,7 +77,7 @@ dependencies:
|
|
76
77
|
version: '0'
|
77
78
|
type: :development
|
78
79
|
prerelease: false
|
79
|
-
version_requirements: *
|
80
|
+
version_requirements: *70095708878680
|
80
81
|
description: A named routes Sinatra DSL inspired by Rails routing
|
81
82
|
email:
|
82
83
|
- macarui@gmail.com
|
@@ -87,12 +88,14 @@ files:
|
|
87
88
|
- .gitignore
|
88
89
|
- .rspec
|
89
90
|
- Gemfile
|
91
|
+
- README.rdoc
|
90
92
|
- Rakefile
|
91
93
|
- lib/sinatra/trails.rb
|
92
94
|
- lib/sinatra/trails/version.rb
|
93
95
|
- spec/route_generation_spec.rb
|
94
96
|
- spec/spec_helper.rb
|
95
97
|
- trails.gemspec
|
98
|
+
has_rdoc: true
|
96
99
|
homepage: http://github.com/maca/trails
|
97
100
|
licenses: []
|
98
101
|
post_install_message:
|
@@ -113,7 +116,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
116
|
version: '0'
|
114
117
|
requirements: []
|
115
118
|
rubyforge_project: sinatra-trails
|
116
|
-
rubygems_version: 1.
|
119
|
+
rubygems_version: 1.6.2
|
117
120
|
signing_key:
|
118
121
|
specification_version: 3
|
119
122
|
summary: A named routes Sinatra DSL inspired by Rails routing
|