Progstr Filer is a developer-friendly file hosting platform built specifically for web apps. It lets you easily associate file attachments with your ActiveRecord models and removes the hassle of actually hosting the files yourself.

Setting up the Ruby gem

Bundler makes that all too easy - add this line to your Gemfile to have the gem pulled into your app and required automatically.

gem "progstr-filer", :require => "progstr-filer"

Credentials

Progstr Filer uses two keys similar to Amazon's cloud services. The access key is a public string that gets rendered publicly in URL's and web pages. We use it to identify your account. The secret key is used to sign and encrypt sensitive data. You should keep it, well, secret.

Provisioning your add-on with Heroku gets you two environment variables: PROGSTR_FILER_ACCESS_KEY and PROGSTR_FILER_SECRET_KEY. Add those to your config/environments/production.rb file:

Progstr::Filer.access_key = ENV['PROGSTR_FILER_ACCESS_KEY']
Progstr::Filer.secret_key = ENV['PROGSTR_FILER_SECRET_KEY']

Defining an uploader

Every attachment can have a specific set of options that take effect while uploading and manipulating it. Those settings get configured via your own class that will extend Progstr::Filer::Uploader. You could store uploader classes in your app/uploaders folder. Here is a sample class:

class AvatarUploader < Progstr::Filer::Uploader
  #uploader options
end

Associating uploaders with your models

Progstr Filer extends ActiveRecord models and lets you use the has_file method in class definitions. Here is a User model class that has an avatar image managed as a Progstr Filer attachment via its avatar property:

class User < ActiveRecord::Base
    has_file :avatar, AvatarUploader
end

Note: make sure your database table has an avatar column created already. Future versions of the progstr-filer gem will let you generate migrations automatically.

Feeding data to your uploaders

That is easy - all you need is assign a Ruby File object to the uploader property and save the model object:

@user.avatar = uploaded_image_file
@user.avatar.save 

Rails can get most of the job done for you automatically if you create a file upload form:

<div class="field">
  <%= f.label :avatar %>
  <%= f.file_field :avatar %>
</div>

...and create your model from the params hash:

@user = User.new(params[:user])
@user.save

Generating URLs for files

Just use the url method on your attachments, say @user.avatar.url. Here is a sample view that generates an img tag pointing to the avatar image:

<p>
  <b>Avatar:</b><br>
  <%= image_tag @user.avatar.url, :style => "max-width: 600px; margin: 10px 0px;" %>
</p>

Validation

Users may upload all types of files to your application and it might be a good idea to restrict that to the set of files that your application knows how to process. For example it would not be a good idea to allow non-image file types as an avatar picture. It might be a good idea to disable executable content that might spread malware too. To help with that the progstr-filer gem currently supports attachment validation according to an extension whitelist through the validates_file_extension_of method that is available to model objects:

class User < ActiveRecord::Base
  has_file :avatar, AvatarUploader
  validates_file_extension_of :avatar, :allowed => ["jpg", "png"]
end

In addition you can restrict the file size using validates_file_size_of:

class User < ActiveRecord::Base
  has_file :avatar, AvatarUploader
  validates_file_size_of :avatar, :less_than => 1 * 1024 * 1024
end

The example above will not allow files larger than 1 MB. You can specify a lower bound using the :greater_than option or even pass a numeric range using the :in option.

Of course, requiring users to always upload a file when saving a model object, use the Rails built-in validates_presence_of validator:

class User < ActiveRecord::Base
  has_file :avatar, AvatarUploader
  validates_presence_of :avatar
end

You can pass a custom error message for all validators using the :message option.

Deleting stale files

Every time you set a new file and save your model, the old file will get scheduled for deletion. The same happens when you destroy your model. Note that calling delete for your model will not delete any associated files.

Source code

The progstr-filer gem is open source and its code is hosted on Github.

All the techniques outlined in this document are available as a fully-functional Rails project on Github too.