weasel_diesel 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/Gemfile.lock +38 -0
- data/LICENSE +23 -0
- data/README.md +231 -0
- data/Rakefile +12 -0
- data/lib/documentation.rb +151 -0
- data/lib/framework_ext/sinatra.rb +30 -0
- data/lib/framework_ext/sinatra_controller.rb +80 -0
- data/lib/inflection.rb +460 -0
- data/lib/json_response_verification.rb +109 -0
- data/lib/params.rb +374 -0
- data/lib/params_verification.rb +268 -0
- data/lib/response.rb +457 -0
- data/lib/weasel_diesel.rb +415 -0
- data/lib/weasel_diesel/version.rb +3 -0
- data/lib/ws_list.rb +58 -0
- data/spec/hello_world_controller.rb +5 -0
- data/spec/hello_world_service.rb +20 -0
- data/spec/json_response_description_spec.rb +124 -0
- data/spec/json_response_verification_spec.rb +225 -0
- data/spec/params_verification_spec.rb +102 -0
- data/spec/preferences_service.rb +10 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/test_services.rb +102 -0
- data/spec/wsdsl_sinatra_ext_spec.rb +26 -0
- data/spec/wsdsl_spec.rb +314 -0
- data/weasel_diesel.gemspec +30 -0
- metadata +127 -0
@@ -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
|
data/lib/params.rb
ADDED
@@ -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
|