strelka 0.0.1.pre.279 → 0.0.1.pre.284

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.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] [tip]
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.28'
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
- # Set up parameters
128
- self.add_parameters( rsrcobj, options )
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.content_type = 'text/plain'
177
- res.body = ''
178
- res.status = HTTP::OK
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 should be a
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 be a
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" % [ rsrcobj, route ]
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" % [ rsrcobj, route ]
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" % [ rsrcobj, route ]
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" % [ rsrcobj, route ]
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 base
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" % [ dsname, rsrcobj ]
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.db.transaction do
490
- resource = rsrcobj[ id ] or
491
- finish_with( HTTP::NOT_FOUND, "No such %s [%p]" % [rsrcobj.table_name, id] )
492
-
493
- # Apply limit and offset parameters if they exist
494
- limit, offset = req.params.values_at( :limit, :offset )
495
- dataset = resource.send( "#{association}_dataset" )
496
- if limit
497
- self.log.debug "Limiting result set to %p records" % [ limit ]
498
- dataset = dataset.limit( limit, offset )
499
- end
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
- # Fetch and return the records as JSON or YAML
502
- # :TODO: Handle other mediatypes
503
- self.log.debug "Returning collection: %s" % [ dataset.sql ]
504
- res.for( :json, :yaml ) { dataset.all }
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
@@ -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.start_with?( 'text' )
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
- raise ArgumentError,
283
- "parameter %p is already defined; perhaps you meant to use #override?" % [name] if
284
- self.param_names.include?( name )
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
- args.unshift( block ) if block
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( :name )
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
@@ -41,6 +41,8 @@ require 'strelka'
41
41
 
42
42
  require 'spec/lib/constants'
43
43
 
44
+ Loggability.format_with( :color ) if $stdout.tty?
45
+
44
46
 
45
47
  ### RSpec helper functions.
46
48
  module Strelka::SpecHelpers
@@ -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( :fatal )
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 including App" do
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 "knows what resources are mounted where" do
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 "knows about the mounted resource" do
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 "knows about the mounted resource" do
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 "route behaviors" do
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
- context "OPTIONS routes" do
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 routes" do
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 routes" do
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 routes" do
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 routes" do
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 "doesn't allow a constraint to be added twice" do
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.279
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-12 00:00:00.000000000 Z
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.28'
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.28'
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