ja.
ja.

Reputation: 467

How to write a PATCH endpoint in Ruby on Rails

I'm trying to write a simple PATCH endpoint in ROR to update a User. Here's what I have:

  def update
    @user = User.find(params[:id])
    if @user.update_attributes(name: params[:name],
                          username: params[:username],
                          email: params[:email],
                          bio: params[:bio])
      render json: {
        message: 'User Updated Successfully',
        id: @user.id,
        name: @user.name,
        username: @user.username,
        email: @user.email,
        bio: @user.bio
      }, status: 200
    else
      render json: {
        message: 'Validation Failed',
        errors: @user.errors.full_messages
      }, status: 422
    end
  end

    def user_params
      {
        device_token: params[:device_token],
        id: params[:id],
        name: params[:name],
        username: params[:username],
        email: params[:email],
        bio: params[:bio], 
        password: params[:password],
        password_confirmation: params[:password_confirmation]
      }
    end

The update method fails the 'if' and returns 422 to my client. I'm not quite sure what I'm doing wrong. I'm thinking that since Password/Confirmation are required when creating a user, that it's failing because they aren't provided in the parameters for this method. Am I totally off base? If not, is there any way around this snag? Thanks in advance.

------edit-------

From the rails console

[3] pry(main)> @user.update_attributes({'name' => 'Jason Smith', 'username' => 'smithja', 'email' => '[email protected]', 'bio' => ''})
   (0.2ms)  BEGIN
  User Exists (0.9ms)  SELECT  1 AS one FROM "users"  WHERE (LOWER("users"."email") = LOWER('[email protected]') AND "users"."id" != 14) LIMIT 1
   (0.3ms)  ROLLBACK
=> false

[4] pry(main)> @user.update_attributes({'name' => 'Jason Smith', 'username' => 'smithja', 'email' => '[email protected]', 'bio' => '', 'password' => 'foobar', 'password_confirmation' => 'foobar'})
   (0.2ms)  BEGIN
  User Exists (0.5ms)  SELECT  1 AS one FROM "users"  WHERE (LOWER("users"."email") = LOWER('[email protected]') AND "users"."id" != 14) LIMIT 1
  SQL (0.5ms)  UPDATE "users" SET "bio" = $1, "email" = $2, "name" = $3, "password_digest" = $4, "updated_at" = $5, "username" = $6 WHERE "users"."id" = 14  [["bio", ""], ["email", "[email protected]"], ["name", "Jason Smith"], ["password_digest", "$2a$10$XvOv1EchEeWAEczbKKTbC.l5svoSTA807Y0dgOMFOh3xUi5uC.vaW"], ["updated_at", "2014-09-08 17:39:50.240324"], ["username", "smithja"]]
   (0.5ms)  COMMIT
=> true

I don't want to have to enter the password.

Upvotes: 0

Views: 1351

Answers (1)

agmcleod
agmcleod

Reputation: 13621

Yeah 422 is when validations fail. Update your validations to only require password & password_confirmation on create. If you're using has_secure_password, it should do this for you.

Also, no need to manually build out your JSON:

render @user.as_json.merge({:message => 'User Updated Successfully'}).to_json

The as_json method converts it to a hash object, which can be used to create a json string. This includes all the database attributes by default. The to_json then takes the modified hash and returns the string value.

Though honestly, i would only pass a message back to JSON that the server needs to determine. So error messages for example for a badly formatted email are great to pass through JSON. A success message can be handled by the client easily enough.

Upvotes: 1

Related Questions