Module: Dis::Model

Extended by:
ActiveSupport::Concern
Defined in:
lib/dis/model.rb,
lib/dis/model/data.rb,
lib/dis/model/class_methods.rb

Overview

Dis Model

ActiveModel extension for the model holding your data. To use it, include the module in your model:

class Document < ActiveRecord::Base
include Dis::Model
end

You'll need to define a few attributes in your database table. Here's a minimal migration:

create_table :documents do |t|
t.string  :content_hash
t.string  :content_type
t.integer :content_length
t.string  :filename
end

You can override the names of any of these by setting dis_attributes.

class Document < ActiveRecord::Base
include Dis::Model
self.dis_attributes = {
  filename:       :my_filename,
  content_length: :filesize
}
end

Usage

To save a file, assign to the file attribute.

document = Document.create(file: params.permit(:file))

content_type and filename will automatically be set if the supplied object quacks like a file. content_length and content_hash will always be set.

To store a binary blob without filenames or content types, set the data attribute directly.

my_data = File.read('document.pdf')
document.update(data: my_data)

The data won't be stored until the record is saved, and only if the record is valid.

To retrieve your data, read the data attribute. The file will be lazily loaded from the store on demand and cached in memory as long as the record stays in scope.

my_data = document.data

Destroying a record will delete the file from the store, unless another record also refers to the same hash. Similarly, stale files will be purged when content changes.

Validations

No validation is performed by default. If you want to ensure that data is present, use the validates_data_presence method.

class Document < ActiveRecord::Base
include Dis::Model
validates_data_presence
end

If you want to validate content types, size or similar, use standard Rails validations on the metadata attributes:

validates :content_type, presence: true, format: /\Aapplication\/pdf\z/
validates :filename, presence: true, format: /\A[\w_\-\.]+\.pdf\z/i
validates :content_length, numericality: { less_than: 5.megabytes }

Defined Under Namespace

Modules: ClassMethods Classes: Data

Instance Method Summary collapse

Instance Method Details

#dataString?

Returns the data as a binary string, or nil if no data has been set.

Returns:

  • (String, nil)


96
97
98
# File 'lib/dis/model.rb', line 96

def data
  dis_data.read
end

#data=(raw_data) ⇒ void

This method returns an undefined value.

Assigns new data. This also sets content_length and content_hash.

Parameters:

  • raw_data (File, IO, String, nil)

    the content to store



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/dis/model.rb', line 112

def data=(raw_data)
  new_data = Dis::Model::Data.new(self, raw_data)
  attribute_will_change!("data") unless new_data == dis_data
  @dis_data = new_data
  dis_set :content_hash, if raw_data.nil?
                           nil
                         else
                           Storage.file_digest(new_data.read)
                         end
  dis_set :content_length, dis_data.content_length
end

#data?Boolean

Returns true if data is set.

Returns:

  • (Boolean)


103
104
105
# File 'lib/dis/model.rb', line 103

def data?
  dis_data.any?
end

#data_changed?Boolean

Returns true if the data has been changed since the object was last saved.

Returns:

  • (Boolean)


128
129
130
# File 'lib/dis/model.rb', line 128

def data_changed?
  changes.include?("data")
end

#dis_stored?Boolean

Returns true if the record has been persisted and its data has not been changed since the last save.

Returns:

  • (Boolean)


136
137
138
# File 'lib/dis/model.rb', line 136

def dis_stored?
  !(new_record? || data_changed?)
end

#file=(file) ⇒ void

This method returns an undefined value.

Assigns new data from an uploaded file. In addition to the actions performed by #data=, this will set content_type and filename.

Parameters:

  • file (ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile)

    an uploaded file that responds to content_type and original_filename



148
149
150
151
152
# File 'lib/dis/model.rb', line 148

def file=(file)
  self.data = file
  dis_set :content_type, file.content_type
  dis_set :filename, file.original_filename
end

#open_data {|file| ... } ⇒ File, Object

Returns the data as an open, read-only file, or yields it and closes it afterwards. The data stays readable until the file is closed, even if the content is deleted or evicted meanwhile.

Yield Parameters:

  • file (File)

    an open file, positioned at the start

Returns:

  • (File, Object)

    the open file, or the block's value



174
175
176
177
178
179
180
181
182
183
# File 'lib/dis/model.rb', line 174

def open_data
  file = dis_data.open
  return file unless block_given?

  begin
    yield file
  ensure
    file.close unless file.closed?
  end
end

#with_data_file {|path| ... } ⇒ Object

Yields the path to a file containing the data, for tools that need a file name rather than the bytes. The path is valid for the duration of the block only, so resolve anything lazy before returning.

Examples:

document.with_data_file { |path| Vips::Image.new_from_file(path.to_s).avg }

Yield Parameters:

  • path (Pathname)

    path to the data

Returns:

  • (Object)

    the return value of the block



164
165
166
# File 'lib/dis/model.rb', line 164

def with_data_file(&)
  dis_data.with_file(&)
end