effective_assets 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/MIT-LICENSE +20 -0
- data/README.md +228 -0
- data/Rakefile +23 -0
- data/app/assets/images/effective_assets/icon_close.png +0 -0
- data/app/assets/images/effective_assets/s3_down_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_over_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_up_button.gif +0 -0
- data/app/assets/images/effective_assets/s3_upload.swf +0 -0
- data/app/assets/images/effective_assets/spinner.gif +0 -0
- data/app/assets/images/mime-types/excel.jpg +0 -0
- data/app/assets/images/mime-types/file.png +0 -0
- data/app/assets/images/mime-types/mp3.png +0 -0
- data/app/assets/images/mime-types/pdf.png +0 -0
- data/app/assets/images/mime-types/video.png +0 -0
- data/app/assets/images/mime-types/word.jpg +0 -0
- data/app/assets/images/mime-types/zip.png +0 -0
- data/app/assets/javascripts/effective_assets.js +1 -0
- data/app/assets/javascripts/effective_assets/asset_box_input.js.coffee +71 -0
- data/app/assets/javascripts/effective_assets/asset_box_uploader.js +122 -0
- data/app/assets/javascripts/effective_assets/asset_box_uploader_customization.js +166 -0
- data/app/assets/javascripts/effective_assets/jquery_ui_sortable.js +2106 -0
- data/app/assets/stylesheets/effective_assets.css.scss +1 -0
- data/app/assets/stylesheets/effective_assets/_asset_box_input.scss +206 -0
- data/app/controllers/effective/attachments_controller.rb +14 -0
- data/app/controllers/effective/s3_uploads_controller.rb +98 -0
- data/app/helpers/effective_assets_helper.rb +55 -0
- data/app/models/concerns/acts_as_asset_box.rb +97 -0
- data/app/models/effective/asset.rb +224 -0
- data/app/models/effective/attachment.rb +25 -0
- data/app/models/effective/delayed_job.rb +99 -0
- data/app/models/inputs/asset_box_input.rb +87 -0
- data/app/models/validators/asset_box_length_validator.rb +11 -0
- data/app/models/validators/asset_box_presence_validator.rb +6 -0
- data/app/uploaders/asset_uploader.rb +26 -0
- data/app/uploaders/effective_assets_uploader.rb +63 -0
- data/app/views/active_admin/effective_assets/_edit.html.haml +35 -0
- data/app/views/active_admin/effective_assets/_form.html.haml +4 -0
- data/app/views/active_admin/effective_assets/_new.html.haml +3 -0
- data/app/views/asset_box_input/_attachment_fields.html.haml +14 -0
- data/app/views/asset_box_input/_uploader.html.haml +119 -0
- data/app/views/assets/_video.html.erb +4 -0
- data/config/routes.rb +6 -0
- data/db/migrate/01_create_effective_assets.rb.erb +42 -0
- data/lib/effective_assets.rb +31 -0
- data/lib/effective_assets/engine.rb +38 -0
- data/lib/effective_assets/version.rb +3 -0
- data/lib/generators/effective_assets/install_generator.rb +41 -0
- data/lib/generators/templates/README +1 -0
- data/lib/generators/templates/asset_uploader.rb +25 -0
- data/lib/generators/templates/effective_assets.rb +19 -0
- data/lib/tasks/effective_assets_tasks.rake +4 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +58 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/schema.rb +16 -0
- data/spec/dummy/db/test.sqlite3 +0 -0
- data/spec/dummy/log/development.log +71 -0
- data/spec/dummy/log/test.log +33 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec_link +3 -0
- data/spec/models/asset_spec.rb +46 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/factories.rb +28 -0
- metadata +431 -0
@@ -0,0 +1,224 @@
|
|
1
|
+
module Effective
|
2
|
+
class Asset < ActiveRecord::Base
|
3
|
+
self.table_name = EffectiveAssets.assets_table_name.to_s
|
4
|
+
|
5
|
+
mount_uploader :data, EffectiveAssets.uploader
|
6
|
+
|
7
|
+
belongs_to :user if defined?(User)
|
8
|
+
# This is the user that uploaded the asset.
|
9
|
+
# We are using belongs_to because it makes all the permissions 'just work',
|
10
|
+
# in letting people create assets.
|
11
|
+
#
|
12
|
+
# But this doesn't exactly belong to a user.
|
13
|
+
# Assets belong to acts_as_asset_box enabled classes through attachments.
|
14
|
+
#
|
15
|
+
# 1. The S3 uploader makes a post to s3_controller#create
|
16
|
+
# 2. The asset is created by the current_user
|
17
|
+
# 3. But it doesn't belong to anything yet. It might be attached to a user's asset_boxes, or a page, or a post through attachments
|
18
|
+
#
|
19
|
+
|
20
|
+
has_many :attachments, :dependent => :delete_all
|
21
|
+
|
22
|
+
structure do
|
23
|
+
title :string
|
24
|
+
description :text
|
25
|
+
tags :string
|
26
|
+
|
27
|
+
content_type :string, :validates => [:presence]
|
28
|
+
upload_file :string # The full url of the file, as originally uploaded
|
29
|
+
processed :boolean, :default => false
|
30
|
+
|
31
|
+
data :string
|
32
|
+
|
33
|
+
data_size :integer
|
34
|
+
height :integer, :validates => [:numericality => { :allow_nil => true }]
|
35
|
+
width :integer, :validates => [:numericality => { :allow_nil => true }]
|
36
|
+
|
37
|
+
versions_info :text # We store a hash of {:thumb => 34567, :medium => 3343434} data sizes
|
38
|
+
|
39
|
+
timestamps
|
40
|
+
end
|
41
|
+
|
42
|
+
serialize :versions_info, Hash
|
43
|
+
|
44
|
+
#attr_accessible :title, :description, :tags, :content_type, :data_size, :upload_file, :user, :user_id, :id
|
45
|
+
#validates_presence_of :user_id
|
46
|
+
|
47
|
+
before_save :update_asset_dimensions
|
48
|
+
|
49
|
+
default_scope order('created_at DESC')
|
50
|
+
|
51
|
+
scope :images, -> { where('content_type LIKE ?', '%image%') }
|
52
|
+
scope :videos, -> { where('content_type LIKE ?', '%video%') }
|
53
|
+
scope :audio, -> { where('content_type LIKE ?', '%audio%') }
|
54
|
+
scope :files, -> { where('(content_type NOT LIKE ?) AND (content_type NOT LIKE ?) AND (content_type NOT LIKE ?)', '%image%', '%video%', '%audio%') }
|
55
|
+
|
56
|
+
scope :today, -> { where("created_at >= ?", Date.today.beginning_of_day) }
|
57
|
+
scope :this_week, -> { where("created_at >= ?", Date.today.beginning_of_week) }
|
58
|
+
scope :this_month, -> { where("created_at >= ?", Date.today.beginning_of_month) }
|
59
|
+
|
60
|
+
class << self
|
61
|
+
def s3_base_path
|
62
|
+
"http://#{EffectiveAssets.aws_bucket}.s3.amazonaws.com"
|
63
|
+
end
|
64
|
+
|
65
|
+
def string_base_path
|
66
|
+
"string://"
|
67
|
+
end
|
68
|
+
|
69
|
+
# Just call me with Asset.create_from_url('http://somewhere.com/somthing.jpg')
|
70
|
+
def create_from_url(url, options = {})
|
71
|
+
opts = {:upload_file => url, :user_id => 1}.merge(options)
|
72
|
+
|
73
|
+
if (asset = Asset.create(opts))
|
74
|
+
Effective::DelayedJob.new.process_asset(asset)
|
75
|
+
asset
|
76
|
+
else
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# We have just uploaded an asset via our s3 uploader
|
82
|
+
# We want this image to be immediately available.
|
83
|
+
def create_from_s3_uploader(url, options = {})
|
84
|
+
opts = {:upload_file => "#{Asset.s3_base_path}/#{url}", :user_id => 1}.merge(options)
|
85
|
+
|
86
|
+
asset = false
|
87
|
+
|
88
|
+
Asset.transaction do
|
89
|
+
begin
|
90
|
+
asset = Asset.create!(opts)
|
91
|
+
|
92
|
+
Rails.logger.info "Copying s3 uploaded file to final resting place..."
|
93
|
+
storage = Fog::Storage.new(:provider => 'AWS', :aws_access_key_id => EffectiveAssets.aws_access_key_id, :aws_secret_access_key => EffectiveAssets.aws_secret_access_key)
|
94
|
+
storage.copy_object(EffectiveAssets.aws_bucket, url, EffectiveAssets.aws_bucket, "#{EffectiveAssets.aws_final_path.chomp('/')}/#{asset.id}/#{asset.file_name}")
|
95
|
+
storage.put_object_acl(EffectiveAssets.aws_bucket, "#{EffectiveAssets.aws_final_path.chomp('/')}/#{asset.id}/#{asset.file_name}", EffectiveAssets.aws_acl)
|
96
|
+
|
97
|
+
Rails.logger.info "Deleting original..."
|
98
|
+
directory = storage.directories.get(EffectiveAssets.aws_bucket)
|
99
|
+
directory.files.new(:key => url).destroy
|
100
|
+
|
101
|
+
asset.update_column(:upload_file, asset.url) # This is our upload file as far as CarrierWave is now concerned
|
102
|
+
|
103
|
+
Effective::DelayedJob.new.process_asset(asset)
|
104
|
+
rescue => e
|
105
|
+
Rails.logger.info e.message
|
106
|
+
Rails.logger.info e.backtrace.join('\n')
|
107
|
+
asset = false
|
108
|
+
end
|
109
|
+
|
110
|
+
raise ActiveRecord::Rollback unless asset
|
111
|
+
end
|
112
|
+
|
113
|
+
asset
|
114
|
+
end
|
115
|
+
|
116
|
+
# This loads the raw contents of a string into a file and uploads that file to s3
|
117
|
+
# Expect to be passed something like
|
118
|
+
# Asset.create_from_string('some binary stuff from a string', :filename => 'icon_close.png', :content_type => 'image/png')
|
119
|
+
def create_from_string(str, options = {})
|
120
|
+
filename = options.delete(:filename) || "file-#{Time.now.strftime('%Y-%m-%d-%H-%M-%S')}.txt"
|
121
|
+
|
122
|
+
opts = {:upload_file => "#{Asset.string_base_path}#{filename}", :user_id => 1}.merge(options)
|
123
|
+
|
124
|
+
asset = Asset.new(opts)
|
125
|
+
asset.data = AssetStringIO.new(filename, str)
|
126
|
+
|
127
|
+
if asset.save
|
128
|
+
Effective::DelayedJob.new.process_asset(asset)
|
129
|
+
asset
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
before_validation do
|
135
|
+
if !content_type.present? or content_type == 'null' or content_type == 'unknown' or content_type == 'application/octet-stream'
|
136
|
+
self.content_type = case url.to_s[-4, 4].downcase
|
137
|
+
when '.mp3' ; 'audio/mp3'
|
138
|
+
when '.mp4' ; 'video/mp4'
|
139
|
+
when '.mov' ; 'video/mov'
|
140
|
+
when '.m2v' ; 'video/m2v'
|
141
|
+
when '.m4v' ; 'video/m4v'
|
142
|
+
when '.3gp' ; 'video/3gp'
|
143
|
+
when '.3g2' ; 'video/3g2'
|
144
|
+
when '.avi' ; 'video/avi'
|
145
|
+
when '.jpg' ; 'image/jpg'
|
146
|
+
when '.gif' ; 'image/gif'
|
147
|
+
when '.png' ; 'image/png'
|
148
|
+
when '.bmp' ; 'image/bmp'
|
149
|
+
when '.ico' ; 'image/x-icon'
|
150
|
+
else ; 'unknown'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def title
|
156
|
+
self[:title].present? ? self[:title] : file_name
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return the final location of this asset
|
160
|
+
def url
|
161
|
+
"#{Asset.s3_base_path}/#{EffectiveAssets.aws_final_path.chomp('/')}/#{self.id.to_i}/#{upload_file.to_s.split('/').last}"
|
162
|
+
end
|
163
|
+
|
164
|
+
def file_name
|
165
|
+
url.split('/').last rescue url
|
166
|
+
end
|
167
|
+
|
168
|
+
def video?
|
169
|
+
content_type.include? 'video'
|
170
|
+
end
|
171
|
+
|
172
|
+
def image?
|
173
|
+
content_type.include? 'image'
|
174
|
+
end
|
175
|
+
|
176
|
+
def icon?
|
177
|
+
content_type.include? 'image/x-icon'
|
178
|
+
end
|
179
|
+
|
180
|
+
def audio?
|
181
|
+
content_type.include? 'audio'
|
182
|
+
end
|
183
|
+
|
184
|
+
def as_json(options={})
|
185
|
+
{:thumbnail => image_tag(:thumb).html_safe}.merge super
|
186
|
+
end
|
187
|
+
|
188
|
+
def still_processing?
|
189
|
+
!processed
|
190
|
+
end
|
191
|
+
|
192
|
+
def versions_info
|
193
|
+
self[:versions_info] || {}
|
194
|
+
end
|
195
|
+
|
196
|
+
protected
|
197
|
+
# Called in the DelayedJob
|
198
|
+
def update_asset_dimensions
|
199
|
+
if data.present? and data_changed? and image?
|
200
|
+
begin
|
201
|
+
image = MiniMagick::Image.open(self.data.path)
|
202
|
+
self.width = image[:width]
|
203
|
+
self.height = image[:height]
|
204
|
+
rescue => e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
true
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class AssetStringIO < StringIO
|
212
|
+
attr_accessor :filepath
|
213
|
+
|
214
|
+
def initialize(*args)
|
215
|
+
super(*args[1..-1])
|
216
|
+
@filepath = args[0]
|
217
|
+
end
|
218
|
+
|
219
|
+
def original_filename
|
220
|
+
File.basename(filepath)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This is a join table for assets and acts_as_attachable
|
2
|
+
|
3
|
+
module Effective
|
4
|
+
class Attachment < ActiveRecord::Base
|
5
|
+
self.table_name = EffectiveAssets.attachments_table_name.to_s
|
6
|
+
|
7
|
+
belongs_to :asset
|
8
|
+
belongs_to :attachable, :polymorphic => true
|
9
|
+
|
10
|
+
structure do
|
11
|
+
position :integer, :validates => [:presence, :numericality]
|
12
|
+
box :string, :default => 'assets', :validates => [:presence] # This is essentially a category
|
13
|
+
end
|
14
|
+
|
15
|
+
validates_presence_of :asset_id
|
16
|
+
|
17
|
+
#attr_accessible :box, :position, :asset_id, :attachable_type, :attachable_id
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def default_scope
|
21
|
+
includes(:asset).order(:position)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
#require 'uri'
|
2
|
+
# Call with DelayedJob.new.process_asset_images(...)
|
3
|
+
# Run jobs locally with "rake jobs:work"
|
4
|
+
|
5
|
+
module Effective
|
6
|
+
class DelayedJob
|
7
|
+
def process_asset(asset)
|
8
|
+
DelayedJob.configure_carrierwave
|
9
|
+
|
10
|
+
if asset and !asset.processed? and asset.upload_file.present?
|
11
|
+
begin
|
12
|
+
puts "Processing an asset ID ##{asset.id}..."
|
13
|
+
|
14
|
+
if asset.upload_file.include?("#{Asset.s3_base_path}")
|
15
|
+
if asset.image?
|
16
|
+
puts "Asset is an image in our S3 assets directory. Downloading and processing..."
|
17
|
+
|
18
|
+
# Carrierwave must download the file, process it, then re-upload it to S3
|
19
|
+
asset.remote_data_url = asset.upload_file
|
20
|
+
asset.processed = true
|
21
|
+
asset.save!
|
22
|
+
else
|
23
|
+
puts "Asset is a non-image in our S3 uploads directory. Copying to final location..."
|
24
|
+
|
25
|
+
# We have uploaded a video, or something non-image to our S3 bucket.
|
26
|
+
# We do not currently process anything.
|
27
|
+
|
28
|
+
puts "Marking local asset as processed..."
|
29
|
+
asset.update_column(:data, asset.file_name)
|
30
|
+
asset.processed = true
|
31
|
+
asset.save!
|
32
|
+
end
|
33
|
+
elsif asset.upload_file.include?(Asset.string_base_path)
|
34
|
+
puts "Asset is a string-based asset. Processing..."
|
35
|
+
|
36
|
+
asset.data.cache_stored_file!
|
37
|
+
asset.data.retrieve_from_cache!(asset.data.cache_name)
|
38
|
+
asset.data.recreate_versions!
|
39
|
+
asset.processed = true
|
40
|
+
asset.save!
|
41
|
+
else
|
42
|
+
puts "Asset is not an s3 uploaded asset. Downloading and processing..."
|
43
|
+
|
44
|
+
# Carrierwave must download the file, process it, then re-upload it to S3
|
45
|
+
asset.remote_data_url = asset.upload_file
|
46
|
+
asset.processed = true
|
47
|
+
asset.save!
|
48
|
+
end
|
49
|
+
|
50
|
+
puts "Successfully processed the asset."
|
51
|
+
rescue => e
|
52
|
+
puts "An error occurred while processing an asset:"
|
53
|
+
puts e.message
|
54
|
+
puts e.backtrace.inspect
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
handle_asynchronously :process_asset
|
59
|
+
|
60
|
+
def reprocess_all_assets
|
61
|
+
DelayedJob.configure_carrierwave
|
62
|
+
|
63
|
+
Asset.all.each do |asset|
|
64
|
+
begin
|
65
|
+
puts "Processing Asset ID=#{asset.id}..."
|
66
|
+
asset.data.cache_stored_file!
|
67
|
+
asset.data.retrieve_from_cache!(asset.data.cache_name)
|
68
|
+
asset.data.recreate_versions!
|
69
|
+
asset.save!
|
70
|
+
puts "Successfully processed #{asset.inspect}"
|
71
|
+
rescue => e
|
72
|
+
puts "ERROR: #{asset.id} -> #{e.to_s}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
handle_asynchronously :reprocess_all_assets
|
77
|
+
|
78
|
+
def self.configure_carrierwave
|
79
|
+
return if defined? @@configured_carrierwave
|
80
|
+
|
81
|
+
CarrierWave.configure do |config|
|
82
|
+
config.fog_credentials = {
|
83
|
+
:provider => 'AWS',
|
84
|
+
:aws_access_key_id => EffectiveAssets.aws_access_key_id,
|
85
|
+
:aws_secret_access_key => EffectiveAssets.aws_secret_access_key
|
86
|
+
}
|
87
|
+
config.fog_directory = EffectiveAssets.aws_bucket
|
88
|
+
config.fog_public = EffectiveAssets.aws_acl.to_s.include?('public')
|
89
|
+
config.fog_attributes = {'Cache-Control'=>'max-age=315576000'}
|
90
|
+
config.cache_dir = "#{Rails.root}/tmp/uploads" # For heroku
|
91
|
+
end
|
92
|
+
|
93
|
+
Rails.logger.info "CONFIGURED CARRIERWAVE"
|
94
|
+
|
95
|
+
@@configured_carrierwave = true
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require "formtastic"
|
2
|
+
|
3
|
+
# With formtastic, just use
|
4
|
+
#
|
5
|
+
# = f.input :pictures, :as => :asset_box
|
6
|
+
# = f.input :fav_icon, :as => :asset_box, :limit => 4, :file_types => [:jpg, :gif, :png]
|
7
|
+
# = f.input :logo, :as => :asset_box, :uploader => false, :dialog => true
|
8
|
+
# = f.input :logo, :as => :asset_box, :uploader => true, :uploader_visible => true
|
9
|
+
|
10
|
+
class AssetBoxInput
|
11
|
+
include ::Formtastic::Inputs::Base
|
12
|
+
|
13
|
+
def to_html
|
14
|
+
@@uid = (@@uid ||= 0) + 1 # We just need a unique number to pass along, incase we have multiple SWF Uploaders per form
|
15
|
+
|
16
|
+
input_wrapping do
|
17
|
+
output = label_html
|
18
|
+
output += header_html
|
19
|
+
output += "<div class='attachments'>".html_safe
|
20
|
+
output += build_values_html
|
21
|
+
output += "</div>".html_safe
|
22
|
+
|
23
|
+
if options[:uploader]
|
24
|
+
output += insert_uploader_html
|
25
|
+
output += uploader_html
|
26
|
+
end
|
27
|
+
|
28
|
+
if options[:dialog]
|
29
|
+
output += insert_dialog_html
|
30
|
+
end
|
31
|
+
|
32
|
+
output += "</div>".html_safe
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def header_html
|
37
|
+
"<div class='asset_box_input #{method.to_s.pluralize}' data-box='#{method.to_s.pluralize}' data-swf='s3_swf_#{@@uid}' data-limit='#{limit}' data-attachable-id='#{attachable_id}' data-attachable-type='#{attachable_type}'>".html_safe
|
38
|
+
end
|
39
|
+
|
40
|
+
def insert_uploader_html
|
41
|
+
"<a href='#' class='asset-box-upload'>Upload...</a>".html_safe
|
42
|
+
end
|
43
|
+
|
44
|
+
def uploader_html
|
45
|
+
template.render(:partial => 'asset_box_input/uploader', :locals => {:attachable_id => attachable_id, :attachable_type => attachable_type, :box => method.to_s.pluralize, :uid => @@uid, :file_types => options[:file_types], :limit => limit, :uploader_visible => options[:uploader_visible]}).html_safe
|
46
|
+
end
|
47
|
+
|
48
|
+
def insert_dialog_html
|
49
|
+
"<a href='#' class='asset-box-dialog'>Attach...</a>".html_safe
|
50
|
+
end
|
51
|
+
|
52
|
+
def build_values_html
|
53
|
+
count = 0
|
54
|
+
attachments_limit = limit
|
55
|
+
|
56
|
+
attachments.map do |attachment|
|
57
|
+
count += 1 unless attachment.marked_for_destruction?
|
58
|
+
|
59
|
+
template.render(
|
60
|
+
:partial => 'asset_box_input/attachment_fields',
|
61
|
+
:locals => {:attachment => attachment, :attachable_type => attachable_type, :hidden => (count > attachments_limit) }
|
62
|
+
)
|
63
|
+
end.join.html_safe
|
64
|
+
end
|
65
|
+
|
66
|
+
def attachments
|
67
|
+
method_name = method.to_s.pluralize
|
68
|
+
object.attachments.select { |attachment| attachment.box == method_name }
|
69
|
+
end
|
70
|
+
|
71
|
+
def attachable_type
|
72
|
+
options[:attachable_type] || object.class.name.titleize.gsub(" ", "_").gsub('/', '_').downcase
|
73
|
+
end
|
74
|
+
|
75
|
+
def attachable_id
|
76
|
+
options[:attachable_id] || (object.try(:id) rescue nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
def limit
|
80
|
+
method.to_s == method.to_s.pluralize ? (options[:limit] || 10000) : 1
|
81
|
+
end
|
82
|
+
|
83
|
+
def options
|
84
|
+
{:uploader => false, :dialog => false, :uploader_visible => false}.merge(super)
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
class AssetBoxLengthValidator < ActiveModel::EachValidator
|
2
|
+
def validate_each(record, attribute, value)
|
3
|
+
num_in_box = (record.attachments || []).select { |attachment| attachment.box == attribute.to_s.pluralize && attachment.marked_for_destruction? == false }.size
|
4
|
+
|
5
|
+
if options[:with]
|
6
|
+
record.errors[attribute] << "requires at least #{options[:with]} #{attribute.to_s.pluralize}" if num_in_box < options[:with]
|
7
|
+
elsif options[:in]
|
8
|
+
record.errors[attribute] << "requires #{options[:in].min} to #{options[:in].max} #{attribute.to_s.pluralize}" if options[:in].include?(num_in_box) == false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|