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.
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