anaconda 0.1.2 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +92 -49
- data/app/assets/javascripts/anaconda_uploader.js.coffee +188 -107
- data/lib/anaconda.rb +28 -1
- data/lib/anaconda/anaconda_for.rb +23 -9
- data/lib/anaconda/errors.rb +7 -0
- data/lib/anaconda/form_builder_helpers.rb +17 -26
- data/lib/anaconda/upload_helper.rb +6 -2
- data/lib/anaconda/version.rb +1 -1
- data/lib/generators/anaconda/migration_generator.rb +12 -2
- data/lib/generators/anaconda/templates/config/initializers/anaconda.rb +6 -0
- metadata +2 -2
- data/app/views/anaconda/_uploader_form_for.html.haml +0 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2138f5230105240fc5af85833f84cb47628397f9
|
4
|
+
data.tar.gz: e4a8dcd9108b21d577f9802222966a0ac29d6f3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c42b5fa24d72da81a1ef9ce3ae2d64df9e15e22bd176138640d585a538d9c1f23915940687e0d7242446010a041e6e3f364f66c0e0773da09805b9c13499b21d
|
7
|
+
data.tar.gz: a8a9589fe6d7809653f25dc657839088d08fec908d0d11b6acfe80afbb2198bedb1c8f34e78f0b8fa91f8d75ca1549d50fc0b0d8d0e381ef7e097946cb38e283
|
data/README.markdown
CHANGED
@@ -27,41 +27,40 @@ If you require stability before that time, you are strongly encouraged to specif
|
|
27
27
|
## Configuration
|
28
28
|
|
29
29
|
### AWS S3 Setup
|
30
|
-
|
31
|
-
### AWS S3 Setup
|
30
|
+
Create a bucket where you want your uploads to go. If you already have a bucket in place, you can certainly use it.
|
32
31
|
|
33
32
|
#### IAM
|
34
|
-
|
35
|
-
Sample IAM Policy (be sure to replace 'your.bucketname'):
|
33
|
+
For best security we recommend creating a user in IAM that will just be used for file uploading. Once you create that user you can apply a security policy to it so they can only access the specified resources. Here is an example IAM policy that will restrict this user to only have access to the one bucket specified (be sure to replace 'your.bucketname'). Be sure to generate security credentials for this user. These are the S3 credentials you will use.
|
36
34
|
|
37
35
|
{
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
36
|
+
"Statement": [
|
37
|
+
{
|
38
|
+
"Effect": "Allow",
|
39
|
+
"Action": "s3:*",
|
40
|
+
"Resource": [
|
41
|
+
"arn:aws:s3:::[your.bucketname]",
|
42
|
+
"arn:aws:s3:::[your.bucketname]/*"
|
43
|
+
]
|
44
|
+
}
|
45
|
+
]
|
46
|
+
}
|
49
47
|
|
50
48
|
#### CORS
|
49
|
+
You will need to set up CORS permissions on the bucket so users can upload to it from your website. Below is a sample CORS configuration.
|
51
50
|
|
52
|
-
|
51
|
+
If users will only upload from one domain, you can put that in your AllowedOrigin. If they will upload from multiple domains you may either add an AllowedOrigin for each of them, or use a wildcard `*` origin as in our example below.
|
53
52
|
|
54
53
|
<?xml version="1.0" encoding="UTF-8"?>
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
54
|
+
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
|
55
|
+
<CORSRule>
|
56
|
+
<AllowedOrigin>*</AllowedOrigin>
|
57
|
+
<AllowedMethod>GET</AllowedMethod>
|
58
|
+
<AllowedMethod>POST</AllowedMethod>
|
59
|
+
<AllowedMethod>PUT</AllowedMethod>
|
60
|
+
<MaxAgeSeconds>3000</MaxAgeSeconds>
|
61
|
+
<AllowedHeader>*</AllowedHeader>
|
62
|
+
</CORSRule>
|
63
|
+
</CORSConfiguration>
|
65
64
|
|
66
65
|
|
67
66
|
### Initializer
|
@@ -74,9 +73,23 @@ We highly recommend the `figaro` gem [https://github.com/laserlemon/figaro](http
|
|
74
73
|
|
75
74
|
## Usage
|
76
75
|
|
77
|
-
* Controller changes
|
76
|
+
* Controller changes
|
77
|
+
|
78
|
+
You must add these parameters to your permitted parameters. In Rails 4 this is done via strong parameters in the controller. In Rails 3 this is done in the model via attr_accessible.
|
79
|
+
|
80
|
+
For each `anaconda_for` (assuming `anaconda_for :asset`):
|
81
|
+
|
82
|
+
* :asset_filename
|
83
|
+
* :asset_file_path
|
84
|
+
* :asset_size
|
85
|
+
* :asset_original_filename
|
86
|
+
* :asset_stored_privately
|
87
|
+
* :asset_type
|
88
|
+
|
78
89
|
|
79
90
|
* Migrations
|
91
|
+
|
92
|
+
We provide a migration generator. Assuming `anaconda_for :asset` inside of PostMedia model:
|
80
93
|
|
81
94
|
$ rails g migration anaconda:migration PostMedia asset
|
82
95
|
|
@@ -97,12 +110,57 @@ We highly recommend the `figaro` gem [https://github.com/laserlemon/figaro](http
|
|
97
110
|
|
98
111
|
* Form setup
|
99
112
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
113
|
+
At this time we only support anaconda fields inside of a [simple_form](https://github.com/plataformatec/simple_form). We plan to expand and add a rails form helper in the future.
|
114
|
+
|
115
|
+
= simple_form_for post_media do |f|
|
116
|
+
= f.anaconda :asset
|
117
|
+
= f.name
|
118
|
+
= f.other_field
|
119
|
+
= f.submit
|
120
|
+
|
121
|
+
**Form helper options**
|
122
|
+
|
123
|
+
There are a variety of options available on the form helper. At this time they are:
|
124
|
+
|
125
|
+
* `upload_details_container` - An element id you would like the upload details located in. Defaults to `<resource>_<attribtue>_details` ex: `post_media_asset_details`
|
126
|
+
* `auto_upload` - If set to true, upload will begin as soon as a file is selected. Default: *false*
|
127
|
+
* `auto_submit` - If set to true, form will submit automatically when upload is completed. Useful when mixed with `auto_upload: true`, especially if the file field is the only field on the form. Default: *true* when auto_upload is false; *false* when auto_upload is true.
|
128
|
+
|
129
|
+
* Fields
|
130
|
+
|
131
|
+
At this point you will have these methods available on a post_media instance:
|
132
|
+
* :asset_filename
|
133
|
+
* :asset_file_path
|
134
|
+
* :asset_size
|
135
|
+
* :asset_original_filename
|
136
|
+
* :asset_stored_privately
|
137
|
+
* :asset_type
|
138
|
+
* :asset_url
|
139
|
+
|
140
|
+
The magic method is asset_url which will return a signed S3 URL if the file is stored with an ACL of `private` and will return a non-signed URL if the file is stored with public access.
|
141
|
+
|
142
|
+
## Changelog
|
143
|
+
|
144
|
+
* 0.2.0
|
145
|
+
|
146
|
+
* Add support for multiple `anaconda_for` calls per model. Currently limited to one per form, however.
|
147
|
+
|
148
|
+
* Improve migration generation file and class naming to include field name
|
149
|
+
|
150
|
+
* `post_media.asset_url` will now return nil if the file_path is nil
|
151
|
+
|
152
|
+
* 0.9.0
|
153
|
+
|
154
|
+
* Add support for multiple anaconda uploaders per form
|
155
|
+
|
156
|
+
* Completely refactor JavaScript file to support multiple uploaders
|
157
|
+
|
158
|
+
* Add auto_submit option
|
159
|
+
|
160
|
+
* Fix support for allowed_types
|
161
|
+
|
162
|
+
* Add file_types to anaconda config
|
163
|
+
|
106
164
|
## Contributing to anaconda
|
107
165
|
|
108
166
|
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
|
@@ -116,19 +174,4 @@ We highly recommend the `figaro` gem [https://github.com/laserlemon/figaro](http
|
|
116
174
|
## Copyright
|
117
175
|
|
118
176
|
Copyright (c) 2014 Forge Apps, LLC. See LICENSE.txt for
|
119
|
-
further details.
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
Columns we're expecting (in the case of `anaconda_for :image`)
|
125
|
-
image_file_path
|
126
|
-
image_size
|
127
|
-
image_original_filename
|
128
|
-
|
129
|
-
Magic Columns we'll make
|
130
|
-
image_url
|
131
|
-
|
132
|
-
|
133
|
-
You'll have to make and run a migration for the columns you want:
|
134
|
-
`rails g migration AddAnacondaToUsers image_file_path:text image_size:integer image_original_filename:text`
|
177
|
+
further details.
|
@@ -1,39 +1,124 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
# Three possible configurations:
|
2
|
+
# Upload automatically and submit form when upload is complete
|
3
|
+
# Done: Upload automatically but do not submit form automatically
|
4
|
+
# Done: Do not upload until submit is pressed. The upload and submit form when uploading is complete.
|
5
|
+
class @AnacondaUploadManager
|
6
|
+
constructor: (options = {}) ->
|
7
|
+
@anaconda_upload_fields = []
|
8
|
+
DLog options
|
9
|
+
@form = $("##{options.form_id}")
|
10
|
+
@upload_automatically = false
|
11
|
+
@submit_automatically = false
|
12
|
+
@setup_form_submit_handler()
|
13
|
+
self = this
|
14
|
+
$(document).on "page:fetch", ->
|
15
|
+
DLog "page:fetch"
|
16
|
+
self.reset()
|
17
|
+
|
18
|
+
register_upload_field: (anaconda_upload_field)->
|
19
|
+
DLog "Registering Upload Field"
|
20
|
+
@anaconda_upload_fields.push anaconda_upload_field
|
21
|
+
if anaconda_upload_field.upload_automatically
|
22
|
+
# If _any_ of them have an auto upload, we want to know
|
23
|
+
@upload_automatically = true
|
24
|
+
if anaconda_upload_field.submit_automatically
|
25
|
+
# If _any_ of them have auto submit enabled, we will submit automatically
|
26
|
+
@submit_automatically = true
|
27
|
+
|
28
|
+
setup_form_submit_handler: ->
|
29
|
+
DLog( "Setting up submit handler for form #{@form.attr('id')}")
|
30
|
+
@form.on( 'submit', { self: this }, this.form_submit_handler )
|
8
31
|
|
32
|
+
form_submit_handler: (e) ->
|
33
|
+
self = e.data.self
|
34
|
+
return if self.upload_automatically
|
35
|
+
e.preventDefault()
|
36
|
+
$(this).off( 'submit', self.form_submit_handler )
|
37
|
+
|
38
|
+
for upload_field, i in self.anaconda_upload_fields
|
39
|
+
upload_field.upload()
|
40
|
+
false
|
41
|
+
reset: ->
|
42
|
+
for upload_field, i in @anaconda_upload_fields
|
43
|
+
upload_field.reset()
|
44
|
+
@anaconda_upload_fields = []
|
45
|
+
|
46
|
+
upload_completed: ->
|
47
|
+
all_completed = true
|
48
|
+
for upload_field, i in @anaconda_upload_fields
|
49
|
+
if upload_field.upload_in_progress
|
50
|
+
all_completed = false
|
51
|
+
break
|
52
|
+
if all_completed
|
53
|
+
@all_uploads_completed()
|
54
|
+
|
55
|
+
all_uploads_completed: ->
|
56
|
+
if !@upload_automatically || @submit_automatically
|
57
|
+
@form.submit()
|
58
|
+
else
|
59
|
+
@enable_submit_button()
|
60
|
+
|
61
|
+
disable_submit_button: ->
|
62
|
+
@form.find("input[type='submit']").prop( "disabled", true );
|
63
|
+
enable_submit_button: ->
|
64
|
+
@form.find("input[type='submit']").prop( "disabled", false );
|
9
65
|
|
66
|
+
class @AnacondaUploadField
|
10
67
|
constructor: (options = {}) ->
|
68
|
+
@upload_in_progress = false
|
69
|
+
@upload_completed = false
|
70
|
+
DLog "options:"
|
71
|
+
DLog options
|
11
72
|
@element_id = options.element_id ? ""
|
12
|
-
@limits = options.limits ? {}
|
13
73
|
@allowed_types = options.allowed_types ? []
|
14
|
-
@
|
74
|
+
DLog @allowed_types
|
75
|
+
@resource = options.resource
|
76
|
+
@attribute = options.attribute
|
77
|
+
if options.upload_details_container != null && options.upload_details_container != ""
|
78
|
+
@upload_details_container = $("##{options.upload_details_container}")
|
79
|
+
else
|
80
|
+
@upload_details_container = $("##{@resource}_#{@attribute}_details")
|
15
81
|
@upload_button = $("##{options.upload_button_id}") ? $("#upload")
|
16
|
-
@upload_complete_post_url = options.upload_complete_post_url ? null
|
17
|
-
@upload_complete_form_to_fill = options.upload_complete_form_to_fill ? null
|
18
82
|
@upload_automatically = options.upload_automatically ? false
|
19
|
-
@
|
20
|
-
@
|
21
|
-
|
22
|
-
@
|
83
|
+
@submit_automatically = options.submit_automatically ? false
|
84
|
+
@file = null
|
85
|
+
@file_data = null
|
86
|
+
@media_types = $(@element_id).data('media-types')
|
87
|
+
|
88
|
+
|
23
89
|
@base_key = options.base_key ? ""
|
90
|
+
@key = options.key ? "#{@base_key}/${filename}"
|
91
|
+
|
92
|
+
@register_with_upload_manager()
|
93
|
+
|
24
94
|
@setup_fileupload()
|
25
95
|
|
96
|
+
register_with_upload_manager: ->
|
97
|
+
if (@closest_form().length == 0 || @closest_form().attr('id') == 'undefined')
|
98
|
+
throw "Anaconda Error: form element not found or missing id attribtue."
|
99
|
+
if (typeof( window.anacondaUploadManagers ) == "undefined")
|
100
|
+
window.anacondaUploadManagers = []
|
101
|
+
if (typeof( window.anacondaUploadManagers[@closest_form().attr('id')] ) == "undefined")
|
102
|
+
DLog "registering new upload manager for form #{@closest_form().attr('id')}"
|
103
|
+
window.anacondaUploadManagers[@closest_form().attr('id')] = new AnacondaUploadManager({form_id: @closest_form().attr('id')})
|
104
|
+
@upload_manager().register_upload_field(this)
|
105
|
+
upload_manager: ->
|
106
|
+
window.anacondaUploadManagers[@closest_form().attr('id')]
|
107
|
+
closest_form: ->
|
108
|
+
$(@element_id).closest("form")
|
109
|
+
|
26
110
|
setup_fileupload: ->
|
27
111
|
self = this
|
28
112
|
$( @element_id ).fileupload
|
29
|
-
dropZone: $("#dropzone")
|
113
|
+
#dropZone: $("#dropzone")
|
30
114
|
add: (e, data) ->
|
31
|
-
|
115
|
+
DLog data
|
116
|
+
self.file_selected data
|
32
117
|
progress: (e, data) ->
|
33
118
|
DLog data
|
34
119
|
progress = parseInt(data.loaded / data.total * 100, 10)
|
35
|
-
DLog( "Progress: " + progress )
|
36
|
-
|
120
|
+
DLog( "Progress for #{self.file.name}: " + progress )
|
121
|
+
self.update_progress_to(progress)
|
37
122
|
|
38
123
|
done: (e, data) ->
|
39
124
|
self.file_completed_upload data
|
@@ -55,69 +140,73 @@ class @AnacondaUploader
|
|
55
140
|
$(document).bind 'drop dragover', (e) ->
|
56
141
|
e.preventDefault()
|
57
142
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
self = e.data.self
|
68
|
-
$(this).off( 'submit', self.form_submit_handler )
|
69
|
-
|
70
|
-
self.files_for_upload[0].submit()
|
71
|
-
false
|
72
|
-
|
73
|
-
files_by_type: (type) ->
|
74
|
-
matches = []
|
75
|
-
for v,i in @files_for_upload
|
76
|
-
if v.media_type == type
|
77
|
-
matches.push v
|
78
|
-
return matches
|
79
|
-
|
80
|
-
upload_files: ->
|
81
|
-
media_type = null
|
82
|
-
for v,i in @files_for_upload
|
83
|
-
$("input#key").val "#{@base_key}/${filename}"
|
84
|
-
v.submit()
|
85
|
-
|
86
|
-
is_allowed_type: (upload_file) ->
|
87
|
-
if 0 == @allowed_types.length || 0 <= @allowed_types.indexOf upload_file.media_type
|
88
|
-
return true
|
89
|
-
return false
|
143
|
+
upload: ->
|
144
|
+
return if @upload_completed
|
145
|
+
if @file != null && @file_data != null
|
146
|
+
$("input#key").val @key
|
147
|
+
@file_data.submit()
|
148
|
+
@upload_in_progress = true
|
149
|
+
@upload_manager().uploads_started = true
|
150
|
+
@upload_manager().disable_submit_button()
|
151
|
+
@hide_file_field()
|
90
152
|
|
91
|
-
|
92
|
-
|
153
|
+
hide_file_field: ->
|
154
|
+
$(@element_id).hide()
|
155
|
+
|
156
|
+
is_allowed_type: (file_obj) ->
|
157
|
+
|
158
|
+
if 0 == @allowed_types.length || 0 <= @allowed_types.indexOf @get_media_type(file_obj)
|
93
159
|
return true
|
94
160
|
return false
|
95
|
-
|
161
|
+
|
162
|
+
get_media_type: (file_obj) ->
|
163
|
+
media_type = "unknown"
|
164
|
+
DLog "get_media_type"
|
165
|
+
for k,v of @media_types
|
166
|
+
regexp = new RegExp(v, "i")
|
167
|
+
if regexp.test(file_obj.type) || regexp.test(file_obj.name)
|
168
|
+
media_type = k
|
169
|
+
return media_type
|
170
|
+
|
96
171
|
reset: ->
|
97
|
-
@files_for_upload = []
|
98
172
|
@upload_details_container.html ''
|
99
173
|
|
100
|
-
|
101
|
-
|
102
|
-
if @is_allowed_type(
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
else
|
112
|
-
@setup_upload_button_handler()
|
174
|
+
file_selected: (data) ->
|
175
|
+
DLog data
|
176
|
+
if @is_allowed_type(data.files[0])
|
177
|
+
@file = data.files[0]
|
178
|
+
@file_data = data
|
179
|
+
DLog @file
|
180
|
+
@upload_details_container.html "<div id='upload_file_#{@get_id()}' class='upload-file #{@get_media_type(@file)}'><span class='file-name'>#{@file.name}</span><span class='size'>#{@readable_size()}</span><span class='progress-percent'></span><div class='progress'><span class='progress-bar'></span></div></div>"
|
181
|
+
|
182
|
+
if @upload_automatically
|
183
|
+
DLog "auto upload"
|
184
|
+
@upload()
|
113
185
|
else
|
114
|
-
|
186
|
+
DLog "Not auto upload"
|
115
187
|
else
|
116
|
-
alert "#{
|
188
|
+
alert "#{data.files[0].name} is a #{@get_media_type(data.files[0])} file. Only #{@allowed_types.join(", ")} files are allowed."
|
189
|
+
get_id: ->
|
190
|
+
hex_md5( "#{@file.name} #{@file.size}" )
|
191
|
+
|
192
|
+
readable_size: ->
|
193
|
+
i = -1;
|
194
|
+
byteUnits = [' kB', ' MB', ' GB', ' TB', 'PB', 'EB', 'ZB', 'YB'];
|
195
|
+
fileSizeInBytes = @file.size
|
196
|
+
loop
|
197
|
+
fileSizeInBytes = fileSizeInBytes / 1024
|
198
|
+
i++
|
199
|
+
break unless fileSizeInBytes > 1024
|
200
|
+
|
201
|
+
Math.max(fileSizeInBytes, 0.1).toFixed(1) + byteUnits[i];
|
202
|
+
|
203
|
+
update_progress_to: (progress) ->
|
204
|
+
@upload_details_container.find(".progress-percent").html progress
|
205
|
+
@upload_details_container.find('.progress-bar').css('width', progress + '%')
|
206
|
+
|
117
207
|
file_completed_upload: (data) ->
|
118
|
-
|
119
|
-
DLog
|
120
|
-
DLog upload_file
|
208
|
+
DLog "#{@file.name} completed uploading"
|
209
|
+
DLog @file
|
121
210
|
|
122
211
|
# if @upload_complete_post_url? && @upload_complete_post_url != ""
|
123
212
|
# DLog "will now post to #{@upload_complete_post_url}"
|
@@ -139,43 +228,35 @@ class @AnacondaUploader
|
|
139
228
|
# #TODO: handle a failure on this POST
|
140
229
|
# })
|
141
230
|
|
142
|
-
DLog "will now fill form #{@upload_complete_form_to_fill}"
|
231
|
+
# DLog "will now fill form #{@upload_complete_form_to_fill}"
|
143
232
|
|
144
233
|
DLog "#{@resource}_#{@attribute}_file_path"
|
145
234
|
|
146
|
-
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_file_path" ).val( "
|
147
|
-
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_filename" ).val(
|
148
|
-
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_size" ).val(
|
149
|
-
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_type" ).val(
|
235
|
+
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_file_path" ).val( @key.replace("${filename}", @file.name) )
|
236
|
+
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_filename" ).val( @file.name )
|
237
|
+
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_size" ).val( @file.size )
|
238
|
+
$( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_type" ).val( @file.type )
|
150
239
|
|
151
|
-
|
240
|
+
@upload_in_progress = false;
|
241
|
+
@upload_completed = true;
|
242
|
+
@upload_manager().upload_completed()
|
152
243
|
|
153
|
-
class @AnacondaUploadFile
|
154
244
|
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
media_type = "unknown"
|
174
|
-
return media_type
|
175
|
-
set_context: ->
|
176
|
-
@data.context = this
|
177
|
-
submit: ->
|
178
|
-
@data.submit()
|
179
|
-
update_progress_to: (progress) ->
|
180
|
-
$("#upload_file_#{@id} .progress-percent").html progress
|
181
|
-
$("#upload_file_#{@id}").find('.progress-bar').css('width', progress + '%')
|
245
|
+
# class @AnacondaUploadFile
|
246
|
+
#
|
247
|
+
# constructor: (@data) ->
|
248
|
+
# @file = @data.files[0]
|
249
|
+
# @media_type = @get_media_type()
|
250
|
+
# @id = @get_id()
|
251
|
+
#
|
252
|
+
# @set_context()
|
253
|
+
# get_media_type: ->
|
254
|
+
# media_type = "unknown"
|
255
|
+
# return media_type
|
256
|
+
# set_context: ->
|
257
|
+
# @data.context = this
|
258
|
+
# submit: ->
|
259
|
+
# @data.submit()
|
260
|
+
# update_progress_to: (progress) ->
|
261
|
+
# $("#upload_file_#{@id} .progress-percent").html progress
|
262
|
+
# $("#upload_file_#{@id}").find('.progress-bar').css('width', progress + '%')
|
data/lib/anaconda.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'rails'
|
2
2
|
require 'anaconda/anaconda'
|
3
|
+
require 'anaconda/errors'
|
3
4
|
require 'anaconda/anaconda_for'
|
4
5
|
require 'anaconda/railtie'
|
5
6
|
require 'anaconda/engine'
|
@@ -10,16 +11,42 @@ ActiveSupport.on_load(:active_record) do
|
|
10
11
|
end
|
11
12
|
|
12
13
|
module Anaconda
|
13
|
-
mattr_accessor :aws
|
14
|
+
mattr_accessor :aws, :file_types
|
14
15
|
@@aws = {
|
15
16
|
aws_access_key: "",
|
16
17
|
aws_secret_key: "",
|
17
18
|
aws_bucket: ""
|
18
19
|
}
|
20
|
+
|
21
|
+
@@file_types = {
|
22
|
+
audio: /(\.|\/)(wav|mp3|m4a|aiff|ogg|flac)$/,
|
23
|
+
video: /(\.|\/)(mp[e]?g|mov|avi|mp4|m4v)$/,
|
24
|
+
image: /(\.|\/)(jp[e]?g|png|bmp)$/,
|
25
|
+
resource: /(\.|\/)(pdf|ppt[x]?|doc[x]?|xls[x]?)$/,
|
26
|
+
}
|
19
27
|
|
20
28
|
# Default way to setup Anaconda. Run rails generate anaconda:install
|
21
29
|
# to create a fresh initializer with all configuration values.
|
22
30
|
def self.config
|
23
31
|
yield self
|
24
32
|
end
|
33
|
+
|
34
|
+
def self.js_file_types
|
35
|
+
# http://stackoverflow.com/questions/4854714/how-to-translate-ruby-regex-to-javascript-i-mx-and-rails-3-0-3
|
36
|
+
js_file_types = {}
|
37
|
+
file_types.each do |group_name, regexp|
|
38
|
+
str = regexp.inspect.
|
39
|
+
sub('\\A' , '^').
|
40
|
+
sub('\\Z' , '$').
|
41
|
+
sub('\\z' , '$').
|
42
|
+
sub(/^\// , '').
|
43
|
+
sub(/\/[a-z]*$/ , '').
|
44
|
+
gsub(/\(\?#.+\)/ , '').
|
45
|
+
gsub(/\(\?-\w+:/ , '(').
|
46
|
+
gsub(/\s/ , '')
|
47
|
+
regexp_str = Regexp.new(str).source
|
48
|
+
js_file_types[group_name.to_s] = regexp_str
|
49
|
+
end
|
50
|
+
return js_file_types
|
51
|
+
end
|
25
52
|
end
|
@@ -7,22 +7,36 @@ module Anaconda
|
|
7
7
|
|
8
8
|
module ClassMethods
|
9
9
|
|
10
|
-
def anaconda_for(
|
10
|
+
def anaconda_for( anaconda_column, options = {})
|
11
11
|
send :include, InstanceMethods
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
|
13
|
+
begin
|
14
|
+
self.anaconda_columns.present?
|
15
|
+
rescue NoMethodError
|
16
|
+
class_attribute :anaconda_columns
|
17
|
+
end
|
18
|
+
self.anaconda_columns = Array.new unless self.anaconda_columns.kind_of? Array
|
19
|
+
if self.anaconda_columns.include? anaconda_column.to_sym
|
20
|
+
raise AnacondaError, "anaconda_for cannot be called multiple times for the same field"
|
21
|
+
end
|
22
|
+
self.anaconda_columns << anaconda_column.to_sym
|
16
23
|
# Class.anaconda_columns is now an array of symbols
|
17
24
|
|
18
|
-
class_attribute
|
19
|
-
|
25
|
+
class_attribute
|
26
|
+
begin
|
27
|
+
self.anaconda_options.present?
|
28
|
+
rescue NoMethodError
|
29
|
+
class_attribute :anaconda_options
|
30
|
+
end
|
31
|
+
self.anaconda_options = Hash.new unless self.anaconda_options.kind_of? Hash
|
32
|
+
self.anaconda_options[anaconda_column.to_sym] = options.reverse_merge(
|
20
33
|
aws_access_key_id: Anaconda.aws[:aws_access_key],
|
21
34
|
aws_secret_access_key: Anaconda.aws[:aws_secret_key],
|
22
35
|
bucket: Anaconda.aws[:aws_bucket],
|
23
36
|
acl: "public-read",
|
24
37
|
max_file_size: 500.megabytes,
|
25
|
-
|
38
|
+
allowed_file_types: [],
|
39
|
+
base_key: "#{self.to_s.pluralize.downcase}/#{anaconda_column.to_s.pluralize}/#{(0...32).map{(65+rand(26)).chr}.join.downcase}"
|
26
40
|
)
|
27
41
|
end
|
28
42
|
end
|
@@ -49,7 +63,7 @@ module Anaconda
|
|
49
63
|
|
50
64
|
private
|
51
65
|
def magic_url(column_name)
|
52
|
-
nil unless send("#{column_name}_file_path").present?
|
66
|
+
return nil unless send("#{column_name}_file_path").present?
|
53
67
|
|
54
68
|
if send("#{column_name}_stored_privately")
|
55
69
|
aws = Fog::Storage.new({:provider => 'AWS', :aws_access_key_id => Anaconda.aws[:aws_access_key], :aws_secret_access_key => Anaconda.aws[:aws_secret_key]})
|
@@ -12,12 +12,16 @@ module Anaconda
|
|
12
12
|
instance = self.object
|
13
13
|
a_class = self.object.class unless self.object.kind_of? Class
|
14
14
|
|
15
|
-
|
15
|
+
begin
|
16
|
+
options = a_class.anaconda_options[anaconda_field_name.to_sym].dup
|
17
|
+
rescue
|
18
|
+
raise AnacondaError, "attribute options not set for column #{anaconda_field_name}. Did you add `anaconda_for :#{anaconda_field_name}` to the model?"
|
19
|
+
end
|
16
20
|
options[:base_key] = instance.send(options[:base_key].to_s) if options[:base_key].kind_of? Symbol
|
17
|
-
|
21
|
+
|
18
22
|
uploader = S3Uploader.new(options)
|
19
23
|
|
20
|
-
output += self.input_field "file", name: "file", id: element_id, as: :file,
|
24
|
+
output += self.input_field "file", name: "file", id: element_id, as: :file, data: {url: uploader.url, form_data: uploader.fields.to_json, media_types: Anaconda.js_file_types}
|
21
25
|
end
|
22
26
|
|
23
27
|
output += self.hidden_field "#{anaconda_field_name}_filename".to_sym
|
@@ -32,33 +36,20 @@ module Anaconda
|
|
32
36
|
options = options.merge(as: anaconda_field_name, form_options: form_options, element_id: element_id )
|
33
37
|
|
34
38
|
output += <<-END
|
35
|
-
<
|
36
|
-
|
37
|
-
<div id="files"></div>
|
39
|
+
<div id="#{instance.class.to_s.underscore}_#{anaconda_field_name}_details"></div>
|
38
40
|
|
39
41
|
<script>
|
40
42
|
(function() {
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
base_key: "#{options[:base_key]}",
|
51
|
-
allowed_types: [],
|
52
|
-
upload_details_container: "files",
|
53
|
-
upload_button_id: "upload",
|
54
|
-
upload_complete_post_url: "#{options[:form_options][:post_url]}",
|
55
|
-
upload_complete_form_to_fill: "#{options[:form_options][:form_el]}",
|
56
|
-
upload_automatically: "#{options[:form_options][:auto_upload]}",
|
57
|
-
resource: "#{instance.class.to_s.underscore}",
|
58
|
-
attribute: "#{options[:as]}"
|
59
|
-
});
|
43
|
+
window.uploader = new AnacondaUploadField({
|
44
|
+
element_id: "##{options[:element_id]}",
|
45
|
+
base_key: "#{options[:base_key]}",
|
46
|
+
allowed_types: #{options[:allowed_file_types].collect{ |i| i.to_s }},
|
47
|
+
upload_details_container: "#{options[:form_options][:upload_details_container]}",
|
48
|
+
upload_automatically: "#{options[:form_options][:auto_upload]}",
|
49
|
+
submit_automatically: "#{options[:form_options][:auto_submit]}",
|
50
|
+
resource: "#{instance.class.to_s.underscore}",
|
51
|
+
attribute: "#{options[:as]}"
|
60
52
|
});
|
61
|
-
|
62
53
|
}).call(this);
|
63
54
|
</script>
|
64
55
|
|
@@ -1,9 +1,13 @@
|
|
1
1
|
module Anaconda
|
2
|
-
#
|
2
|
+
# Greatly inspired by http://railscasts.com/episodes/383-uploading-to-amazon-s3
|
3
3
|
module UploadHelper
|
4
4
|
def anaconda_uploader_form_for(instance, attribute, form_options = {})
|
5
5
|
a_class = instance.class unless instance.kind_of? Class
|
6
|
-
|
6
|
+
if a_class.anaconda_options[attribute].present
|
7
|
+
options = a_class.anaconda_options[attribute].dup
|
8
|
+
else
|
9
|
+
raise AnacondaError, "attribute options not set for column #{attribute}. Did you add `anaconda_for :#{attribute}` to the model?"
|
10
|
+
end
|
7
11
|
options[:base_key] = instance.send(options[:base_key].to_s) if options[:base_key].kind_of? Symbol
|
8
12
|
render(:template =>"anaconda/_uploader_form_for.html.haml", :locals => {resource: instance, options: options.merge(as: attribute, form_options: form_options)}, layout: false).to_s
|
9
13
|
end
|
data/lib/anaconda/version.rb
CHANGED
@@ -8,8 +8,18 @@ module Anaconda
|
|
8
8
|
argument :field_name, :type => :string, :default => "asset"
|
9
9
|
|
10
10
|
def create_migration_file
|
11
|
-
|
12
|
-
|
11
|
+
destination = "db/migrate/#{Time.now.strftime('%Y%m%d%H%M%S')}_anaconda_migration_for_#{file_name}_#{field_name}.rb"
|
12
|
+
migration_name = "AnacondaMigrationFor#{file_name.titlecase}#{field_name.titlecase}"
|
13
|
+
count = nil
|
14
|
+
i = 1
|
15
|
+
while !Dir.glob("db/migrate/*_anaconda_migration_for_#{file_name}_#{field_name}#{count}.rb").empty?
|
16
|
+
i += 1
|
17
|
+
count = "_#{i}"
|
18
|
+
destination = "db/migrate/#{Time.now.strftime('%Y%m%d%H%M%S')}_anaconda_migration_for_#{file_name}_#{field_name}#{count}.rb"
|
19
|
+
migration_name = "AnacondaMigrationFor#{file_name.titlecase}#{field_name.titlecase}#{i}"
|
20
|
+
end
|
21
|
+
create_file destination, <<-FILE
|
22
|
+
class #{migration_name} < ActiveRecord::Migration
|
13
23
|
def change
|
14
24
|
add_column :#{plural_name}, :#{field_name}_filename, :string
|
15
25
|
add_column :#{plural_name}, :#{field_name}_file_path, :text
|
@@ -5,4 +5,10 @@ Anaconda.config do |config|
|
|
5
5
|
aws_secret_key: ENV["AWS_SECRET_KEY"] || nil,
|
6
6
|
aws_bucket: ENV["AWS_BUCKET"] || nil
|
7
7
|
}
|
8
|
+
config.file_types = {
|
9
|
+
audio: /(\.|\/)(wav|mp3|m4a|aiff|ogg|flac)$/,
|
10
|
+
video: /(\.|\/)(mp[e]?g|mov|avi|mp4|m4v)$/,
|
11
|
+
image: /(\.|\/)(jp[e]?g|png|bmp)$/,
|
12
|
+
resource: /(\.|\/)(pdf|ppt[x]?|doc[x]?|xls[x]?)$/,
|
13
|
+
}
|
8
14
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anaconda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.9.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben McFadden
|
@@ -55,11 +55,11 @@ files:
|
|
55
55
|
- anaconda.gemspec
|
56
56
|
- app/assets/javascripts/anaconda.js
|
57
57
|
- app/assets/javascripts/anaconda_uploader.js.coffee
|
58
|
-
- app/views/anaconda/_uploader_form_for.html.haml
|
59
58
|
- lib/anaconda.rb
|
60
59
|
- lib/anaconda/anaconda.rb
|
61
60
|
- lib/anaconda/anaconda_for.rb
|
62
61
|
- lib/anaconda/engine.rb
|
62
|
+
- lib/anaconda/errors.rb
|
63
63
|
- lib/anaconda/form_builder_helpers.rb
|
64
64
|
- lib/anaconda/railtie.rb
|
65
65
|
- lib/anaconda/upload_helper.rb
|
@@ -1,28 +0,0 @@
|
|
1
|
-
/ Moved to form_builder_helpers.rb
|
2
|
-
|
3
|
-
%strong Files:
|
4
|
-
#files
|
5
|
-
|
6
|
-
:javascript
|
7
|
-
(function() {
|
8
|
-
jQuery(function() {
|
9
|
-
return window.uploader = new AnacondaUploader({
|
10
|
-
limits: {
|
11
|
-
audio: 1,
|
12
|
-
video: 1,
|
13
|
-
resource: 1,
|
14
|
-
image: 1
|
15
|
-
},
|
16
|
-
element_id: "#{option[:element_id]}"
|
17
|
-
allowed_types: [],
|
18
|
-
upload_details_container: "files",
|
19
|
-
upload_button_id: "upload",
|
20
|
-
upload_complete_post_url: "#{options[:form_options][:post_url]}",
|
21
|
-
upload_complete_form_to_fill: "#{options[:form_options][:form_el]}",
|
22
|
-
upload_automatically: "#{options[:form_options][:auto_upload]}",
|
23
|
-
resource: "#{resource.class.to_s.underscore}",
|
24
|
-
attribute: "#{options[:as]}"
|
25
|
-
});
|
26
|
-
});
|
27
|
-
|
28
|
-
}).call(this);
|