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.
- 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
|