rubyrest 0.0.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,233 +0,0 @@
1
- # WEBrick servlet that implements a simple mapping between
2
- # HTTP methods and request paths into business methods, using some
3
- # conventions.
4
- #
5
- # Developpers should only subclass this servlet, and implement
6
- # standard methods that return a xml content.
7
- #
8
- # $Id:$
9
- module RubyRest
10
-
11
- # This is the servlet that actually exposes a REST API
12
- # by translating HTTP requests into 'generic' or 'custom' service
13
- # methods.
14
- #
15
- class RESTServlet < WEBrick::HTTPServlet::AbstractServlet
16
- include RubyRest::Atom, RubyRest::Tools
17
-
18
- # Injects some RubyRest configuration options
19
- # from the server to the servlet
20
- def initialize( server, *options )
21
- super( server, options )
22
- @servicemodule = server.rubyrest[ :servicemodule ]
23
-
24
- if server.rubyrest.has( :authmodel )
25
- @authmodel = server.rubyrest[ :authmodel ]
26
- end
27
- end
28
-
29
- # Inspects the request, and resolve the parameters
30
- # The format of a request is the following:
31
- #
32
- # /:model/:id/:property
33
- #
34
- def resolve_params( request )
35
- params = request.path.split( "/" )
36
- @http_method = request.request_method
37
- @token = request[ 'token']
38
- @model = params[1]
39
- @id = params[2]
40
- @property = params[3]
41
- begin
42
- @body = REXML::Document.new( request.body ) if request.body != nil
43
- rescue => e
44
- puts "unable to parse request body: #{request.body}"
45
- end
46
- end
47
-
48
- def resolve_custom_method
49
- if @property != nil
50
- @custom_method = "#{@model}_#{@property}"
51
- else
52
- @custom_method = WORKSPACE_METHOD
53
- @custom_method = "#{@model}_#{@generic_method}" if @model != nil
54
- end
55
- end
56
-
57
- def resolve_service_method
58
- resolve_custom_method
59
- return @custom_method if self.respond_to? @custom_method
60
- return @generic_method
61
- end
62
-
63
- def dispatch( request, response )
64
-
65
- @service_method = resolve_service_method
66
- check_security( request ) if @authmodel != nil
67
- @result = self.method( @service_method ).call( request )
68
-
69
- if @result.respond_to? "unauthorized" and @result.unauthorized == true
70
- raise WEBrick::HTTPStatus::Unauthorized
71
- else
72
- response.status = @success_code
73
- format_response( request, response )
74
- end
75
- end
76
-
77
-
78
- def resolve_get_method
79
- if @property != nil
80
- @generic_method = :retrieve_related
81
- else
82
- if @id != nil
83
- @generic_method = :show
84
- else @generic_method = :retrieve end
85
- end
86
- end
87
-
88
- def do_GET( request, response )
89
- resolve_params( request )
90
- @success_code = 200
91
- resolve_get_method
92
- dispatch( request, response )
93
- end
94
-
95
- def do_POST( request, response )
96
- resolve_params( request )
97
- incompatible_path( request ) if @id != nil
98
- @generic_method = :create
99
- @success_code = 201
100
- dispatch( request, response )
101
- end
102
-
103
- def do_PUT( request, response )
104
- resolve_params( request )
105
- incompatible_path( request ) if @id == nil
106
- @generic_method = :update
107
- @success_code = 200
108
- dispatch( request, response )
109
- end
110
-
111
- def do_DELETE( request, response )
112
- resolve_params( request )
113
- incompatible_path( request ) if @id == nil
114
- @generic_method = :delete
115
- @success_code = 410
116
- dispatch( request, response )
117
- end
118
-
119
- # Raises an error stating that the current
120
- # http method is not compatible with the requested path.
121
- def incompatible_path( request )
122
- error( 100, @http_method, request.path )
123
- end
124
-
125
- # Custom service method that authenticates a username/password pair
126
- # found in the request body. The authentication is left to the class defined by the
127
- # :authmodel configuration option.
128
- #
129
- # Developpers can provide their own implementation, however it is recommended to
130
- # subclass the class *Credentials*
131
- def credentials_create( request )
132
- auth_class = to_class( @servicemodule, @authmodel )
133
- auth = auth_class.new
134
- auth_class.rest_bind( auth, @body )
135
- auth = auth_class.authenticate( auth )
136
- raise WEBrick::HTTPStatus::Unauthorized if auth == nil
137
- return auth
138
- end
139
-
140
- ANONYMOUS_ACCESS = "POST/credentials"
141
-
142
- # Defines whether the request is allowed to be processed
143
- # without the need of a security token
144
- #
145
- def anonymous_access
146
- "#{@http_method}/#{@model}" == ANONYMOUS_ACCESS
147
- end
148
-
149
- # Checks that a token is present in the request
150
- # exception if doing a POST on a credentials property
151
- def check_security( request )
152
- auth_class = to_class( @servicemodule , @authmodel )
153
- if @token == nil and !anonymous_access
154
- raise WEBrick::HTTPStatus::Unauthorized
155
- end
156
- if @token != nil
157
- @principal = auth_class.validate( @token )
158
- raise WEBrick::HTTPStatus::Unauthorized if @principal == nil
159
- end
160
- end
161
-
162
- # Returns the model defined by the configuration
163
- # option :dashboard
164
- def dashboard( request )
165
- clazz = to_class( @servicemodule, "dashboard" )
166
- clazz.rest_retrieve( @principal )
167
- end
168
-
169
- end
170
-
171
-
172
-
173
- # Generic servlet, that implements the Moodisland Grape API
174
- # This layer returns domain objects that can be included
175
- # in atom feeds as entries.
176
- #
177
- class CRUDServlet < RESTServlet
178
-
179
- # Creates and saves a new object. The object is then
180
- # returned
181
- def create( request )
182
- clazz = to_class( @servicemodule, @model )
183
- object = clazz.rest_create( @principal )
184
- clazz.rest_bind( object, @body )
185
- clazz.rest_save( object, @principal )
186
- end
187
-
188
- # Retrieves a list of objects
189
- #
190
- def retrieve( request )
191
- clazz = to_class( @servicemodule, @model )
192
- clazz.rest_retrieve( @principal )
193
- end
194
-
195
- # Retrieve a list of related objects
196
- #
197
- def retrieve_related( request )
198
- clazz = to_class( @servicemodule, @model )
199
- service_method = "rest_#{@property}"
200
- error( 500, clazz.name, service_method ) if !clazz.respond_to?( service_method )
201
- clazz.method( service_method ).call( @id, @principal )
202
- end
203
-
204
- # Retrieves a single object
205
- #
206
- def show( request )
207
- clazz = to_class( @servicemodule, @model )
208
- single = clazz.rest_single( @id, @principal )
209
- raise error( 200, @model, @id ) if single == nil
210
- return single
211
- end
212
-
213
- # Deletes a single object
214
- #
215
- def delete( request )
216
- clazz = to_class( @servicemodule, @model )
217
- clazz.rest_delete( @id, @principal )
218
- end
219
-
220
- # Retrieves, updates and saves an existing object.
221
- # The object is then returned
222
- #
223
- def update( request )
224
- object = show( request )
225
- clazz = object.class
226
- clazz.rest_bind( object, @body )
227
- clazz.rest_save( object, @principal )
228
- end
229
-
230
- end
231
-
232
-
233
- end
@@ -1,72 +0,0 @@
1
- # RubyRest: $Id:$
2
- #
3
- #
4
- #
5
-
6
- module RubyRest
7
-
8
- # This module provides a catalog of errors
9
- # the application is supposed to throw.
10
- #
11
- #
12
- module Tools
13
-
14
-
15
- ATOM_DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
16
-
17
- def nvl( value, default )
18
- return value if value != nil
19
- return default
20
- end
21
-
22
- def format_atom_date( value )
23
- nvl( value, Time.now ).strftime( ATOM_DATE_FORMAT )
24
- end
25
-
26
- def parse_atom_date( value )
27
- Date.strptime( value, ATOM_DATE_FORMAT )
28
- end
29
-
30
- # Resolves the specified module name and model
31
- # into a class, and returns it
32
- def to_class( modulename, model )
33
- Class.by_name( "#{modulename}::#{model.capitalize}" )
34
- end
35
-
36
- # Builds a gem name
37
- def to_gem_name( moduleprefix, modulename )
38
- return modulename if moduleprefix == nil
39
- return "#{moduleprefix}-#{modulename}"
40
- end
41
-
42
- # Builds a fully qualified module name
43
- def to_module_name( moduleprefix, modulename )
44
- return modulename.capitalize if moduleprefix == nil
45
- return "#{moduleprefix.capitalize}::#{modulename.capitalize}"
46
- end
47
-
48
- # Catalog of error messages, indexed by error
49
- # number
50
- ERRORS = {
51
- 000 => "Missing configuration option",
52
- 001 => "Unable to connect to database. Missing method",
53
- 002 => "Unable to load schema. Missing method",
54
- 003 => "Unable to create table",
55
- 004 => "Please subclass and override",
56
- 005 => "Sorry, configuration method did not return a valid database connection",
57
- 006 => "Class was not property configured with its database connection (Sequel)",
58
- 100 => "Request path and HTTP method are not compatible",
59
- 200 => "No resource found for model and id",
60
- 500 => "No service method found in model class"
61
- }
62
-
63
- # Raises a new error. Resolves the specified
64
- # number into a human readable message
65
- def error( number, *params )
66
- raise "\##{number}: #{ERRORS[number]}: #{params.join( ', ') }"
67
- end
68
-
69
-
70
- end
71
-
72
- end