module Dis::Model
Dis
Model
¶ ↑
ActiveModel extension for the model holding your data. To use it, simply 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, simply 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
will always be set. content_hash
won’t be set until the record is being saved.
If you don’t care about filenames and content types and just want to store a binary blob, you can also just set the data
attribute.
my_data = File.read('document.pdf') document.update(data: my_data)
The data won’t be stored until the record is saved, and not unless the record is valid.
To retrieve your data, simply 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, simply 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 }
Public Instance Methods
Returns the data as a binary string, or nil if no data has been set.
# File lib/dis/model.rb, line 93 def data dis_data.read end
Assigns new data. This also sets content_length
, and resets content_hash
to nil.
# File lib/dis/model.rb, line 104 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
Returns true if data is set.
# File lib/dis/model.rb, line 98 def data? dis_data.any? end
Returns true if the data has been changed since the object was last saved.
# File lib/dis/model.rb, line 117 def data_changed? changes.include?("data") end
# File lib/dis/model.rb, line 121 def dis_stored? !(new_record? || data_changed?) end
Assigns new data from an uploaded file. In addition to the actions performed by data=
, this will set content_type
and filename
.
# File lib/dis/model.rb, line 128 def file=(file) self.data = file dis_set :content_type, file.content_type dis_set :filename, file.original_filename end
Private Instance Methods
# File lib/dis/model.rb, line 139 def cleanup_data return if @previous_content_hash.blank? dis_data.expire(@previous_content_hash) @previous_content_hash = nil end
# File lib/dis/model.rb, line 146 def delete_data dis_data.expire(dis_get(:content_hash)) end
# File lib/dis/model.rb, line 168 def dis_attribute(attribute_name) self.class.dis_attributes[attribute_name] end
# File lib/dis/model.rb, line 160 def dis_data @dis_data ||= Dis::Model::Data.new(self) end
# File lib/dis/model.rb, line 156 def dis_get(attribute_name) self[dis_attribute(attribute_name)] end
# File lib/dis/model.rb, line 164 def dis_set(attribute_name, value) self[dis_attribute(attribute_name)] = value end
We don’t want the data column when doing a partial write.
# File lib/dis/model.rb, line 173 def keys_for_partial_write super.reject { |a| a == "data" } end
# File lib/dis/model.rb, line 150 def store_data dis_set :content_hash, dis_data.store! if dis_data.changed? @previous_content_hash = changes[dis_attribute(:content_hash)] .try(&:first) end