weasel_diesel 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -108,7 +108,131 @@ Or a more complex example using XML:
108
108
  end
109
109
  ```
110
110
 
111
- ## JSON APIs
111
+ ## INPUT DSL
112
+
113
+ As shown in the two examples above, input parameters can be:
114
+ * optional or required
115
+ * namespaced
116
+ * typed
117
+ * marked as not being null if passed
118
+ * set to have a value defined in a list
119
+ * set to have a min value
120
+ * set to have a min length
121
+ * set to have a max value
122
+ * set to have a max length
123
+ * documented
124
+
125
+ Most of these settings are used to verify the input requests.
126
+
127
+ ### Supported defined types:
128
+
129
+ * integer
130
+ * float, decimal
131
+ * string
132
+ * boolean
133
+ * array (comma delimited string)
134
+ * binary, file
135
+
136
+ #### Note regarding required vs optional params.
137
+
138
+ You can't set a required param to be `:null => true`, if you do so, the
139
+ setting will be ignored since all required params have to be present.
140
+
141
+ If you set an optional param to be `:null => false`, the verification
142
+ will only fail if the param was present in the request but the passed
143
+ value is nil. You might want to use that setting if you have an optional
144
+ param that, by definition isn't required but, if passed has to not be
145
+ null.
146
+
147
+
148
+ ### Validation and other param options
149
+
150
+ You can set many rules to define an input parameter.
151
+ Here is a quick overview of the available param options, check the specs for more examples.
152
+ Options can be combined.
153
+
154
+ * `required` by default the defined optional input parameters are
155
+ optional. However their presence can be required by using this flag.
156
+ (Setting `:null => true` will be ignored if the paramter is required)
157
+ Example: `service.param.string :id, :required => true`
158
+ * `in` or `options` limits the range of the possible values being
159
+ passed. Example: `service.param.string :skills, :options %w{ruby scala clojure}`
160
+ * `default` sets a value for your in case you don't pass one. Example:
161
+ `service.param.datetime :timestamp, :default => Time.now`
162
+ * `min_value` forces the param value to be equal or greater than the
163
+ option's value. Example: `service.param.integer :age, :min_value => 21
164
+ * `max_value` forces the param value to be equal or less than the
165
+ options's value. Example: `service.param.integer :votes, :max_value => 7
166
+ * `min_length` forces the length of the param value to be equal or
167
+ greater than the option's value. Example: `service.param.string :name, :min_length => 2`
168
+ * `max_length` forces the length of the param value to be equal or
169
+ lesser than the options's value. Example: `service.param.string :name, :max_length => 251`
170
+ * `null` in the case of an optional parameter, if the parameter is being
171
+ passed, the value can't be nil or empty.
172
+ * `doc` document the param.
173
+
174
+ ### Namespaced/nested object
175
+
176
+ Input parameters can be defined nested/namespaced.
177
+ This is particuliarly frequent when using Rails for instance.
178
+
179
+ ```ruby
180
+ service.params do |param|
181
+ param.string :framework,
182
+ :in => ['RSpec', 'Bacon'],
183
+ :required => true,
184
+ :doc => "The test framework used, could be one of the two following: #{WeaselDieselSpecOptions.join(", ")}."
185
+
186
+ param.datetime :timestamp, :default => Time.now
187
+ param.string :alpha, :in => ['a', 'b', 'c']
188
+ param.string :version, :null => false, :doc => "The version of the framework to use."
189
+ param.integer :num, :min_value => 42, :max_value => 1000, :doc => "The number to test"
190
+ param.string :name, :min_length => 5, :max_length => 25
191
+ end
192
+
193
+ service.params.namespace :user do |user|
194
+ user.integer :id, :required => :true
195
+ user.string :sex, :in => %Q{female, male}
196
+ user.boolean :mailing_list, :default => true, :doc => "is the user subscribed to the ML?"
197
+ user.array :skills, :in => %w{ruby js cooking}
198
+ end
199
+ ```
200
+
201
+
202
+
203
+ Here is the same type of input but this time using a JSON jargon,
204
+ `namespace` and `object` are aliases and can therefore can be used based
205
+ on how the input type.
206
+
207
+ ```ruby
208
+ # INPUT using 1.9 hash syntax
209
+ service.params do |param|
210
+ param.integer :playlist_id,
211
+ doc: "The ID of the playlist to which the track belongs.",
212
+ required: true
213
+ param.object :track do |track|
214
+ track.string :title,
215
+ doc: "The title of the track.",
216
+ required: true
217
+ track.string :album_title,
218
+ doc: "The title of the album to which the track belongs.",
219
+ required: true
220
+ track.string :artist_name,
221
+ doc: "The name of the track's artist.",
222
+ required: true
223
+ track.string :rdio_id,
224
+ doc: "The Rdio ID of the track.",
225
+ required: true
226
+ end
227
+ end
228
+ ```
229
+
230
+
231
+
232
+ ## OUTPUT DSL
233
+
234
+
235
+ ### JSON API example
112
236
 
113
237
  Consider the following JSON response:
114
238
 
@@ -164,28 +288,31 @@ JSON response validation can be done using an optional module as shown in
164
288
  The goal of this module is to help automate API testing by
165
289
  validating the data structure of the returned object.
166
290
 
291
+ Another simple examples:
167
292
 
168
- Other JSON DSL examples:
169
-
293
+ Actual output:
170
294
  ```
171
295
  {"organization": {"name": "Example"}}
172
296
  ```
173
297
 
298
+ Output DSL:
174
299
  ``` Ruby
175
- describe_service "example" do |service|
176
- service.formats :json
177
- service.response do |response|
178
- response.object :organization do |node|
179
- node.string :name
180
- end
300
+ describe_service "example" do |service|
301
+ service.formats :json
302
+ service.response do |response|
303
+ response.object :organization do |node|
304
+ node.string :name
181
305
  end
182
306
  end
307
+ end
183
308
  ```
184
309
 
310
+ Actual output:
185
311
  ```
186
312
  {"name": "Example"}
187
313
  ```
188
314
 
315
+ Output DSL:
189
316
  ``` Ruby
190
317
  describe_service "example" do |service|
191
318
  service.formats :json
@@ -198,8 +325,6 @@ end
198
325
  ```
199
326
 
200
327
 
201
-
202
-
203
328
  ## Test Suite & Dependencies
204
329
 
205
330
  The test suite requires Ruby 1.9.* along with `RSpec`, `Rack`, and `Sinatra` gems.
data/lib/params.rb CHANGED
@@ -22,8 +22,11 @@ class WeaselDiesel
22
22
  # @option options [Symbol] :in A list of acceptable values.
23
23
  # @option options [Symbol] :options A list of acceptable values.
24
24
  # @option options [Symbol] :default The default value of the param.
25
- # @option options [Symbol] :minvalue The minimum acceptable value.
26
- # @option options [Symbol] :maxvalue The maximim acceptable value.
25
+ # @option options [Symbol] :min_value The minimum acceptable value.
26
+ # @option options [Symbol] :max_value The maximum acceptable value.
27
+ # @option options [Symbol] :min_length The minimum acceptable string length.
28
+ # @option options [Symbol] :max_length The maximum acceptable string length.
29
+ # @option options [Boolean] :null Can this value be null?
27
30
  # @option options [Symbol] :doc Documentation for the param.
28
31
  # @api public
29
32
  attr_reader :options
@@ -34,8 +37,11 @@ class WeaselDiesel
34
37
  # @option opts [Symbol] :in A list of acceptable values.
35
38
  # @option opts [Symbol] :options A list of acceptable values.
36
39
  # @option opts [Symbol] :default The default value of the param.
37
- # @option opts [Symbol] :minvalue The minimum acceptable value.
38
- # @option opts [Symbol] :maxvalue The maximim acceptable value.
40
+ # @option options [Symbol] :min_value The minimum acceptable value.
41
+ # @option options [Symbol] :max_value The maximum acceptable value.
42
+ # @option options [Symbol] :min_length The minimum acceptable string length.
43
+ # @option options [Symbol] :max_length The maximum acceptable string length.
44
+ # @option options [Boolean] :null Can this value be null?
39
45
  # @option opts [Symbol] :doc Documentation for the param.
40
46
  # @api public
41
47
  def initialize(name, opts = {})
@@ -61,7 +61,7 @@ module ParamsVerification
61
61
 
62
62
  # Set optional defaults if any optional
63
63
  service_params.list_optional.each do |rule|
64
- updated_params = run_optional_rule(rule, updated_params)
64
+ updated_params = validate_optional_rule(rule, updated_params)
65
65
  end
66
66
 
67
67
  # check the namespaced params
@@ -70,9 +70,8 @@ module ParamsVerification
70
70
  updated_params = validate_required_rule(rule, updated_params, param.space_name.to_s)
71
71
  end
72
72
  param.list_optional.each do |rule|
73
- updated_params = run_optional_rule(rule, updated_params, param.space_name.to_s)
73
+ updated_params = validate_optional_rule(rule, updated_params, param.space_name.to_s)
74
74
  end
75
-
76
75
  end
77
76
 
78
77
  # verify nested params, only 1 level deep tho
@@ -90,7 +89,7 @@ module ParamsVerification
90
89
 
91
90
  private
92
91
 
93
- # Validate a required rule against a list of params passed.
92
+ # Validates a required rule against a list of params passed.
94
93
  #
95
94
  #
96
95
  # @param [WeaselDiesel::Params::Rule] rule The required rule to check against.
@@ -103,56 +102,145 @@ module ParamsVerification
103
102
  # @api private
104
103
  def self.validate_required_rule(rule, params, namespace=nil)
105
104
  param_name = rule.name.to_s
106
-
107
105
  param_value, namespaced_params = extract_param_values(params, param_name, namespace)
108
- # puts "verify #{param_name} params, current value: #{param_value}"
109
-
110
- #This is disabled since required params shouldn't have a default, otherwise, why are they required?
111
- #if param_value.nil? && rule.options && rule.options[:default]
112
- #param_value = rule.options[:default]
113
- #end
114
106
 
115
107
  # Checks presence
116
108
  if !(namespaced_params || params).keys.include?(param_name)
117
109
  raise MissingParam, "'#{rule.name}' is missing - passed params: #{params.inspect}."
118
- # checks null
119
- elsif param_value.nil? && !rule.options[:null]
120
- raise InvalidParamValue, "Value for parameter '#{param_name}' is missing - passed params: #{params.inspect}."
121
- # checks type
122
- elsif rule.options[:type]
123
- verify_cast(param_name, param_value, rule.options[:type])
124
110
  end
125
111
 
126
- if rule.options[:options] || rule.options[:in.inspect]
127
- choices = rule.options[:options] || rule.options[:in]
128
- if rule.options[:type]
129
- # Force the cast so we can compare properly
130
- param_value = params[param_name] = type_cast_value(rule.options[:type], param_value)
112
+ updated_param_value, updated_params = validate_and_cast_type(param_value, param_name, rule.options[:type], params, namespace)
113
+
114
+ # check for nulls in params that don't allow them
115
+ if !valid_null_param?(param_name, updated_param_value, rule)
116
+ raise InvalidParamValue, "Value for parameter '#{param_name}' cannot be null - passed params: #{updated_params.inspect}."
117
+ elsif updated_param_value
118
+ value_errors = validate_ruled_param_value(param_name, updated_param_value, rule)
119
+ raise InvalidParamValue, value_errors.join(', ') if value_errors
120
+ end
121
+
122
+ updated_params
123
+ end
124
+
125
+
126
+ # Validates that an optional rule is respected.
127
+ # If the rule contains default values, the params might be updated.
128
+ #
129
+ # @param [#WeaselDiesel::Params::Rule] rule The optional rule
130
+ # @param [Hash] params The request params
131
+ # @param [String] namespace An optional namespace
132
+ #
133
+ # @return [Hash] The potentially modified params
134
+ #
135
+ # @api private
136
+ def self.validate_optional_rule(rule, params, namespace=nil)
137
+ param_name = rule.name.to_s
138
+ param_value, namespaced_params = extract_param_values(params, param_name, namespace)
139
+
140
+ if param_value && !valid_null_param?(param_name, param_value, rule)
141
+ raise InvalidParamValue, "Value for parameter '#{param_name}' cannot be null if passed - passed params: #{params.inspect}."
142
+ end
143
+
144
+ # Use a default value if one is available and the submitted param value is nil
145
+ if param_value.nil? && rule.options[:default]
146
+ param_value = rule.options[:default]
147
+ if namespace
148
+ params[namespace] ||= {}
149
+ params[namespace][param_name] = param_value
150
+ else
151
+ params[param_name] = param_value
131
152
  end
132
- raise InvalidParamValue, "Value for parameter '#{param_name}' (#{param_value}) is not in the allowed set of values." unless choices.include?(param_value)
133
- # You can have a "in" rule that also applies a min value since they are mutually exclusive
134
- elsif rule.options[:minvalue]
135
- min = rule.options[:minvalue]
136
- raise InvalidParamValue, "Value for parameter '#{param_name}' is lower than the min accepted value (#{min})." if param_value.to_i < min
137
153
  end
138
- # Returns the updated params
139
-
140
- # cast the type if a type is defined and if a range of options isn't defined since the casting should have been done already
141
- if rule.options[:type] && !(rule.options[:options] || rule.options[:in])
142
- # puts "casting #{param_value} into type: #{rule.options[:type]}"
143
- params[param_name] = type_cast_value(rule.options[:type], param_value)
154
+
155
+ updated_param_value, updated_params = validate_and_cast_type(param_value, param_name, rule.options[:type], params, namespace)
156
+ value_errors = validate_ruled_param_value(param_name, updated_param_value, rule) if updated_param_value
157
+ raise InvalidParamValue, value_errors.join(', ') if value_errors
158
+
159
+ updated_params
160
+ end
161
+
162
+
163
+ # Validates the param value against the rule and cast the param in the appropriate type.
164
+ # The modified params containing the cast value is returned along the cast param value.
165
+ #
166
+ # @param [Object] param_value The value to validate and cast.
167
+ # @param [String] param_name The name of the param we are validating.
168
+ # @param [Symbol] type The expected object type.
169
+ # @param [Hash] params The params that might need to be updated.
170
+ # @param [String, Symbol] namespace The optional namespace used to access the `param_value`
171
+ #
172
+ # @return [Array<Object, Hash>] An array containing the param value and
173
+ # a hash representing the potentially modified params after going through the filter.
174
+ #
175
+ def self.validate_and_cast_type(param_value, param_name, rule_type, params, namespace=nil)
176
+ # checks type & modifies params if needed
177
+ if rule_type
178
+ verify_cast(param_name, param_value, rule_type)
179
+ param_value = type_cast_value(rule_type, param_value)
180
+ # update the params hash with the type cast value
181
+ if namespace
182
+ params[namespace] ||= {}
183
+ params[namespace][param_name] = param_value
184
+ else
185
+ params[param_name] = param_value
186
+ end
144
187
  end
145
- params
188
+ [param_value, params]
146
189
  end
147
190
 
148
191
 
149
- # Extract the param valie and the namespaced params
192
+ # Validates a value against a few rule options.
193
+ #
194
+ # @return [NilClass, Array<String>] Returns an array of error messages if an option didn't validate.
195
+ def self.validate_ruled_param_value(param_name, param_value, rule)
196
+
197
+ # checks the value against a whitelist style 'in'/'options' list
198
+ if rule.options[:options] || rule.options[:in]
199
+ choices = rule.options[:options] || rule.options[:in]
200
+ unless param_value.is_a?(Array) ? (param_value & choices == param_value) : choices.include?(param_value)
201
+ errors ||= []
202
+ errors << "Value for parameter '#{param_name}' (#{param_value}) is not in the allowed set of values."
203
+ end
204
+ end
205
+
206
+ # enforces a minimum numeric value
207
+ if rule.options[:min_value]
208
+ min = rule.options[:min_value]
209
+ errors ||= []
210
+ errors << "Value for parameter '#{param_name}' is lower than the min accepted value (#{min})." if param_value.to_i < min
211
+ end
212
+
213
+ # enforces a maximum numeric value
214
+ if rule.options[:max_value]
215
+ max = rule.options[:max_value]
216
+ errors ||= []
217
+ errors << "Value for parameter '#{param_name}' is higher than the max accepted value (#{max})." if param_value.to_i > max
218
+ end
219
+
220
+ # enforces a minimum string length
221
+ if rule.options[:min_length]
222
+ min = rule.options[:min_length]
223
+ errors ||= []
224
+ errors << "Length of parameter '#{param_name}' is shorter than the min accepted value (#{min})." if param_value.to_s.length < min
225
+ end
226
+
227
+ # enforces a maximum string length
228
+ if rule.options[:max_length]
229
+ max = rule.options[:max_length]
230
+ errors ||= []
231
+ errors << "Length of parameter '#{param_name}' is longer than the max accepted value (#{max})." if param_value.to_s.length > max
232
+ end
233
+
234
+ errors
235
+ end
236
+
237
+ # Extract the param value and the namespaced params
150
238
  # based on a passed namespace and params
151
239
  #
152
240
  # @param [Hash] params The passed params to extract info from.
153
241
  # @param [String] param_name The param name to find the value.
154
242
  # @param [NilClass, String] namespace the params' namespace.
155
- # @return [Arrays<Object, String>]
243
+ # @return [Array<Object, String>]
156
244
  #
157
245
  # @api private
158
246
  def self.extract_param_values(params, param_name, namespace=nil)
@@ -170,41 +258,6 @@ module ParamsVerification
170
258
  end
171
259
  end
172
260
 
173
- # @param [#WeaselDiesel::Params::Rule] rule The optional rule
174
- # @param [Hash] params The request params
175
- # @param [String] namespace An optional namespace
176
- # @return [Hash] The potentially modified params
177
- #
178
- # @api private
179
- def self.run_optional_rule(rule, params, namespace=nil)
180
- param_name = rule.name.to_s
181
-
182
- param_value, namespaced_params = extract_param_values(params, param_name, namespace)
183
-
184
- if param_value.nil? && rule.options[:default]
185
- if namespace
186
- params[namespace][param_name] = param_value = rule.options[:default]
187
- else
188
- params[param_name] = param_value = rule.options[:default]
189
- end
190
- end
191
-
192
- # cast the type if a type is defined and if a range of options isn't defined since the casting should have been done already
193
- if rule.options[:type] && !param_value.nil?
194
- if namespace
195
- params[namespace][param_name] = param_value = type_cast_value(rule.options[:type], param_value)
196
- else
197
- params[param_name] = param_value = type_cast_value(rule.options[:type], param_value)
198
- end
199
- end
200
-
201
- choices = rule.options[:options] || rule.options[:in]
202
- if choices && param_value && !choices.include?(param_value)
203
- raise InvalidParamValue, "Value for parameter '#{param_name}' (#{param_value}) is not in the allowed set of values."
204
- end
205
-
206
- params
207
- end
208
261
 
209
262
  def self.unexpected_params?(params, param_names)
210
263
  # Raise an exception unless no unexpected params were found
@@ -216,6 +269,7 @@ module ParamsVerification
216
269
 
217
270
 
218
271
  def self.type_cast_value(type, value)
272
+ return value if value == nil
219
273
  case type
220
274
  when :integer
221
275
  value.to_i
@@ -241,30 +295,49 @@ module ParamsVerification
241
295
  # An array type is a comma delimited string, we need to cast the passed strings.
242
296
  when :array
243
297
  value.respond_to?(:split) ? value.split(',') : value
244
- when :binary, :array, :file
298
+ when :binary, :file
245
299
  value
246
300
  else
247
301
  value
248
302
  end
249
303
  end
250
304
 
251
- # Checks that the value's type matches the expected type for a given param
305
+ # Checks that the value's type matches the expected type for a given param. If a nil value is passed
306
+ # the verification is skipped.
252
307
  #
253
308
  # @param [Symbol, String] Param name used if the verification fails and that an error is raised.
254
- # @param [#to_s] The value to validate.
309
+ # @param [NilClass, #to_s] The value to validate.
255
310
  # @param [Symbol] The expected type, such as :boolean, :integer etc...
256
311
  # @raise [InvalidParamType] Custom exception raised when the validation isn't found or the value doesn't match.
257
312
  #
258
- # @return [Nil]
313
+ # @return [NilClass]
259
314
  # @api public
260
- # TODO raising an exception really isn't a good idea since it forces the stack to unwind.
261
- # More than likely developers are using exceptions to control the code flow and a different approach should be used.
262
- # Catch/throw is a bit more efficient but is still the wrong approach for this specific problem.
263
315
  def self.verify_cast(name, value, expected_type)
316
+ return if value == nil
264
317
  validation = ParamsVerification.type_validations[expected_type.to_sym]
265
318
  unless validation.nil? || value.to_s =~ validation
266
319
  raise InvalidParamType, "Value for parameter '#{name}' (#{value}) is of the wrong type (expected #{expected_type})"
267
320
  end
268
321
  end
322
+
323
+ # Checks that a param explicitly set to not be null is present.
324
+ # if 'null' is found in the ruleset and set to 'false' (default is 'true' to allow null),
325
+ # then confirm that the submitted value isn't nil or empty
326
+ # @param [String] param_name The name of the param to verify.
327
+ # @param [NilClass, String, Integer, TrueClass, FalseClass] param_value The value to check.
328
+ # @param [WeaselDiesel::Params::Rule] rule The rule to check against.
329
+ #
330
+ # @return [Boolean] true if the param is valid, false otherwise
331
+ def self.valid_null_param?(param_name, param_value, rule)
332
+ if rule.options.has_key?(:null) && rule.options[:null] == false
333
+ if rule.options[:type] && rule.options[:type] == :array
334
+ return false if param_value.nil? || (param_value.respond_to?(:split) && param_value.split(',').empty?)
335
+ else
336
+ return false if param_value.nil? || param_value == ''
337
+ end
338
+ end
339
+ true
340
+ end
341
+
269
342
 
270
343
  end
@@ -1,3 +1,3 @@
1
1
  class WeaselDiesel
2
- VERSION = "1.0.4"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -5,37 +5,67 @@ describe ParamsVerification do
5
5
  before :all do
6
6
  @service = WSList.all.find{|s| s.url == 'services/test.xml'}
7
7
  @service.should_not be_nil
8
- @valid_params = {'framework' => 'RSpec', 'version' => '1.02', 'user' => {'id' => '123'}}
8
+ @valid_params = {'framework' => 'RSpec', 'version' => '1.02', 'user' => {'id' => '123', 'groups' => 'manager,developer', 'skills' => 'java,ruby'}}
9
+ end
10
+
11
+ def copy(params)
12
+ Marshal.load( Marshal.dump(params) )
9
13
  end
10
14
 
11
15
  it "should validate valid params" do
12
- params = @valid_params.dup
16
+ params = copy(@valid_params)
13
17
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should_not raise_exception
14
18
  end
15
19
 
16
20
  it "should return the params" do
17
- params = @valid_params.dup
21
+ params = copy(@valid_params)
18
22
  returned_params = ParamsVerification.validate!(params, @service.defined_params)
19
23
  returned_params.should be_an_instance_of(Hash)
20
24
  returned_params.keys.size.should >= 3
21
25
  end
22
26
 
27
+ it "should return array in the params" do
28
+ params = copy(@valid_params)
29
+ returned_params = ParamsVerification.validate!(params, @service.defined_params)
30
+ returned_params['user']['groups'].should be == @valid_params['user']['groups'].split(",")
31
+ returned_params['user']['skills'].should be == @valid_params['user']['skills'].split(",")
32
+ end
33
+
34
+ it "should not duplicate params in the root level" do
35
+ params = copy(@valid_params)
36
+ returned_params = ParamsVerification.validate!(params, @service.defined_params)
37
+ returned_params['groups'].should be_nil
38
+ returned_params['skills'].should be_nil
39
+ end
40
+
41
+ it "should raise an exception when values of required param are not in the allowed list" do
42
+ params = copy(@valid_params)
43
+ params['user']['groups'] = 'admin,root,manager'
44
+ lambda { ParamsVerification.validate!(params, @service.defined_params) }.should raise_error(ParamsVerification::InvalidParamValue)
45
+ end
46
+
47
+ it "should raise an exception when values of optional param are not in the allowed list" do
48
+ params = copy(@valid_params)
49
+ params['user']['skills'] = 'ruby,java,php'
50
+ lambda { ParamsVerification.validate!(params, @service.defined_params) }.should raise_error(ParamsVerification::InvalidParamValue)
51
+ end
52
+
23
53
  it "should set the default value for an optional param" do
24
- params = @valid_params.dup
54
+ params = copy(@valid_params)
25
55
  params['timestamp'].should be_nil
26
56
  returned_params = ParamsVerification.validate!(params, @service.defined_params)
27
57
  returned_params['timestamp'].should_not be_nil
28
58
  end
29
59
 
30
60
  it "should set the default value for a namespace optional param" do
31
- params = {'framework' => 'RSpec', 'version' => '1.02', 'user' => {'id' => '123'}}
61
+ params = copy(@valid_params)
32
62
  params['user']['mailing_list'].should be_nil
33
63
  returned_params = ParamsVerification.validate!(params, @service.defined_params)
34
64
  returned_params['user']['mailing_list'].should be_true
35
65
  end
36
66
 
37
67
  it "should raise an exception when a required param is missing" do
38
- params = @valid_params.dup
68
+ params = copy(@valid_params)
39
69
  params.delete('framework')
40
70
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::MissingParam)
41
71
  end
@@ -55,27 +85,47 @@ describe ParamsVerification do
55
85
  end
56
86
 
57
87
  it "should raise an exception when a param is of the wrong type" do
58
- params = @valid_params.dup
88
+ params = copy(@valid_params)
59
89
  params['user']['id'] = 'abc'
60
90
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
61
91
  end
62
92
 
63
- it "should raise an exception when a param is under the minvalue" do
64
- params = @valid_params.dup
93
+ it "should raise an exception when a param is under the min_value" do
94
+ params = copy(@valid_params)
95
+ params['num'] = '1'
96
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
65
97
  params['num'] = 1
66
- lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
98
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
99
+ end
100
+
101
+ it "should raise an exception when a param is over the max_value" do
102
+ params = copy(@valid_params)
103
+ params['num'] = 10_000
104
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
105
+ end
106
+
107
+ it "should raise an exception when a param is under the min_length" do
108
+ params = copy(@valid_params)
109
+ params['name'] ='bob'
110
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
111
+ end
112
+
113
+ it "should raise an exception when a param is over the max_length" do
114
+ params = copy(@valid_params)
115
+ params['name'] = "Whether 'tis nobler in the mind to suffer The slings and arrows of outrageous fortune"
116
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
67
117
  end
68
118
 
69
119
  it "should raise an exception when a param isn't in the param option list" do
70
- params = @valid_params.dup
120
+ params = copy(@valid_params)
71
121
  params['alpha'] = 'z'
72
122
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
73
123
  end
74
124
 
75
125
  it "should raise an exception when a nested optional param isn't in the param option list" do
76
- params = @valid_params.dup
126
+ params = copy(@valid_params)
77
127
  params['user']['sex'] = 'large'
78
- lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamType)
128
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
79
129
  # other service
80
130
  params = {'preference' => {'region_code' => 'us', 'language_code' => 'de'}}
81
131
  service = WSList.all.find{|s| s.url == 'preferences.xml'}
@@ -91,25 +141,34 @@ describe ParamsVerification do
91
141
  lambda{ ParamsVerification.validate!(params, service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
92
142
  end
93
143
 
94
-
95
144
  it "should validate that no params are passed when accept_no_params! is set on a service" do
96
145
  service = WSList.all.find{|s| s.url == "services/test_no_params.xml"}
97
146
  service.should_not be_nil
98
- params = @valid_params.dup
147
+ params = copy(@valid_params)
99
148
  lambda{ ParamsVerification.validate!(params, service.defined_params) }.should raise_exception
100
149
  end
101
150
 
102
151
  it "should raise an exception when an unexpected param is found" do
103
- params = @valid_params.dup
152
+ params = copy(@valid_params)
104
153
  params['attack'] = true
105
154
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::UnexpectedParam)
106
155
  end
107
156
 
108
157
  it "should prevent XSS attack on unexpected param name being listed in the exception message" do
109
- params = @valid_params.dup
158
+ params = copy(@valid_params)
110
159
  params["7e22c<script>alert('xss vulnerability')</script>e88ff3f0952"] = 1
111
160
  escaped_error_message = /7e22c&lt;script&gt;alert\('xss vulnerability'\)&lt;\/script&gt;e88ff3f0952/
112
161
  lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::UnexpectedParam, escaped_error_message)
113
162
  end
163
+
164
+ it "should make sure that optional params marked as not false are being set" do
165
+ params = copy(@valid_params)
166
+ ParamsVerification.validate!(params, @service.defined_params).should be_true
167
+ params.delete('version')
168
+ # if omitted, the param should not raise an exception
169
+ ParamsVerification.validate!(params, @service.defined_params).should be_true
170
+ params['version'] = ''
171
+ lambda{ ParamsVerification.validate!(params, @service.defined_params) }.should raise_exception(ParamsVerification::InvalidParamValue)
172
+ end
114
173
 
115
174
  end
@@ -16,8 +16,8 @@ describe_service "services/test.xml" do |service|
16
16
  p.datetime :timestamp, :default => Time.now
17
17
  p.string :alpha, :in => ['a', 'b', 'c']
18
18
  p.string :version, :null => false, :doc => "The version of the framework to use."
19
- p.integer :num, :minvalue => 42, :doc => "The number to test"
20
-
19
+ p.integer :num, :min_value => 42, :max_value => 1000, :doc => "The number to test"
20
+ p.string :name, :min_length => 5, :max_length => 25
21
21
  end
22
22
 
23
23
  # service.param :delta, :optional => true, :type => 'float'
@@ -26,8 +26,10 @@ describe_service "services/test.xml" do |service|
26
26
 
27
27
  service.params.namespace :user do |user|
28
28
  user.integer :id, :required => :true
29
- user.string :sex, :in => %Q{female, male}
29
+ user.string :sex, :in => %Q{female, male}
30
30
  user.boolean :mailing_list, :default => true, :doc => "is the user subscribed to the ML?"
31
+ user.array :groups, :required => true, :in => %w{developer admin manager}
32
+ user.array :skills, :in => %w{ruby java networking}
31
33
  end
32
34
 
33
35
  # the response contains a list of player creation ratings each object in the list
@@ -21,7 +21,7 @@ describe WeaselDiesel::Params do
21
21
 
22
22
  it "should have a list of optional param rules" do
23
23
  @sparams.list_optional.should be_an_instance_of(Array)
24
- @sparams.list_optional.length.should == 4
24
+ @sparams.list_optional.length.should == 5
25
25
  end
26
26
 
27
27
  it "should have a list of namespaced param rules" do
metadata CHANGED
@@ -1,131 +1,103 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: weasel_diesel
3
- version: !ruby/object:Gem::Version
4
- hash: 31
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
5
5
  prerelease:
6
- segments:
7
- - 1
8
- - 0
9
- - 4
10
- version: 1.0.4
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Matt Aimonetti
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2012-05-24 00:00:00 +01:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
12
+ date: 2012-07-18 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
22
15
  name: rspec
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 3
30
- segments:
31
- - 0
32
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
33
22
  type: :development
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: rack-test
37
23
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: !ruby/object:Gem::Requirement
39
25
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
47
- type: :development
48
- version_requirements: *id002
49
- - !ruby/object:Gem::Dependency
50
- name: yard
51
- prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack-test
32
+ requirement: !ruby/object:Gem::Requirement
53
33
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- hash: 3
58
- segments:
59
- - 0
60
- version: "0"
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
61
38
  type: :development
62
- version_requirements: *id003
63
- - !ruby/object:Gem::Dependency
64
- name: sinatra
65
39
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
40
+ version_requirements: !ruby/object:Gem::Requirement
67
41
  none: false
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- hash: 3
72
- segments:
73
- - 0
74
- version: "0"
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: yard
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
75
54
  type: :development
76
- version_requirements: *id004
77
- - !ruby/object:Gem::Dependency
78
- name: rake
79
55
  prerelease: false
80
- requirement: &id005 !ruby/object:Gem::Requirement
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: sinatra
64
+ requirement: !ruby/object:Gem::Requirement
81
65
  none: false
82
- requirements:
83
- - - ">="
84
- - !ruby/object:Gem::Version
85
- hash: 3
86
- segments:
87
- - 0
88
- version: "0"
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
89
70
  type: :development
90
- version_requirements: *id005
91
- - !ruby/object:Gem::Dependency
92
- name: backports
93
71
  prerelease: false
94
- requirement: &id006 !ruby/object:Gem::Requirement
72
+ version_requirements: !ruby/object:Gem::Requirement
95
73
  none: false
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- hash: 3
100
- segments:
101
- - 0
102
- version: "0"
103
- type: :runtime
104
- version_requirements: *id006
105
- - !ruby/object:Gem::Dependency
106
- name: json
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
107
87
  prerelease: false
108
- requirement: &id007 !ruby/object:Gem::Requirement
88
+ version_requirements: !ruby/object:Gem::Requirement
109
89
  none: false
110
- requirements:
111
- - - ">="
112
- - !ruby/object:Gem::Version
113
- hash: 3
114
- segments:
115
- - 0
116
- version: "0"
117
- type: :runtime
118
- version_requirements: *id007
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
119
94
  description: Ruby DSL describing Web Services without implementation details.
120
- email:
95
+ email:
121
96
  - mattaimonetti@gmail.com
122
97
  executables: []
123
-
124
98
  extensions: []
125
-
126
99
  extra_rdoc_files: []
127
-
128
- files:
100
+ files:
129
101
  - .gitignore
130
102
  - .travis.yml
131
103
  - Gemfile
@@ -158,41 +130,31 @@ files:
158
130
  - spec/ws_list_spec.rb
159
131
  - spec/wsdsl_sinatra_ext_spec.rb
160
132
  - weasel_diesel.gemspec
161
- has_rdoc: true
162
133
  homepage: https://github.com/mattetti/Weasel-Diesel
163
134
  licenses: []
164
-
165
135
  post_install_message:
166
136
  rdoc_options: []
167
-
168
- require_paths:
137
+ require_paths:
169
138
  - lib
170
- required_ruby_version: !ruby/object:Gem::Requirement
139
+ required_ruby_version: !ruby/object:Gem::Requirement
171
140
  none: false
172
- requirements:
173
- - - ">="
174
- - !ruby/object:Gem::Version
175
- hash: 3
176
- segments:
177
- - 0
178
- version: "0"
179
- required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
180
146
  none: false
181
- requirements:
182
- - - ">="
183
- - !ruby/object:Gem::Version
184
- hash: 3
185
- segments:
186
- - 0
187
- version: "0"
147
+ requirements:
148
+ - - ! '>='
149
+ - !ruby/object:Gem::Version
150
+ version: '0'
188
151
  requirements: []
189
-
190
152
  rubyforge_project: wsdsl
191
- rubygems_version: 1.5.3
153
+ rubygems_version: 1.8.24
192
154
  signing_key:
193
155
  specification_version: 3
194
156
  summary: Web Service DSL
195
- test_files:
157
+ test_files:
196
158
  - spec/hello_world_controller.rb
197
159
  - spec/hello_world_service.rb
198
160
  - spec/json_response_description_spec.rb
@@ -207,3 +169,4 @@ test_files:
207
169
  - spec/wd_spec.rb
208
170
  - spec/ws_list_spec.rb
209
171
  - spec/wsdsl_sinatra_ext_spec.rb
172
+ has_rdoc: