2

I have a web app made with Ruby On Rails. For now when I want to display an object I have to access the following page: http://mywebapp.com/object/1234 with 1234 the id of the object.

I would like to encode that object id and have the following result: http://mywebapp.com/object/5k (it is just an example).

How can it be done?

Many thanks,
Martin

MartinMoizard
  • 6,600
  • 12
  • 45
  • 75
  • That lengthens the URL! I presume you mean you want to obscure the id of the object in some way with a reversible encoding scheme? – brad Sep 23 '11 at 10:50
  • @nurinur - is the oldest question really supposed to be tagged as a duplicate of the most recent one? that's nonsense. – MartinMoizard Jun 18 '15 at 08:18
  • i thought so but apparently i'm wrong. sorry, it's my first flag, thought the other question was richer. my bad! – Nuriel Jun 28 '15 at 12:50

2 Answers2

5

All these converting methods are reversible, so IMHO if your object has some name or title or whatever, then the best way is adding a slug. In such case add a new attribute :slug to your object, let automatically generate it's value from object name (or something else) on the model:

class MyObject

  validates_format_of :slug, :with => /\A[a-z\-0-9]*\Z/
  before_validation :generate_slug, :on => :create    

  def generate_slug
    if self.slug.blank?
      slug = self.name.mb_chars.downcase.normalize(:kd).to_s.gsub(/-/, " ").squeeze(" ")
      slug = slug.gsub(/\s/, "-").gsub(/[^a-z\-0-9]/, "")

      current = 1
      self.slug = slug
      while true
        conflicts = MyObject.where("slug = ?", self.slug).count
        if conflicts != 0
          self.slug = "#{slug}-#{current}"
          current += 1
        else
          break
        end
      end
    end
  end
end

then the URL can be http://mywebapp.com/object/my_object_slug, because in action you find the object via this slug:

class MyObjectController

  def some_action
    my_object = MyObject.find_by_slug(params[:slug])

    ...

  end
end

Don't forget modify routes.rb:

match "object/:slug", :to => "my_objects#some_action"
MartinMoizard
  • 6,600
  • 12
  • 45
  • 75
bender
  • 1,430
  • 10
  • 14
  • Thanks! I'll try that out in a couple minutes and let you know. – MartinMoizard Sep 23 '11 at 11:25
  • 1
    You can also keep using your resource-ful routes by overriding the `:to_param` method of your model(s) to return your slug value. Then Rails would use it instead of the "ID", but would still pass it ias `:id` in the `params` hash. – Romain Sep 23 '11 at 11:37
  • https://github.com/norman/friendly_id – lloydpick Sep 23 '11 at 11:46
  • You can have Rails (actually ActiveSupport) do the string-magic for you: http://api.rubyonrails.org/classes/String.html#method-i-parameterize – rdvdijk Sep 23 '11 at 13:11
  • And the retry-logic is supported by ruby itself, see: [ruby: how to know if script is on 3rd retry](http://stackoverflow.com/questions/1746177/ruby-how-to-know-if-script-is-on-3rd-retry) – rdvdijk Sep 23 '11 at 13:20
1

You could probably do this with Base64 encoding (although if you're really trying to keep the internal id secret someone could probably guess you're using Base64 encoding and easily determine the id).

Your controller would need to look a bit like this

class ThingsController < ApplicationController
  require 'base64'

  def show
    @thing = Thing.find Base64.urlsafe_decode64(params[:id])
  end

  def edit
    @thing = Thing.find Base64.urlsafe_decode64(params[:id])
  end

  #These are just a couple of very simple example actions

end

Now actually encoding your URLs is going to be a little bit trickier - I'll look into it as it seems like an interesting problem (but I'm not making any promises).

  • Edit -

A bit of reading reveals that ActionView uses the to_param method in url_for to get the id of an object. We can override this in the model itself to encode the id like so

class Thing < ActiveRecord::Base

  def to_param
    Base64.urlsafe_encode64 self.id.to_s
  end

end

Everything I've written here is conjectural. I haven't done this before or tested the code so I can't give any guarantee as to whether it will work or whether it will introduce unforeseen problems. I'd be very interested to hear how you go.

brad
  • 9,573
  • 12
  • 62
  • 89