strelka 0.0.1.pre.279 → 0.0.1.pre.284
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/ChangeLog +42 -1
- data/Rakefile +1 -1
- data/bin/strelka +1 -1
- data/lib/strelka/app/restresources.rb +127 -51
- data/lib/strelka/httpresponse.rb +2 -1
- data/lib/strelka/httpresponse/negotiation.rb +2 -1
- data/lib/strelka/paramvalidator.rb +36 -23
- data/spec/lib/helpers.rb +2 -0
- data/spec/strelka/app/restresources_spec.rb +42 -13
- data/spec/strelka/httpresponse_spec.rb +4 -0
- data/spec/strelka/paramvalidator_spec.rb +9 -4
- metadata +4 -4
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/ChangeLog
CHANGED
@@ -1,3 +1,44 @@
|
|
1
|
+
2012-07-17 Michael Granger <ged@FaerieMUD.org>
|
2
|
+
|
3
|
+
* bin/strelka, lib/strelka/app/restresources.rb,
|
4
|
+
lib/strelka/httpresponse/negotiation.rb,
|
5
|
+
lib/strelka/paramvalidator.rb, spec/lib/helpers.rb,
|
6
|
+
spec/strelka/app/restresources_spec.rb,
|
7
|
+
spec/strelka/paramvalidator_spec.rb:
|
8
|
+
Clean up some :restresources behavior
|
9
|
+
[153e65a22805] [tip]
|
10
|
+
|
11
|
+
2012-07-16 Michael Granger <ged@FaerieMUD.org>
|
12
|
+
|
13
|
+
* lib/strelka/httpresponse.rb, spec/strelka/httpresponse_spec.rb:
|
14
|
+
Bugfix for content-type-less responses
|
15
|
+
[3540a46a9b25]
|
16
|
+
|
17
|
+
2012-07-13 Michael Granger <ged@FaerieMUD.org>
|
18
|
+
|
19
|
+
* lib/strelka/httpresponse/negotiation.rb:
|
20
|
+
Add a default stringifier (#to_s) to
|
21
|
+
Strelka::HTTPResponse::Negotiation
|
22
|
+
[4fe7c945616b]
|
23
|
+
|
24
|
+
* lib/strelka/app/restresources.rb:
|
25
|
+
Fix the OPTIONS action in the :restresources plugin
|
26
|
+
[b832003605e0]
|
27
|
+
|
28
|
+
* .rvm.gems, Rakefile:
|
29
|
+
Bump mongrel2 dependency
|
30
|
+
[6149242307af]
|
31
|
+
|
32
|
+
2012-07-12 Michael Granger <ged@FaerieMUD.org>
|
33
|
+
|
34
|
+
* .rvm.gems, Rakefile:
|
35
|
+
Bump the mongrel2 dependency
|
36
|
+
[389401bb6480]
|
37
|
+
|
38
|
+
* lib/strelka/app/restresources.rb:
|
39
|
+
Fix a buggy log message.
|
40
|
+
[75f9be31cb8c]
|
41
|
+
|
1
42
|
2012-07-06 Michael Granger <ged@FaerieMUD.org>
|
2
43
|
|
3
44
|
* .rvm.gems, Rakefile, lib/strelka/app/templating.rb:
|
@@ -6,7 +47,7 @@
|
|
6
47
|
- bump Inversion dependency for encoding support
|
7
48
|
- Use the default_internal encoding or UTF-8 if that isn't set as the
|
8
49
|
encoding of templates loaded by the :templating plugin.
|
9
|
-
[4f7bb0f3a8f7]
|
50
|
+
[4f7bb0f3a8f7]
|
10
51
|
|
11
52
|
* Manifest.txt:
|
12
53
|
Add multipart-form support files to the manifest
|
data/Rakefile
CHANGED
@@ -28,7 +28,7 @@ hoespec = Hoe.spec 'strelka' do
|
|
28
28
|
self.dependency 'highline', '~> 1.6'
|
29
29
|
self.dependency 'inversion', '~> 0.11'
|
30
30
|
self.dependency 'loggability', '~> 0.4'
|
31
|
-
self.dependency 'mongrel2', '~> 0.
|
31
|
+
self.dependency 'mongrel2', '~> 0.29'
|
32
32
|
self.dependency 'pluginfactory', '~> 1.0'
|
33
33
|
self.dependency 'sysexits', '~> 1.0'
|
34
34
|
self.dependency 'trollop', '~> 1.16'
|
data/bin/strelka
CHANGED
@@ -295,8 +295,8 @@ class Strelka::CLICommand
|
|
295
295
|
]
|
296
296
|
fork do
|
297
297
|
self.log.debug " in the child."
|
298
|
-
apps = Strelka::App.load( path )
|
299
298
|
Strelka.load_config( self.options.config ) if self.options.config
|
299
|
+
apps = Strelka::App.load( path )
|
300
300
|
self.log.debug " loaded: %p" % [ apps ]
|
301
301
|
apps.first.run
|
302
302
|
end
|
@@ -4,6 +4,7 @@
|
|
4
4
|
|
5
5
|
require 'set'
|
6
6
|
require 'sequel'
|
7
|
+
require 'sequel/extensions/pretty_table'
|
7
8
|
|
8
9
|
require 'strelka' unless defined?( Strelka )
|
9
10
|
require 'strelka/app' unless defined?( Strelka::App )
|
@@ -64,7 +65,8 @@ module Strelka::App::RestResources
|
|
64
65
|
|
65
66
|
# Class methods to add to classes with REST resources.
|
66
67
|
module ClassMethods # :nodoc:
|
67
|
-
include Sequel::Inflections
|
68
|
+
include Sequel::Inflections,
|
69
|
+
Strelka::Constants
|
68
70
|
|
69
71
|
# Set of verbs that are valid for a resource, keyed by the resource path
|
70
72
|
@resource_verbs = Hash.new {|h,k| h[k] = Set.new }
|
@@ -83,13 +85,12 @@ module Strelka::App::RestResources
|
|
83
85
|
def self::extended( obj )
|
84
86
|
super
|
85
87
|
|
88
|
+
# Enable text tables for text/plain responses
|
89
|
+
Sequel.extension( :pretty_table )
|
90
|
+
|
86
91
|
# Load the plugins this one depends on if they aren't already
|
87
92
|
obj.plugins :routing, :negotiation, :parameters
|
88
93
|
|
89
|
-
# Add validations for the limit and offset parameters
|
90
|
-
obj.param :limit, :integer
|
91
|
-
obj.param :offset, :integer
|
92
|
-
|
93
94
|
# Use the 'exclusive' router instead of the more-flexible
|
94
95
|
# Mongrel2-style default one
|
95
96
|
obj.router :exclusive
|
@@ -120,12 +121,20 @@ module Strelka::App::RestResources
|
|
120
121
|
self.log.debug "Adding REST resource for %p" % [ rsrcobj ]
|
121
122
|
options = self.service_options.merge( options )
|
122
123
|
|
124
|
+
# Add a parameter for the primary key
|
125
|
+
pkey = rsrcobj.primary_key
|
126
|
+
pkey_schema = rsrcobj.db_schema[ pkey.to_sym ] or
|
127
|
+
raise ArgumentError,
|
128
|
+
"cannot generate services for %p: resource has no schema" % [ rsrcobj ]
|
129
|
+
self.param( pkey, pkey_schema[:type] ) unless
|
130
|
+
self.paramvalidator.param_names.include?( pkey.to_s )
|
131
|
+
|
123
132
|
# Figure out what the resource name is, and make the route from it
|
124
133
|
name = options[:name] || rsrcobj.implicit_table_name
|
125
134
|
route = [ options[:prefix], name ].compact.join( '/' )
|
126
135
|
|
127
|
-
#
|
128
|
-
self.
|
136
|
+
# Ensure validated parameters are untainted
|
137
|
+
self.untaint_all_constraints
|
129
138
|
|
130
139
|
# Make and install handler methods
|
131
140
|
self.log.debug " adding readers"
|
@@ -150,17 +159,6 @@ module Strelka::App::RestResources
|
|
150
159
|
end
|
151
160
|
|
152
161
|
|
153
|
-
### Add parameter declarations for parameters related to +rsrcobj+.
|
154
|
-
def add_parameters( rsrcobj, options )
|
155
|
-
self.log.debug "Declaring validations for columns from %p" % [ rsrcobj ]
|
156
|
-
self.untaint_all_constraints
|
157
|
-
rsrcobj.db_schema.each do |col, config|
|
158
|
-
self.log.debug " %s (%p)" % [ col, config[:type] ]
|
159
|
-
param col, config[:type]
|
160
|
-
end
|
161
|
-
end
|
162
|
-
|
163
|
-
|
164
162
|
### Add a handler method for discovery for the specified +rsrcobj+.
|
165
163
|
### OPTIONS /resources
|
166
164
|
def add_options_handler( route, rsrcobj, options )
|
@@ -168,14 +166,29 @@ module Strelka::App::RestResources
|
|
168
166
|
self.log.debug "Adding OPTIONS handler for %s (%p)" % [ route, rsrcobj ]
|
169
167
|
self.add_route( :OPTIONS, route, options ) do |req|
|
170
168
|
self.log.debug "OPTIONS handler!"
|
171
|
-
verbs = self.class.resource_verbs[ route ].sort
|
172
169
|
res = req.response
|
173
170
|
|
171
|
+
# Gather up metadata describing the resource
|
172
|
+
verbs = self.class.resource_verbs[ route ].sort
|
173
|
+
columns = rsrcobj.allowed_columns || rsrcobj.columns
|
174
|
+
attributes = columns.each_with_object({}) do |col, hash|
|
175
|
+
hash[ col ] = rsrcobj.db_schema[ col ][:type]
|
176
|
+
end
|
177
|
+
|
174
178
|
self.log.debug " making a reply with Allowed: %s" % [ verbs.join(', ') ]
|
175
179
|
res.header.allowed = verbs.join(', ')
|
176
|
-
res.
|
177
|
-
|
178
|
-
|
180
|
+
res.for( :json, :yaml ) do |req|
|
181
|
+
{
|
182
|
+
'methods' => verbs,
|
183
|
+
'attributes' => attributes,
|
184
|
+
}
|
185
|
+
end
|
186
|
+
res.for( :text ) do
|
187
|
+
"Methods: #{verbs.join(', ')}\n" +
|
188
|
+
"Attributes: \n" +
|
189
|
+
attributes.map {|name,type| " "}
|
190
|
+
end
|
191
|
+
|
179
192
|
|
180
193
|
return res
|
181
194
|
end
|
@@ -184,9 +197,8 @@ module Strelka::App::RestResources
|
|
184
197
|
end
|
185
198
|
|
186
199
|
|
187
|
-
### Add a handler method for reading a single instance of the specified +rsrcobj+, which
|
188
|
-
### Sequel::Model class or a ducktype-alike.
|
189
|
-
### GET /resources/{id}
|
200
|
+
### Add a handler method for reading a single instance of the specified +rsrcobj+, which
|
201
|
+
### should be a Sequel::Model class or a ducktype-alike. GET /resources/{id}
|
190
202
|
def add_read_handler( route_prefix, rsrcobj, options )
|
191
203
|
pkey = rsrcobj.primary_key
|
192
204
|
route = "#{route_prefix}/:#{pkey}"
|
@@ -202,6 +214,7 @@ module Strelka::App::RestResources
|
|
202
214
|
|
203
215
|
res = req.response
|
204
216
|
res.for( :json, :yaml ) { resource }
|
217
|
+
res.for( :text ) { Sequel::PrettyTable.string(resource) }
|
205
218
|
|
206
219
|
return res
|
207
220
|
end
|
@@ -210,19 +223,36 @@ module Strelka::App::RestResources
|
|
210
223
|
end
|
211
224
|
|
212
225
|
|
213
|
-
### Add a handler method for reading a collection of the specified +rsrcobj+, which should
|
214
|
-
### Sequel::Model class or a ducktype-alike.
|
226
|
+
### Add a handler method for reading a collection of the specified +rsrcobj+, which should
|
227
|
+
### be a Sequel::Model class or a ducktype-alike.
|
215
228
|
### GET /resources
|
216
229
|
def add_collection_read_handler( route, rsrcobj, options )
|
217
|
-
self.log.debug "Creating handler for reading collections of %p: GET %s" %
|
230
|
+
self.log.debug "Creating handler for reading collections of %p: GET %s" %
|
231
|
+
[ rsrcobj, route ]
|
232
|
+
|
233
|
+
# Make a column regexp for validating the order field
|
234
|
+
colunion = Regexp.union( (rsrcobj.allowed_columns || rsrcobj.columns).map(&:to_s) )
|
235
|
+
colre = /^(?<column>#{colunion})$/
|
236
|
+
|
218
237
|
self.add_route( :GET, route, options ) do |req|
|
238
|
+
# Add validations for limit, offset, and order parameters
|
239
|
+
req.params.add :limit, :integer
|
240
|
+
req.params.add :offset, :integer
|
241
|
+
req.params.add :order, colre
|
242
|
+
|
219
243
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join("\n") ) unless
|
220
244
|
req.params.okay?
|
221
245
|
|
222
|
-
limit, offset = req.params.values_at( :limit, :offset )
|
246
|
+
limit, offset, order = req.params.values_at( :limit, :offset, :order )
|
223
247
|
res = req.response
|
224
248
|
|
225
249
|
dataset = rsrcobj.dataset
|
250
|
+
if order
|
251
|
+
order = Array( order ).map( &:to_sym )
|
252
|
+
self.log.debug "Ordering result set by %p" % [ order ]
|
253
|
+
dataset = dataset.order( *order )
|
254
|
+
end
|
255
|
+
|
226
256
|
if limit
|
227
257
|
self.log.debug "Limiting result set to %p records" % [ limit ]
|
228
258
|
dataset = dataset.limit( limit, offset )
|
@@ -230,6 +260,7 @@ module Strelka::App::RestResources
|
|
230
260
|
|
231
261
|
self.log.debug "Returning collection: %s" % [ dataset.sql ]
|
232
262
|
res.for( :json, :yaml ) { dataset.all }
|
263
|
+
res.for( :text ) { Sequel::PrettyTable.string(dataset) }
|
233
264
|
|
234
265
|
return res
|
235
266
|
end
|
@@ -241,9 +272,11 @@ module Strelka::App::RestResources
|
|
241
272
|
### Add a handler method for creating a new instance of +rsrcobj+.
|
242
273
|
### POST /resources
|
243
274
|
def add_collection_create_handler( route, rsrcobj, options )
|
244
|
-
self.log.debug "Creating handler for creating %p resources: POST %s" %
|
275
|
+
self.log.debug "Creating handler for creating %p resources: POST %s" %
|
276
|
+
[ rsrcobj, route ]
|
245
277
|
|
246
278
|
self.add_route( :POST, route, options ) do |req|
|
279
|
+
add_resource_params( req, rsrcobj )
|
247
280
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join(", ") ) unless
|
248
281
|
req.params.okay?
|
249
282
|
|
@@ -279,8 +312,10 @@ module Strelka::App::RestResources
|
|
279
312
|
pkey = rsrcobj.primary_key
|
280
313
|
route = "#{route_prefix}/:#{pkey}"
|
281
314
|
|
282
|
-
self.log.debug "Creating handler for creating %p resources: POST %s" %
|
315
|
+
self.log.debug "Creating handler for creating %p resources: POST %s" %
|
316
|
+
[ rsrcobj, route ]
|
283
317
|
self.add_route( :PUT, route, options ) do |req|
|
318
|
+
add_resource_params( req, rsrcobj )
|
284
319
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join(", ") ) unless
|
285
320
|
req.params.okay?
|
286
321
|
|
@@ -312,9 +347,11 @@ module Strelka::App::RestResources
|
|
312
347
|
### PUT /resources
|
313
348
|
def add_collection_update_handler( route, rsrcobj, options )
|
314
349
|
pkey = rsrcobj.primary_key
|
315
|
-
self.log.debug "Creating handler for updating every %p resources: PUT %s" %
|
350
|
+
self.log.debug "Creating handler for updating every %p resources: PUT %s" %
|
351
|
+
[ rsrcobj, route ]
|
316
352
|
|
317
353
|
self.add_route( :PUT, route, options ) do |req|
|
354
|
+
add_resource_params( req, rsrcobj )
|
318
355
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join(", ") ) unless
|
319
356
|
req.params.okay?
|
320
357
|
|
@@ -341,8 +378,8 @@ module Strelka::App::RestResources
|
|
341
378
|
end
|
342
379
|
|
343
380
|
|
344
|
-
### Add a handler method for deleting an instance of +rsrcobj+ with +route_prefix+ as the
|
345
|
-
### URI path.
|
381
|
+
### Add a handler method for deleting an instance of +rsrcobj+ with +route_prefix+ as the
|
382
|
+
### base URI path.
|
346
383
|
### DELETE /resources/{id}
|
347
384
|
def add_delete_handler( route_prefix, rsrcobj, options )
|
348
385
|
pkey = rsrcobj.primary_key
|
@@ -442,8 +479,13 @@ module Strelka::App::RestResources
|
|
442
479
|
def add_dataset_read_handler( path, rsrcobj, dsname, param, options )
|
443
480
|
self.log.debug "Adding dataset method read handler: %s" % [ path ]
|
444
481
|
|
482
|
+
config = rsrcobj.db_schema[ param ] or
|
483
|
+
raise ArgumentError, "no such column %p for %p" % [ col, rsrcobj ]
|
484
|
+
param( param, config[:type] )
|
485
|
+
|
445
486
|
self.add_route( :GET, path, options ) do |req|
|
446
|
-
self.log.debug "Resource dataset GET request for dataset %s on %p" %
|
487
|
+
self.log.debug "Resource dataset GET request for dataset %s on %p" %
|
488
|
+
[ dsname, rsrcobj ]
|
447
489
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join("\n") ) unless
|
448
490
|
req.params.okay?
|
449
491
|
|
@@ -464,6 +506,7 @@ module Strelka::App::RestResources
|
|
464
506
|
# :TODO: Handle other mediatypes
|
465
507
|
self.log.debug " returning collection: %s" % [ dataset.sql ]
|
466
508
|
res.for( :json, :yaml ) { dataset.all }
|
509
|
+
res.for( :text ) { Sequel::PrettyTable.string(dataset) }
|
467
510
|
|
468
511
|
return res
|
469
512
|
end
|
@@ -473,10 +516,18 @@ module Strelka::App::RestResources
|
|
473
516
|
### Add a GET route for the specified +association+ of the +rsrcobj+ at the given
|
474
517
|
### +path+.
|
475
518
|
def add_composite_read_handler( path, rsrcobj, association, options )
|
476
|
-
pkey = rsrcobj.primary_key
|
477
519
|
self.log.debug "Adding composite read handler for association: %s" % [ association ]
|
478
520
|
|
521
|
+
pkey = rsrcobj.primary_key
|
522
|
+
colunion = Regexp.union( (rsrcobj.allowed_columns || rsrcobj.columns).map(&:to_s) )
|
523
|
+
colre = /^(?<column>#{colunion})$/
|
524
|
+
|
479
525
|
self.add_route( :GET, path, options ) do |req|
|
526
|
+
|
527
|
+
# Add validations for limit, offset, and order parameters
|
528
|
+
req.params.add :limit, :integer
|
529
|
+
req.params.add :offset, :integer
|
530
|
+
req.params.add :order, colre
|
480
531
|
finish_with( HTTP::BAD_REQUEST, req.params.error_messages.join("\n") ) unless
|
481
532
|
req.params.okay?
|
482
533
|
|
@@ -486,24 +537,31 @@ module Strelka::App::RestResources
|
|
486
537
|
|
487
538
|
# Look up the resource, and if it exists, use it to fetch its associated
|
488
539
|
# objects
|
489
|
-
rsrcobj
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
540
|
+
resource = rsrcobj[ id ] or
|
541
|
+
finish_with( HTTP::NOT_FOUND, "No such %s [%p]" % [rsrcobj.table_name, id] )
|
542
|
+
|
543
|
+
limit, offset, order = req.params.values_at( :limit, :offset, :order )
|
544
|
+
dataset = resource.send( "#{association}_dataset" )
|
545
|
+
|
546
|
+
# Apply the order parameter if it exists
|
547
|
+
if order
|
548
|
+
order = Array( order ).map( &:to_sym )
|
549
|
+
self.log.debug "Ordering result set by %p" % [ order ]
|
550
|
+
dataset = dataset.order( *order )
|
551
|
+
end
|
500
552
|
|
501
|
-
|
502
|
-
|
503
|
-
self.log.debug "
|
504
|
-
|
553
|
+
# Apply limit and offset parameters if they exist
|
554
|
+
if limit
|
555
|
+
self.log.debug "Limiting result set to %p records" % [ limit ]
|
556
|
+
dataset = dataset.limit( limit, offset )
|
505
557
|
end
|
506
558
|
|
559
|
+
# Fetch and return the records as JSON or YAML
|
560
|
+
# :TODO: Handle other mediatypes
|
561
|
+
self.log.debug "Returning collection: %s" % [ dataset.sql ]
|
562
|
+
res.for( :json, :yaml ) { dataset.all }
|
563
|
+
res.for( :text ) { Sequel::PrettyTable.string(dataset) }
|
564
|
+
|
507
565
|
return res
|
508
566
|
end
|
509
567
|
end
|
@@ -518,4 +576,22 @@ module Strelka::App::RestResources
|
|
518
576
|
super
|
519
577
|
end
|
520
578
|
|
579
|
+
|
580
|
+
#######
|
581
|
+
private
|
582
|
+
#######
|
583
|
+
|
584
|
+
### Add parameter validations for the given +columns+ of the specified resource object +rsrcobj+
|
585
|
+
### to the specified +req+uest.
|
586
|
+
def add_resource_params( req, rsrcobj, *columns )
|
587
|
+
columns = rsrcobj.allowed_columns || rsrcobj.columns if columns.empty?
|
588
|
+
|
589
|
+
columns.each do |col|
|
590
|
+
config = rsrcobj.db_schema[ col ] or
|
591
|
+
raise ArgumentError, "no such column %p for %p" % [ col, rsrcobj ]
|
592
|
+
req.params.add( col, config[:type] ) unless req.params.param_names.include?( col.to_s )
|
593
|
+
end
|
594
|
+
|
595
|
+
end
|
596
|
+
|
521
597
|
end # module Strelka::App::RestResources
|
data/lib/strelka/httpresponse.rb
CHANGED
@@ -111,7 +111,8 @@ class Strelka::HTTPResponse < Mongrel2::HTTPResponse
|
|
111
111
|
|
112
112
|
### Add a charset to the content-type header in +headers+ if possible.
|
113
113
|
def add_content_type_charset( headers )
|
114
|
-
return unless headers.content_type
|
114
|
+
return unless headers.content_type &&
|
115
|
+
headers.content_type.start_with?( 'text' )
|
115
116
|
|
116
117
|
charset = self.find_header_charset
|
117
118
|
self.log.debug "Setting the charset in the content-type header to: %p" % [ charset.name ]
|
@@ -49,6 +49,7 @@ module Strelka::HTTPResponse::Negotiation
|
|
49
49
|
STRINGIFIERS = {
|
50
50
|
'application/x-yaml' => YAML.method( :dump ),
|
51
51
|
'application/json' => Yajl.method( :dump ),
|
52
|
+
'text/plain' => Proc.new {|obj| obj.to_s },
|
52
53
|
}
|
53
54
|
|
54
55
|
# Transcoding to Unicode is likely enough to work to warrant auto-transcoding. These
|
@@ -333,7 +334,7 @@ module Strelka::HTTPResponse::Negotiation
|
|
333
334
|
|
334
335
|
new_body = callback.call( mimetype ) or return false
|
335
336
|
|
336
|
-
self.log.debug " successfully transformed! Setting up response."
|
337
|
+
self.log.debug " successfully transformed: %p! Setting up response." % [ new_body ]
|
337
338
|
new_body = STRINGIFIERS[ mimetype ].call( new_body ) if
|
338
339
|
STRINGIFIERS.key?( mimetype ) && !new_body.is_a?( String )
|
339
340
|
|
@@ -279,12 +279,18 @@ class Strelka::ParamValidator < ::FormValidator
|
|
279
279
|
### a constraint, a description, and one or more flags.
|
280
280
|
def add( name, *args, &block )
|
281
281
|
name = name.to_s
|
282
|
-
|
283
|
-
|
284
|
-
|
282
|
+
constraint = self.make_param_validator( name, args, &block )
|
283
|
+
|
284
|
+
# No-op if there's already a parameter with the same name and constraint
|
285
|
+
if self.param_names.include?( name )
|
286
|
+
return if self.profile[:constraints][ name ] == constraint
|
287
|
+
raise ArgumentError,
|
288
|
+
"parameter %p is already defined as a '%s'; perhaps you meant to use #override?" %
|
289
|
+
[ name, self.profile[:constraints][name] ]
|
290
|
+
end
|
285
291
|
|
286
292
|
self.log.debug "Adding parameter '%s' to profile" % [ name ]
|
287
|
-
self.set_param( name, *args, &block )
|
293
|
+
self.set_param( name, constraint, *args, &block )
|
288
294
|
end
|
289
295
|
|
290
296
|
|
@@ -296,8 +302,30 @@ class Strelka::ParamValidator < ::FormValidator
|
|
296
302
|
"no parameter %p defined; perhaps you meant to use #add?" % [name] unless
|
297
303
|
self.param_names.include?( name )
|
298
304
|
|
305
|
+
constraint = self.make_param_validator( name, args, &block )
|
299
306
|
self.log.debug "Overriding parameter '%s' in profile" % [ name ]
|
300
|
-
self.set_param( name, *args, &block )
|
307
|
+
self.set_param( name, constraint, *args, &block )
|
308
|
+
end
|
309
|
+
|
310
|
+
|
311
|
+
### Extract a validator from the given +args+ and return it.
|
312
|
+
def make_param_validator( name, args, &block )
|
313
|
+
self.log.debug "Finding param validator out of: %s, %p, %p" % [ name, args, block ]
|
314
|
+
args.unshift( block ) if block
|
315
|
+
|
316
|
+
# Custom validator -- either a callback or a regex
|
317
|
+
if args.first.is_a?( Regexp ) || args.first.respond_to?( :call )
|
318
|
+
return args.shift
|
319
|
+
|
320
|
+
# Builtin match validator, either explicit or implied by the name
|
321
|
+
else
|
322
|
+
return args.shift if args.first.is_a?( Symbol ) && !FLAGS.include?( args.first )
|
323
|
+
|
324
|
+
raise ArgumentError, "no builtin %p validator" % [ name ] unless
|
325
|
+
self.respond_to?( "match_#{name}" )
|
326
|
+
return name
|
327
|
+
end
|
328
|
+
|
301
329
|
end
|
302
330
|
|
303
331
|
|
@@ -376,23 +404,8 @@ class Strelka::ParamValidator < ::FormValidator
|
|
376
404
|
|
377
405
|
### Set the parameter +name+ in the profile to validate using the given +args+,
|
378
406
|
### which are the same as the ones passed to #add and #override.
|
379
|
-
def set_param( name, *args, &block )
|
380
|
-
|
381
|
-
|
382
|
-
# Custom validator -- either a callback or a regex
|
383
|
-
if args.first.is_a?( Regexp ) ||
|
384
|
-
args.first.respond_to?( :call )
|
385
|
-
self.profile[:constraints][ name ] = args.shift
|
386
|
-
|
387
|
-
# Builtin match validator, either explicit or implied by the name
|
388
|
-
else
|
389
|
-
constraint = args.shift if args.first.is_a?( Symbol ) && !FLAGS.include?( args.first )
|
390
|
-
constraint ||= name
|
391
|
-
|
392
|
-
raise ArgumentError, "no builtin %p validator" % [ constraint ] unless
|
393
|
-
self.respond_to?( "match_#{constraint}" )
|
394
|
-
self.profile[:constraints][ name ] = constraint
|
395
|
-
end
|
407
|
+
def set_param( name, validator, *args, &block )
|
408
|
+
self.profile[:constraints][ name ] = validator
|
396
409
|
|
397
410
|
self.profile[:descriptions][ name ] = args.shift if args.first.is_a?( String )
|
398
411
|
|
@@ -407,7 +420,7 @@ class Strelka::ParamValidator < ::FormValidator
|
|
407
420
|
if args.include?( :untaint )
|
408
421
|
self.profile[:untaint_constraint_fields] |= [ name ]
|
409
422
|
else
|
410
|
-
self.profile[:untaint_constraint_fields].delete(
|
423
|
+
self.profile[:untaint_constraint_fields].delete( name )
|
411
424
|
end
|
412
425
|
|
413
426
|
self.revalidate if self.validated?
|
data/spec/lib/helpers.rb
CHANGED
@@ -28,7 +28,7 @@ describe Strelka::App::RestResources do
|
|
28
28
|
include Mongrel2::Config::DSL
|
29
29
|
|
30
30
|
before( :all ) do
|
31
|
-
setup_logging(
|
31
|
+
setup_logging()
|
32
32
|
setup_config_db()
|
33
33
|
|
34
34
|
@request_factory = Mongrel2::RequestFactory.new( route: '/api/v1' )
|
@@ -42,7 +42,7 @@ describe Strelka::App::RestResources do
|
|
42
42
|
it_should_behave_like( "A Strelka::App Plugin" )
|
43
43
|
|
44
44
|
|
45
|
-
describe "an
|
45
|
+
describe "included in an App" do
|
46
46
|
|
47
47
|
before( :each ) do
|
48
48
|
@app = Class.new( Strelka::App ) do
|
@@ -54,7 +54,7 @@ describe Strelka::App::RestResources do
|
|
54
54
|
end
|
55
55
|
|
56
56
|
|
57
|
-
it "
|
57
|
+
it "keeps track of what resources are mounted where" do
|
58
58
|
@app.resource_verbs.should be_a( Hash )
|
59
59
|
@app.resource_verbs.should be_empty()
|
60
60
|
end
|
@@ -70,7 +70,7 @@ describe Strelka::App::RestResources do
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
it "
|
73
|
+
it "keeps track of what resources are mounted where" do
|
74
74
|
@app.resource_verbs.should have( 1 ).member
|
75
75
|
@app.resource_verbs.should include( 'servers' )
|
76
76
|
@app.resource_verbs[ 'servers' ].
|
@@ -107,7 +107,7 @@ describe Strelka::App::RestResources do
|
|
107
107
|
end
|
108
108
|
end
|
109
109
|
|
110
|
-
it "
|
110
|
+
it "keeps track of what resources are mounted where" do
|
111
111
|
@app.resource_verbs.should have( 1 ).member
|
112
112
|
@app.resource_verbs.should include( 'servers' )
|
113
113
|
@app.resource_verbs[ 'servers' ].
|
@@ -136,17 +136,20 @@ describe Strelka::App::RestResources do
|
|
136
136
|
end
|
137
137
|
|
138
138
|
|
139
|
-
describe "
|
139
|
+
describe "auto-generates routes:" do
|
140
140
|
|
141
141
|
before( :each ) do
|
142
142
|
# Create two servers in the config db to test with
|
143
143
|
server 'test-server' do
|
144
|
+
name "Test"
|
144
145
|
host 'main'
|
145
146
|
host 'monitor'
|
146
147
|
host 'adminpanel'
|
147
148
|
host 'api'
|
148
149
|
end
|
149
|
-
server 'step-server'
|
150
|
+
server 'step-server' do
|
151
|
+
name 'Step'
|
152
|
+
end
|
150
153
|
|
151
154
|
@app.class_eval do
|
152
155
|
resource Mongrel2::Config::Server
|
@@ -158,7 +161,8 @@ describe Strelka::App::RestResources do
|
|
158
161
|
Mongrel2::Config.subclasses.each {|klass| klass.truncate }
|
159
162
|
end
|
160
163
|
|
161
|
-
|
164
|
+
|
165
|
+
context "OPTIONS route" do
|
162
166
|
|
163
167
|
it "responds to a top-level OPTIONS request with a resource description (JSON Schema?)"
|
164
168
|
|
@@ -173,7 +177,7 @@ describe Strelka::App::RestResources do
|
|
173
177
|
end # OPTIONS routes
|
174
178
|
|
175
179
|
|
176
|
-
context "GET
|
180
|
+
context "GET route" do
|
177
181
|
it "has a GET route to fetch the resource collection" do
|
178
182
|
req = @request_factory.get( '/api/v1/servers', 'Accept' => 'application/json' )
|
179
183
|
res = @app.new.handle( req )
|
@@ -209,6 +213,31 @@ describe Strelka::App::RestResources do
|
|
209
213
|
body[0]['uuid'].should == 'step-server'
|
210
214
|
end
|
211
215
|
|
216
|
+
it "supports ordering the result by a single column" do
|
217
|
+
req = @request_factory.get( '/api/v1/servers?order=name', 'Accept' => 'application/json' )
|
218
|
+
res = @app.new.handle( req )
|
219
|
+
|
220
|
+
res.status.should == HTTP::OK
|
221
|
+
res.content_type.should == 'application/json'
|
222
|
+
body = Yajl.load( res.body )
|
223
|
+
|
224
|
+
body.should have( 2 ).members
|
225
|
+
body[0]['name'].should == 'Step'
|
226
|
+
end
|
227
|
+
|
228
|
+
it "supports ordering the result by multiple columns" do
|
229
|
+
pending "fixing the multi-value paramvalidator bug"
|
230
|
+
req = @request_factory.get( '/api/v1/servers?order=id;order=name', 'Accept' => 'application/json' )
|
231
|
+
res = @app.new.handle( req )
|
232
|
+
|
233
|
+
res.status.should == HTTP::OK
|
234
|
+
res.content_type.should == 'application/json'
|
235
|
+
body = Yajl.load( res.body )
|
236
|
+
|
237
|
+
body.should have( 2 ).members
|
238
|
+
body[0]['name'].should == 'Test'
|
239
|
+
end
|
240
|
+
|
212
241
|
it "has a GET route to fetch a single resource by its ID" do
|
213
242
|
req = @request_factory.get( '/api/v1/servers/1', 'Accept' => 'application/json' )
|
214
243
|
res = @app.new.handle( req )
|
@@ -282,7 +311,7 @@ describe Strelka::App::RestResources do
|
|
282
311
|
end # GET routes
|
283
312
|
|
284
313
|
|
285
|
-
context "POST
|
314
|
+
context "POST route" do
|
286
315
|
|
287
316
|
before( :each ) do
|
288
317
|
@server_values = {
|
@@ -330,7 +359,7 @@ describe Strelka::App::RestResources do
|
|
330
359
|
end # POST routes
|
331
360
|
|
332
361
|
|
333
|
-
context "PUT
|
362
|
+
context "PUT route" do
|
334
363
|
|
335
364
|
before( :each ) do
|
336
365
|
@posted_values = {
|
@@ -368,7 +397,7 @@ describe Strelka::App::RestResources do
|
|
368
397
|
end # PUT routes
|
369
398
|
|
370
399
|
|
371
|
-
context "DELETE
|
400
|
+
context "DELETE route" do
|
372
401
|
|
373
402
|
it "has a DELETE route to delete single instances in the resource collection" do
|
374
403
|
req = @request_factory.delete( '/api/v1/servers/1' )
|
@@ -395,7 +424,7 @@ describe Strelka::App::RestResources do
|
|
395
424
|
end # route behaviors
|
396
425
|
|
397
426
|
|
398
|
-
describe "supports inheritance" do
|
427
|
+
describe "supports inheritance:" do
|
399
428
|
|
400
429
|
subject do
|
401
430
|
@app.resource( Mongrel2::Config::Server )
|
@@ -94,6 +94,10 @@ describe Strelka::HTTPResponse do
|
|
94
94
|
@res.header_data.should_not =~ /charset/i
|
95
95
|
end
|
96
96
|
|
97
|
+
it "doesn't try to add an encoding to a response that doesn't have a content type" do
|
98
|
+
@res.content_type = nil
|
99
|
+
@res.header_data.should_not =~ /charset/
|
100
|
+
end
|
97
101
|
|
98
102
|
it "adds a Content-encoding header if there is one encoding" do
|
99
103
|
@res.encodings << 'gzip'
|
@@ -57,14 +57,19 @@ describe Strelka::ParamValidator do
|
|
57
57
|
@validator.param_names.should include( 'a_field' )
|
58
58
|
end
|
59
59
|
|
60
|
-
it "
|
60
|
+
it "ignores identical duplicate constraints to be added twice" do
|
61
|
+
@validator.add( :a_field, :string )
|
61
62
|
@validator.add( :a_field, :string )
|
62
|
-
expect {
|
63
|
-
@validator.add( :a_field, :string )
|
64
|
-
}.to raise_error( /parameter "a_field" is already defined/i )
|
65
63
|
@validator.param_names.should include( 'a_field' )
|
66
64
|
end
|
67
65
|
|
66
|
+
it "throws an error if a constraint is re-added with different values" do
|
67
|
+
@validator.add( :a_field, :string )
|
68
|
+
expect {
|
69
|
+
@validator.add( :a_field, :integer )
|
70
|
+
}.to raise_error( /parameter "a_field" is already defined as a 'string'/i )
|
71
|
+
end
|
72
|
+
|
68
73
|
it "allows an existing constraint to be overridden" do
|
69
74
|
@validator.add( :a_field, :string )
|
70
75
|
@validator.override( :a_field, :integer )
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strelka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.1.pre.
|
4
|
+
version: 0.0.1.pre.284
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
YUhDS0xaZFNLai9SSHVUT3QrZ2JsUmV4OEZBaDhOZUEKY21saFhlNDZwWk5K
|
37
37
|
Z1dLYnhaYWg4NWpJang5NWhSOHZPSStOQU01aUg5a09xSzEzRHJ4YWNUS1Bo
|
38
38
|
cWo1UGp3RgotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
|
39
|
-
date: 2012-07-
|
39
|
+
date: 2012-07-18 00:00:00.000000000 Z
|
40
40
|
dependencies:
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: configurability
|
@@ -141,7 +141,7 @@ dependencies:
|
|
141
141
|
requirements:
|
142
142
|
- - ~>
|
143
143
|
- !ruby/object:Gem::Version
|
144
|
-
version: '0.
|
144
|
+
version: '0.29'
|
145
145
|
type: :runtime
|
146
146
|
prerelease: false
|
147
147
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -149,7 +149,7 @@ dependencies:
|
|
149
149
|
requirements:
|
150
150
|
- - ~>
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version: '0.
|
152
|
+
version: '0.29'
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: pluginfactory
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
metadata.gz.sig
CHANGED
Binary file
|