class Nozzle::Adapter::Base

Constants

MIN_PATH_MAX

minimum filename length for Nozzle to try and fit it to the filesystem.

Public Class Methods

new( record, column, filename, options = {} ) click to toggle source

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

access_path() click to toggle source

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
adapter_folder() click to toggle source

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
adapter_path() click to toggle source

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
as_json(*args) click to toggle source
# File lib/nozzle/adapter/base.rb, line 208
def as_json(*args)
  { :url => url }
end
content_type() click to toggle source

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
default_url() click to toggle source

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
delete() click to toggle source

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
dump( value ) click to toggle source

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
filename() click to toggle source

Returns stored filename.

instance.avatar.filename # => 'image.jpg'
# File lib/nozzle/adapter/base.rb, line 91
def filename
  @filename
end
load( value ) click to toggle source

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
path() click to toggle source

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
public_path() click to toggle source

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
relative_folder() click to toggle source

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
root() click to toggle source

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
settings() click to toggle source

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
size() click to toggle source

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
store!() click to toggle source

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
system_path() click to toggle source

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
to_s() click to toggle source

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
url() click to toggle source

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
url?() click to toggle source

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

delete_file_and_folder!( file_path ) click to toggle source

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
detect_properties() click to toggle source

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
expand_argument( value ) click to toggle source

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
fit_to_filesystem(candidate) click to toggle source

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
prepare_filename(candidate=filename) click to toggle source

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
reset() click to toggle source

Resets internal paths.

# File lib/nozzle/adapter/base.rb, line 255
def reset
  @original_path = nil
  @tempfile_path = nil
end