1

I am building a Rails 5 team management app which lets users manage organizations and users. I would like to be able to change from using the :id in the path (e.g: /organizations/43) and use an alpha-numeric slug instead (e.g: /organizations/H6Y47Nr7). Similar to how Trello do this (i.e: https://trello.com/b/M9X71pE6/board-name). Is there an easy way of doing this?

I have seen the FriendlyId gem which could take care of the slugging in the path but what would be the best way to generate the slug in the first place?

Ideally, for the most bang for buck the slug would include A-Z, a-z and 0-9 (as I understand it, this is Base58?) and for the sake of not blowing out the url too much, 8 characters at the most. If my calculations are correct, this gives 218 trillion combinations, which should be plenty.

Am I on the right track? Any help would be much appreciated.

Thanks

simonlehmann
  • 852
  • 1
  • 10
  • 27
  • You can check [hashids](https://github.com/peterhellberg/hashids.rb) gem which support customizing number of character and character to be included in slug. I recently made a rails [plugin](https://github.com/sajan45/hashids_activerecord) for that but I have not used either of them in production yet. – Sajan Feb 27 '17 at 05:56
  • 1
    Take a look my old answer here http://stackoverflow.com/a/16096646/1297435 – rails_id Feb 27 '17 at 06:38

2 Answers2

2

To create a slug, easiest way is to use SecureRandom. You can add something like the following in your model

before_create :generate_slug

private

def generate_slug
  begin
    self.slug = SecureRandom.urlsafe_base64(8)
  end while Organization.exists?(slug: slug)
end

One small caveat here with respect to what you want is that the slug will sometimes contain an underscore or a dash but that should be fine.

irb(main):014:0> SecureRandom.urlsafe_base64(8)
=> "HlHHV_6rN3k"
irb(main):015:0> SecureRandom.urlsafe_base64(8)
=> "naRqT-NmYDU"
irb(main):016:0> SecureRandom.urlsafe_base64(8)
=> "9h04l4jEEsM"
jvnill
  • 29,479
  • 4
  • 83
  • 86
  • Thanks for your response. Could I not just use `SecureRandom.base58(8)` instead? It returns something like `KBz4i9ux`. – simonlehmann Feb 27 '17 at 06:31
  • Also, what about handling collisions? Or does `SecureRandom` take care of it? Thanks again! – simonlehmann Feb 27 '17 at 07:19
  • not familiar with `base58`. The reason why I used `urlsafe_base64` is because you need a url-safe string. As for collisions, there's a very small chance of race condition happening but I wouldn't worry about it – jvnill Feb 27 '17 at 10:34
  • I see. Thanks. As I understand it, base58 is similar to base64 except it omits punctuation and ambiguous characters (i.e: 0 and O, and I and l) so still URL safe. – simonlehmann Feb 27 '17 at 10:53
0

If you go that route, I would create a table were you save the slugs that you are generating and don't delete them even when you delete an organization. When you create a new organization query this model to make sure there are no duplicates slugs. Also add a unique index in the slug column of the organizations table.

You should not give up the id column with integers so in the show method you will need to do:

org = Organization.where(slug: params[:id]).first
s1mpl3
  • 1,456
  • 1
  • 10
  • 14