Module: FileColumn::ClassMethods
- Defined in:
- lib/file_column.rb
Overview
The FileColumn module allows you to easily handle file uploads. You can designate one or more columns of your model’s table as “file columns” like this:
class Entry < ActiveRecord::Base
file_column :image
end
Now, by default, an uploaded file “test.png” for an entry object with primary key 42 will be stored in in “public/entry/image/42/test.png”. The filename “test.png” will be stored in the record’s “image” column. The “entries” table should have a VARCHAR
column named “image”.
The methods of this module are automatically included into ActiveRecord::Base
as class methods, so that you can use them in your models.
Generated Methods
After calling “file_column :image
” as in the example above, a number of instance methods will automatically be generated, all prefixed by “image”:
-
Entry#image=(uploaded_file)
: this will handle a newly uploaded file (see below). Note that you can simply call your upload field “entry” in your view (or use the helper). -
Entry#image(subdir=nil)
: This will return an absolute path (as a string) to the currently uploaded file or nil if no file has been uploaded -
Entry#image_relative_path(subdir=nil)
: This will return a path relative to this file column’s base directory as a string or nil if no file has been uploaded. This would be “42/test.png” in the example. -
Entry#image_just_uploaded?
: Returns true if a new file has been uploaded to this instance. You can use this in your code to perform certain actions (e. g., validation, custom post-processing) only on newly uploaded files.
You can access the raw value of the “image” column (which will contain the filename) via the ActiveRecord::Base#attributes
or ActiveRecord::Base#[]
methods like this:
entry['image'] # e.g."test.png"
Storage of uploaded files
For a model class Entry
and a column image
, all files will be stored under “public/entry/image”. A sub-directory named after the primary key of the object will be created, so that files can be stored using their real filename. For example, a file “test.png” stored in an Entry object with id 42 will be stored in
public/entry/image/42/test.png
Files will be moved to this location in an after_save
callback. They will be stored in a temporary location previously as explained in the next section.
By default, files will be created with unix permissions of 0644
(i. e., owner has read/write access, group and others only have read access). You can customize this by passing the desired mode as a :permissions
options. The value you give here is passed directly to File::chmod
, so on Unix you should give some octal value like 0644, for example.
Handling of form redisplay
Suppose you have a form for creating a new object where the user can upload an image. The form may have to be re-displayed because of validation errors. The uploaded file has to be stored somewhere so that the user does not have to upload it again. FileColumn will store these in a temporary directory (called “tmp” and located under the column’s base directory by default) so that it can be moved to the final location if the object is successfully created. If the form is never completed, though, you can easily remove all the images in this “tmp” directory once per day or so.
So in the example above, the image “test.png” would first be stored in “public/entry/image/tmp/<some_random_key>/test.png” and be moved to “public/entry/image/<primary_key>/test.png”.
This temporary location of newly uploaded files has another advantage when updating objects. If the update fails for some reasons (e.g. due to validations), the existing image will not be overwritten, so it has a kind of “transactional behaviour”.
Additional Files and Directories
FileColumn allows you to keep more than one file in a directory and will move/delete all the files and directories it finds in a model object’s directory when necessary.
As a convenience you can access files stored in sub-directories via the subdir
parameter if they have the same filename.
Suppose your uploaded file is named “vancouver.jpg” and you want to create a thumb-nail and store it in the “thumb” directory. If you call image("thumb")
, you will receive an absolute path for the file “thumb/vancouver.jpg” in the same directory “vancouver.jpg” is stored. Look at the documentation of FileColumn::Magick for more examples and how to create these thumb-nails automatically.
File Extensions
FileColumn will try to fix the file extension of uploaded files, so that the files are served with the correct mime-type by your web-server. Most web-servers are setting the mime-type based on the file’s extension. You can disable this behaviour by passing the :fix_file_extensions
option with a value of nil
to file_column
.
In order to set the correct extension, FileColumn tries to determine the files mime-type first. It then uses the MIME_EXTENSIONS
hash to choose the corresponding file extension. You can override this hash by passing in a :mime_extensions
option to file_column
.
The mime-type of the uploaded file is determined with the following steps:
-
Run the external “file” utility. You can specify the full path to the executable in the
:file_exec
option or set this option tonil
to disable this step -
If the file utility couldn’t determine the mime-type or the utility was not present, the content-type provided by the user’s browser is used as a fallback.
Custom Storage Directories
FileColumn’s storage location is determined in the following way. All files are saved below the so-called “root_path” directory, which defaults to “RAILS_ROOT/public”. For every file_column, you can set a separte “store_dir” option. It defaults to “model_name/attribute_name”.
Files will always be stored in sub-directories of the store_dir path. The subdirectory is named after the instance’s id
attribute for a saved model, or “tmp/<randomkey>” for unsaved models.
You can specify a custom root_path by setting the :root_path
option.
You can specify a custom storage_dir by setting the :storage_dir
option.
For setting a static storage_dir that doesn’t change with respect to a particular instance, you assign :storage_dir
a String representing a directory as an absolute path.
If you need more fine-grained control over the storage directory, you can use the name of a callback-method as a symbol for the :store_dir
option. This method has to be defined as an instance method in your model. It will be called without any arguments whenever the storage directory for an uploaded file is needed. It should return a String representing a directory relativeo to root_path.
Uploaded files for unsaved models objects will be stored in a temporary directory. By default this directory will be a “tmp” directory in your :store_dir
. You can override this via the :tmp_base_dir
option.
Constant Summary collapse
- MIME_EXTENSIONS =
default mapping of mime-types to file extensions. FileColumn will try to rename a file to the correct extension if it detects a known mime-type
{ "image/gif" => "gif", "image/jpeg" => "jpg", "image/pjpeg" => "jpg", "image/x-png" => "png", "image/jpg" => "jpg", "image/png" => "png", "application/x-shockwave-flash" => "swf", "application/pdf" => "pdf", "application/pgp-signature" => "sig", "application/futuresplash" => "spl", "application/msword" => "doc", "application/postscript" => "ps", "application/x-bittorrent" => "torrent", "application/x-dvi" => "dvi", "application/x-gzip" => "gz", "application/x-ns-proxy-autoconfig" => "pac", "application/x-shockwave-flash" => "swf", "application/x-tgz" => "tar.gz", "application/x-tar" => "tar", "application/zip" => "zip", "audio/mpeg" => "mp3", "audio/x-mpegurl" => "m3u", "audio/x-ms-wma" => "wma", "audio/x-ms-wax" => "wax", "audio/x-wav" => "wav", "image/x-xbitmap" => "xbm", "image/x-xpixmap" => "xpm", "image/x-xwindowdump" => "xwd", "text/css" => "css", "text/html" => "html", "text/javascript" => "js", "text/plain" => "txt", "text/xml" => "xml", "video/mpeg" => "mpeg", "video/quicktime" => "mov", "video/x-msvideo" => "avi", "video/x-ms-asf" => "asf", "video/x-ms-wmv" => "wmv" }
- EXTENSIONS =
Set.new MIME_EXTENSIONS.values
- DEFAULT_OPTIONS =
default options. You can override these with
file_column
‘soptions
parameter { :root_path => File.join(RAILS_ROOT, "public"), :web_root => "", :mime_extensions => MIME_EXTENSIONS, :extensions => EXTENSIONS, :fix_file_extensions => true, :permissions => 0644, # path to the unix "file" executbale for # guessing the content-type of files :file_exec => "file" }
Instance Method Summary collapse
-
#file_column(attr, options = {}) ⇒ Object
handle the
attr
attribute as a “file-upload” column, generating additional methods as explained above.
Instance Method Details
#file_column(attr, options = {}) ⇒ Object
handle the attr
attribute as a “file-upload” column, generating additional methods as explained above. You should pass the attribute’s name as a symbol, like this:
file_column :image
You can pass in an options hash that overrides the options in DEFAULT_OPTIONS
.
616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 |
# File 'lib/file_column.rb', line 616 def file_column(attr, ={}) = DEFAULT_OPTIONS.merge() if = FileColumn::(, ActiveSupport::Inflector.underscore(self.name).to_s, attr.to_s) state_attr = "@#{attr}_state".to_sym state_method = "#{attr}_state".to_sym define_method state_method do result = instance_variable_get state_attr if result.nil? result = FileColumn::create_state(self, attr.to_s) instance_variable_set state_attr, result end result end private state_method define_method attr do |*args| send(state_method).absolute_path *args end define_method "#{attr}_relative_path" do |*args| send(state_method).relative_path *args end define_method "#{attr}_dir" do send(state_method).absolute_dir end define_method "#{attr}_relative_dir" do send(state_method).relative_dir end define_method "#{attr}=" do |file| state = send(state_method).assign(file) instance_variable_set state_attr, state if state.[:after_upload] and state.just_uploaded? state.[:after_upload].each do |sym| self.send sym end end end define_method "#{attr}_temp" do send(state_method).temp_path end define_method "#{attr}_temp=" do |temp_path| instance_variable_set state_attr, send(state_method).assign_temp(temp_path) end after_save_method = "#{attr}_after_save".to_sym define_method after_save_method do instance_variable_set state_attr, send(state_method).after_save end after_save after_save_method after_destroy_method = "#{attr}_after_destroy".to_sym define_method after_destroy_method do send(state_method).after_destroy end after_destroy after_destroy_method define_method "#{attr}_just_uploaded?" do send(state_method).just_uploaded? end # this creates a closure keeping a reference to my_options # right now that's the only way we store the options. We # might use a class attribute as well define_method "#{attr}_options" do end private after_save_method, after_destroy_method FileColumn::MagickExtension::file_column(self, attr, ) if [:magick] end |