rack-autocrud 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/rack/autocrud.rb +158 -155
  2. 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
- 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
- # Enumerate through all defined classes, checking for the model / endpoint
37
- ObjectSpace.each_object(Class) { |klass|
38
- model_klass = klass if String(klass.name).downcase == String(@model_namespace + '::' + endpoint).downcase
39
- endpoint_klass = klass if String(klass.name).downcase == String(@endpoint_namespace + '::' + endpoint).downcase
40
- }
41
-
42
- # Lazily locate the endpoint namespace module (if we haven't already)
43
- if endpoint_klass.nil? && @endpoint_mod.nil?
44
- ObjectSpace.each_object(Module) { |klass|
45
- @endpoint_mod = klass if String(klass.name).downcase == @endpoint_namespace.downcase
46
- }
47
- end
48
-
49
- # Now, if we've got something, do our magic.
50
- if !model_klass.nil?
51
- # If we don't have an endpoint class, make one
52
- if endpoint_klass.nil?
53
- endpoint_klass = Class.new(Sinatra::Base)
54
- @endpoint_mod.const_set(endpoint.capitalize,endpoint_klass)
55
- end
56
-
57
- # Patch in the routes
58
- endpoint_klass.class_exec(model_klass,endpoint,env) { |model,endpoint,env|
59
- def set_request_body(new_body,content_type='text/json')
60
- env['rack.input'] = StringIO.new(new_body)
61
- env['CONTENT_LENGTH'] = new_body.length
62
- env['CONTENT_TYPE'] = content_type
63
- return nil
64
- end
65
-
66
- get '/' do
67
- halt [403, '{ "error": "Access Denied" }']
68
- end
69
-
70
- post '/' do
71
- # Call the pre-create hook
72
- if self.respond_to?(:pre_create)
73
- ret = pre_create(env,request)
74
- return ret unless ret.nil?
75
- end
76
-
77
- # Rewind the body
78
- request.body.rewind if request.body.respond_to?(:rewind)
79
-
80
- # Attempt to create the model object
81
- obj = nil
82
- begin
83
- obj = model.create(JSON.parse(request.body.read))
84
- halt [402, '{ "error": "Failed to save ' + endpoint + '" }'] unless obj && obj.saved?
85
- rescue JSON::ParserError
86
- halt [ 400, { 'error' => 'Invalid JSON in request body.', 'details' => $! }.to_json ]
87
- end
88
-
89
- # Call the post-create hook
90
- if self.respond_to?(:post_create)
91
- ret = post_create(env,request,obj)
92
- return ret unless ret.nil?
93
- end
94
-
95
- [ 201, { 'id' => obj.id.to_i }.to_json ]
96
- end
97
-
98
- get '/:id' do
99
- # Call the pre-retrieve hook
100
- if self.respond_to?(:pre_retrieve)
101
- ret = pre_retrieve(env,request)
102
- return ret unless ret.nil?
103
- end
104
-
105
- obj = model.get(params[:id])
106
-
107
- # Call the post-retrieve hook
108
- if self.respond_to?(:post_retrieve)
109
- ret = post_retrieve(env,request,obj)
110
- return ret unless ret.nil?
111
- end
112
-
113
- obj.to_json
114
- end
115
-
116
- put '/:id' do
117
- # Call the pre-update hook
118
- if self.respond_to?(:pre_update)
119
- ret = pre_update(env,request)
120
- return ret unless ret.nil?
121
- end
122
-
123
- # Rewind the body
124
- request.body.rewind if request.body.respond_to?(:rewind)
125
-
126
- # Attempt to update the model
127
- begin
128
- saved = model.update(JSON.parse(request.body.read))
129
- halt [403, 'Access Denied'] unless saved
130
- rescue JSON::ParserError
131
- halt [ 400, { 'error' => 'Invalid JSON in request body.', 'details' => $! }.to_json ]
132
- end
133
-
134
- # Call the post-update hook
135
- if self.respond_to?(:post_update)
136
- ret = post_update(env,request)
137
- return ret unless ret.nil?
138
- end
139
-
140
- [ 201, '{ "status": "ok" }' ]
141
- end
142
-
143
- delete '/:id' do
144
- # Call the pre-destroy hook
145
- if self.respond_to?(:pre_destroy)
146
- ret = pre_destroy(env,request)
147
- return ret unless ret.nil?
148
- end
149
-
150
- obj = model.get(params[:id])
151
- obj.destroy if obj
152
-
153
- # Call the post-destroy hook
154
- if self.respond_to?(:post_destroy)
155
- ret = post_destroy(env,request,obj)
156
- return ret unless ret.nil?
157
- end
158
-
159
- [ 204 ]
160
- end
161
- }
162
-
163
- # Now, call the endpoint class (assuming it will return a response)
164
- env['PATH_INFO'] = '/' + uri.join('/')
165
- return endpoint_klass.call(env)
166
- end
167
-
168
- # Otherwise, pass the request down the chain...
169
- @app.call(env)
170
- end
171
- end
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.5
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-10-30 00:00:00.000000000 Z
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: ! "\tRack middleware that works with Sinatra and DataMapper to dynamically\n\tcreate
63
- CRUD endpoints and routes based on models. It ain't perfect, but\n\tit works.\n\n\tThese
64
- generated CRUD routes are assumed to return a Rack response.\n\n\tIt's important
65
- to note, that you models and endpoints must be in separate\n\tmodules (read: namespaces).\n\n\tInput
66
- and Response data are formatted as JSON.\n\n\tSee the README for more info.\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.24
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