davegson
davegson

Reputation: 8331

Rails using variables for column name

I have a Like model, it connects users liking items. Additonally to the standard liked status, items can be spinned & pinned, all being boolean fields.

Like Model

 _________________________________________________
| item_id | user_id |  liked  |  spinned | pinned |
|---------|---------|---------|----------|--------|
|    1    |    1    |  true   |   true   | false  |
|    2    |    1    |  false  |   true   | true   |
|    3    |    2    |  true   |   false  | false  |
 -------------------------------------------------

The different states of an item all get updated via the same AJAX call, calling the same action in my likes controller. When the like does not yet exist, it creates a like with the desired state being true.

likes_controller

def index
  # get the item id
  action = params[:act]
  mid = params[:mid]

  # gets the desired like item to see if it already exists
  like = Like.where(:item_id => mid, :user_id => current_user.id).first

  case action

    # like the item
    when "like"
      # if the item doesn't exist
      if like.blank?
        # create the like
        Like.create(:user_id => current_user.id, :item_id => mid, :liked => true)
      else
        # else toggle the liked status
        like.toggle! :liked
      end

    # pin the item
    when "pin"
      if like.blank?
        # create the like
        Like.create(:user_id => current_user.id, :item_id => mid, :pinned => true)
      else
        # else toggle the pinned status
        like.toggle! :pinned
      end

    # spin the item
    when "spin"
      if like.blank?
        # create the like
        Like.create(:user_id => current_user.id, :item_id => mid, :spinned => true)
      else
        # else toggle the spinned status
        like.toggle! :spinned
      end
  end
end

As you see, the code in the case gets very repetitive, I wanted to know if I could use my action ('like', 'pin', 'spin') variable more effectively.

I could easily convert the action to the appropriate field name ("like" => "liked", "pin" => "pinned", "spin" => "spinned") by sending another parameter value from my view.

Could I then use this to only have one block of code instead of 3 similar ones?

iE

action = "liked" # this would change depending on the desired action

# if the item doesn't exist
if like.blank?
  # original
  Like.create(:user_id => current_user.id, :item_id => mid, :liked => true)
  # doesn't work, how would I do this appropriately?
  Like.create("user_id = ?, item_id = ?, ? = true", current_user.id, mid, action)
else
  # this works!, what about SQL injection? How can I prevent this?
  like.toggle!("#{action}")
end

Upvotes: 0

Views: 700

Answers (2)

nishu
nishu

Reputation: 1493

You can use select or new and then save if the atomicity of events is not a major issue.

like = Like.where(:item_id => mid, :user_id => current_user.id).first
like ||= Like.new(:item_id => mid, :user_id => current_user.id, :pinned => false, :spinned => false, :liked => false)
when "like"
like.liked = !like.liked
when "spin"
like.spinned = !like.spinned
when "pin"
like.pinned = !like.pinned
end
like.save!

Upvotes: 1

davegson
davegson

Reputation: 8331

As it turns out I could simply use my variable action instead of the column selector :column when using create() and toggle().

The result being:

if like.blank?
  # create the like
  Like.create(:user_id => current_user, :item_id => mid, action => true)
else
  # else toggle the desired status
  like.toggle!(action)
end  

Upvotes: 0

Related Questions