user1658756
user1658756

Reputation: 1024

Change Params[:id] in Ruby on Rails

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 it to generater a string of random characters and numbers, for example /49slc8sd, /l9scs8dl, etc?

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

And here is what I have in the post.rb model

class Document < ActiveRecord::Base
  attr_accessible :content, :name
end

Upvotes: 1

Views: 1931

Answers (5)

omninonsense
omninonsense

Reputation: 6892

There is a number of ways how you can generate a random string in Ruby.

Now, to the second part of your question. If you want to access your posts using a route like /post/rndm5tr, you can simply change this line of code inside your controller:

@post = Post.find(params[:id])

to

@post = Post.find_by_randomness(params[:id])

Now, simply create a migration: rails g migration AddRandomnessToPost randomness:string and run rake db:migrate (or bundle exec rake db:migrate, depending on how it's set up).

Of course, you are free to name the field whatever you want, randomness is just a random name I used. I think the common convention is to call them slugs or tokens, but I might be wrong.

Now, add a method to before_create in your model to generate the random string and add it to the soon-to-be-saved Post object (using one of the examples from the above link). It would be wise to check if the string you're generating is already taken (you could write a recursive method that calls itself again if a post already has the random token).

Upvotes: 0

vpsz
vpsz

Reputation: 457

You should also be aware of the to_param method for ActiveRecord::Base objects.

Basically, Rails calls this method on your objects to know what to put in the URL and params[:id]. By default it is just the primary key of the record in the database. Say you override it as such:

class Post < ActiveRecord::Base

  def to_param
    return id*100
  end

  def self.find_by_new_id(n)
    return self.find(n/100) # really you'd want to handle strings and integers
  end
end

The first record in your database would have url /posts/100.

In your controller, to retrieve the object you just do

@post = Post.find_by_new_id(params[:id])

(Of course you could override the default find method as well, but that is probably frowned upon.) Basically the to_param method transforms your id and the new finder undoes it. Usually you just point to another database column that has been automatically populated via a hook when the record is created. This is what is described in the link posted by Qumara otBurgas.

Upvotes: 1

R Milushev
R Milushev

Reputation: 4315

If you would like to obfuscate numerical ID's , you could take a look at this interesting discusion .

Upvotes: 1

praveenag
praveenag

Reputation: 145

If you want your models not to have their primary key id in a predictable sequence, you can generate the id based on uuid or guid with the help of something like http://codesnipers.com/?q=using-uuid-guid-as-primary-key-in-rails

However you can also route based on any other property which uniquely identifies the resource which is the recommended approach if in case you dont want to expose the database identifiers in your routes

person/:person_random_token, :controller => :persons, :action => :show #adding this in your route file directing to the controller where you can use params[:person_random_token] to uniquely identify your person object in Persons model

In your controller's action you can say

Person.find_by_random_token(params[:person_random_token]) #assuming random_token is your column name

to get the Person object

Upvotes: 1

weexpectedTHIS
weexpectedTHIS

Reputation: 3376

It's not clear what you are asking here. The path to the action specified in the routes does not require the id passed to be of a certain format. You can pass non-numeric ids if you want and within your action use the id however you'd like. Maybe if you supplied more info about the routes and actions we could understand what you are asking for.

Upvotes: 0

Related Questions