title: How do I implement hashids in ruby on rails
body: <p>I will go ahead and apologize upfront as I am new to ruby and rails and I cannot for the life of me figure out how to implement using hashids in my project. The project is a simple image host. I have it already working using Base58 to encode the sql ID and then decode it in the controller. However I wanted to make the URLs more random hence switching to hashids. </p>
  
  <p>I have placed the hashids.rb file in my lib directory from here: <a href="https://github.com/peterhellberg/hashids.rb" rel="nofollow">https://github.com/peterhellberg/hashids.rb</a></p>
  
  <p>Now some of the confusion starts here. Do I need to initialize hashids on every page that uses hashids.encode and hashids.decode via</p>
  
  <pre><code>hashids = Hashids.new("mysalt")
  </code></pre>
  
  <p>I found this post (<a href="http://zogovic.com/post/75234760043/youtube-like-ids-for-your-activerecord-models" rel="nofollow">http://zogovic.com/post/75234760043/youtube-like-ids-for-your-activerecord-models</a>) which leads me to believe I can put it into an initializer however after doing that I am still getting NameError (undefined local variable or method `hashids' for ImageManager:Class)</p>
  
  <p>so in my ImageManager.rb class I have</p>
  
  <pre><code>require 'hashids'
  
  class ImageManager
  class &lt;&lt; self
  def save_image(imgpath, name)
  
    mime = %x(/usr/bin/exiftool -MIMEType #{imgpath})[34..-1].rstrip
    if mime.nil? || !VALID_MIME.include?(mime)
      return { status: 'failure', message: "#{name} uses an invalid format." }
    end
  
    hash = Digest::MD5.file(imgpath).hexdigest
    image = Image.find_by_imghash(hash)
  
    if image.nil?
      image = Image.new
      image.mimetype = mime
      image.imghash = hash
      unless image.save!
        return { status: 'failure', message: "Failed to save #{name}." }
      end
  
      unless File.directory?(Rails.root.join('uploads'))
        Dir.mkdir(Rails.root.join('uploads'))
      end
      #File.open(Rails.root.join('uploads', "#{Base58.encode(image.id)}.png"), 'wb') { |f| f.write(File.open(imgpath, 'rb').read) }
      File.open(Rails.root.join('uploads', "#{hashids.encode(image.id)}.png"), 'wb') { |f| f.write(File.open(imgpath, 'rb').read) }
    end
  
    link = ImageLink.new
    link.image = image
    link.save
  
  #return { status: 'success', message: Base58.encode(link.id) }
  return { status: 'success', message: hashids.encode(link.id) }
  end
  
  private
  
      VALID_MIME = %w(image/png image/jpeg image/gif)
    end
  end
  </code></pre>
  
  <p>And in my controller I have:</p>
  
  <pre><code>require 'hashids'
  
  class MainController &lt; ApplicationController
  MAX_FILE_SIZE = 10 * 1024 * 1024
  MAX_CACHE_SIZE = 128 * 1024 * 1024
  
  @links = Hash.new
  @files = Hash.new
  @tstamps = Hash.new
  @sizes = Hash.new
  @cache_size = 0
  
  class &lt;&lt; self
    attr_accessor :links
    attr_accessor :files
    attr_accessor :tstamps
    attr_accessor :sizes
    attr_accessor :cache_size
    attr_accessor :hashids
  end
  
  def index
  end
  
  def transparency
  end
  
  def image
    #@imglist = params[:id].split(',').map{ |id| ImageLink.find(Base58.decode(id)) }
    @imglist = params[:id].split(',').map{ |id| ImageLink.find(hashids.decode(id)) }
  end
  
  def image_direct
    #linkid = Base58.decode(params[:id])
    linkid = hashids.decode(params[:id])
  
    file =
      if Rails.env.production?
        puts "#{Base58.encode(ImageLink.find(linkid).image.id)}.png"
        File.open(Rails.root.join('uploads', "#{Base58.encode(ImageLink.find(linkid).image.id)}.png"), 'rb') { |f| f.read }
      else
        puts "#{hashids.encode(ImageLink.find(linkid).image.id)}.png"
        File.open(Rails.root.join('uploads', "#{hashids.encode(ImageLink.find(linkid).image.id)}.png"), 'rb') { |f| f.read }
      end
  
    send_data(file, type: ImageLink.find(linkid).image.mimetype, disposition: 'inline')
  end
  
  def upload
    imgparam = params[:image]
  
    if imgparam.is_a?(String)
      name = File.basename(imgparam)
      imgpath = save_to_tempfile(imgparam).path
    else
      name = imgparam.original_filename
      imgpath = imgparam.tempfile.path
    end
  
    File.chmod(0666, imgpath)
    %x(/usr/bin/exiftool -all= -overwrite_original #{imgpath})
    logger.debug %x(which exiftool)
    render json: ImageManager.save_image(imgpath, name)
  end
  
  private
  
    def save_to_tempfile(url)
    uri = URI.parse(url)
  
    http = Net::HTTP.new(uri.host, uri.port)
    http.use_ssl = uri.scheme == 'https'
    http.start do
      resp = http.get(uri.path)
      file = Tempfile.new('urlupload', Dir.tmpdir, :encoding =&gt; 'ascii-8bit')
      file.write(resp.body)
      file.flush
      return file
    end
  end
  end
  </code></pre>
  
  <p>Then in my image.html.erb view I have this:</p>
  
  <pre><code>&lt;%
     @imglist.each_with_index { |link, i|
     id = hashids.encode(link.id)
     ext = link.image.mimetype.split('/')[1]
     if ext == 'jpeg'
       ext = 'jpg'
     end
     puts id + '.' + ext
  %&gt;
  </code></pre>
  
  <p>Now if I add </p>
  
  <pre><code>hashids = Hashids.new("mysalt")
  </code></pre>
  
  <p>in ImageManager.rb main_controller.rb and in my image.html.erb I am getting this error:</p>
  
  <pre><code>ActionView::Template::Error (undefined method `id' for #&lt;Array:0x000000062f69c0&gt;)
  </code></pre>
  
  <p>So all in all implementing hashids.encode/decode is not as easy as implementing Base58.encode/decode and I am confused on how to get it working... Any help would be greatly appreciated. </p>