New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

activerecord-jsonapi_update

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

activerecord-jsonapi_update

  • 0.0.1
  • Rubygems
  • Socket score

Version published
Maintainers
1
Created
Source

Update ActiveRecord instances and their associations, consistent with JSON API specification.

Why is this needed?

The JSON API specification says the following:

If a relationship is provided in the relationships member of a resource object in a PATCH request, its value ... will be replaced with the value specified in this member.

The example provided is particularly clear:

... the following PATCH request performs a complete replacement of the tags for an article:

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "id": "1",
    "relationships": {
      "tags": {
        "data": [
          { "type": "tags", "id": "2" },
          { "type": "tags", "id": "3" }
        ]
      }
    }
  }
}

Which, depending on your deserialization solution, would likely be interpreted as:

article_attributes = {
  id: '1',
  tag_ids: ['2', '3']
}                                                

@article.update(article_attributes) 

@article.reload.tag_ids # => [2, 3]

Which would work correctly (any tags that were previously associated with the article that were not mentioned in the new tag_ids would be destroyed).

However consider when your model accepts nested attributes, and you then want to specify a new tag as well as keep an old one:

PATCH /articles/1 HTTP/1.1
Content-Type: application/vnd.api+json
Accept: application/vnd.api+json

{
  "data": {
    "type": "articles",
    "id": "1",
    "relationships": {
      "tags": {
        "data": [
          { "type": "tags", "id": "2" },
          { "type": "tags", "id": "clientid1" }   // New tag
        ]
      }
    },
    "included": [
     {
       "type": "tag",
       "id": "clientid1",
       "attributes": {
         "name": "New Tag"
       }
     }
    ]
  }
}

This will likely get processed as (assuming you're removing client ids):

article_attributes = {
  id: '1',
  tags_attributes: {
    0: {
      id: '2'
    },
    1:  {
      name: 'New Tag'
    },
  }
} 

@article.update(article_attributes) # The new tag is given id 4  

@article.reload.tag_ids # => [2, 3, 4]

@article.update(article_attributes) correctly creates the new tag and keeps the one that should be kept (id: 2), but it doesn't get rid of the old one (id: 3).

That's when you need activerecord-jsonapi_update:

@article.jsonapi_update(article_attributes) # The new tag is given id 4  

@article.reload.tag_ids # => [2, 4]

Installation

Add this line to your application's Gemfile:

gem 'activerecord-jsonapi_update'

And then execute:

$ bundle

Or install it yourself as:

$ gem install activerecord-jsonapi_update

Usage

activerecord-jsonapi_update provides 3 new methods on ActiveRecord instances:

  • jsonapi_update instead of update
  • jsonapi_update! instead of update!
  • assign_jsonapi_attributes instead of assign_attributes

Each has the same method signature (arguments) as the ActiveRecord method it replaces.

For example:

def update
  @article = Article.find(params[:id])

  if @article.jsonapi_update(allowed_params)
    render json: @article, status: :success
  end
end

jsonapi_update does not side-step any of the normal ActiveRecord functionality (and limits itself to pre-processing the attributes it's provided), so in order to allow destroying nested attributes you must still explicitly permit it in the accepts_nested_attributes declaration.

class Article < ActiveRecord::Base
  accepts_nested_attributes_for :tags, allow_destroy: true
end

You will also have configure strong parameters correctly in your controller to permit the associated resource attributes:

def allowed_params
  params.from_jsonapi.permit(:id, tags: [:id, :name])
end

How it works

To accommodate this, nested hashes or arrays with the _attributes suffix are processed as follows:

  • For hashes that have no id value, a new record is created (matching the behaviour of the normal #update method).
  • For hashes that have an id value, a find-and-update operation will occur (again, matching #update)
  • Any existing associated records not mentioned in the *_attributes hash or array will be destroyed (which is not how the normal #update method functions.)

Should I always use jsonapi_update now?

The jsonapi_update method intends to be as light-weight as possible, but it does carry with it a slight overhead that depends on the size and complexity of the hash of values you're using to update your models.

It also only has any effect when:

  • Strong parameters permit nested parameters to update a nested resource
  • The model declares it accepts nested parameters for an associated resources that are allowed to be destroyed

So only use jsonapi_update when you actually want to support the replacement of the entire collection of associated resources on a particular endpoint and you have configured your controller and models to allow it.

Please see the relevant section of the JSON API specification for a full discussion of supporting or rejecting updating associated resources.

FAQs

Package last updated on 17 Nov 2020

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc