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.
Files changed (2) hide show
  1. data/lib/bulk/#resource.rb# +275 -0
  2. 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.3
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-16 00:00:00 +02:00
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: "0"
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