playerconnect-wsdsl 0.2.2
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/.rvmrc_example +15 -0
- data/LICENSE +23 -0
- data/README.md +100 -0
- data/Rakefile +36 -0
- data/VERSION +1 -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/params.rb +367 -0
- data/lib/params_verification.rb +267 -0
- data/lib/response.rb +301 -0
- data/lib/ws_list.rb +48 -0
- data/lib/wsdsl.rb +359 -0
- data/playerconnect-wsdsl.gemspec +60 -0
- data/spec/hello_world_controller.rb +5 -0
- data/spec/hello_world_service.rb +20 -0
- data/spec/params_verification_spec.rb +93 -0
- data/spec/preferences_service.rb +10 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/test_services.rb +83 -0
- data/spec/wsdsl_sinatra_ext_spec.rb +26 -0
- data/spec/wsdsl_spec.rb +222 -0
- data/wsdsl.gemspec +60 -0
- metadata +71 -0
data/lib/response.rb
ADDED
@@ -0,0 +1,301 @@
|
|
1
|
+
class WSDSL
|
2
|
+
# Response DSL class
|
3
|
+
# @api public
|
4
|
+
class Response
|
5
|
+
|
6
|
+
# The list of all the elements inside the response
|
7
|
+
#
|
8
|
+
# @return [Array<WSDSL::Response::Element>]
|
9
|
+
# @api public
|
10
|
+
attr_reader :elements
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@elements = []
|
14
|
+
end
|
15
|
+
|
16
|
+
# Defines a new element and yields the content of an optional block
|
17
|
+
# Each new element is then stored in the elements array.
|
18
|
+
#
|
19
|
+
# @param [Hash] opts Options used to define the element
|
20
|
+
# @option opts [String, Symbol] :name The element name
|
21
|
+
# @option opts [String, Symbol] :type The optional type
|
22
|
+
#
|
23
|
+
# @yield [WSDSL::Response::Element] the newly created element
|
24
|
+
# @example create an element called 'my_stats'.
|
25
|
+
# service.response do |response|
|
26
|
+
# response.element(:name => "my_stats", :type => 'Leaderboard')
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# @return [Array<WSDSL::Response::Element>]
|
30
|
+
# @api public
|
31
|
+
def element(opts={})
|
32
|
+
el = Element.new(opts[:name], opts[:type])
|
33
|
+
yield(el) if block_given?
|
34
|
+
@elements << el
|
35
|
+
end
|
36
|
+
|
37
|
+
# Returns a response element object based on its name
|
38
|
+
# @param [String, Symbol] The element name we want to match
|
39
|
+
#
|
40
|
+
# @return [WSDSL::Response::Element]
|
41
|
+
# @api public
|
42
|
+
def element_named(name)
|
43
|
+
@elements.find{|e| e.name.to_s == name.to_s}
|
44
|
+
end
|
45
|
+
|
46
|
+
# The Response element class describing each element of a service response.
|
47
|
+
# Instances are usually not instiated directly but via the Response#element accessor.
|
48
|
+
#
|
49
|
+
# @see WSDSL::Response#element
|
50
|
+
# @api public
|
51
|
+
class Element
|
52
|
+
|
53
|
+
# @return [String, #to_s] The name of the element
|
54
|
+
# @api public
|
55
|
+
attr_reader :name
|
56
|
+
|
57
|
+
# @api public
|
58
|
+
attr_reader :type
|
59
|
+
|
60
|
+
# @return [Array<WSDSL::Response::Element::Attribute>] An array of attributes
|
61
|
+
# @api public
|
62
|
+
attr_reader :attributes
|
63
|
+
|
64
|
+
# @return [Array] An array of vectors/arrays
|
65
|
+
# @api public
|
66
|
+
attr_reader :vectors
|
67
|
+
|
68
|
+
# @return [WSDSL::Documentation::ElementDoc] Response element documentation
|
69
|
+
# @api public
|
70
|
+
attr_reader :doc
|
71
|
+
|
72
|
+
# @return [NilClass, Array<WSDSL::Response::Element>] The optional nested elements
|
73
|
+
attr_reader :elements
|
74
|
+
|
75
|
+
# param [String, Symbol] name The name of the element
|
76
|
+
# param [String, Symbol] type The optional type of the element
|
77
|
+
# @api public
|
78
|
+
def initialize(name, type=nil)
|
79
|
+
# sets a documentation placeholder since the response doc is defined at the same time
|
80
|
+
# the response is defined.
|
81
|
+
@doc = Documentation::ElementDoc.new(name)
|
82
|
+
@name = name
|
83
|
+
@type = type
|
84
|
+
@attributes = []
|
85
|
+
@vectors = []
|
86
|
+
# we don't need to initialize the nested elements, by default they should be nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# sets a new attribute and returns the entire list of attributes
|
90
|
+
#
|
91
|
+
# @param [Hash] opts An element's attribute options
|
92
|
+
# @option opts [String, Symbol] attribute_name The name of the attribute, the value being the type
|
93
|
+
# @option opts [String, Symbol] :doc The attribute documentation
|
94
|
+
# @option opts [String, Symbol] :mock An optional mock value used by service related tools
|
95
|
+
#
|
96
|
+
# @example Creation of a response attribute called 'best_lap_time'
|
97
|
+
# service.response do |response|
|
98
|
+
# response.element(:name => "my_stats", :type => 'Leaderboard') do |e|
|
99
|
+
# e.attribute "best_lap_time" => :float, :doc => "Best lap time in seconds."
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# @return [Array<WSDSL::Response::Attribute>]
|
104
|
+
# @api public
|
105
|
+
def attribute(opts)
|
106
|
+
raise ArgumentError unless opts.is_a?(Hash)
|
107
|
+
# extract the documentation part and add it where it belongs
|
108
|
+
new_attribute = Attribute.new(opts)
|
109
|
+
@attributes << new_attribute
|
110
|
+
# document the attribute if description available
|
111
|
+
# we might want to have a placeholder message when a response attribute isn't defined
|
112
|
+
if opts.has_key?(:doc)
|
113
|
+
@doc.attribute(new_attribute.name, opts[:doc])
|
114
|
+
end
|
115
|
+
@attributes
|
116
|
+
end
|
117
|
+
|
118
|
+
# Defines an array aka vector of elements.
|
119
|
+
#
|
120
|
+
# @param [Hash] opts A hash representing the array information, usually a name and a type.
|
121
|
+
# @option opts [String, Symbol] :name The name of the defined array
|
122
|
+
# @option opts [String, Symbol] :type The class name of the element inside the array
|
123
|
+
#
|
124
|
+
# @param [Proc] &block
|
125
|
+
# A block to execute against the newly created array.
|
126
|
+
#
|
127
|
+
# @example Defining an element array called 'player_creation_rating'
|
128
|
+
# element.array :name => 'player_creation_rating', :type => 'PlayerCreationRating' do |a|
|
129
|
+
# a.attribute :comments => :string
|
130
|
+
# a.attribute :player_id => :integer
|
131
|
+
# a.attribute :rating => :integer
|
132
|
+
# a.attribute :username => :string
|
133
|
+
# end
|
134
|
+
# @yield [Vector] the newly created array/vector instance
|
135
|
+
# @see Vector#initialize
|
136
|
+
#
|
137
|
+
# @return [Array<WSDSL::Response::Element::Vector>]
|
138
|
+
# @api public
|
139
|
+
def array(opts)
|
140
|
+
vector = Vector.new(opts)
|
141
|
+
yield(vector) if block_given?
|
142
|
+
@vectors << vector
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the arrays/vectors contained in the response.
|
146
|
+
# This is an alias to access @vectors
|
147
|
+
# @see @vectors
|
148
|
+
#
|
149
|
+
# @return [Array<WSDSL::Response::Element::Vector>]
|
150
|
+
# @api public
|
151
|
+
def arrays
|
152
|
+
@vectors
|
153
|
+
end
|
154
|
+
|
155
|
+
# Defines a new element and yields the content of an optional block
|
156
|
+
# Each new element is then stored in the elements array.
|
157
|
+
#
|
158
|
+
# @param [Hash] opts Options used to define the element
|
159
|
+
# @option opts [String, Symbol] :name The element name
|
160
|
+
# @option opts [String, Symbol] :type The optional type
|
161
|
+
#
|
162
|
+
# @yield [WSDSL::Response::Element] the newly created element
|
163
|
+
# @example create an element called 'my_stats'.
|
164
|
+
# service.response do |response|
|
165
|
+
# response.element(:name => "my_stats", :type => 'Leaderboard')
|
166
|
+
# end
|
167
|
+
#
|
168
|
+
# @return [Array<WSDSL::Response::Element>]
|
169
|
+
# @api public
|
170
|
+
def element(opts={})
|
171
|
+
el = Element.new(opts[:name], opts[:type])
|
172
|
+
yield(el) if block_given?
|
173
|
+
@elements ||= []
|
174
|
+
@elements << el
|
175
|
+
end
|
176
|
+
|
177
|
+
# Response element's attribute class
|
178
|
+
# @api public
|
179
|
+
class Attribute
|
180
|
+
|
181
|
+
# @return [String, #to_s] The attribute's name.
|
182
|
+
# @api public
|
183
|
+
attr_reader :name
|
184
|
+
|
185
|
+
# @return [Symbol, String, #to_s] The attribute's type such as boolean, string etc..
|
186
|
+
# @api public
|
187
|
+
attr_reader :type
|
188
|
+
|
189
|
+
# @return [String] The documentation associated with this attribute.
|
190
|
+
# @api public
|
191
|
+
attr_reader :doc
|
192
|
+
|
193
|
+
# @see {Attribute#new}
|
194
|
+
# @return [Hash, Nil, Object] Could be a hash, nil or any object depending on how the attribute is created.
|
195
|
+
# @api public
|
196
|
+
attr_reader :opts
|
197
|
+
|
198
|
+
# Takes a Hash or an Array and extract the attribute name, type
|
199
|
+
# doc and extra options.
|
200
|
+
# If the passed objects is a Hash, the name will be extract from
|
201
|
+
# the first key and the type for the first value.
|
202
|
+
# An entry keyed by :doc will be used for the doc and the rest will go
|
203
|
+
# as extra options.
|
204
|
+
#
|
205
|
+
# If an Array is passed, the elements will be 'shifted' in this order:
|
206
|
+
# name, type, doc, type
|
207
|
+
#
|
208
|
+
# @param [Hash, Array] o_params
|
209
|
+
#
|
210
|
+
# @api public
|
211
|
+
def initialize(o_params)
|
212
|
+
params = o_params.dup
|
213
|
+
if params.is_a?(Hash)
|
214
|
+
@name, @type = params.shift
|
215
|
+
@doc = params.delete(:doc) if params.has_key?(:doc)
|
216
|
+
@opts = params
|
217
|
+
elsif params.is_a?(Array)
|
218
|
+
@name = params.shift
|
219
|
+
@type = params.shift
|
220
|
+
@doc = params.shift
|
221
|
+
@opts = params
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Array of objects inside an element
|
227
|
+
# @api public
|
228
|
+
class Vector
|
229
|
+
|
230
|
+
# @api public
|
231
|
+
attr_reader :name
|
232
|
+
|
233
|
+
# @api public
|
234
|
+
attr_reader :obj_type
|
235
|
+
|
236
|
+
# @api public
|
237
|
+
attr_accessor :attributes
|
238
|
+
|
239
|
+
# A vector can have nested elements.
|
240
|
+
# This value is nil by default.
|
241
|
+
#
|
242
|
+
# @return [NilClass, Array<WSDSL::Response::Element>]
|
243
|
+
# @see #element
|
244
|
+
# @api public
|
245
|
+
attr_reader :elements
|
246
|
+
|
247
|
+
# Initialize a Vector object, think about it as an array of objects of a certain type.
|
248
|
+
# It is recommended to passthe type argument as a string so the constant doesn't need to be resolved.
|
249
|
+
# In other words, if you say you are creating a vector of Foo objects, the Foo class doesn't need to be
|
250
|
+
# loaded yet. That makes service parsing easier and avoids dependency challenges.
|
251
|
+
#
|
252
|
+
# @param [Hash] opts A hash representing the vector information, usually a name and a type, both as strings
|
253
|
+
# @option opts [String] :name The array's name
|
254
|
+
# @option opts [Symbol, String] :type The type of the objects inside the array
|
255
|
+
#
|
256
|
+
# @example
|
257
|
+
# Vector.new(:name => 'player_creation_rating', :type => 'PlayerCreationRating')
|
258
|
+
#
|
259
|
+
# @api public
|
260
|
+
def initialize(opts)
|
261
|
+
@name = opts[:name]
|
262
|
+
@obj_type = opts[:type]
|
263
|
+
@attributes = []
|
264
|
+
end
|
265
|
+
|
266
|
+
# Sets a vector attribute
|
267
|
+
#
|
268
|
+
# @param (see Attribute#initialize)
|
269
|
+
# @api public
|
270
|
+
def attribute(opts)
|
271
|
+
raise ArgumentError unless opts.is_a?(Hash)
|
272
|
+
@attributes << Attribute.new(opts)
|
273
|
+
end
|
274
|
+
|
275
|
+
# Defines a new element and yields the content of an optional block
|
276
|
+
# Each new element is then stored in the elements array.
|
277
|
+
#
|
278
|
+
# @param [Hash] opts Options used to define the element
|
279
|
+
# @option opts [String, Symbol] :name The element name
|
280
|
+
# @option opts [String, Symbol] :type The optional type
|
281
|
+
#
|
282
|
+
# @yield [WSDSL::Response::Element] the newly created element
|
283
|
+
# @example create an element called 'my_stats'.
|
284
|
+
# service.response do |response|
|
285
|
+
# response.element(:name => "my_stats", :type => 'Leaderboard')
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# @return [Array<WSDSL::Response::Element>]
|
289
|
+
# @api public
|
290
|
+
def element(opts={})
|
291
|
+
el = Element.new(opts[:name], opts[:type])
|
292
|
+
yield(el) if block_given?
|
293
|
+
@elements ||= []
|
294
|
+
@elements << el
|
295
|
+
end
|
296
|
+
|
297
|
+
end # of Vector
|
298
|
+
end # of Element
|
299
|
+
|
300
|
+
end # of Response
|
301
|
+
end
|
data/lib/ws_list.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Wrapper module to keep track of all defined services
|
2
|
+
#
|
3
|
+
# @api public
|
4
|
+
module WSList
|
5
|
+
|
6
|
+
class UnknownService < StandardError; end
|
7
|
+
|
8
|
+
module_function
|
9
|
+
|
10
|
+
# Add a service to the array tracking
|
11
|
+
# the playco services
|
12
|
+
#
|
13
|
+
# @param [WSDSL] The service to add.
|
14
|
+
# @return [Array<WSDSL>] All the added services.
|
15
|
+
# @api public
|
16
|
+
def add(service)
|
17
|
+
@list ||= []
|
18
|
+
@list << service unless @list.find{|s| s.url == service.url && s.verb == service.verb}
|
19
|
+
@list
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns an array of services
|
23
|
+
#
|
24
|
+
# @return [Array<WSDSL>] All the added services.
|
25
|
+
# @api public
|
26
|
+
def all
|
27
|
+
@list || []
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns a service based on its name
|
31
|
+
#
|
32
|
+
# @param [String] name The name of the service you are looking for.
|
33
|
+
# @raise [UnknownService] if a service with the passed name isn't found.
|
34
|
+
# @return [WSDSL] The found service.
|
35
|
+
#
|
36
|
+
# @api public
|
37
|
+
def self.named(name)
|
38
|
+
service = all.find{|service| service.name == name}
|
39
|
+
if service.nil?
|
40
|
+
raise UnknownService, "Service named #{name} isn't available"
|
41
|
+
else
|
42
|
+
service
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
end
|
48
|
+
|
data/lib/wsdsl.rb
ADDED
@@ -0,0 +1,359 @@
|
|
1
|
+
require File.expand_path('inflection', File.dirname(__FILE__))
|
2
|
+
require File.expand_path('params', File.dirname(__FILE__))
|
3
|
+
require File.expand_path('response', File.dirname(__FILE__))
|
4
|
+
require File.expand_path('documentation', File.dirname(__FILE__))
|
5
|
+
require File.expand_path('ws_list', File.dirname(__FILE__))
|
6
|
+
|
7
|
+
# WSDSL offers a web service DSL to define web services,
|
8
|
+
# their params, http verbs, formats expected as well as the documentation
|
9
|
+
# for all these aspects of a web service.
|
10
|
+
#
|
11
|
+
# This DSL is only meant to describe a web service and isn't meant to cover any type
|
12
|
+
# of implementation details. It is meant to be framework/tool agnostic.
|
13
|
+
#
|
14
|
+
# However, tools can be built around the Web Service DSL data structure to extract documentation,
|
15
|
+
# generate routing information, verify that an incoming request is valid, generate automated tests...
|
16
|
+
#
|
17
|
+
#
|
18
|
+
#
|
19
|
+
# WSDSL
|
20
|
+
# |
|
21
|
+
# |__ service options (name, url, SSL, auth required formats, verbs, controller name, action, version, extra)
|
22
|
+
# |__ defined_params (instance of WSDSL::Params)
|
23
|
+
# | | | |_ Optional param rules
|
24
|
+
# | | |_ Required param rules
|
25
|
+
# | |_ Namespaced params (array containing nested optional and required rules)
|
26
|
+
# |__ response (instance of WSDSL::Response)
|
27
|
+
# | |_ elements (array of elements with each element having a name, type, attributes and vectors
|
28
|
+
# | | |_ attributes (array of WSDSL::Response::Attribute, each attribute has a name, a type, a doc and some extra options)
|
29
|
+
# | |_ vectors (array of WSDSL::Response::Vector), each vector has a name, obj_type, & an array of attributes
|
30
|
+
# | |_ attributes (array of WSDSL::Response::Attribute, each attribute has a name, a type and a doc)
|
31
|
+
# |__ doc (instance of WSDSL::Documentation)
|
32
|
+
# | | | |_ overal) description
|
33
|
+
# | | |_ examples (array of examples as strings)
|
34
|
+
# | |_ params documentation (Hash with the key being the param name and the value being the param documentation)
|
35
|
+
# |_ response (instance of Documentation.new)
|
36
|
+
# |_ elements (array of instances of WSDSL::Documentation::ElementDoc, each element has a name and a list of attributes)
|
37
|
+
# |_ attributes (Hash with the key being the attribute name and the value being the attribute's documentation)
|
38
|
+
#
|
39
|
+
# @since 0.0.3
|
40
|
+
# @api public
|
41
|
+
class WSDSL
|
42
|
+
|
43
|
+
# Returns the service url
|
44
|
+
#
|
45
|
+
# @return [String] The service url
|
46
|
+
# @api public
|
47
|
+
attr_reader :url
|
48
|
+
|
49
|
+
# List of all the service params
|
50
|
+
#
|
51
|
+
# @return [Array<WSDSL::Params>]
|
52
|
+
# @api public
|
53
|
+
attr_reader :defined_params
|
54
|
+
|
55
|
+
# Documentation instance containing all the service doc
|
56
|
+
#
|
57
|
+
# @return [WSDSL::Documentation]
|
58
|
+
# @api public
|
59
|
+
attr_reader :doc
|
60
|
+
|
61
|
+
# The HTTP verb supported
|
62
|
+
#
|
63
|
+
# @return [Symbol]
|
64
|
+
# @api public
|
65
|
+
attr_reader :verb
|
66
|
+
|
67
|
+
# Service's version
|
68
|
+
#
|
69
|
+
# @return [String]
|
70
|
+
# @api public
|
71
|
+
attr_reader :version
|
72
|
+
|
73
|
+
# Controller instance associated with the service
|
74
|
+
#
|
75
|
+
# @return [WSController]
|
76
|
+
# @api public
|
77
|
+
attr_reader :controller
|
78
|
+
|
79
|
+
# Name of the controller action associated with the service
|
80
|
+
#
|
81
|
+
# @return [String]
|
82
|
+
# @api public
|
83
|
+
attr_accessor :action
|
84
|
+
|
85
|
+
# Name of the controller associated with the service
|
86
|
+
#
|
87
|
+
# @return [String]
|
88
|
+
# @api public
|
89
|
+
attr_accessor :controller_name
|
90
|
+
|
91
|
+
# Name of the service
|
92
|
+
#
|
93
|
+
# @return [String]
|
94
|
+
# @api public
|
95
|
+
attr_reader :name
|
96
|
+
|
97
|
+
# Is SSL required?
|
98
|
+
#
|
99
|
+
# @return [Boolean]
|
100
|
+
# @api public
|
101
|
+
attr_reader :ssl
|
102
|
+
|
103
|
+
# Is authentication required?
|
104
|
+
#
|
105
|
+
# @return [Boolean]
|
106
|
+
# @api public
|
107
|
+
attr_reader :auth_required
|
108
|
+
|
109
|
+
# Extra placeholder to store data in based on developer's discretion.
|
110
|
+
#
|
111
|
+
# @return [Hash] A hash storing extra data based.
|
112
|
+
# @api public
|
113
|
+
# @since 0.1
|
114
|
+
attr_reader :extra
|
115
|
+
|
116
|
+
# Service constructor which is usually used via {Kernel#describe_service}
|
117
|
+
#
|
118
|
+
# @param [String] url Service's url
|
119
|
+
# @see #describe_service See how this class is usually initialized using `describe_service`
|
120
|
+
# @api public
|
121
|
+
def initialize(url)
|
122
|
+
@url = url
|
123
|
+
@defined_params = WSDSL::Params.new
|
124
|
+
@doc = WSDSL::Documentation.new
|
125
|
+
@response = WSDSL::Response.new
|
126
|
+
@name = extract_service_root_name(url)
|
127
|
+
if WSDSL.use_pluralized_controllers
|
128
|
+
base_name = ExtlibCopy::Inflection.pluralize(ExtlibCopy::Inflection.singular(name))
|
129
|
+
@controller_name = "#{ExtlibCopy.classify(base_name)}Controller"
|
130
|
+
else
|
131
|
+
@controller_name = "#{ExtlibCopy.classify(name)}Controller"
|
132
|
+
end
|
133
|
+
@action = extract_service_action(url)
|
134
|
+
@verb = :get
|
135
|
+
@formats = []
|
136
|
+
@version = '0.1'
|
137
|
+
@ssl = false
|
138
|
+
@auth_required = true
|
139
|
+
@extra = {}
|
140
|
+
end
|
141
|
+
|
142
|
+
# Checks the WSDSL flag to see if the controller names are pluralized.
|
143
|
+
#
|
144
|
+
# @return [Boolean] The updated value, default to false
|
145
|
+
# @api public
|
146
|
+
# @since 0.1.1
|
147
|
+
def self.use_pluralized_controllers
|
148
|
+
@pluralized_controllers ||= false
|
149
|
+
end
|
150
|
+
|
151
|
+
# Sets a WSDSL global flag so all controller names will be automatically pluralized.
|
152
|
+
#
|
153
|
+
# @param [Boolean] True if the controllers are pluralized, False otherwise.
|
154
|
+
#
|
155
|
+
# @return [Boolean] The updated value
|
156
|
+
# @api public
|
157
|
+
# @since 0.1.1
|
158
|
+
def self.use_pluralized_controllers=(val)
|
159
|
+
@pluralized_controllers = val
|
160
|
+
end
|
161
|
+
|
162
|
+
# Offers a way to dispatch the service at runtime
|
163
|
+
# Basically, it dispatches the request to the defined controller/action
|
164
|
+
# The full request cycle looks like that:
|
165
|
+
# client -> webserver -> rack -> env -> [service dispatcher] -> controller action -> rack -> webserver -> client
|
166
|
+
# @param [Object] app Reference object such as a Sinatra::Application to be passed to the controller.
|
167
|
+
#
|
168
|
+
# @return [#to_s] The response from the controller action
|
169
|
+
# @api private
|
170
|
+
def controller_dispatch(app)
|
171
|
+
unless @controller
|
172
|
+
if Object.const_defined?(@controller_name)
|
173
|
+
@controller = Object.const_get(@controller_name)
|
174
|
+
else
|
175
|
+
raise "The #{@controller_name} class was not found"
|
176
|
+
end
|
177
|
+
end
|
178
|
+
# We are passing the service object to the controller so the
|
179
|
+
# param verification could be done when the controller gets initialized.
|
180
|
+
@controller.new(app, self).send(@action)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Returns the defined params
|
184
|
+
# for DSL use only!
|
185
|
+
# To keep the distinction between the request params and the service params
|
186
|
+
# using the +defined_params+ accessor is recommended.
|
187
|
+
# @see WSDSL::Params
|
188
|
+
#
|
189
|
+
# @return [WSDSL::Params] The defined params
|
190
|
+
# @api public
|
191
|
+
def params
|
192
|
+
if block_given?
|
193
|
+
yield(@defined_params)
|
194
|
+
else
|
195
|
+
@defined_params
|
196
|
+
end
|
197
|
+
end
|
198
|
+
alias :param :params
|
199
|
+
|
200
|
+
# Returns an array of required param rules
|
201
|
+
#
|
202
|
+
# @return [Array<WSDSL::Params::Rule>] Only the required param rules
|
203
|
+
# @api public
|
204
|
+
def required_rules
|
205
|
+
@defined_params.list_required
|
206
|
+
end
|
207
|
+
|
208
|
+
# Returns an array of optional param rules
|
209
|
+
#
|
210
|
+
# @return [Array<WSDSL::Params::Rule>]Only the optional param rules
|
211
|
+
# @api public
|
212
|
+
def optional_rules
|
213
|
+
@defined_params.list_optional
|
214
|
+
end
|
215
|
+
|
216
|
+
# Returns an array of namespaced params
|
217
|
+
# @see WSDSL::Params#namespaced_params
|
218
|
+
#
|
219
|
+
# @return [Array<WSDSL::Params>] the namespaced params
|
220
|
+
# @api public
|
221
|
+
def nested_params
|
222
|
+
@defined_params.namespaced_params
|
223
|
+
end
|
224
|
+
|
225
|
+
# Mark that the service doesn't require authentication.
|
226
|
+
# Note: Authentication is turned on by default
|
227
|
+
#
|
228
|
+
# @return [Boolean]
|
229
|
+
# @api public
|
230
|
+
def disable_auth
|
231
|
+
@auth_required = false
|
232
|
+
end
|
233
|
+
|
234
|
+
# Mark that the service requires a SSL connection
|
235
|
+
#
|
236
|
+
# @return [Boolean]
|
237
|
+
# @api public
|
238
|
+
def enable_ssl
|
239
|
+
@ssl = true
|
240
|
+
end
|
241
|
+
|
242
|
+
# Mark the current service as not accepting any params.
|
243
|
+
# This is purely for expressing the developer's objective since
|
244
|
+
# by default an error is raise if no params are defined and some
|
245
|
+
# params are sent.
|
246
|
+
#
|
247
|
+
# @return [Nil]
|
248
|
+
# @api public
|
249
|
+
def accept_no_params!
|
250
|
+
# no op operation since this is the default behavior
|
251
|
+
# unless params get defined. Makes sense for documentation tho.
|
252
|
+
end
|
253
|
+
|
254
|
+
# Returns the service response
|
255
|
+
# @yield The service response object
|
256
|
+
#
|
257
|
+
# @return [WSDSL::Response]
|
258
|
+
# @api public
|
259
|
+
def response
|
260
|
+
if block_given?
|
261
|
+
yield(@response)
|
262
|
+
else
|
263
|
+
@response
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Sets or returns the supported formats
|
268
|
+
# @param [String, Symbol] f_types Format type supported, such as :xml
|
269
|
+
#
|
270
|
+
# @return [Array<Symbol>] List of supported formats
|
271
|
+
# @api public
|
272
|
+
def formats(*f_types)
|
273
|
+
f_types.each{|f| @formats << f unless @formats.include?(f) }
|
274
|
+
@formats
|
275
|
+
end
|
276
|
+
|
277
|
+
# Sets the accepted HTTP verbs or return it if nothing is passed.
|
278
|
+
#
|
279
|
+
# @return [String, Symbol]
|
280
|
+
# @api public
|
281
|
+
def http_verb(s_verb=nil)
|
282
|
+
return @verb if s_verb.nil?
|
283
|
+
@verb = s_verb.to_sym
|
284
|
+
# Depending on the service settings and url, the service action might need to be updated.
|
285
|
+
# This is how we can support restful routes where a PUT request automatically uses the update method.
|
286
|
+
update_restful_action(@verb)
|
287
|
+
@verb
|
288
|
+
end
|
289
|
+
|
290
|
+
# Yields and returns the documentation object
|
291
|
+
# @yield [WSDSL::Documentation]
|
292
|
+
#
|
293
|
+
# @return [WSDSL::Documentation] The service documentation object
|
294
|
+
# @api public
|
295
|
+
def documentation
|
296
|
+
yield(doc)
|
297
|
+
end
|
298
|
+
|
299
|
+
SERVICE_ROOT_REGEXP = /(.*?)[\/\(\.]/
|
300
|
+
SERVICE_ACTION_REGEXP = /[\/\(\.]([a-z0-9_]+)[\/\(\.\?]/i
|
301
|
+
SERVICE_RESTFUL_SHOW_REGEXP = /\/:[a-z0-9_]+\.\w{3}$/
|
302
|
+
|
303
|
+
private
|
304
|
+
|
305
|
+
# extracts the service root name out of the url using a regexp
|
306
|
+
def extract_service_root_name(url)
|
307
|
+
url[SERVICE_ROOT_REGEXP, 1] || url
|
308
|
+
end
|
309
|
+
|
310
|
+
# extracts the action name out of the url using a regexp
|
311
|
+
# Defaults to the list action
|
312
|
+
def extract_service_action(url)
|
313
|
+
if url =~ SERVICE_RESTFUL_SHOW_REGEXP
|
314
|
+
'show'
|
315
|
+
else
|
316
|
+
url[SERVICE_ACTION_REGEXP, 1] || 'list'
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Check if we need to use a restful route in which case we need
|
321
|
+
# to update the service action
|
322
|
+
def update_restful_action(verb)
|
323
|
+
if verb != :get && @action && %w{list show}.include?(@action)
|
324
|
+
case verb
|
325
|
+
when :post
|
326
|
+
@action = 'create'
|
327
|
+
when :put
|
328
|
+
@action = 'update'
|
329
|
+
when :delete
|
330
|
+
@action = 'destroy'
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
end
|
336
|
+
|
337
|
+
# Extending the top level module to add some helpers
|
338
|
+
#
|
339
|
+
# @api public
|
340
|
+
module Kernel
|
341
|
+
|
342
|
+
# Base DSL method called to describe a service
|
343
|
+
#
|
344
|
+
# @param [String] url The url of the service to add.
|
345
|
+
# @yield [WSDSL] The newly created service.
|
346
|
+
# @return [Array] The services already defined
|
347
|
+
# @example Describing a basic service
|
348
|
+
# describe_service "hello-world.xml" do |service|
|
349
|
+
# # describe the service
|
350
|
+
# end
|
351
|
+
#
|
352
|
+
# @api public
|
353
|
+
def describe_service(url, &block)
|
354
|
+
service = WSDSL.new(url)
|
355
|
+
yield service
|
356
|
+
WSList.add(service)
|
357
|
+
end
|
358
|
+
|
359
|
+
end
|