weasel_diesel 1.0.0

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.
@@ -0,0 +1,109 @@
1
+ # include this module in WeaselDiesel
2
+ # to add response verification methods.
3
+ #
4
+ module JSONResponseVerification
5
+
6
+ # Validates a hash against the service's response description.
7
+ #
8
+ # @return [Array<TrueClass, FalseClass, Array<String>>] True/false and an array of errors.
9
+ def validate_hash_response(hash)
10
+ errors = []
11
+ # nodes without the arrays
12
+ response.nodes.each do |node|
13
+ if node.name
14
+ # Verify that the named node exists in the hash
15
+ unless hash.has_key?(node.name.to_s)
16
+ errors << json_response_error(node, hash)
17
+ return [false, errors]
18
+ end
19
+ end
20
+ errors += validate_hash_against_template_node(hash, node)
21
+ end
22
+
23
+ [errors.empty?, errors]
24
+ end
25
+
26
+ private
27
+
28
+ # Recursively validates a hash representing a json response.
29
+ #
30
+ # @param [Hash>] hash the hash to verify.
31
+ # @param [WDSL::Response::Element] node the reference element defined in the response description.
32
+ # @param [TrueClass, FalseClass] nested if the node/hash to verify is nested or not. If nested, the method expects to get the subhash
33
+ # & won't verify that the name exists since it was done a level higher.
34
+ # @param [Arrays<String>] errors the list of errors encountered while verifying.
35
+ # @param []
36
+ # @return [TrueClass, FalseClass]
37
+ def validate_hash_against_template_node(hash, node, nested=false, errors=[], array_item=false)
38
+ if hash.nil?
39
+ errors << json_response_error(node, hash)
40
+ return errors
41
+ end
42
+
43
+ if node.name && !nested
44
+ if hash.has_key?(node.name.to_s)
45
+ subhash = hash[node.name.to_s]
46
+ else
47
+ errors << json_response_error(node, hash)
48
+ end
49
+ end
50
+
51
+ subhash ||= hash
52
+ if node.is_a?(WeaselDiesel::Response::Vector) && !array_item
53
+ errors << json_response_error(node, subhash, true) unless subhash.is_a?(Array)
54
+ subhash.each do |obj|
55
+ validate_hash_against_template_node(obj, node, true, errors, true)
56
+ end
57
+ else
58
+ node.properties.each do |prop|
59
+ if !array_item && !subhash.has_key?(prop.name.to_s) && (prop.opts && prop.respond_to?(:opts) && !prop.opts[:null])
60
+ errors << json_response_error(prop, subhash)
61
+ end
62
+ errors << json_response_error(prop, subhash, true) unless valid_hash_type?(subhash, prop)
63
+ end
64
+
65
+ node.objects.each do |obj|
66
+ # recursive call
67
+ validate_hash_against_template_node(subhash[obj.name.to_s], obj, true, errors)
68
+ end if node.objects
69
+ end
70
+
71
+ errors
72
+ end
73
+
74
+ def json_response_error(el_or_attr, hash, type_error=false)
75
+ if el_or_attr.is_a?(WeaselDiesel::Response::Element)
76
+ "#{el_or_attr.name || 'top level'} Node/Object/Element is missing"
77
+ elsif type_error
78
+ error = "#{el_or_attr.name || el_or_attr.inspect} was of wrong type, expected #{el_or_attr.type}"
79
+ if el_or_attr.name
80
+ error << " and the value was: #{hash[el_or_attr.name.to_s]}"
81
+ end
82
+ error
83
+ else
84
+ "#{el_or_attr.name || el_or_attr.inspect} is missing in #{hash.inspect}"
85
+ end
86
+ end
87
+
88
+ def valid_hash_type?(hash, prop_template)
89
+ name = prop_template.name.to_s
90
+ attribute = hash[name]
91
+
92
+ # Check for nullity
93
+ if attribute.nil?
94
+ return prop_template.opts[:null] == true
95
+ end
96
+
97
+ type = prop_template.type
98
+ return true if type.nil?
99
+
100
+ rule = ParamsVerification.type_validations[type.to_sym]
101
+ if rule.nil?
102
+ puts "Don't know how to validate attributes of type #{type}" if type.to_sym != :string
103
+ return true
104
+ end
105
+
106
+ attribute.to_s =~ rule
107
+ end
108
+
109
+ end
@@ -0,0 +1,374 @@
1
+ class WeaselDiesel
2
+ # Service params class letting you define param rules.
3
+ # Usually not initialized directly but accessed via the service methods.
4
+ #
5
+ # @see WeaselDiesel#params
6
+ #
7
+ # @api public
8
+ class Params
9
+
10
+ # Params usually have a few rules used to validate requests.
11
+ # Rules are not usually initialized directly but instead via
12
+ # the service's #params accessor.
13
+ #
14
+ # @api public
15
+ class Rule
16
+
17
+ # @return [Symbol, String] name The name of the param the rule applies to.
18
+ # @api public
19
+ attr_reader :name
20
+
21
+ # @return [Hash] options The rule options.
22
+ # @option options [Symbol] :in A list of acceptable values.
23
+ # @option options [Symbol] :options A list of acceptable values.
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.
27
+ # @api public
28
+ attr_reader :options
29
+
30
+
31
+ # @param [Symbol, String] name
32
+ # The param's name
33
+ # @param [Hash] opts The rule options
34
+ # @option opts [Symbol] :in A list of acceptable values.
35
+ # @option opts [Symbol] :options A list of acceptable values.
36
+ # @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.
39
+ # @api public
40
+ def initialize(name, opts = {})
41
+ @name = name
42
+ @options = opts
43
+ end
44
+
45
+ # The namespace used if any
46
+ #
47
+ # @return [NilClass, String]
48
+ # @api public
49
+ def namespace
50
+ @options[:space_name]
51
+ end
52
+
53
+ # Converts the rule into a hash with its name and options.
54
+ #
55
+ # @return [Hash]
56
+ def to_hash
57
+ {:name => name, :options => options}
58
+ end
59
+
60
+ end # of Rule
61
+
62
+ # The namespace used if any
63
+ #
64
+ # @return [String]
65
+ # @api public
66
+ attr_reader :space_name
67
+
68
+ # @param [Hash] opts The params options
69
+ # @option opts [:symbol] :space_name Optional namespace.
70
+ # @api public
71
+ def initialize(opts={})
72
+ @space_name = opts[:space_name]
73
+ end
74
+
75
+ # Defines a new param and add it to the optional or required list based
76
+ # the passed options.
77
+ # @param [Symbol] type
78
+ # The type of param
79
+ #
80
+ # @param [Symbol, String] name
81
+ # The name of the param
82
+ #
83
+ # @param [Hash] options
84
+ # A hash representing the param settings
85
+ #
86
+ # @example Declaring an integer service param called id
87
+ # service.param(:id, :integer, :default => 9999, :in => [0, 9999])
88
+ #
89
+ # @return [Array] the typed list of params (required or optional)
90
+ # @api public]
91
+ def param(type, name, options={})
92
+ options[:type] = type
93
+ options[:space_name] = options[:space_name] || space_name
94
+ if options.delete(:required)
95
+ list_required << Rule.new(name, options)
96
+ else
97
+ list_optional << Rule.new(name, options)
98
+ end
99
+ end
100
+
101
+ # @group Params defintition DSL (accept_param style)
102
+
103
+ # Defines a new string param and add it to the required or optional list
104
+ #
105
+ # @param [String] name
106
+ # The name of the param
107
+ # @param [Hash] options
108
+ # A hash representing the param settings
109
+ #
110
+ # @example Defining a string service param named type which has various options.
111
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
112
+ #
113
+ # @api public
114
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
115
+ # List of optional or required param rules depending on the new param rule type
116
+ def string(name, options={})
117
+ param(:string, name, options)
118
+ end
119
+
120
+ # Defines a new integer param and add it to the required or optional list
121
+ #
122
+ # @param [String] name
123
+ # The name of the param
124
+ # @param [Hash] options
125
+ # A hash representing the param settings
126
+ #
127
+ # @example Defining a string service param named type which has various options.
128
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
129
+ #
130
+ # @api public
131
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
132
+ # List of optional or required param rules depending on the new param rule type
133
+ def integer(name, options={})
134
+ param(:integer, name, options)
135
+ end
136
+
137
+ # Defines a new float param and add it to the required or optional list
138
+ #
139
+ # @param [String] name
140
+ # The name of the param
141
+ # @param [Hash] options
142
+ # A hash representing the param settings
143
+ #
144
+ # @example Defining a string service param named type which has various options.
145
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
146
+ #
147
+ # @api public
148
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
149
+ # List of optional or required param rules depending on the new param rule type
150
+ def float(name, options={})
151
+ param(:float, name, options)
152
+ end
153
+
154
+ # Defines a new decimal param and add it to the required or optional list
155
+ #
156
+ # @param [String] name
157
+ # The name of the param
158
+ # @param [Hash] options
159
+ # A hash representing the param settings
160
+ #
161
+ # @example Defining a string service param named type which has various options.
162
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
163
+ #
164
+ # @api public
165
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
166
+ # List of optional or required param rules depending on the new param rule type
167
+ def decimal(name, options={})
168
+ param(:decimal, name, options)
169
+ end
170
+
171
+ # Defines a new boolean param and add it to the required or optional list
172
+ #
173
+ # @param [String] name
174
+ # The name of the param
175
+ # @param [Hash] options
176
+ # A hash representing the param settings
177
+ #
178
+ # @example Defining a string service param named type which has various options.
179
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
180
+ #
181
+ # @api public
182
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
183
+ # List of optional or required param rules depending on the new param rule type
184
+ def boolean(name, options={})
185
+ param(:boolean, name, options)
186
+ end
187
+
188
+ # Defines a new datetime param and add it to the required or optional list
189
+ #
190
+ # @param [String] name
191
+ # The name of the param
192
+ # @param [Hash] options
193
+ # A hash representing the param settings
194
+ #
195
+ # @example Defining a string service param named type which has various options.
196
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
197
+ #
198
+ # @api public
199
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
200
+ # List of optional or required param rules depending on the new param rule type
201
+ def datetime(name, options={})
202
+ param(:datetime, name, options)
203
+ end
204
+
205
+ # Defines a new text param and add it to the required or optional list
206
+ #
207
+ # @param [String] name
208
+ # The name of the param
209
+ # @param [Hash] options
210
+ # A hash representing the param settings
211
+ #
212
+ # @example Defining a string service param named type which has various options.
213
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
214
+ #
215
+ # @api public
216
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
217
+ # List of optional or required param rules depending on the new param rule type
218
+ def text(name, options={})
219
+ param(:text, name, options)
220
+ end
221
+
222
+ # Defines a new binary param and add it to the required or optional list
223
+ #
224
+ # @param [String] name
225
+ # The name of the param
226
+ # @param [Hash] options
227
+ # A hash representing the param settings
228
+ #
229
+ # @example Defining a string service param named type which has various options.
230
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
231
+ #
232
+ # @api public
233
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
234
+ # List of optional or required param rules depending on the new param rule type
235
+ def binary(name, options={})
236
+ param(:binary, name, options)
237
+ end
238
+
239
+ # Defines a new array param and add it to the required or optional list
240
+ #
241
+ # @param [String] name
242
+ # The name of the param
243
+ # @param [Hash] options
244
+ # A hash representing the param settings
245
+ #
246
+ # @example Defining a string service param named type which has various options.
247
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
248
+ #
249
+ # @api public
250
+ # @return [Array<WeaselDiesel::Params::Rule>]
251
+ # List of optional or required param rules depending on the new param rule type
252
+ def array(name, options={})
253
+ param(:array, name, options)
254
+ end
255
+
256
+ # Defines a new file param and add it to the required or optional list
257
+ #
258
+ # @param [String] name
259
+ # The name of the param
260
+ # @param [Hash] options
261
+ # A hash representing the param settings
262
+ #
263
+ # @example Defining a string service param named type which has various options.
264
+ # service.param.string :type, :in => LeaderboardType.names, :default => LeaderboardType::LIFETIME
265
+ #
266
+ # @api public
267
+ # @return [Arrays<WeaselDiesel::Params::Rule>]
268
+ # List of optional or required param rules depending on the new param rule type
269
+ def file(name, options={})
270
+ param(:file, name, options)
271
+ end
272
+
273
+ # @group param setters based on the state (required or optional)
274
+
275
+ # Defines a new required param
276
+ #
277
+ # @param [Symbol, String] param_name
278
+ # The name of the param to define
279
+ # @param [Hash] opts
280
+ # A hash representing the required param, the key being the param name name
281
+ # and the value being a hash of options.
282
+ #
283
+ # @example Defining a required service param called 'id' of `Integer` type
284
+ # service.params.required :id, :type => 'integer', :default => 9999
285
+ #
286
+ # @return [Array<WeaselDiesel::Params::Rule>] The list of required rules
287
+ #
288
+ # @api public
289
+ def required(param_name, opts={})
290
+ # # support for when a required param doesn't have any options
291
+ # unless opts.respond_to?(:each_pair)
292
+ # opts = {opts => nil}
293
+ # end
294
+ # # recursive rule creation
295
+ # if opts.size > 1
296
+ # opts.each_pair{|k,v| requires({k => v})}
297
+ # else
298
+ list_required << Rule.new(param_name, opts)
299
+ # end
300
+ end
301
+
302
+ # Defines a new optional param rule
303
+ #
304
+ # @param [Symbol, String] param_name
305
+ # The name of the param to define
306
+ # @param [Hash] opts
307
+ # A hash representing the required param, the key being the param name name
308
+ # and the value being a hash of options.
309
+ #
310
+ # @example Defining an optional service param called 'id' of `Integer` type
311
+ # service.params.optional :id, :type => 'integer', :default => 9999
312
+ #
313
+ # @return [Array<WeaselDiesel::Params::Rule>] The list of optional rules
314
+ # @api public
315
+ def optional(param_name, opts={})
316
+ # # recursive rule creation
317
+ # if opts.size > 1
318
+ # opts.each_pair{|k,v| optional({k => v})}
319
+ # else
320
+ list_optional << Rule.new(param_name, opts)
321
+ # end
322
+ end
323
+
324
+ # @group params accessors per status (required or optional)
325
+
326
+ # Returns an array of all the required params
327
+ #
328
+ # @return [Array<WeaselDiesel::Params::Rule>] The list of required rules
329
+ # @api public
330
+ def list_required
331
+ @required ||= []
332
+ end
333
+
334
+ # Returns an array of all the optional params
335
+ #
336
+ # @return [Array<WeaselDiesel::Params::Rule>] all the optional params
337
+ # @api public
338
+ def list_optional
339
+ @optional ||= []
340
+ end
341
+
342
+ # @endgroup
343
+
344
+ # Defines a namespaced param
345
+ #
346
+ # @yield [Params] the newly created namespaced param
347
+ # @return [Array<WeaselDiesel::Params>] the list of all the namespaced params
348
+ # @api public
349
+ def namespace(name)
350
+ params = Params.new(:space_name => name)
351
+ yield(params) if block_given?
352
+ namespaced_params << params unless namespaced_params.include?(params)
353
+ end
354
+
355
+ # Returns the namespaced params
356
+ #
357
+ # @return [Array<WeaselDiesel::Params>] the list of all the namespaced params
358
+ # @api public
359
+ def namespaced_params
360
+ @namespaced_params ||= []
361
+ end
362
+
363
+ # Returns the names of the first level expected params
364
+ #
365
+ # @return [Array<WeaselDiesel::Params>]
366
+ # @api public
367
+ def param_names
368
+ first_level_expected_params = (list_required + list_optional).map{|rule| rule.name.to_s}
369
+ first_level_expected_params += namespaced_params.map{|r| r.space_name.to_s}
370
+ first_level_expected_params
371
+ end
372
+
373
+ end # of Params
374
+ end