Dis
Dis is a content-adressable store for file uploads in your Rails app.
Data can be stored either on disk or in the cloud - anywhere that Fog knows how to talk to.
It doesn't do any processing, but it's a simple foundation to roll your own on. If you're looking to handle image uploads, check out DynamicImage. It's built on top of Dis and handles resizing, cropping and more on demand.
Requires Rails 5+
Layers
The underlaying storage consists of one or more layers. A layer is a unit of storage location, which can either be a local path, or a cloud provider like Amazon S3 or Google Cloud Storage.
There are two types of layers, immediate and delayed. Files are written to immediate layers and then replicated to the rest in the background using ActiveJob.
Reads are performed from the first available layer. In case of a read miss, the file is backfilled from the next layer.
An example configuration could be to have a local layer first, and then for example an Amazon S3 bucket. This provides you with an on-disk cache backed by cloud storage. You can also add additional layers if you want fault tolerance across regions or even providers.
Layers can be configured as read-only. This can be useful if you want to read from your staging or production environment while developing locally, or if you're transitioning away from a provider.
Installation
Add the gem to your Gemfile and run bundle install
:
gem "dis"
Now, run the generator to install the initializer:
bin/rails generate dis:install
By default, files will be stored in db/dis
. You can edit
config/initializers/dis.rb
if you want to change the path or add
additional layers. Note that you also need the corresponding
Fog gem if you want to use cloud storage:
gem "fog-aws"
Usage
Run the generator to create your model.
bin/rails generate dis:model Document
This will create a model along with a migration.
Here's what your model might look like. Note that Dis does not validate any data by default, but you can use the standard Rails validators. A validator for validating presence of data is provided.
class Document < ActiveRecord::Base
include Dis::Model
validates_data_presence
validates :content_type, presence: true, format: /\Aapplication\/(x\-)?pdf\z/
validates :filename, presence: true, format: /\A[\w_\-\.]+\.pdf\z/i
validates :content_length, numericality: { less_than: 5.megabytes }
end
To save your document, simply set the file
attribute.
document_params = params.require(:document).permit(:file)
@document = Document.create(document_params)
You can also pass a file directly:
Document.create(data: File.open('document.pdf'),
content_type: 'application/pdf',
filename: 'document.pdf')
..or even a string:
Document.create(data: 'foo', content_type: 'text/plain', filename: 'foo.txt')
Getting your file back out is straightforward:
class DocumentsController < ApplicationController
def show
@document = Document.find(params[:id])
if stale?(@document)
send_data(@document.data,
filename: @document.filename,
type: @document.content_type,
disposition: "attachment)
end
end
end
Behind the scenes
You can interact directly with the store if you want.
file = File.open("foo.txt")
hash = Dis::Storage.store("documents", file) # => "8843d7f92416211de9ebb963ff4ce28125932878"
Dis::Storage.exists?("documents", hash) # => true
Dis::Storage.get("documents", hash).body # => "foobar"
Dis::Storage.delete("documents", hash) # => true
Documentation
See the generated documentation on RubyDoc.info
License
Copyright 2014 Inge Jørgensen
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.