class Nozzle::Adapter::Base
Constants
- MIN_PATH_MAX
minimum filename length for
Nozzle
to try and fit it to the filesystem.
Public Class Methods
Initializes internal structure of new adapter.
outlet_class.new( instance, :avatar, 'image.jpg', :fake => true )
# File lib/nozzle/adapter/base.rb, line 16 def initialize( record, column, filename, options = {} ) @record = record @model = record.class @column = column.to_sym @filename = filename settings.merge! options end
Public Instance Methods
Returns intermediate path to the tempfile if the record is not yet saved and file is not yet stored at path.
# File lib/nozzle/adapter/base.rb, line 74 def access_path @tempfile_path || path end
Returns folder name of the adapter relative to application public.
instance.avatar.adapter_folder # => 'uploads'
This MAY be overridden to specify where all the files should be stored.
# File lib/nozzle/adapter/base.rb, line 113 def adapter_folder 'uploads' end
Returns filesystem folder path of the adapter relative to adapter root.
instance.avatar.adapter_path # => 'public/uploads'
It is constructed from root
, ‘public’ and adapter_folder
.
# File lib/nozzle/adapter/base.rb, line 120 def adapter_path File.join root, adapter_folder end
# File lib/nozzle/adapter/base.rb, line 208 def as_json(*args) { :url => url } end
Returns file content_type
stored in avatar_content_type column of the record.
# File lib/nozzle/adapter/base.rb, line 80 def content_type @record.send( :"#{@column}_content_type" ) rescue '' end
Returns nil. This SHOULD be overridden by subclasses of Nozzle::Adapter::Base
.
instance.avatar.default_url # => nil
# File lib/nozzle/adapter/base.rb, line 98 def default_url nil end
Sets adapter to delete stored file on adapеr_after_save.
instance.avatar.delete # => nil
# File lib/nozzle/adapter/base.rb, line 154 def delete @record.send(:"#{@column}=", nil) end
Fills internal structure of the adapter with new file’s path. It’s used in Nozzle::Adapter#avatar=
before sending filename to the object.
# File lib/nozzle/adapter/base.rb, line 167 def dump( value ) reset @original_path = path return nil unless value new_path, filename_candidate = expand_argument(value) raise Errno::ENOENT, "'#{new_path}'" unless File.exists?(new_path) @tempfile_path = File.expand_path(new_path) detect_properties @filename = fit_to_filesystem(prepare_filename(filename_candidate)) end
Returns stored filename.
instance.avatar.filename # => 'image.jpg'
# File lib/nozzle/adapter/base.rb, line 91 def filename @filename end
Returns adapter instance. It’s used in Nozzle::Adapter#avatar
after retrieving filename from the object.
# File lib/nozzle/adapter/base.rb, line 160 def load( value ) @filename = value self end
Constructs a filesustem path which absolutely points to stored file.
instance.avatar.path # => 'public/uploads/Model/avatar/image.jpg'
How it’s constructed:
"/#{system_path}/#{filename}" "/#{adapter_path}/#{relative_folder}/#{filename}" "#{root}/#{adapter_folder}/#{@model}/#{@column}/#{filename}" "public/uploads/#{@model}/#{@column}/#{filename}" "public/uploads/Model/avatar/image.jpg"
Note: if filename is not yet stored, nil is returned.
# File lib/nozzle/adapter/base.rb, line 66 def path if filename File.join system_path, filename end end
Returns folder path relative to public folder.
instance.avatar.public_path # => 'uploads/Model/avatar'
It is constructed from adapter_folder
and relative_folder
# File lib/nozzle/adapter/base.rb, line 142 def public_path File.join adapter_folder, relative_folder end
Returns file’s folder relative to adapter_path
.
instance.avatar.relative_folder # => 'Model/avatar'
It is constructed from object’s class name and column name. This MAY be overridden to place files somwhere other than ‘Model/avatar’.
# File lib/nozzle/adapter/base.rb, line 128 def relative_folder File.join @model.to_s, @column.to_s end
Returns root path of application’s static assets.
instance.avatar.root # => 'public'
This MAY be overridden to return an application root different from the current folder.
# File lib/nozzle/adapter/base.rb, line 106 def root 'public' end
Sets or gets settings provided by options.
if instance.avatar.settings[:fake] instance.avatar.settings[:fake] = false end
# File lib/nozzle/adapter/base.rb, line 28 def settings @settings ||= {} end
Returns file size stored in avatar_size column of the record.
# File lib/nozzle/adapter/base.rb, line 85 def size @record.send( :"#{@column}_size" ) rescue -1 end
Stores temporary filename by the constructed path. Deletes old file. Note: the file is moved if it’s path contains /tmp/ or /temp/, copied otherwise.
# File lib/nozzle/adapter/base.rb, line 183 def store! unlink! @original_path return nil unless @tempfile_path new_path = path FileUtils.mkdir_p File.dirname(new_path) result = if @tempfile_path =~ /\/te?mp\// FileUtils.move @tempfile_path, new_path else FileUtils.copy @tempfile_path, new_path end File.chmod 0644, new_path reset result end
Returns filesystem folder path relative to adapter root.
instance.avatar.system_path # => 'public/uploads/Model/avatar'
It is constructed from adapter_path
and relative_folder
# File lib/nozzle/adapter/base.rb, line 135 def system_path File.join adapter_path, relative_folder end
Inspects class name and url of the object.
instance.avatar.to_s # => 'Model#url: /uploads/Model/avatar/image.jpg'
# File lib/nozzle/adapter/base.rb, line 148 def to_s "#{self.class}#url: #{url}" end
Deletes file by path. Do not use, it will break adapter’s integrity. It’s called in avatar_after_destroy after the object is destroyed.
unlink! # deletes path unlink! @original_path # deletes @original_path unlink! nil # deletes nothing
# File lib/nozzle/adapter/base.rb, line 204 def unlink!( target = path ) delete_file_and_folder! target if target end
Constructs an URL which relatively points to the file.
instance.avatar.url # => '/uploads/Model/avatar/image.jpg'
How it’s constructed:
"#{public_path}/#{filename}" "/#{adapter_folder}/#{relative_folder}/#{filename}" "/uploads/#{@model}/#{@column}/#{filename}" "/uploads/Model/avatar/image.jpg"
Note: if filename is not yet stored, default_url
is called.
# File lib/nozzle/adapter/base.rb, line 40 def url if filename File.join '', public_path, filename else default_url end end
Gets url and adds a cache busting timestamp to it.
instance.avatar.url? # => '/uploads/Model/avatar/image.jpg?1373369401'
# File lib/nozzle/adapter/base.rb, line 50 def url? result = url result && "#{result}?#{File.mtime(path).to_i}" rescue Errno::ENOENT result end
Private Instance Methods
Deletes the specified file and all empty folders recursively stopping at adapter_folder
.
# File lib/nozzle/adapter/base.rb, line 279 def delete_file_and_folder!( file_path ) FileUtils.rm_f file_path boundary = adapter_path + '/' loop do file_path = File.dirname file_path break unless file_path.index boundary FileUtils.rmdir file_path end end
Tries to detect content_type
and size of the file. Note: this method calls ‘file` system command to detect file content type.
# File lib/nozzle/adapter/base.rb, line 247 def detect_properties @record.send( :"#{@column}_content_type=", `file -bp --mime-type '#{access_path}'`.to_s.strip ) @record.send( :"#{@column}_size=", File.size(access_path) ) rescue NoMethodError nil end
Analyzes the value assigned to adapter and fills @filename. Returns system path where temporary file is located. The value
MUST be File, String, Hash or nil. See Nozzle::Adapter#avatar=
.
# File lib/nozzle/adapter/base.rb, line 263 def expand_argument( value ) tempfile_path = case value when String value when File, Tempfile value.path when Hash expand_argument( value[:tempfile] || value['tempfile'] ).first else raise ArgumentError, "#{@model}##{@column}= argument must be kind of String, File, Tempfile or Hash[:tempfile => 'path']" end [tempfile_path, value.kind_of?(Hash) && ( value[:filename] || value['filename'] ) || File.basename(tempfile_path)] end
Returns a filename that will be acceptable for filesystem chopping it if nessesary.
# File lib/nozzle/adapter/base.rb, line 222 def fit_to_filesystem(candidate) return candidate if candidate.bytesize <= MIN_PATH_MAX ext = File.extname(candidate) base = File.basename(candidate, ext) tmp = Dir.tmpdir loop do begin candidate = base + ext try_file = File.join(tmp, candidate) FileUtils.touch try_file FileUtils.rm try_file return candidate rescue Errno::ENAMETOOLONG if base.length > 0 base.chop! retry else raise end end end end
Returns a filename prepared for saving. Should be overridden.
instance.avatar.new_filename('image.jpg') # => '0006-avatary-image.jpg'
# File lib/nozzle/adapter/base.rb, line 216 def prepare_filename(candidate=filename) candidate end
Resets internal paths.
# File lib/nozzle/adapter/base.rb, line 255 def reset @original_path = nil @tempfile_path = nil end