views:

353

answers:

2

Hi, I have a user stored in a session which I would like to add as the owner of a comment. Rather than having a hidden field for user_id, I would like to add the user before the comment is saved in the controller.

What would be the best way to do this?

@comment = @post.comments.create(params[:comment])

Thanks.

+1  A: 

There are a few strategies that work fairly well. You can have a call in the controller that staples the User on to the created comment:

def create
  @comment = @post.comments.build(params[:comment])
  @comment.user = session_user
  @comment.save!

  redirect_to(post_path(@post))

rescue ActiveRecord::RecordInvalid
  # Take appropriate action, such as show comment create form
  render(:action => 'new')
end

Another way is to use something like model_helper (http://github.com/theworkinggroup/model_helper/) to provide access to controller properties within the model environment:

class ApplicationController < ActionController::Base
  # Makes the session_user method callable from the ActiveRecord context.
  model_helper :session_user
end

class Comment < ActiveRecord::Base
  before_validation :assign_session_user

protected
  def assign_session_user
    if (self.user.blank?)
      self.user = session_user
    end
  end
end

This method is more automatic, but at the price of transparency and possibly complicating your unit test environment.

A third approach is to merge in the parameters on the create call:

@comment = @post.comments.build((params[:comment] || { }).merge(:user => session_user))

This has the disadvantage of not working very well if some of the properties of your model are protected, as they probably should be in any production environment.

Another trick is to create a class method that helps build things for you:

class Comment < ActiveRecord::Base
  def self.create_for_user(user, params)
    created = new(params)
    created.user = user
    created.save
    created
  end
end

This is called on the relationship and will build in the correct scope:

@comment = @post.comments.create_for_user(session_user, params[:comment])
tadman
A: 

Firstly, for security reasons, you probably want to protect the user_id attribute of the comment, so you should have something like this in your model:

attr_protected :user_id

Or, use attr_accessible and list all the attributes which can be set via mass-assignment (ie, Comment.create(...) or @comment.update_attributes(...)). Then, because you have to assign via assignment, your controller will look like this:

@comment = @post.comments.new(params[:comment])
@comment.user_id = current_user.id
@comment.save

It's not as slick, but it's necessary so that someone can't submit a fake user_id value.

Alex Reisner