While building a Rails API backend for a recent project I came across a challenge when updating jsonb columns.
You see as a pure Rail'ist (Is that a thing...?) I try to stay away from writing SQL strings in my app when I could use Active Record native methods, but since I had defined some columns as Jsonb it seemed like I would need to use SQL since all the tutorials were making use of jsonb_set SQL method as follows:
sql = 'UPDATE users SET preferences = jsonb_set(
options, \'{email_opt_in}\', \'false\', FALSE) WHERE id = 1;'
c.execute(sql)
But as a Rail'ist I was looking for something more like user.update(params)
.
So...
This is what I came up with:
First get the current column value which Active Record returns as a Ruby hash:
user = session_user
currentKeys = user.api_keys
Convert post params to a hash and extract the nested values which were nested under keys:
hashedParams = user_params.to_h[:keys]
Use the Ruby hash merge method to merge my post params and current user column data:
newKeys = currentKeys.merge(hashedParams)
I could now use the Rails object update method as usual:
user.update(api_keys: newKeys)
Here is the complete code:
user = session_user
currentKeys = user.api_keys
hashedParams = user_params.to_h[:keys]
newKeys = currentKeys.merge(hashedParams)
if user.update(api_keys: newKeys)
Bla, bla, bla...
Ok, now here's the thing: I need validation if the above is acceptable as a Rail'ist(you heard it here first) so please If I'm missing anything(method, convention, irk etc.) comment below :)
Thanks For Reading!
Top comments (2)
Your code can result in a race condition when two request execute the above code at the same time. Hence, the tutorials use the
jsonb_set
, which performs the modification directly at DB level.Good point. Wrote this when I was just starting out, should definitely revise this :)