The Attachment class manages the files for a given attachment. It saves when the model saves, deletes when the model is destroyed, and processes the file upon assignment.
- assign
- background
- content_type
- default_options
- dirty?
- errors
- exist?
- instance_read
- instance_write
- interpolations
- new
- original_filename
- reprocess!
- save
- size
- to_s
- updated_at
- url
- valid?
| [R] | convert_options | |
| [R] | default_style | |
| [R] | instance | |
| [R] | name | |
| [R] | queued_for_write | |
| [R] | styles |
[ show source ]
# File lib/data_base/attachment/attach.rb, line 108
108: def self.default_options
109: @default_options ||= {
110: :url => "/uploads/:id_:style_:basename.:extension",
111: :path => ":rails_root/public/uploads/:id_:style_:basename.:extension",
112: :styles => {},
113: :default_url => "/images/backend/no-image.png",
114: :default_style => :original,
115: :validations => {},
116: :storage => :filesystem
117: }
118: end
A hash of procs that are run during the interpolation of a path or url. A variable of the format :name will be replaced with the return value of the proc named ":name". Each lambda takes the attachment and the current style as arguments. This hash can be added to with your own proc if necessary.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 297
297: def self.interpolations
298: @interpolations ||= {
299: :rails_root => lambda{|attachment,style| RAILS_ROOT },
300: :rails_env => lambda{|attachment,style| RAILS_ENV },
301: :class => lambda do |attachment,style|
302: attachment.instance.class.name.underscore.pluralize
303: end,
304: :basename => lambda do |attachment,style|
305: attachment.original_filename.gsub(/#{File.extname(attachment.original_filename)}$/, "")
306: end,
307: :extension => lambda do |attachment,style|
308: ((style = attachment.styles[style]) && style[:format]) ||
309: File.extname(attachment.original_filename).gsub(/^\.+/, "")
310: end,
311: :id => lambda{|attachment,style| attachment.instance.id },
312: :id_partition => lambda do |attachment, style|
313: ("%09d" % attachment.instance.id).scan(/\d{3}/).join("/")
314: end,
315: :attachment => lambda{|attachment,style| attachment.name.to_s.downcase.pluralize },
316: :style => lambda{|attachment,style| style || attachment.default_style },
317: }
318: end
Creates an Attachment object. name is the name of the attachment, instance is the ActiveRecord object instance it‘s attached to, and options is the same as the hash passed to has_attached_file.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 125
125: def initialize(name, instance, options = {})
126: @name = name
127: @instance = instance
128:
129: options = self.class.default_options.merge(options)
130:
131: @url = options[:url]
132: @path = options[:path]
133: @styles = options[:styles]
134: @default_url = options[:default_url]
135: @validations = options[:validations]
136: @default_style = options[:default_style]
137: @storage = options[:storage]
138: @whiny = options[:whiny_thumbnails]
139: @convert_options = options[:convert_options] || {}
140: @background = options[:background].nil? ? instance.respond_to?(:spawn) : options[:background]
141: @processors = options[:processors] || [:thumbnail]
142: @options = options
143: @queued_for_delete = []
144: @queued_for_write = {}
145: @errors = {}
146: @validation_errors = nil
147: @dirty = false
148:
149: normalize_style_definition
150: initialize_storage
151:
152: log("Attachment on #{instance.class} initialized.")
153: end
What gets called when you call instance.attachment = File. It clears errors, assigns attributes, processes the file, and runs validations. It also queues up the previous file for deletion, to be flushed away on save of its host. In addition to form uploads, you can also assign another Attachment attachment:
new_user.avatar = old_user.avatar
If the file that is assigned is not valid, the processing (i.e. thumbnailing, etc) will NOT be run.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 163
163: def assign(uploaded_file)
164: %w(file_name).each do |field|
165: unless @instance.class.column_names.include?("#{name}_#{field}")
166: raise AttachmentError.new("#{@instance.class} model does not have required column '#{name}_#{field}'")
167: end
168: end
169:
170: if uploaded_file.is_a?(Lipsiadmin::Attachment::Attach)
171: uploaded_file = uploaded_file.to_file(:original)
172: close_uploaded_file = uploaded_file.respond_to?(:close)
173: end
174:
175: return nil unless valid_assignment?(uploaded_file)
176: log("Assigning #{uploaded_file.inspect} to #{name}")
177:
178: uploaded_file.binmode if uploaded_file.respond_to? :binmode
179: queue_existing_for_delete
180: @errors = {}
181: @validation_errors = nil
182:
183: return nil if uploaded_file.nil?
184:
185: log("Writing attributes for #{name}")
186: @queued_for_write[:original] = uploaded_file.to_tempfile
187: instance_write(:file_name, uploaded_file.original_filename.strip.gsub(/[^\w\d\.\-]+/, '_'))
188: instance_write(:content_type, uploaded_file.content_type.to_s.strip)
189: instance_write(:file_size, uploaded_file.size.to_i)
190: instance_write(:updated_at, Time.now)
191:
192: @dirty = true
193:
194: solidify_style_definitions
195: post_process if valid?
196:
197: # Reset the file size if the original file was reprocessed.
198: instance_write(:file_size, @queued_for_write[:original].size.to_i)
199: ensure
200: uploaded_file.close if close_uploaded_file
201: validate
202: end
When processing, if the spawn plugin is installed, processing can be done in a background fork or thread if desired.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 469
469: def background(&blk)
470: # if instance.respond_to?(:spawn) && @background
471: # instance.spawn(&blk)
472: # else
473: blk.call
474: # end
475: end
Returns the content_type of the file as originally assigned, and lives in the <attachment>_content_type attribute of the model.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 281
281: def content_type
282: instance_read(:content_type)
283: end
Returns true if there are changes that need to be saved.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 247
247: def dirty?
248: @dirty
249: end
Returns an array containing the errors on this attachment.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 242
242: def errors
243: @errors
244: end
Returns true if a file has been assigned.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 343
343: def exist?
344: !original_filename.blank?
345: end
Reads the attachment-specific attribute on the instance. See instance_write for more details.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 358
358: def instance_read(attr)
359: getter = "#{name}_#{attr}""#{name}_#{attr}"
360: responds = instance.respond_to?(getter)
361: instance.send(getter) if responds || attr.to_s == "file_name"
362: end
Writes the attachment-specific attribute on the instance. For example, instance_write(:file_name, "me.jpg") will write "me.jpg" to the instance‘s "avatar_file_name" field (assuming the attachment is called avatar).
[ show source ]
# File lib/data_base/attachment/attach.rb, line 350
350: def instance_write(attr, value)
351: setter = "#{name}_#{attr}=""#{name}_#{attr}="
352: responds = instance.respond_to?(setter)
353: instance.send(setter, value) if responds || attr.to_s == "file_name"
354: end
Returns the name of the file as originally assigned, and lives in the <attachment>_file_name attribute of the model.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 269
269: def original_filename
270: instance_read(:file_name)
271: end
This method really shouldn‘t be called that often. It‘s expected use is in the attachment:refresh rake task and that‘s it. It will regenerate all thumbnails forcefully, by reobtaining the original file and going through the post-process again.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 324
324: def reprocess!
325: new_original = Tempfile.new("attachment-reprocess")
326: new_original.binmode
327: if old_original = to_file(:original)
328: new_original.write( old_original.read )
329: new_original.rewind
330:
331: @queued_for_write = { :original => new_original }
332: post_process
333:
334: old_original.close if old_original.respond_to?(:close)
335:
336: save
337: else
338: true
339: end
340: end
Saves the file, if there are no errors. If there are, it flushes them to the instance‘s errors and returns false, cancelling the save.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 253
253: def save
254: if valid?
255: log("Saving files for #{name}")
256: flush_deletes
257: flush_writes
258: @dirty = false
259: true
260: else
261: log("Errors on #{name}. Not saving.")
262: flush_errors
263: false
264: end
265: end
Returns the size of the file as originally assigned, and lives in the <attachment>_file_size attribute of the model.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 275
275: def size
276: instance_read(:file_size) || (@queued_for_write[:original] && @queued_for_write[:original].size)
277: end
Alias to url
[ show source ]
# File lib/data_base/attachment/attach.rb, line 231
231: def to_s(style = nil)
232: url(style)
233: end
Returns the last modified time of the file as originally assigned, and lives in the <attachment>_updated_at attribute of the model.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 287
287: def updated_at
288: time = instance_read(:updated_at)
289: time && time.to_i
290: end
Returns the public URL of the attachment, with a given style. Note that this does not necessarily need to point to a file that your web server can access and can point to an action in your app, if you need fine grained security. This is not recommended if you don‘t need the security, however, for performance reasons. set include_updated_timestamp to false if you want to stop the attachment update time appended to the url
[ show source ]
# File lib/data_base/attachment/attach.rb, line 211
211: def url(style = default_style, include_updated_timestamp = true)
212: if original_filename.nil?
213: url = interpolate(@default_url, style)
214: elsif File.exist?(path(style))
215: url = interpolate(@url, style)
216: else
217: url = interpolate(@url, :original)
218: end
219: include_updated_timestamp && updated_at ? [url, updated_at].compact.join(url.include?("?") ? "&" : "?") : url
220: end
Returns true if there are no errors on this attachment.
[ show source ]
# File lib/data_base/attachment/attach.rb, line 236
236: def valid?
237: validate
238: errors.empty?
239: end