Navigation Menu

Skip to content

toy/upload_column

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

UploadColumn

Upload_column is a plugin for the Ruby on Rails framework that enables easy uploading of files, especially images.

Suppose you have a list of users, and you would like to associate a picture to each of them. You could upload the image to a database, or you could use upload_column for simple storage to the file system.

Let’s create our database first, generate a migration (if you’re not using migrations yet you are missing out!) and add

create_table "users" do |t|
  t.column "name", :string
  t.column "picture", :string
end

Now generate a scaffold for your user class

Create your model class and add the upload_column method call:

class User < ActiveRecord::Base
  upload_column :picture
end

Have a look at UploadColumn::ClassMethods.upload_column to find out more about specialized options. Note that the picture column in the database will not store the actual picture, it will only store the filename.

Now just open up your _form.rhtml partial, and edit it so it looks like the following:

<p><label for="user_picture">Picture</label><br/>
<%= upload_column_field 'user', 'picture'  %></p>

Here we’re making a call to UploadColumnHelper.upload_column_field, this will create an input field of the file type.

We’ll need to set the form to multipart, so that the picture will actually be sent as well. You can use the upload_form_tag helper:

<%= upload_form_tag( :action => 'create' ) %>

And that’s it! Your uploads are up and running (hopefully) and you should now be able to add pictures to your users. The madness doesn’t stop there of course!

Storage Path

You won’t always want to store the pictures in the directory that upload_column selects for you, but that’s not a problem, because changing that directory is trivial. You can pass a :store_dir key to the upload_column declaration, this will override the default mechanism and always use that directory as the basis.

upload_column :picture, :store_dir => "pictures"

might be sensible in our case. Note that this way, all files will be stored in the same directory.

If you need more refined control over the storage path (maybe you need to store it by the id of an association?) then you can use a callback method. In our case the method would be called picture_store_dir. Just append _store_dir to your upload_column field.

def picture_store_dir
  "images/#{self.category.name}/#{self.id}"
end

A shorter way to do something like this is to pass a Proc to :store_dir, like so:

upload_column :picture, :store_dir => proc{|inst, attr| "images/#{inst.category.name}/#{inst.id}"}

The proc will be passed two parameters, the first is the current instance of your model class, the second is the name of the attribute that is being uploaded to (in our case attr would be :picture).

You can change the :tmp_dir in the same way. For reference: the default for :store_dir is the following proc:

proc{|inst, attr| File.join(Inflector.underscore(inst.class).to_s, attr.to_s, inst.id.to_s) }

Note how it uses File.join, if you plan to use your code on Windows systems you should use File.join, since Windows uses backslashes instead of forwardslashes. File.join takes care of that automatically.

Filename

By default, UploadColumn will keep the name of the original file, however this might be inconvenient in some cases. You can pass a :filename directive to your upload_column declaration:

upload_column :picture, :filename => "donkey.png"

In which case all files will be named donkey.png. This is not desirable if the file in question is a jpeg file of course. Usually it is more sensible to pass a Proc to :filename.

upload_column :picture, :filename => proc{|inst, orig, ext| "avatar#{inst.id}.#{ext}"}

The Proc will be passed three parameters, the current instance, the basename of the original file (without the extension) and the properly corrected extension.

You can also use the picture_filename callback, which must take two arguments, the original basename and the corrected extension.

Manipulating Images with RMagick

Say you would want (for whatever reason) to have a funky solarize effect on your users’ images. Manipulating images with upload_column can be done either at runtime or after the image is saved, let’s look at some possibilities:

class User < ActiveRecord::Base
  upload_column :picture

  def picture_after_assign
    picture.process! do |img|
      img.solarize
    end      
  end
end

Or maybe we want different versions of our image, then we could simply specify:

class User < ActiveRecord::Base
  upload_column :picture, :versions => [ :solarized, :sepiatoned ]

  def picture_after_assign
    picture.solarized.process! do |img|
      img.solarize
    end
    picture.sepiatoned.process! do |img|
      img.sepiatone
    end
  end
end

Image column

If you only want to upload images, then UploadColumn comes with a convenient method, image_column works basically the same way as upload_column but it is especially trimmed for work with images.

The mime_extensions and extensions parameters are restricted to those used for images, so validates_integrity_of can be used to easily restrict uploads to images only.

Most importantly if you use image_column you can resize the images automagickaly (sorry) when they are assigned, just pass a Hash, like in the following example:

class User < ActiveRecord::Base
  image_column :picture, :versions => { :thumb => "100x100", :large => "200x300" }
end

If you need the image to be cropped to the exact dimensions, you can pass :crop => true.

Runtime rendering

You can manipulate images at runtime (it’s a huge performance hit though!). In your controller add an action and use UploadColumnRenderHelper.render_image.

def sepiatone
  @user = User.find(parms[:id])
  render_image @user.picture do |img|
    img.sepiatone
  end
end

And that’s it!

In your view, you can use UploadColumnHelper.image to easily create an image tag for your action:

<%= image :action => "sepiatone", :id => 5 %>

Views

If your uploaded file is an image you would most likely want to display it in your view, if it’s another kind of file you’ll want to link to it. Both of these are easy using UploadColumn::BaseUploadedFile.url.

<%= link_to "Guitar Tablature", @song.tab.url %>

<%= image_tag @user.picture.url %>

Magic Columns

UploadColumn allows you to add ‘magic’ columns to your model, which will be automatically filled with the appropriate data. Just add the column, for example via migrations:

add_column :users, :picture_mime_type

And if our model looks like this:

class User < ActiveRecord::Base
  upload_column :picture
end

The column picture_mime_type will now automatically be filled with the file’s mime-type (or at least with UploadColumn’s best guess ;).

The following are available for upload_column fields:

_mime_type

Will contain the file’s mime_type

_filesize

Will contain the file’s size in bytes

If you use image_column you can even add:

_width

Will contain the image’s width

_height

Will contain the image’s height

You can also do picture_exif_date_time or picture_exif_model, etc. if picture is an image_column and the uploaded image i a JPEG file. This requires EXIFR, which you can get by installing the gem via gem install exifr.

Validations

You can use SOME of Rails validations with UploadColumn

validates_presence_of and validates_size_of have been verified to work.

validates_size_of :image, :maximum => 200000, :message => "is too big, must be smaller than 200kB!"

Remember to change the error message, the default one sounds a bit stupid with UploadColumn.

validates_uniqueness_of does NOT work, this is because validates_uniqueness_of will send(:your_upload_column) instead of asking for the instance variable, thus it will get an UploadedFile object, which it can’t really compare to other values in the database, this is rather difficult to work around without messing with Rails internals (if you manage, please let me know!). Meanwhile you could do

validates_each :your_upload_column do |record, attr, value|    
  record.errors.add attr, 'already exists!' if YourModel.find( :first, :conditions => ["#{attr.to_s} = ?", value ] )
end

It’s not elegant I know, but it should work.

About

upload_column 0.2.1 with some fixes

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages