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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7fdbbf12ffefec5c116f99be38fc11d908ba60a9
4
- data.tar.gz: 9320e3c6cdef34e2e968c668b357dba366d37914
3
+ metadata.gz: 2138f5230105240fc5af85833f84cb47628397f9
4
+ data.tar.gz: e4a8dcd9108b21d577f9802222966a0ac29d6f3d
5
5
  SHA512:
6
- metadata.gz: 36c1eb4ac4d6597b285edcee1a5502af3f95b4bb24dbc00c8a2143afe6f20547641c4798246a238e6fcf9afece4f4c37f5a26e99258039eb49d1b85a3e85e7b0
7
- data.tar.gz: 12ace30c835230a065e6ea66fee390d79f37e8533bba47c2bf2817e48f8f3b49619ea8c57c62bad1aa568965325ce90a673d63e43698477480c28ccea49c55ae
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
- "Statement": [
39
- {
40
- "Effect": "Allow",
41
- "Action": "s3:*",
42
- "Resource": [
43
- "arn:aws:s3:::[your.bucketname]",
44
- "arn:aws:s3:::[your.bucketname]/*"
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
- Sample CORS configuration:
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
- <CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
56
- <CORSRule>
57
- <AllowedOrigin>*</AllowedOrigin>
58
- <AllowedMethod>GET</AllowedMethod>
59
- <AllowedMethod>POST</AllowedMethod>
60
- <AllowedMethod>PUT</AllowedMethod>
61
- <MaxAgeSeconds>3000</MaxAgeSeconds>
62
- <AllowedHeader>*</AllowedHeader>
63
- </CORSRule>
64
- </CORSConfiguration>
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 (if any)
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
- #anaconda_upload_form_wrapper
101
- = anaconda_uploader_form_for post_media, :asset, form_el: '#new_post_media', limits: { images: 9999 }, auto_upload: true
102
-
103
-
104
- * Options
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
- class @AnacondaUploader
2
- @debug_enabled: false
3
- @upload_started: false
4
- @audio_types = /(\.|\/)(wav|mp3|m4a|aiff|ogg|flac)$/i
5
- @video_types = /(\.|\/)(mp[e]?g|mov|avi|mp4|m4v)$/i
6
- @image_types = /(\.|\/)(jp[e]?g|png|bmp)$/i
7
- @resource_types = /(\.|\/)(pdf|ppt[x]?|doc[x]?)$/i
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
- @upload_details_container = $("##{options.upload_details_container}") ? $("#files")
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
- @resource = options.resource ? null
20
- @attribute = options.attribute ? null
21
-
22
- @files_for_upload = []
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
- self.add_file data
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
- data.context.update_progress_to(progress)
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
- setup_upload_button_handler: ->
59
- #alert( "setup_upload_button_handler for #{@element_id}" )
60
- unless ( @upload_automatically == true )
61
- DLog( "Setting up submit handler for element #{@element_id}")
62
- $(@element_id).closest( 'form' ).on( 'submit', { self: this }, this.form_submit_handler )
63
-
64
- form_submit_handler: (e) ->
65
- # alert( 'form_submit_handler' )
66
- e.preventDefault()
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
- is_within_limits: (upload_file) ->
92
- if !@limits[upload_file.media_type]? || @limits[upload_file.media_type] > @files_by_type(upload_file.media_type).length
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
- add_file: (data) ->
101
- upload_file = new AnacondaUploadFile data
102
- if @is_allowed_type(upload_file)
103
- if @is_within_limits(upload_file)
104
- @files_for_upload.push upload_file
105
- DLog(upload_file)
106
- @upload_details_container.append "<div id='upload_file_#{upload_file.id}' class='upload-file #{upload_file.media_type}'><span class='file-name'>#{upload_file.file.name}</span><span class='size'>#{upload_file.file.size}</span><span class='progress-percent'></span><div class='progress'><span class='progress-bar'></span></div></div>"
107
-
108
- if @upload_automatically == true
109
- DLog( "Upload Automatically: #{@upload_automatically}")
110
- upload_file.submit()
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
- alert "Only #{@limits[upload_file.media_type]} #{upload_file.media_type} files are allowed"
186
+ DLog "Not auto upload"
115
187
  else
116
- alert "#{upload_file.file.name} is a #{upload_file.media_type} file. Only #{@allowed_types.join(", ")} files are allowed."
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
- upload_file = data.context
119
- DLog "#{upload_file.file.name} completed uploading"
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( "#{@base_key}/#{upload_file.file.name}" )
147
- $( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_filename" ).val( upload_file.file.name )
148
- $( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_size" ).val( upload_file.file.size )
149
- $( @element_id ).siblings( '#' + "#{@resource}_#{@attribute}_type" ).val( upload_file.file.type )
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
- $( @element_id ).closest( 'form' ).submit() unless ( @upload_automatically == true )
240
+ @upload_in_progress = false;
241
+ @upload_completed = true;
242
+ @upload_manager().upload_completed()
152
243
 
153
- class @AnacondaUploadFile
154
244
 
155
- constructor: (@data) ->
156
- @file = @data.files[0]
157
- @media_type = @get_media_type()
158
- @id = @get_id()
159
-
160
- @set_context()
161
- get_id: ->
162
- hex_md5( "#{@file.name} #{@file.size}" )
163
- get_media_type: ->
164
- if AnacondaUploader.audio_types.test(@file.type) || AnacondaUploader.audio_types.test(@file.name)
165
- media_type = "audio"
166
- else if AnacondaUploader.video_types.test(@file.type) || AnacondaUploader.video_types.test(@file.name)
167
- media_type = "video"
168
- else if AnacondaUploader.image_types.test(@file.type) || AnacondaUploader.image_types.test(@file.name)
169
- media_type = "image"
170
- else if AnacondaUploader.resource_types.test(@file.type) || AnacondaUploader.resource_types.test(@file.name)
171
- media_type = "resource"
172
- else
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( anaconda_columns, options = {})
10
+ def anaconda_for( anaconda_column, options = {})
11
11
  send :include, InstanceMethods
12
-
13
- anaconda_columns = [anaconda_columns] if anaconda_columns.kind_of?(Symbol) || anaconda_columns.kind_of?(String)
14
- class_attribute :anaconda_columns
15
- self.anaconda_columns = anaconda_columns.collect{ |c| c.to_sym }
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 :anaconda_options
19
- self.anaconda_options = options.reverse_merge(
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
- base_key: "#{self.to_s.pluralize.downcase}/#{anaconda_columns.first.to_s.pluralize}/#{(0...32).map{(65+rand(26)).chr}.join.downcase}"
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]})
@@ -0,0 +1,7 @@
1
+ module Anaconda
2
+ class Error < RuntimeError
3
+ end
4
+
5
+ class AnacondaError < Error
6
+ end
7
+ end
@@ -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
- options = a_class.anaconda_options.dup
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, "data-url" => uploader.url, "data-form-data" => uploader.fields.to_json
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
- <strong>Files:</strong>
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
- jQuery(function() {
42
- return window.uploader = new AnacondaUploader({
43
- limits: {
44
- audio: 1,
45
- video: 1,
46
- resource: 1,
47
- image: 1
48
- },
49
- element_id: "##{options[:element_id]}",
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
- # Vastly inspired by http://railscasts.com/episodes/383-uploading-to-amazon-s3
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
- options = a_class.anaconda_options.dup
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
@@ -1,5 +1,5 @@
1
1
  module Anaconda
2
2
  module Rails
3
- VERSION = "0.1.2"
3
+ VERSION = "0.9.0"
4
4
  end
5
5
  end
@@ -8,8 +8,18 @@ module Anaconda
8
8
  argument :field_name, :type => :string, :default => "asset"
9
9
 
10
10
  def create_migration_file
11
- create_file "db/migrate/#{Time.now.strftime('%Y%m%d%H%M%S')}_anaconda_migration_for_#{file_name}.rb", <<-FILE
12
- class AnacondaMigrationFor#{file_name.titlecase} < ActiveRecord::Migration
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.1.2
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);