condo 0.0.1
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/LGPL3-LICENSE +165 -0
- data/README.textile +20 -0
- data/Rakefile +40 -0
- data/app/assets/javascripts/condo.js +7 -0
- data/app/assets/javascripts/condo/amazon.js +409 -0
- data/app/assets/javascripts/condo/base64.js +192 -0
- data/app/assets/javascripts/condo/controller.js +162 -0
- data/app/assets/javascripts/condo/google.js +292 -0
- data/app/assets/javascripts/condo/rackspace.js +340 -0
- data/app/assets/javascripts/condo/spark-md5.js +470 -0
- data/app/assets/javascripts/condo/uploader.js +298 -0
- data/lib/condo.rb +267 -0
- data/lib/condo/configuration.rb +129 -0
- data/lib/condo/engine.rb +36 -0
- data/lib/condo/errors.rb +9 -0
- data/lib/condo/strata/amazon_s3.rb +301 -0
- data/lib/condo/strata/google_cloud_storage.rb +306 -0
- data/lib/condo/strata/rackspace_cloud_files.rb +223 -0
- data/lib/condo/version.rb +3 -0
- data/lib/tasks/condo_tasks.rake +4 -0
- data/test/condo_test.rb +27 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +59 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +25 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +15 -0
- metadata +180 -0
@@ -0,0 +1,298 @@
|
|
1
|
+
/**
|
2
|
+
* CoTag Condo
|
3
|
+
* Direct to cloud resumable uploads
|
4
|
+
*
|
5
|
+
* Copyright (c) 2012 CoTag Media.
|
6
|
+
*
|
7
|
+
* @author Stephen von Takach <steve@cotag.me>
|
8
|
+
* @copyright 2012 cotag.me
|
9
|
+
*
|
10
|
+
*
|
11
|
+
* References:
|
12
|
+
* * https://github.com/umdjs/umd
|
13
|
+
* * https://github.com/addyosmani/jquery-plugin-patterns
|
14
|
+
* * http://docs.angularjs.org/api/ng.$http
|
15
|
+
* * http://docs.angularjs.org/api/ng.$q
|
16
|
+
*
|
17
|
+
**/
|
18
|
+
|
19
|
+
(function (factory) {
|
20
|
+
if (typeof define === 'function' && define.amd) {
|
21
|
+
// AMD
|
22
|
+
define('condo_uploader', ['jquery'], factory);
|
23
|
+
} else {
|
24
|
+
// Browser globals
|
25
|
+
window.CondoUploader = factory(jQuery);
|
26
|
+
}
|
27
|
+
}(function ($, undefined) {
|
28
|
+
'use strict';
|
29
|
+
|
30
|
+
var uploads = angular.module('CondoUploader', []);
|
31
|
+
|
32
|
+
|
33
|
+
//
|
34
|
+
// Implements the Condo API
|
35
|
+
//
|
36
|
+
uploads.factory('Condo.Api', ['$http', '$rootScope', '$q', 'Condo.AmazonS3', 'Condo.RackspaceCloudFiles', 'Condo.GoogleCloudStorage', function($http, $rootScope, $q, AmazonS3Condo, RackspaceFilesCondo, GoogleStorageCondo) {
|
37
|
+
|
38
|
+
|
39
|
+
var token = $('meta[name="csrf-token"]').attr('content'),
|
40
|
+
residencies = {
|
41
|
+
AmazonS3: AmazonS3Condo,
|
42
|
+
RackspaceCloudFiles: RackspaceFilesCondo,
|
43
|
+
GoogleCloudStorage: GoogleStorageCondo
|
44
|
+
},
|
45
|
+
|
46
|
+
|
47
|
+
condoConnection = function(api_endpoint, params) {
|
48
|
+
this.endpoint = api_endpoint; // The API mounting point
|
49
|
+
this.params = params; // Custom API parameters
|
50
|
+
|
51
|
+
this.upload_id = null; // The current upload ID
|
52
|
+
this.aborting = false; // Has the user has requested an abort?
|
53
|
+
this.xhr = null; // Any active cloud file xhr requests
|
54
|
+
};
|
55
|
+
|
56
|
+
|
57
|
+
$http.defaults.headers = {};
|
58
|
+
$http.defaults.headers['common'] = {'X-Requested-With': 'XMLHttpRequest'};
|
59
|
+
$http.defaults.headers['post'] = {'X-CSRF-Token': token};
|
60
|
+
$http.defaults.headers['put'] = {'X-CSRF-Token': token};
|
61
|
+
$http.defaults.headers['delete'] = {'X-CSRF-Token': token};
|
62
|
+
|
63
|
+
condoConnection.prototype = {
|
64
|
+
|
65
|
+
|
66
|
+
//
|
67
|
+
// Creates an entry in the database for the requested file and returns the upload signature
|
68
|
+
// If an entry already exists it returns a parts request signature for resumable uploads
|
69
|
+
//
|
70
|
+
create: function(options) { // file_id: 123, options: {}
|
71
|
+
var self = this;
|
72
|
+
options = options || {};
|
73
|
+
this.aborting = false;
|
74
|
+
|
75
|
+
if(!!options['file_id'])
|
76
|
+
this.params['file_id'] = options['file_id'];
|
77
|
+
|
78
|
+
if(!!options['parameters'])
|
79
|
+
this.params['parameters'] = options['parameters']; // We may be requesting the next set of parts
|
80
|
+
|
81
|
+
return $http({
|
82
|
+
method: 'POST',
|
83
|
+
url: this.endpoint,
|
84
|
+
params: this.params
|
85
|
+
}).then(function(result){
|
86
|
+
result = result.data;
|
87
|
+
self.upload_id = result.upload_id; // Extract the upload id from the results
|
88
|
+
|
89
|
+
if (!self.aborting)
|
90
|
+
return result;
|
91
|
+
else
|
92
|
+
return $q.reject(undefined);
|
93
|
+
}, function(reason) {
|
94
|
+
return $q.reject('upload error');
|
95
|
+
});
|
96
|
+
},
|
97
|
+
|
98
|
+
|
99
|
+
//
|
100
|
+
// This requests a chunk signature
|
101
|
+
// Only used for resumable uploads
|
102
|
+
//
|
103
|
+
edit: function(part_number, part_id) {
|
104
|
+
var self = this;
|
105
|
+
this.aborting = false;
|
106
|
+
|
107
|
+
return $http({
|
108
|
+
method: 'GET',
|
109
|
+
url: this.endpoint + '/' + this.upload_id + '/edit',
|
110
|
+
params: {
|
111
|
+
part: part_number,
|
112
|
+
file_id: part_id
|
113
|
+
}
|
114
|
+
}).then(function(result){
|
115
|
+
if (!self.aborting)
|
116
|
+
return result.data;
|
117
|
+
else
|
118
|
+
return $q.reject(undefined);
|
119
|
+
}, function(reason) {
|
120
|
+
return $q.reject('upload error');
|
121
|
+
});
|
122
|
+
},
|
123
|
+
|
124
|
+
|
125
|
+
//
|
126
|
+
// If resumable id is present the upload is updated
|
127
|
+
// Otherwise the upload deemed complete
|
128
|
+
//
|
129
|
+
update: function(params) { // optional parameters (resumable_id, file_id and part)
|
130
|
+
var self = this;
|
131
|
+
|
132
|
+
this.aborting = false;
|
133
|
+
params = params || {};
|
134
|
+
|
135
|
+
return $http({
|
136
|
+
method: 'PUT',
|
137
|
+
url: this.endpoint + '/' + this.upload_id,
|
138
|
+
params: params
|
139
|
+
}).then(function(result){
|
140
|
+
if (!self.aborting)
|
141
|
+
return result.data;
|
142
|
+
else
|
143
|
+
return $q.reject(undefined);
|
144
|
+
}, function(reason) {
|
145
|
+
if (reason.status == 401 && params.resumable_id == undefined) {
|
146
|
+
return ''; // User may have paused upload as put was being sent. We should let this through just to update the UI
|
147
|
+
} else
|
148
|
+
return $q.reject('upload error');
|
149
|
+
});
|
150
|
+
},
|
151
|
+
|
152
|
+
|
153
|
+
//
|
154
|
+
// Cancels a resumable upload
|
155
|
+
// The actual destruction of the file is handled on the server side as we can't trust the client to do this
|
156
|
+
// We don't care if this succeeds as the back-end will destroy the file eventually anyway.
|
157
|
+
//
|
158
|
+
destroy: function() {
|
159
|
+
return $http({
|
160
|
+
method: 'DELETE',
|
161
|
+
url: this.endpoint + '/' + this.upload_id
|
162
|
+
});
|
163
|
+
},
|
164
|
+
|
165
|
+
|
166
|
+
|
167
|
+
//
|
168
|
+
// Provides a promise for any request this is what communicated with the cloud storage servers
|
169
|
+
//
|
170
|
+
process_request: function(signature, progress_callback) {
|
171
|
+
var self = this,
|
172
|
+
result = $q.defer(),
|
173
|
+
params = {
|
174
|
+
url: signature.signature.url,
|
175
|
+
type: signature.signature.verb,
|
176
|
+
headers: signature.signature.headers,
|
177
|
+
processData: false,
|
178
|
+
success: function(response, textStatus, jqXHR) {
|
179
|
+
self.xhr = null;
|
180
|
+
result.resolve(response);
|
181
|
+
},
|
182
|
+
error: function(jqXHR, textStatus, errorThrown) {
|
183
|
+
self.xhr = null;
|
184
|
+
if (!self.aborting)
|
185
|
+
result.reject('upload error');
|
186
|
+
else
|
187
|
+
result.reject(undefined);
|
188
|
+
},
|
189
|
+
complete: function(jqXHR, textStatus) {
|
190
|
+
if(!$rootScope.$$phase) {
|
191
|
+
$rootScope.$apply(); // This triggers the promise response
|
192
|
+
}
|
193
|
+
}
|
194
|
+
};
|
195
|
+
|
196
|
+
this.aborting = false;
|
197
|
+
|
198
|
+
if (!!self.xhr) {
|
199
|
+
result.reject('request in progress'); // This is awesome
|
200
|
+
return result.promise;
|
201
|
+
}
|
202
|
+
|
203
|
+
if(!!signature.data){
|
204
|
+
params['data'] = signature.data;
|
205
|
+
}
|
206
|
+
|
207
|
+
if(!!progress_callback) {
|
208
|
+
params['xhr'] = function() {
|
209
|
+
var xhr = $.ajaxSettings.xhr();
|
210
|
+
if(!!xhr.upload){
|
211
|
+
xhr.upload.addEventListener('progress', function(e) {
|
212
|
+
if (e.lengthComputable) {
|
213
|
+
var phase = $rootScope.$$phase;
|
214
|
+
if(phase == '$apply' || phase == '$digest') {
|
215
|
+
progress_callback(e.loaded);
|
216
|
+
} else {
|
217
|
+
$rootScope.$apply(function(){
|
218
|
+
progress_callback(e.loaded);
|
219
|
+
});
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}, false);
|
223
|
+
}
|
224
|
+
return xhr;
|
225
|
+
};
|
226
|
+
}
|
227
|
+
|
228
|
+
this.xhr = $.ajax(params);
|
229
|
+
|
230
|
+
return result.promise;
|
231
|
+
},
|
232
|
+
|
233
|
+
|
234
|
+
//
|
235
|
+
// Will trigger the error call-back of the xhr object
|
236
|
+
//
|
237
|
+
abort: function() {
|
238
|
+
this.aborting = true;
|
239
|
+
if(!!this.xhr) {
|
240
|
+
this.xhr.abort();
|
241
|
+
}
|
242
|
+
}
|
243
|
+
};
|
244
|
+
|
245
|
+
return {
|
246
|
+
//
|
247
|
+
// Used to determine what upload strategy to use (Amazon, Google, etc)
|
248
|
+
//
|
249
|
+
check_provider: function(api_endpoint, the_file, params) {
|
250
|
+
params = params || {};
|
251
|
+
params['file_size'] = the_file.size;
|
252
|
+
params['file_name'] = the_file.name;
|
253
|
+
|
254
|
+
return $http({
|
255
|
+
method: 'GET',
|
256
|
+
url: api_endpoint + '/new',
|
257
|
+
params: params
|
258
|
+
}).then(function(result){
|
259
|
+
if(!!residencies[result.data.residence]) {
|
260
|
+
|
261
|
+
var api = new condoConnection(api_endpoint, params);
|
262
|
+
|
263
|
+
//
|
264
|
+
// TODO:: Check if a file is already in the list and reject if it is
|
265
|
+
//
|
266
|
+
return residencies[result.data.residence].new_upload(api, the_file); // return the instantiated provider
|
267
|
+
|
268
|
+
} else {
|
269
|
+
return $q.reject({
|
270
|
+
reason: 'storage provider not found'
|
271
|
+
});
|
272
|
+
}
|
273
|
+
}, function(reason) {
|
274
|
+
if(reason.status == 406) {
|
275
|
+
return $q.reject({
|
276
|
+
reason: 'file not accepted',
|
277
|
+
details: reason.data,
|
278
|
+
file: the_file
|
279
|
+
});
|
280
|
+
} else {
|
281
|
+
return $q.reject({
|
282
|
+
reason: 'server error',
|
283
|
+
file: the_file
|
284
|
+
});
|
285
|
+
}
|
286
|
+
});
|
287
|
+
}
|
288
|
+
};
|
289
|
+
}]);
|
290
|
+
|
291
|
+
|
292
|
+
|
293
|
+
//
|
294
|
+
// Anonymous function return
|
295
|
+
//
|
296
|
+
return uploads;
|
297
|
+
|
298
|
+
}));
|
data/lib/condo.rb
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'condo/engine'
|
2
|
+
require 'condo/errors'
|
3
|
+
require 'condo/configuration'
|
4
|
+
|
5
|
+
|
6
|
+
#Dir[File.join('condo', 'strata', '*.rb')].each do |file| # Using autoload_paths now
|
7
|
+
# require file[0..-4] # Removes the .rb ext name
|
8
|
+
#end
|
9
|
+
|
10
|
+
|
11
|
+
module Condo
|
12
|
+
|
13
|
+
#
|
14
|
+
# TODO:: Simplify the parameters passed in
|
15
|
+
# Object options should be set at the application level
|
16
|
+
# The application can set these based on the custom params.
|
17
|
+
# Have an instance member that holds the parameter set: @upload
|
18
|
+
#
|
19
|
+
def self.included(base)
|
20
|
+
base.class_eval do
|
21
|
+
|
22
|
+
|
23
|
+
def new
|
24
|
+
#
|
25
|
+
# Returns the provider that will be used for this file upload
|
26
|
+
#
|
27
|
+
resident = current_resident
|
28
|
+
|
29
|
+
@upload ||= {}
|
30
|
+
@upload[:file_size] = params[:file_size].to_i
|
31
|
+
@upload[:file_name] = (instance_eval &@@callbacks[:sanitize_filename])
|
32
|
+
|
33
|
+
valid, errors = instance_eval &@@callbacks[:pre_validation] # Ensure the upload request is valid before uploading
|
34
|
+
|
35
|
+
if !!valid
|
36
|
+
set_residence(nil, {:resident => resident, :params => @upload}) if condo_config.dynamic_provider_present?(@@namespace)
|
37
|
+
residence = current_residence
|
38
|
+
|
39
|
+
render :json => {:residence => residence.name}
|
40
|
+
|
41
|
+
elsif errors.is_a? Hash
|
42
|
+
render :json => errors, :status => :not_acceptable
|
43
|
+
else
|
44
|
+
render :nothing => true, :status => :not_acceptable
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def create
|
49
|
+
#
|
50
|
+
# Check for existing upload or create a new one
|
51
|
+
# => mutually exclusive so can send back either the parts signature from show or a bucket creation signature and the upload_id
|
52
|
+
#
|
53
|
+
resident = current_resident
|
54
|
+
|
55
|
+
@upload = {}
|
56
|
+
@upload[:file_size] = params[:file_size].to_i
|
57
|
+
@upload[:file_id] = params[:file_id]
|
58
|
+
@upload[:file_name] = (instance_eval &@@callbacks[:sanitize_filename])
|
59
|
+
|
60
|
+
upload = condo_backend.check_exists({
|
61
|
+
:user_id => resident,
|
62
|
+
:file_name => @upload[:file_name],
|
63
|
+
:file_size => @upload[:file_size],
|
64
|
+
:file_id => @upload[:file_id]
|
65
|
+
})
|
66
|
+
|
67
|
+
if upload.present?
|
68
|
+
residence = set_residence(upload.provider_name, {
|
69
|
+
:provider_location => upload.provider_location,
|
70
|
+
:upload => upload
|
71
|
+
})
|
72
|
+
|
73
|
+
#
|
74
|
+
# Return the parts or direct upload sig
|
75
|
+
#
|
76
|
+
request = nil
|
77
|
+
if upload.resumable_id.present? && upload.resumable
|
78
|
+
upload.object_options[:parameters] = {} || params[:parameters] # May need to request the next set of parts
|
79
|
+
request = residence.get_parts({
|
80
|
+
:bucket_name => upload.bucket_name,
|
81
|
+
:object_key => upload.object_key,
|
82
|
+
:object_options => upload.object_options,
|
83
|
+
:resumable_id => upload.resumable_id
|
84
|
+
})
|
85
|
+
else
|
86
|
+
request = residence.new_upload({
|
87
|
+
:bucket_name => upload.bucket_name,
|
88
|
+
:object_key => upload.object_key,
|
89
|
+
:object_options => upload.object_options,
|
90
|
+
:file_size => upload.file_size,
|
91
|
+
:file_id => upload.file_id
|
92
|
+
})
|
93
|
+
end
|
94
|
+
|
95
|
+
render :json => request.merge(:upload_id => upload.id, :residence => residence.name)
|
96
|
+
else
|
97
|
+
#
|
98
|
+
# Create a new upload
|
99
|
+
#
|
100
|
+
valid, errors = instance_eval &@@callbacks[:pre_validation] # Ensure the upload request is valid before uploading
|
101
|
+
|
102
|
+
|
103
|
+
if !!valid
|
104
|
+
set_residence(nil, {:resident => resident, :params => @upload}) if condo_config.dynamic_provider_present?(@@namespace)
|
105
|
+
residence = current_residence
|
106
|
+
|
107
|
+
#
|
108
|
+
# Build the request
|
109
|
+
#
|
110
|
+
request = residence.new_upload(@upload.merge!({
|
111
|
+
:bucket_name => (instance_eval &@@callbacks[:bucket_name]), # Allow the application to define a custom bucket name
|
112
|
+
:object_key => (instance_eval &@@callbacks[:object_key]), # The object key should also be generated by the application
|
113
|
+
:object_options => (instance_eval &@@callbacks[:object_options]) # Do we want to mess with any of the options?
|
114
|
+
}))
|
115
|
+
resumable = request[:type] == :chunked_upload
|
116
|
+
|
117
|
+
#
|
118
|
+
# Save a reference to this upload in the database
|
119
|
+
# => This should throw an error on failure
|
120
|
+
#
|
121
|
+
upload = condo_backend.add_entry(@upload.merge!({:user_id => resident, :provider_name => residence.name, :provider_location => residence.location, :resumable => resumable}))
|
122
|
+
render :json => request.merge!(:upload_id => upload.id, :residence => residence.name)
|
123
|
+
|
124
|
+
elsif errors.is_a? Hash
|
125
|
+
render :json => errors, :status => :not_acceptable
|
126
|
+
else
|
127
|
+
render :nothing => true, :status => :not_acceptable
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
#
|
134
|
+
# Authorisation check all of these
|
135
|
+
#
|
136
|
+
def edit
|
137
|
+
#
|
138
|
+
# Get the signature for parts + final commit
|
139
|
+
#
|
140
|
+
upload = current_upload
|
141
|
+
|
142
|
+
if upload.resumable_id.present? && upload.resumable
|
143
|
+
residence = set_residence(upload.provider_name, {:location => upload.provider_location, :upload => upload})
|
144
|
+
|
145
|
+
request = residence.set_part({
|
146
|
+
:bucket_name => upload.bucket_name,
|
147
|
+
:object_key => upload.object_key,
|
148
|
+
:object_options => upload.object_options,
|
149
|
+
:resumable_id => upload.resumable_id,
|
150
|
+
:part => params[:part], # part may be called 'finish' for commit signature
|
151
|
+
:file_id => params[:file_id]
|
152
|
+
})
|
153
|
+
|
154
|
+
render :json => request.merge!(:upload_id => upload.id)
|
155
|
+
else
|
156
|
+
render :nothing => true, :status => :not_acceptable
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
|
161
|
+
def update
|
162
|
+
#
|
163
|
+
# Provide the upload id after creating a resumable upload (may not be completed)
|
164
|
+
# => We then provide the first part signature
|
165
|
+
#
|
166
|
+
# OR
|
167
|
+
#
|
168
|
+
# Complete an upload
|
169
|
+
#
|
170
|
+
if params[:resumable_id]
|
171
|
+
upload = current_upload
|
172
|
+
if upload.resumable
|
173
|
+
@current_upload = upload.update_entry :resumable_id => params[:resumable_id]
|
174
|
+
edit
|
175
|
+
else
|
176
|
+
render :nothing => true, :status => :not_acceptable
|
177
|
+
end
|
178
|
+
else
|
179
|
+
response = instance_exec current_upload, &@@callbacks[:upload_complete]
|
180
|
+
if !!response
|
181
|
+
current_upload.remove_entry
|
182
|
+
render :nothing => true
|
183
|
+
else
|
184
|
+
render :nothing => true, :status => :not_acceptable
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
def destroy
|
191
|
+
#
|
192
|
+
# Delete the file from the cloud system - the client is not responsible for this
|
193
|
+
#
|
194
|
+
response = instance_exec current_upload, &@@callbacks[:destroy_upload]
|
195
|
+
if !!response
|
196
|
+
current_upload.remove_entry
|
197
|
+
render :nothing => true
|
198
|
+
else
|
199
|
+
render :nothing => true, :status => :not_acceptable
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
|
204
|
+
protected
|
205
|
+
|
206
|
+
|
207
|
+
#
|
208
|
+
# A before filter can be used to select the cloud provider for the current user
|
209
|
+
# Otherwise the dynamic residence can be used when users are define their own storage locations
|
210
|
+
#
|
211
|
+
def set_residence(name, options = {})
|
212
|
+
options[:namespace] = @@namespace
|
213
|
+
@current_residence = condo_config.set_residence(name, options)
|
214
|
+
end
|
215
|
+
|
216
|
+
def current_residence
|
217
|
+
@current_residence ||= condo_config.residencies[0]
|
218
|
+
end
|
219
|
+
|
220
|
+
def current_upload
|
221
|
+
@current_upload ||= condo_backend.check_exists({:user_id => current_resident, :upload_id => (params[:upload_id] || params[:id])}).tap do |object| #current_residence.name && current_residence.location && resident.id.exists?
|
222
|
+
raise Condo::Errors::NotYourPlace unless object.present?
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def current_resident
|
227
|
+
@current_resident ||= (instance_eval &@@callbacks[:resident_id]).tap do |object| # instance_exec for params
|
228
|
+
raise Condo::Errors::LostTheKeys unless object.present?
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
def condo_backend
|
233
|
+
Condo::Store
|
234
|
+
end
|
235
|
+
|
236
|
+
def condo_config
|
237
|
+
Condo::Configuration.instance
|
238
|
+
end
|
239
|
+
|
240
|
+
|
241
|
+
#
|
242
|
+
# Defines the default callbacks
|
243
|
+
#
|
244
|
+
(@@callbacks ||= {}).merge! Condo::Configuration.callbacks
|
245
|
+
@@namespace ||= :global
|
246
|
+
|
247
|
+
|
248
|
+
def self.set_callback(name, callback = nil, &block)
|
249
|
+
if callback.is_a?(Proc)
|
250
|
+
@@callbacks[name.to_sym] = callback
|
251
|
+
elsif block.present?
|
252
|
+
@@callbacks[name.to_sym] = block
|
253
|
+
else
|
254
|
+
raise ArgumentError, 'Condo callbacks must be defined with a Proc or Proc (lamba) object present'
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
|
259
|
+
def self.set_namespace(name)
|
260
|
+
@@namespace = name.to_sym
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
|
267
|
+
end
|