rack-autocrud 0.1.5 → 0.1.6
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/lib/rack/autocrud.rb +158 -155
- metadata +9 -8
data/lib/rack/autocrud.rb
CHANGED
@@ -15,160 +15,163 @@ require 'sinatra/base'
|
|
15
15
|
require 'json'
|
16
16
|
|
17
17
|
module Rack
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
18
|
+
class AutoCRUD
|
19
|
+
def initialize(app,options={})
|
20
|
+
@app = app
|
21
|
+
@model_namespace = options[:model_namespace]
|
22
|
+
@endpoint_namespace = options[:endpoint_namespace]
|
23
|
+
@endpoint_mod = nil
|
24
|
+
end
|
25
|
+
|
26
|
+
def call(env)
|
27
|
+
dup._call(env) # For thread safety...
|
28
|
+
end
|
29
|
+
|
30
|
+
def _call(env)
|
31
|
+
model_klass = nil
|
32
|
+
endpoint_klass = nil
|
33
|
+
verb,endpoint,*uri = env['REQUEST_URI'].split('/')
|
34
|
+
verb = env['REQUEST_METHOD'].downcase
|
35
|
+
|
36
|
+
# If this is to '/' pass it on
|
37
|
+
return @app.call(env) if endpoint.nil?
|
38
|
+
|
39
|
+
# Enumerate through all defined classes, checking for the model / endpoint
|
40
|
+
ObjectSpace.each_object(Class) { |klass|
|
41
|
+
model_klass = klass if String(klass.name).downcase == String(@model_namespace + '::' + endpoint).downcase
|
42
|
+
endpoint_klass = klass if String(klass.name).downcase == String(@endpoint_namespace + '::' + endpoint).downcase
|
43
|
+
}
|
44
|
+
|
45
|
+
# Lazily locate the endpoint namespace module (if we haven't already)
|
46
|
+
if endpoint_klass.nil? && @endpoint_mod.nil?
|
47
|
+
ObjectSpace.each_object(Module) { |klass|
|
48
|
+
@endpoint_mod = klass if String(klass.name).downcase == @endpoint_namespace.downcase
|
49
|
+
}
|
50
|
+
end
|
51
|
+
|
52
|
+
# Now, if we've got something, do our magic.
|
53
|
+
if !model_klass.nil?
|
54
|
+
# If we don't have an endpoint class, make one
|
55
|
+
if endpoint_klass.nil?
|
56
|
+
endpoint_klass = Class.new(Sinatra::Base)
|
57
|
+
@endpoint_mod.const_set(endpoint.capitalize,endpoint_klass)
|
58
|
+
end
|
59
|
+
|
60
|
+
# Patch in the routes
|
61
|
+
endpoint_klass.class_exec(model_klass,endpoint,env) { |model,endpoint,env|
|
62
|
+
def set_request_body(new_body,content_type='text/json')
|
63
|
+
env['rack.input'] = StringIO.new(new_body)
|
64
|
+
env['CONTENT_LENGTH'] = new_body.length
|
65
|
+
env['CONTENT_TYPE'] = content_type
|
66
|
+
return nil
|
67
|
+
end
|
68
|
+
|
69
|
+
get '/' do
|
70
|
+
halt [403, '{ "error": "Access Denied" }']
|
71
|
+
end
|
72
|
+
|
73
|
+
post '/' do
|
74
|
+
# Call the pre-create hook
|
75
|
+
if self.respond_to?(:pre_create)
|
76
|
+
ret = pre_create(env,request)
|
77
|
+
return ret unless ret.nil?
|
78
|
+
end
|
79
|
+
|
80
|
+
# Rewind the body
|
81
|
+
request.body.rewind if request.body.respond_to?(:rewind)
|
82
|
+
|
83
|
+
# Attempt to create the model object
|
84
|
+
obj = nil
|
85
|
+
begin
|
86
|
+
obj = model.create(JSON.parse(request.body.read))
|
87
|
+
halt [402, '{ "error": "Failed to save ' + endpoint + '" }'] unless obj && obj.saved?
|
88
|
+
rescue JSON::ParserError
|
89
|
+
halt [ 400, { 'error' => 'Invalid JSON in request body.', 'details' => $! }.to_json ]
|
90
|
+
end
|
91
|
+
|
92
|
+
# Call the post-create hook
|
93
|
+
if self.respond_to?(:post_create)
|
94
|
+
ret = post_create(env,request,obj)
|
95
|
+
return ret unless ret.nil?
|
96
|
+
end
|
97
|
+
|
98
|
+
[ 201, { 'id' => obj.id.to_i }.to_json ]
|
99
|
+
end
|
100
|
+
|
101
|
+
get '/:id' do
|
102
|
+
# Call the pre-retrieve hook
|
103
|
+
if self.respond_to?(:pre_retrieve)
|
104
|
+
ret = pre_retrieve(env,request)
|
105
|
+
return ret unless ret.nil?
|
106
|
+
end
|
107
|
+
|
108
|
+
obj = model.get(params[:id])
|
109
|
+
|
110
|
+
# Call the post-retrieve hook
|
111
|
+
if self.respond_to?(:post_retrieve)
|
112
|
+
ret = post_retrieve(env,request,obj)
|
113
|
+
return ret unless ret.nil?
|
114
|
+
end
|
115
|
+
|
116
|
+
obj.to_json
|
117
|
+
end
|
118
|
+
|
119
|
+
put '/:id' do
|
120
|
+
# Call the pre-update hook
|
121
|
+
if self.respond_to?(:pre_update)
|
122
|
+
ret = pre_update(env,request)
|
123
|
+
return ret unless ret.nil?
|
124
|
+
end
|
125
|
+
|
126
|
+
# Rewind the body
|
127
|
+
request.body.rewind if request.body.respond_to?(:rewind)
|
128
|
+
|
129
|
+
# Attempt to update the model
|
130
|
+
begin
|
131
|
+
saved = model.update(JSON.parse(request.body.read))
|
132
|
+
halt [403, 'Access Denied'] unless saved
|
133
|
+
rescue JSON::ParserError
|
134
|
+
halt [ 400, { 'error' => 'Invalid JSON in request body.', 'details' => $! }.to_json ]
|
135
|
+
end
|
136
|
+
|
137
|
+
# Call the post-update hook
|
138
|
+
if self.respond_to?(:post_update)
|
139
|
+
ret = post_update(env,request)
|
140
|
+
return ret unless ret.nil?
|
141
|
+
end
|
142
|
+
|
143
|
+
[ 201, '{ "status": "ok" }' ]
|
144
|
+
end
|
145
|
+
|
146
|
+
delete '/:id' do
|
147
|
+
# Call the pre-destroy hook
|
148
|
+
if self.respond_to?(:pre_destroy)
|
149
|
+
ret = pre_destroy(env,request)
|
150
|
+
return ret unless ret.nil?
|
151
|
+
end
|
152
|
+
|
153
|
+
obj = model.get(params[:id])
|
154
|
+
obj.destroy if obj
|
155
|
+
|
156
|
+
# Call the post-destroy hook
|
157
|
+
if self.respond_to?(:post_destroy)
|
158
|
+
ret = post_destroy(env,request,obj)
|
159
|
+
return ret unless ret.nil?
|
160
|
+
end
|
161
|
+
|
162
|
+
[ 204 ]
|
163
|
+
end
|
164
|
+
}
|
165
|
+
|
166
|
+
# Now, call the endpoint class (assuming it will return a response)
|
167
|
+
env['PATH_INFO'] = '/' + uri.join('/')
|
168
|
+
return endpoint_klass.call(env)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Otherwise, pass the request down the chain...
|
172
|
+
@app.call(env)
|
173
|
+
end
|
174
|
+
end
|
172
175
|
end
|
173
176
|
|
174
|
-
# vi:set ts=2:
|
177
|
+
# vi:set ts=2 sw=2 expandtab sta:
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rack-autocrud
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-11-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: json
|
@@ -59,11 +59,12 @@ dependencies:
|
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
|
-
description: ! "
|
63
|
-
CRUD endpoints and routes based on models. It ain't perfect, but\n
|
64
|
-
generated CRUD routes are assumed to return a Rack response.\n\n
|
65
|
-
to note, that you models and endpoints must be in separate\n
|
66
|
-
and Response data are formatted as JSON.\n\n
|
62
|
+
description: ! " Rack middleware that works with Sinatra and DataMapper to dynamically\n
|
63
|
+
\ create CRUD endpoints and routes based on models. It ain't perfect, but\n it
|
64
|
+
works.\n\n These generated CRUD routes are assumed to return a Rack response.\n\n
|
65
|
+
\ It's important to note, that you models and endpoints must be in separate\n modules
|
66
|
+
(read: namespaces).\n\n Input and Response data are formatted as JSON.\n\n See
|
67
|
+
the README for more info.\n"
|
67
68
|
email: tim.hentenaar@gmail.com
|
68
69
|
executables: []
|
69
70
|
extensions: []
|
@@ -92,7 +93,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
92
93
|
version: '0'
|
93
94
|
requirements: []
|
94
95
|
rubyforge_project:
|
95
|
-
rubygems_version: 1.8.
|
96
|
+
rubygems_version: 1.8.23
|
96
97
|
signing_key:
|
97
98
|
specification_version: 3
|
98
99
|
summary: Rack middleware that automagically handles basic CRUD operations
|