condo 1.0.6 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.textile +19 -32
- data/lib/condo.rb +124 -127
- data/lib/condo/configuration.rb +41 -76
- data/lib/condo/engine.rb +32 -39
- data/lib/condo/errors.rb +6 -8
- data/lib/condo/strata/amazon_s3.rb +246 -294
- data/lib/condo/strata/google_cloud_storage.rb +238 -272
- data/lib/condo/strata/open_stack_swift.rb +251 -0
- data/lib/condo/version.rb +1 -1
- metadata +31 -96
- data/app/assets/javascripts/condo.js +0 -9
- data/app/assets/javascripts/condo/amazon.js +0 -403
- data/app/assets/javascripts/condo/condo.js +0 -184
- data/app/assets/javascripts/condo/config.js +0 -69
- data/app/assets/javascripts/condo/google.js +0 -338
- data/app/assets/javascripts/condo/md5/hash.worker.emulator.js +0 -23
- data/app/assets/javascripts/condo/md5/hash.worker.js +0 -11
- data/app/assets/javascripts/condo/md5/hasher.js +0 -119
- data/app/assets/javascripts/condo/md5/spark-md5.js +0 -599
- data/app/assets/javascripts/condo/rackspace.js +0 -326
- data/app/assets/javascripts/condo/services/abstract-md5.js.erb +0 -86
- data/app/assets/javascripts/condo/services/base64.js +0 -184
- data/app/assets/javascripts/condo/services/broadcaster.js +0 -26
- data/app/assets/javascripts/condo/services/uploader.js +0 -302
- data/app/assets/javascripts/core/core.js +0 -4
- data/app/assets/javascripts/core/services/1-safe-apply.js +0 -17
- data/app/assets/javascripts/core/services/2-messaging.js +0 -171
- data/lib/condo/strata/rackspace_cloud_files.rb +0 -245
- data/test/condo_test.rb +0 -27
- data/test/dummy/README.rdoc +0 -261
- data/test/dummy/Rakefile +0 -7
- data/test/dummy/app/assets/javascripts/application.js +0 -15
- data/test/dummy/app/assets/stylesheets/application.css +0 -13
- data/test/dummy/app/controllers/application_controller.rb +0 -3
- data/test/dummy/app/helpers/application_helper.rb +0 -2
- data/test/dummy/app/views/layouts/application.html.erb +0 -14
- data/test/dummy/config.ru +0 -4
- data/test/dummy/config/application.rb +0 -59
- data/test/dummy/config/boot.rb +0 -10
- data/test/dummy/config/database.yml +0 -25
- data/test/dummy/config/environment.rb +0 -5
- data/test/dummy/config/environments/development.rb +0 -37
- data/test/dummy/config/environments/production.rb +0 -67
- data/test/dummy/config/environments/test.rb +0 -37
- data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
- data/test/dummy/config/initializers/inflections.rb +0 -15
- data/test/dummy/config/initializers/mime_types.rb +0 -5
- data/test/dummy/config/initializers/secret_token.rb +0 -7
- data/test/dummy/config/initializers/session_store.rb +0 -8
- data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
- data/test/dummy/config/locales/en.yml +0 -5
- data/test/dummy/config/routes.rb +0 -58
- data/test/dummy/public/404.html +0 -26
- data/test/dummy/public/422.html +0 -26
- data/test/dummy/public/500.html +0 -25
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +0 -6
- data/test/integration/navigation_test.rb +0 -10
- data/test/test_helper.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 92cc7003ecce7499b7c92bc3aed7facde40cf79d
|
4
|
+
data.tar.gz: a4b4e8d9a93bace70e3c236d13d23d97116dd44e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c45dcf3f6aed9abe71705db52d83ffd3731c105259d10d43672f2deff33d2e19bcb6e3d7e77a7b47e373ef882ddf3ffc9949661363460a103f25356787e402cc
|
7
|
+
data.tar.gz: bbd682e45ad4a9f651d77abd5cae57e14b163ca476d38cc89c78ff8b3678c35ab37a9c6df36a601e2f28339a605ff67958a2bef7faf82d59f661aad6a6ba3ebd
|
data/README.textile
CHANGED
@@ -3,6 +3,7 @@ h1. Condominios aka Condo
|
|
3
3
|
A "Rails plugin":http://guides.rubyonrails.org/plugins.html and "AngularJS application":http://angularjs.org/ that makes direct uploads to multiple cloud storage providers easy.
|
4
4
|
Only supports "XMLHttpRequest Level 2":http://en.wikipedia.org/wiki/XMLHttpRequest capable browsers and cloud providers that have a "RESTful API":http://en.wikipedia.org/wiki/Representational_state_transfer with "CORS":http://en.wikipedia.org/wiki/Cross-origin_resource_sharing support.
|
5
5
|
|
6
|
+
|
6
7
|
Why compromise?
|
7
8
|
|
8
9
|
Get started now: @gem install condo@ or checkout the "example application":https://github.com/cotag/condo_example
|
@@ -22,18 +23,23 @@ The API is RESTful, providing an abstraction layer and signed URLs that can be u
|
|
22
23
|
The main advantages are:
|
23
24
|
* Off-loads processing to client machines
|
24
25
|
* Better guarantees against upload corruption
|
25
|
-
** file hashing on the client side
|
26
|
-
* Upload results are guaranteed
|
26
|
+
** file hashing on the client side
|
27
|
+
* Upload results are guaranteed
|
27
28
|
** user is always aware of any failures in the process
|
28
29
|
* Detailed progress and control over the upload
|
29
30
|
|
30
31
|
This has numerous advantages over traditional Form Data style post uploads too.
|
31
32
|
* Progress bars
|
32
33
|
* Resumability when uploading large files
|
34
|
+
* Optional parallel uploads (multiple parts of the file simultaneously)
|
33
35
|
|
34
36
|
|
35
37
|
Support for all major browsers
|
36
|
-
*
|
38
|
+
* Chrome
|
39
|
+
* Firefox
|
40
|
+
* Safari
|
41
|
+
* Opera
|
42
|
+
* IE10+
|
37
43
|
|
38
44
|
|
39
45
|
h2. Usage
|
@@ -88,12 +94,11 @@ In an initialiser do the following:
|
|
88
94
|
|
89
95
|
<pre><code class="ruby">
|
90
96
|
Condo::Configuration.add_residence(:AmazonS3, {
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
# Controller must have the following line 'set_namespace :admin_resident'
|
97
|
+
:access_id => ENV['S3_KEY'],
|
98
|
+
:secret_key => ENV['S3_SECRET']
|
99
|
+
# :location => 'us-west-1' # or 'ap-southeast-1' etc (see http://docs.amazonwebservices.com/general/latest/gr/rande.html#s3_region)
|
100
|
+
# Defaults to 'us-east-1' or US Standard - not required for Google
|
101
|
+
# :namespace => :admin_resident # Allows you to assign different defaults to different controllers or users etc
|
97
102
|
})
|
98
103
|
|
99
104
|
</code></pre>
|
@@ -102,32 +107,14 @@ The first residence to be defined in a namespace will be the default. To change
|
|
102
107
|
Currently available residencies:
|
103
108
|
* :AmazonS3
|
104
109
|
* :GoogleCloudStorage
|
105
|
-
* :RackspaceCloudFiles
|
106
|
-
|
110
|
+
* :RackspaceCloudFiles (Works with Swift left as rackspace for backwards compatibility)
|
107
111
|
|
108
|
-
You can also define a dynamic residence each request (maybe clients provided you with access information for their storage provider)
|
109
112
|
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
:secret_key => user.s3_secret,
|
114
|
-
:dynamic => true # Otherwise the same as add_residence
|
115
|
-
});
|
116
|
-
|
117
|
-
|
118
|
-
</code></pre>
|
113
|
+
Note:: There is also a callback to dynamically set storage provider. Which is useful if your users use:
|
114
|
+
* Their own bucket
|
115
|
+
* Different cloud providers etc
|
119
116
|
|
120
117
|
|
121
118
|
h3. Callbacks
|
122
119
|
|
123
|
-
These are pretty well defined "here":https://github.com/cotag/
|
124
|
-
|
125
|
-
|
126
|
-
h2. TODO::
|
127
|
-
|
128
|
-
# Write tests... So many tests
|
129
|
-
# Create a wiki describing things in more detail
|
130
|
-
# Implement API for more residencies
|
131
|
-
# Sign other useful requests (bucket listings with search etc)
|
132
|
-
#* For Dropbox or Megaupload style applications
|
133
|
-
|
120
|
+
These are pretty well defined "here":https://github.com/cotag/Condominios/blob/master/lib/condo/configuration.rb
|
data/lib/condo.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fog'
|
1
4
|
require 'condo/engine'
|
2
5
|
require 'condo/errors'
|
3
6
|
require 'condo/configuration'
|
@@ -6,14 +9,12 @@ require 'condo/configuration'
|
|
6
9
|
module Condo
|
7
10
|
def self.included(base)
|
8
11
|
base.class_eval do
|
9
|
-
|
10
|
-
|
12
|
+
|
13
|
+
|
11
14
|
def new
|
12
|
-
#
|
13
15
|
# Returns the provider that will be used for this file upload
|
14
16
|
resident = current_resident
|
15
17
|
|
16
|
-
#
|
17
18
|
# Ensure parameters are correct
|
18
19
|
params.require(:file_size)
|
19
20
|
params.require(:file_name)
|
@@ -23,30 +24,25 @@ module Condo
|
|
23
24
|
file_name: @@callbacks[:sanitize_filename].call(permitted[:file_name])
|
24
25
|
}
|
25
26
|
@upload[:file_path] = @@callbacks[:sanitize_filepath].call(permitted[:file_path]) if permitted[:file_path]
|
26
|
-
|
27
|
+
|
27
28
|
valid, errors = instance_exec(@upload, &@@callbacks[:pre_validation]) # Ensure the upload request is valid before uploading
|
28
29
|
|
29
|
-
if
|
30
|
-
set_residence(nil, {:resident => resident, :params => @upload}) if condo_config.dynamic_provider_present?(@@namespace)
|
30
|
+
if valid
|
31
31
|
residence = current_residence
|
32
|
-
|
33
|
-
render :
|
34
|
-
|
32
|
+
|
33
|
+
render json: {residence: residence.name}
|
35
34
|
elsif errors.is_a? Hash
|
36
|
-
render :
|
35
|
+
render json: errors, status: :not_acceptable
|
37
36
|
else
|
38
|
-
render :
|
37
|
+
render nothing: true, status: :not_acceptable
|
39
38
|
end
|
40
39
|
end
|
41
|
-
|
40
|
+
|
42
41
|
def create
|
43
|
-
#
|
44
42
|
# Check for existing upload or create a new one
|
45
43
|
# => mutually exclusive so can send back either the parts signature from show or a bucket creation signature and the upload_id
|
46
|
-
#
|
47
44
|
resident = current_resident
|
48
|
-
|
49
|
-
#
|
45
|
+
|
50
46
|
# Ensure parameters are correct
|
51
47
|
params.require(:file_size)
|
52
48
|
params.require(:file_name)
|
@@ -59,54 +55,48 @@ module Condo
|
|
59
55
|
user_id: resident
|
60
56
|
}
|
61
57
|
@upload[:file_path] = @@callbacks[:sanitize_filepath].call(permitted[:file_path]) if permitted[:file_path]
|
62
|
-
|
63
|
-
#
|
58
|
+
|
64
59
|
# Check for existing uploads
|
65
60
|
upload = condo_backend.check_exists(@upload)
|
66
|
-
|
61
|
+
|
67
62
|
if upload.present?
|
68
|
-
residence =
|
69
|
-
|
70
|
-
:upload => upload
|
71
|
-
})
|
72
|
-
|
73
|
-
#
|
63
|
+
residence = current_residence(upload)
|
64
|
+
|
74
65
|
# Return the parts or direct upload sig
|
75
|
-
#
|
76
66
|
request = nil
|
77
67
|
if upload.resumable_id.present? && upload.resumable
|
78
68
|
request = residence.get_parts({
|
79
|
-
:
|
80
|
-
:
|
81
|
-
:
|
82
|
-
:
|
83
|
-
:
|
69
|
+
bucket_name: upload.bucket_name,
|
70
|
+
object_key: upload.object_key,
|
71
|
+
object_options: upload.object_options,
|
72
|
+
file_size: upload.file_size,
|
73
|
+
resumable_id: upload.resumable_id
|
84
74
|
})
|
75
|
+
|
76
|
+
request[:part_list] = upload.part_list || []
|
77
|
+
request[:part_data] = upload.part_data if upload.part_data
|
85
78
|
else
|
86
79
|
request = residence.new_upload({
|
87
|
-
:
|
88
|
-
:
|
89
|
-
:
|
90
|
-
:
|
91
|
-
:
|
80
|
+
bucket_name: upload.bucket_name,
|
81
|
+
object_key: upload.object_key,
|
82
|
+
object_options: upload.object_options,
|
83
|
+
file_size: upload.file_size,
|
84
|
+
file_id: upload.file_id
|
92
85
|
})
|
93
86
|
end
|
94
87
|
|
95
|
-
render :
|
88
|
+
render json: request.merge!({
|
89
|
+
upload_id: upload.id,
|
90
|
+
residence: residence.name
|
91
|
+
})
|
96
92
|
else
|
97
|
-
#
|
98
93
|
# Create a new upload
|
99
|
-
#
|
100
94
|
valid, errors = instance_exec(@upload, &@@callbacks[:pre_validation]) # Ensure the upload request is valid before uploading
|
101
|
-
|
102
|
-
|
95
|
+
|
103
96
|
if valid
|
104
|
-
set_residence(nil, {:resident => resident, :params => @upload}) if condo_config.dynamic_provider_present?(@@namespace)
|
105
97
|
residence = current_residence
|
106
|
-
|
107
|
-
#
|
98
|
+
|
108
99
|
# Build the request
|
109
|
-
#
|
110
100
|
@upload.merge!({
|
111
101
|
bucket_name: (instance_eval &@@callbacks[:bucket_name]), # Allow the application to define a custom bucket name
|
112
102
|
object_key: instance_exec(@upload, &@@callbacks[:object_key]), # The object key should also be generated by the application
|
@@ -114,142 +104,155 @@ module Condo
|
|
114
104
|
})
|
115
105
|
request = residence.new_upload(@upload)
|
116
106
|
resumable = request[:type] == :chunked_upload
|
117
|
-
|
118
|
-
#
|
107
|
+
|
119
108
|
# Save a reference to this upload in the database
|
120
109
|
# => This should throw an error on failure
|
121
|
-
|
122
|
-
|
123
|
-
render :json => request.merge!(:upload_id => upload.id, :residence => residence.name)
|
110
|
+
upload = condo_backend.add_entry(@upload.merge!({provider_name: residence.name, provider_location: residence.location, resumable: resumable}))
|
111
|
+
render json: request.merge!(upload_id: upload.id, residence: residence.name)
|
124
112
|
|
125
113
|
elsif errors.is_a? Hash
|
126
|
-
render :
|
114
|
+
render json: errors, status: :not_acceptable
|
127
115
|
else
|
128
|
-
render :
|
116
|
+
render nothing: true, status: :not_acceptable
|
129
117
|
end
|
130
118
|
end
|
131
119
|
end
|
132
|
-
|
133
|
-
|
134
|
-
#
|
120
|
+
|
135
121
|
# Authorization check all of these
|
136
|
-
#
|
137
122
|
def edit
|
138
|
-
#
|
139
123
|
# Get the signature for parts + final commit
|
140
|
-
#
|
141
124
|
upload = current_upload
|
125
|
+
|
142
126
|
params.require(:part)
|
143
127
|
safe_params = params.permit(:part, :file_id)
|
144
|
-
|
128
|
+
|
145
129
|
if upload.resumable_id.present? && upload.resumable
|
146
|
-
residence =
|
147
|
-
|
130
|
+
residence = current_residence(upload)
|
131
|
+
|
148
132
|
request = residence.set_part({
|
149
|
-
:
|
150
|
-
:
|
151
|
-
:
|
152
|
-
:
|
153
|
-
|
154
|
-
:
|
155
|
-
:
|
133
|
+
bucket_name: upload.bucket_name,
|
134
|
+
object_key: upload.object_key,
|
135
|
+
object_options: upload.object_options,
|
136
|
+
resumable_id: upload.resumable_id,
|
137
|
+
# part may be called 'finish' for commit signature
|
138
|
+
part: safe_params[:part],
|
139
|
+
file_size: upload.file_size,
|
140
|
+
file_id: safe_params[:file_id]
|
156
141
|
})
|
157
|
-
|
158
|
-
render :
|
142
|
+
|
143
|
+
render json: request.merge!(upload_id: upload.id)
|
159
144
|
else
|
160
|
-
render :
|
145
|
+
render nothing: true, status: :not_acceptable
|
161
146
|
end
|
162
147
|
end
|
163
|
-
|
164
|
-
|
148
|
+
|
149
|
+
|
165
150
|
def update
|
166
|
-
#
|
167
151
|
# Provide the upload id after creating a resumable upload (may not be completed)
|
168
152
|
# => We then provide the first part signature
|
169
153
|
#
|
170
154
|
# OR
|
171
155
|
#
|
172
156
|
# Complete an upload
|
173
|
-
|
174
|
-
|
175
|
-
|
157
|
+
upload = current_upload
|
158
|
+
|
159
|
+
safe = params.permit(:resumable_id, {part_list: []}, {part_data: [
|
160
|
+
:md5,
|
161
|
+
:size_bytes,
|
162
|
+
:path,
|
163
|
+
:part
|
164
|
+
]})
|
165
|
+
part_list = safe[:part_list]
|
166
|
+
resumable_id = safe[:resumable_id]
|
167
|
+
|
168
|
+
if resumable_id || part_list
|
176
169
|
if upload.resumable
|
177
|
-
|
178
|
-
|
170
|
+
if part_list
|
171
|
+
upload.part_list = part_list || []
|
172
|
+
|
173
|
+
# We incrementally update this as it might otherwise contain
|
174
|
+
# sum(1 -> 10,000) parts over the time of the upload
|
175
|
+
# (as is the case with the largest file amazon supports)
|
176
|
+
if safe[:part_data]
|
177
|
+
upload.part_data ||= {}
|
178
|
+
safe[:part_data].each do |part|
|
179
|
+
upload.part_data[part[:part]] = part
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
upload.resumable_id = resumable_id if resumable_id
|
185
|
+
upload.save!
|
186
|
+
|
187
|
+
if params[:part_update]
|
188
|
+
render nothing: true
|
189
|
+
else
|
190
|
+
@current_upload = upload
|
191
|
+
|
192
|
+
# Render is called from edit
|
193
|
+
edit
|
194
|
+
end
|
179
195
|
else
|
180
|
-
render :
|
196
|
+
render nothing: true, status: :not_acceptable
|
181
197
|
end
|
182
|
-
|
183
|
-
|
198
|
+
|
199
|
+
else # We are completeing the upload
|
200
|
+
response = instance_exec upload, &@@callbacks[:upload_complete]
|
184
201
|
if response
|
185
|
-
render :
|
202
|
+
render nothing: true
|
186
203
|
else
|
187
|
-
render :
|
204
|
+
render nothing: true, status: :not_acceptable
|
188
205
|
end
|
189
206
|
end
|
190
207
|
end
|
191
|
-
|
192
|
-
|
208
|
+
|
193
209
|
def destroy
|
194
|
-
#
|
195
210
|
# Delete the file from the cloud system - the client is not responsible for this
|
196
|
-
#
|
197
211
|
response = instance_exec current_upload, &@@callbacks[:destroy_upload]
|
198
212
|
if response
|
199
|
-
render :
|
213
|
+
render nothing: true
|
200
214
|
else
|
201
|
-
render :
|
215
|
+
render nothing: true, status: :not_acceptable
|
202
216
|
end
|
203
217
|
end
|
204
|
-
|
205
|
-
|
218
|
+
|
219
|
+
|
206
220
|
protected
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
# Otherwise the dynamic residence can be used when users are define their own storage locations
|
212
|
-
#
|
213
|
-
def set_residence(name, options = {})
|
214
|
-
options[:namespace] = @@namespace
|
215
|
-
@current_residence = condo_config.set_residence(name, options)
|
216
|
-
end
|
217
|
-
|
218
|
-
def current_residence
|
219
|
-
@current_residence ||= condo_config.residencies[0]
|
221
|
+
|
222
|
+
|
223
|
+
def current_residence(upload = nil)
|
224
|
+
@current_residence ||= instance_exec(condo_config, current_resident, upload, &@@callbacks[:select_residence])
|
220
225
|
end
|
221
|
-
|
226
|
+
|
222
227
|
def current_upload
|
223
228
|
return @current_upload if @current_upload
|
224
229
|
|
225
230
|
safe_params = params.permit(:upload_id, :id)
|
226
|
-
|
231
|
+
# current_residence.name && current_residence.location && resident.id.exists?
|
232
|
+
@current_upload = condo_backend.check_exists({user_id: current_resident, upload_id: (safe_params[:upload_id] || safe_params[:id])}).tap do |object|
|
227
233
|
raise Condo::Errors::NotYourPlace unless object.present?
|
228
234
|
end
|
229
235
|
end
|
230
|
-
|
236
|
+
|
231
237
|
def current_resident
|
232
|
-
|
238
|
+
# instance_exec for params
|
239
|
+
@current_resident ||= (instance_eval &@@callbacks[:resident_id]).tap do |object|
|
233
240
|
raise Condo::Errors::LostTheKeys unless object.present?
|
234
241
|
end
|
235
242
|
end
|
236
|
-
|
243
|
+
|
237
244
|
def condo_backend
|
238
|
-
Condo::Store
|
245
|
+
::Condo::Store
|
239
246
|
end
|
240
|
-
|
247
|
+
|
241
248
|
def condo_config
|
242
|
-
Condo::Configuration
|
249
|
+
::Condo::Configuration
|
243
250
|
end
|
244
|
-
|
245
|
-
|
246
|
-
#
|
251
|
+
|
252
|
+
|
247
253
|
# Defines the default callbacks
|
248
|
-
#
|
249
254
|
(@@callbacks ||= {}).merge! Condo::Configuration.callbacks
|
250
|
-
|
251
|
-
|
252
|
-
|
255
|
+
|
253
256
|
def self.condo_callback(name, callback = nil, &block)
|
254
257
|
callback ||= block
|
255
258
|
if callback.respond_to?(:call)
|
@@ -258,12 +261,6 @@ module Condo
|
|
258
261
|
raise ArgumentError, 'No callback provided'
|
259
262
|
end
|
260
263
|
end
|
261
|
-
|
262
|
-
|
263
|
-
def self.condo_namespace(name)
|
264
|
-
@@namespace = name.to_sym
|
265
|
-
end
|
266
|
-
|
267
264
|
end
|
268
265
|
end
|
269
266
|
end
|