user1658756
user1658756

Reputation: 1024

Ruby on Rails URL Format

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

Upvotes: 6

Views: 2237

Answers (6)

kburkhardt
kburkhardt

Reputation: 153

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.

Upvotes: 0

Ylan S
Ylan S

Reputation: 1786

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).

Upvotes: 0

Paola Cerioli
Paola Cerioli

Reputation: 741

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.

Upvotes: 0

R Milushev
R Milushev

Reputation: 4315

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 .

Upvotes: 2

Thomas Klemm
Thomas Klemm

Reputation: 10856

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.

Upvotes: 0

Erez Rabih
Erez Rabih

Reputation: 15808

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.

Upvotes: 15

Related Questions