6

I have a Ruby on Rails application where you can create 'posts'. I started of by using the scaffold generator to give generate the title which is a string and the body which is the content.

Each 'post' has a url of the id, for example /1, /2, /3, etc.

Is there a way to change that to a string of random characters, for example /49sl, /l9sl, etc?

Update

Here is what I have for the posts_controller.rb

class PostsController < ApplicationController
  # GET /posts
  # GET /posts.json
  def index
    @posts = Post.all

    respond_to do |format|
      format.html # index.html.erb
      format.json { render json: @posts }
    end
  end

  # GET /posts/1
  # GET /posts/1.json
  def show
    @post = Post.find(params[:id])

    respond_to do |format|
      format.html # show.html.erb
      format.json { render json: @post }
    end
  end

  # GET /posts/new
  # GET /posts/new.json
  def new
    @post = Post.new

    respond_to do |format|
      format.html # new.html.erb
      format.json { render json: @post }
    end
  end

  # GET /posts/1/edit
  def edit
    @post = Post.find(params[:id])
  end

  # POST /posts
  # POST /posts.json
  def create
   @post = Post.new(params[:post])

    respond_to do |format|
      if @post.save
        format.html { redirect_to @post, notice: 'Post was successfully created.' }
        format.json { render json: @post, status: :created, location: @post }
      else
        format.html { render action: "new" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PUT /posts/1
  # PUT /posts/1.json
  def update
    @post = Post.find(params[:id])

    respond_to do |format|
      if @post.update_attributes(params[:post])
        format.html { redirect_to @post, notice: 'Post was successfully updated.' }
        format.json { head :no_content }
      else
        format.html { render action: "edit" }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /posts/1
  # DELETE /posts/1.json
  def destroy
    @post = Post.find(params[:id])
    @post.destroy

    respond_to do |format|
      format.html { redirect_to posts_url }
      format.json { head :no_content }
    end
  end
end

6 Answers 6

15

Rails uses the to_param method of an ActiveRecord object in order to resolve it into a URL.

Assuming you have a way to generate these unique ids (referring to it as IdGenerator) you can do the following:

1- Generate this id whenever you persist a Post object and save it to the database, let's say under the column url_id

class Post < ActiveRecord::Base
  before_create :generate_url_id
  def generate_url_id
    self.url_id = IdGenerator.generate_id
  end
end

2- Inside your Post model override the to_param method:

class Post < ActiveRecord::Base
  def to_param
    return url_id
  end
end

Now post_path(@post) will resolve to /posts/url_id

By the way, you can use SecureRandom.urlsafe_base64 or look here if you don't have an ID generator yet.

Read more on the documentation for to_param.

Sign up to request clarification or add additional context in comments.

8 Comments

I currently don't have a way to generate the id's, do you have an idea on how I would do that? Also, I've updated my question with what I have in the posts_controller.rb
You can use SecureRandom.urlsafe_base64
Is there no gem that automatically does all this? It should exist. I'm getting errors.
Why use a Gem? it is not that hard to implement yourself.
I tried adding the ID generator and then including the code you gave me but it's not working.
|
2

I hope these two resources are going to help you :

  1. The gem , named obfuscate_id . It represents the ID in a format like :

    http://tennisfans.eu/products/4656446465

  2. Another gem - masked_id . It provides a similar functionality . You are in control with a format of the url creation , defining it in a class . Looking at the source it appears , that this gem uses a strategy of obfuscate_id gem .

Comments

0
+50

You can give your posts random URLs by following these 3 steps:

1- In your model (Post.rb), generate a random string for each post before it is saved. For example,

class Post < ActiveRecord::Base
  before_create :generate_url_id
  def generate_url_id
    self.url_id = SecureRandom.urlsafe_base64
  end
end

2- In your model (Post.rb), supply a to_param method to override Rails default URL generation. For example:

class Post < ActiveRecord::Base
  def to_param
    self.url_id
  end
end

3- In your controller (PostsController.rb), use a dynamic finder to find your post by its random string. For instance,

class PostsController < ApplicationController
  def show
    @post = Post.find_by_url_id(params[:id])
    ...
  end
end

I went ahead and put together a complete example and posted it to Github.

2 Comments

The example on Github helped a lot, thanks! Is it possible we can chat? I have a couple of questions if that is ok.
Thanks. Hmm, it doesn't seem to be letting me create a chat.
0

Next to Erez manual way you can use the friendly_id gem, with a unique id as your slug.

class Post < ActiveRecord::Base
  # FriendlyId
  friendly_id :uid

  # Set a unique user id on create
  before_save :set_uid, on: :create

  def set_uid
    self[uid] = rand(36**8).to_s(36)
  end
end

Please note that the setting of the uid here does not ensure uniqueness. You certainly need to add some kind of validation, but that whole topic is a different one to google.

7 Comments

And that will create a random string of characters and numbers?
Yes, just give it a try in irb / rails console. It's the shortest way I know, I'll try to find the source of inspiration for it. The 8 in rand(36**8).to_s(36) determines the length of your random string.
Did not find the post, but here's a nice Stackoverflow discussion on the topic of random strings in Ruby.
I'm getting this error when I add that to the model: Routing Error undefined method `friendly_id' for #<Class:0x007f8bc94a4af0> Try running rake routes for more information on available routes.
You need to add the friendly_id gem first. Take a look at its readme.
|
0

Friendly_id is a good solution, if you want to use a gem for it.

Follow this screencast:

http://railscasts.com/episodes/314-pretty-urls-with-friendlyid

(either video or asciicast, as you prefer)

Screencasts by Ryan Bates are really well done.

1 Comment

This shows friendly URLs, I'm looking for randomly generated URLs, e.g. /sfk23a9cdc6
0

If you still want another option for id generation, you can try using UUIDs:

https://en.wikipedia.org/wiki/Universally_unique_identifier

And a ruby gem to generate them easily:

https://github.com/assaf/uuid

However, I would ask: Why do you want to make them random anyway? If what you are trying to do is to avoid one of your users from typing another id in the url and accessing data that is not theirs, then probably you would want to limit access in the controller by scoping the finder, with something like this:

def show
    @post = current_user.posts.where(:id => params[:id]).first
    respond_to do |format|
       format.html # show.html.erb
       format.json { render json: @post }
    end
end

In this case, current_user is a function that returns the current authenticated user (from session, or whatever you application logic dictates).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.