bulk_api 0.0.3 → 0.0.5
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/bulk/#resource.rb# +275 -0
- metadata +5 -4
@@ -0,0 +1,275 @@
|
|
1
|
+
module Bulk
|
2
|
+
class AuthenticationError < StandardError; end
|
3
|
+
class AuthorizationError < StandardError; end
|
4
|
+
|
5
|
+
class Resource
|
6
|
+
|
7
|
+
attr_reader :controller
|
8
|
+
delegate :session, :params, :to => :controller
|
9
|
+
delegate :resource_class, :to => "self.class"
|
10
|
+
@@resources = []
|
11
|
+
|
12
|
+
class << self
|
13
|
+
attr_writer :application_resource_class
|
14
|
+
attr_reader :abstract
|
15
|
+
alias_method :abstract?, :abstract
|
16
|
+
|
17
|
+
def resource_class(klass = nil)
|
18
|
+
@resource_class = klass if klass
|
19
|
+
@resource_class
|
20
|
+
end
|
21
|
+
|
22
|
+
def resource_name(name = nil)
|
23
|
+
@resource_name = name if name
|
24
|
+
@resource_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def resources(*resources)
|
28
|
+
@@resources = resources unless resources.blank?
|
29
|
+
@@resources
|
30
|
+
end
|
31
|
+
|
32
|
+
def application_resource_class
|
33
|
+
@application_resource_class ||= ApplicationResource
|
34
|
+
@application_resource_class.is_a?(Class) ? @application_resource_class : Object.const_get(@application_resource_class.to_sym)
|
35
|
+
end
|
36
|
+
|
37
|
+
def inherited(base)
|
38
|
+
if base.name == application_resource_class.to_s
|
39
|
+
base.abstract!
|
40
|
+
elsif base.name =~ /(.*)Resource$/
|
41
|
+
base.resource_name($1.underscore.pluralize)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
%w/get create update delete/.each do |method|
|
46
|
+
define_method(method) do |controller|
|
47
|
+
handle_response(method, controller)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def abstract!
|
52
|
+
@abstract = true
|
53
|
+
@@resources = []
|
54
|
+
end
|
55
|
+
protected :abstract!
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
# TODO: refactor this to some kind of Response class
|
60
|
+
def handle_response(method, controller)
|
61
|
+
response = {}
|
62
|
+
application_resource = application_resource_class.new(controller, :abstract => true)
|
63
|
+
|
64
|
+
if application_resource.respond_to?(:authenticate)
|
65
|
+
raise AuthenticationError unless application_resource.authenticate(method)
|
66
|
+
end
|
67
|
+
|
68
|
+
if application_resource.respond_to?(:authorize)
|
69
|
+
raise AuthorizationError unless application_resource.authorize(method)
|
70
|
+
end
|
71
|
+
|
72
|
+
controller.params.each do |resource, hash|
|
73
|
+
next unless resources.blank? || resources.include?(resource.to_sym)
|
74
|
+
resource_object = instantiate_resource_class(controller, resource)
|
75
|
+
next unless resource_object
|
76
|
+
collection = resource_object.send(method, hash)
|
77
|
+
as_json_options = resource_object.send(:as_json_options, resource_object.send(:klass))
|
78
|
+
options = {:only_ids => (method == 'delete'), :as_json_options => as_json_options}
|
79
|
+
response.deep_merge! collection.to_hash(resource_object.resource_name.to_sym, options)
|
80
|
+
end
|
81
|
+
|
82
|
+
{ :json => response }
|
83
|
+
rescue AuthenticationError
|
84
|
+
{ :status => 401, :json => {} }
|
85
|
+
rescue AuthorizationError
|
86
|
+
{ :status => 403, :json => {} }
|
87
|
+
end
|
88
|
+
|
89
|
+
def instantiate_resource_class(controller, resource)
|
90
|
+
begin
|
91
|
+
"#{resource.to_s.singularize}_resource".classify.constantize.new(controller)
|
92
|
+
rescue NameError
|
93
|
+
begin
|
94
|
+
application_resource_class.new(controller, :resource_name => resource)
|
95
|
+
rescue NameError
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def initialize(controller, options = {})
|
102
|
+
@controller = controller
|
103
|
+
@resource_name = options[:resource_name].to_s if options[:resource_name]
|
104
|
+
|
105
|
+
# try to get klass to raise error early if something is not ok
|
106
|
+
klass unless options[:abstract]
|
107
|
+
end
|
108
|
+
|
109
|
+
def get(ids = 'all')
|
110
|
+
all = ids.to_s == 'all'
|
111
|
+
collection = Collection.new
|
112
|
+
with_records_auth :get, collection, (all ? nil : ids) do
|
113
|
+
records = all ? klass.all : klass.where(:id => ids)
|
114
|
+
records.each do |r|
|
115
|
+
with_record_auth :get, collection, r.id, r do
|
116
|
+
collection.set(r.id, r)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
collection
|
121
|
+
end
|
122
|
+
|
123
|
+
def create(hashes)
|
124
|
+
collection = Collection.new
|
125
|
+
ids = hashes.map { |r| r[:_local_id] }
|
126
|
+
with_records_auth :create, collection, ids do
|
127
|
+
hashes.each do |attrs|
|
128
|
+
local_id = attrs.delete(:_local_id)
|
129
|
+
record = klass.new(filter_params(attrs))
|
130
|
+
record[:_local_id] = local_id
|
131
|
+
with_record_auth :create, collection, local_id, record do
|
132
|
+
record.save
|
133
|
+
set_with_validity_check(collection, local_id, record)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
collection
|
138
|
+
end
|
139
|
+
|
140
|
+
def update(hashes)
|
141
|
+
collection = Collection.new
|
142
|
+
ids = hashes.map { |r| r[:id] }
|
143
|
+
with_records_auth :update, collection, ids do
|
144
|
+
hashes.each do |attrs|
|
145
|
+
attrs.delete(:_local_id)
|
146
|
+
record = klass.where(:id => attrs[:id]).first
|
147
|
+
with_record_auth :update, collection, record.id, record do
|
148
|
+
record.update_attributes(filter_params(attrs))
|
149
|
+
set_with_validity_check(collection, record.id, record)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
collection
|
154
|
+
end
|
155
|
+
|
156
|
+
def delete(ids)
|
157
|
+
collection = Collection.new
|
158
|
+
with_records_auth :delete, collection, ids do
|
159
|
+
ids.each do |id|
|
160
|
+
record = klass.where(:id => id).first
|
161
|
+
with_record_auth :delete, collection, record.id, record do
|
162
|
+
record.destroy
|
163
|
+
set_with_validity_check(collection, record.id, record)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
collection
|
168
|
+
end
|
169
|
+
|
170
|
+
def resource_name
|
171
|
+
@resource_name || self.class.resource_name
|
172
|
+
end
|
173
|
+
|
174
|
+
def as_json_options(klass)
|
175
|
+
{}
|
176
|
+
end
|
177
|
+
|
178
|
+
private
|
179
|
+
delegate :abstract?, :to => "self.class"
|
180
|
+
|
181
|
+
def with_record_auth(action, collection, id, record, &block)
|
182
|
+
with_record_authentication(action, collection, id, record) do
|
183
|
+
with_record_authorization(action, collection, id, record, &block)
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
def with_records_auth(action, collection, ids, &block)
|
188
|
+
with_records_authentication(action, collection, ids) do
|
189
|
+
with_records_authorization(action, collection, ids, &block)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def with_record_authentication(action, collection, id, record)
|
194
|
+
authenticated = self.respond_to?(:authenticate_record) ? authenticate_record(action, record) : true
|
195
|
+
if authenticated
|
196
|
+
yield
|
197
|
+
else
|
198
|
+
collection.errors.set(id, 'not_authenticated')
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def with_record_authorization(action, collection, id, record)
|
203
|
+
authorized = self.respond_to?(:authorize_record) ? authorize_record(action, record) : true
|
204
|
+
if authorized
|
205
|
+
yield
|
206
|
+
else
|
207
|
+
collection.errors.set(id, 'forbidden')
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
def with_records_authentication(action, collection, ids)
|
212
|
+
authenticated = self.respond_to?(:authenticate_records) ? authenticate_records(action, klass) : true
|
213
|
+
if authenticated
|
214
|
+
yield
|
215
|
+
else
|
216
|
+
ids.each do |id|
|
217
|
+
collection.errors.set(id, 'not_authenticated')
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def with_records_authorization(action, collection, ids)
|
223
|
+
authorized = self.respond_to?(:authorize_records) ? authorize_records(action, klass) : true
|
224
|
+
if authorized
|
225
|
+
yield
|
226
|
+
else
|
227
|
+
ids.each do |id|
|
228
|
+
collection.errors.set(id, 'forbidden')
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
def set_with_validity_check(collection, id, record)
|
234
|
+
collection.set(id, record)
|
235
|
+
unless record.errors.empty?
|
236
|
+
collection.errors.set(id, :invalid, record.errors.to_hash)
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
def filter_params(attributes)
|
241
|
+
if self.respond_to?(:params_accessible)
|
242
|
+
filter_params_for(:accessible, attributes)
|
243
|
+
elsif self.respond_to?(:params_protected)
|
244
|
+
filter_params_for(:protected, attributes)
|
245
|
+
else
|
246
|
+
attributes
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def filter_params_for(type, attributes)
|
251
|
+
filter = send("params_#{type}", klass)
|
252
|
+
filter = filter ? filter[resource_name.to_sym] : nil
|
253
|
+
|
254
|
+
if filter
|
255
|
+
attributes.delete_if do |k, v|
|
256
|
+
delete_if = filter.include?(k)
|
257
|
+
type == :accessible ? !delete_if : delete_if
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
attributes
|
262
|
+
end
|
263
|
+
|
264
|
+
|
265
|
+
|
266
|
+
def klass
|
267
|
+
@_klass ||= begin
|
268
|
+
resource_class || (resource_name ? resource_name.to_s.singularize.classify.constantize : nil) ||
|
269
|
+
raise("Could not get resource class, please either set resource_class or resource_name that matches model that you want to use")
|
270
|
+
rescue NameError
|
271
|
+
raise NameError.new("Could not find class matching your resource_name (#{resource_name} - we were looking for #{resource_name.classify})")
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: bulk_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.5
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Piotr Sarnacki
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-05-
|
13
|
+
date: 2011-05-17 00:00:00 +02:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -30,9 +30,9 @@ dependencies:
|
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
32
32
|
requirements:
|
33
|
-
- -
|
33
|
+
- - ~>
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 1.6.0.rc.1
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
38
|
description: Easy integration of rails apps with sproutcore.
|
@@ -45,6 +45,7 @@ extra_rdoc_files: []
|
|
45
45
|
|
46
46
|
files:
|
47
47
|
- app/controllers/bulk/api_controller.rb
|
48
|
+
- lib/bulk/#resource.rb#
|
48
49
|
- lib/bulk/abstract_collection.rb
|
49
50
|
- lib/bulk/collection.rb
|
50
51
|
- lib/bulk/engine.rb
|