rubyrest 0.0.5 → 0.1.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.
@@ -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