Why render json: is returning NULL instead of boolean value

radubogdan

Why I'm getting null instead of true/false for is_read when calling resource from API?

Offquestion: Could be related to render json: internals?

This is the first time when I see this sorcery so please bear with me. Looking for good answer :)

$ curl -X GET -H localhost:3000/api/v1/alerts/1/show | python -m json.tool


{
    "body": "Deserunt laboriosam quod consequuntur est dolor cum molestias.",
    "created_at": "2015-03-22T15:02:01.927Z",
    "id": 1,
    "is_read": null,
    "subtitle": "Aspernatur non voluptatem minus qui laudantium molestiae.",
    "title": "Et nemo magni autem similique consequuntur.",
    "updated_at": "2015-03-22T15:02:01.927Z"
}

-i

HTTP/1.1 200 OK
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Content-Type: application/json; charset=utf-8
Etag: "3232ec2058ffb13db1f9244366fe2ea8"
Cache-Control: max-age=0, private, must-revalidate
X-Request-Id: c520cabe-6334-4997-956b-d1f21724b80c
X-Runtime: 0.016337
Server: WEBrick/1.3.1 (Ruby/2.1.2/2014-05-08)
Date: Sun, 22 Mar 2015 15:28:21 GMT
Content-Length: 300
Connection: Keep-Alive

Rails console:

Alert.find(1)
  Alert Load (1.1ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> #<Alert id: 1, title: "Et nemo magni autem similique consequuntur.", subtitle: "Aspernatur non voluptatem minus qui laudantium mol...", body: "Deserunt laboriosam quod consequuntur est dolor cu...", is_read: false, created_at: "2015-03-22 15:02:01", updated_at: "2015-03-22 15:02:01">

And weird thing happens when I call #to_json

Alert.find(1).to_json
  Alert Load (4.1ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> "{\"id\":1,\"title\":\"Et nemo magni autem similique consequuntur.\",\"subtitle\":\"Aspernatur non voluptatem minus qui laudantium molestiae.\",\"body\":\"Deserunt laboriosam quod consequuntur est dolor cum molestias.\",\"is_read\":null,\"created_at\":\"2015-03-22T15:02:01.927Z\",\"updated_at\":\"2015-03-22T15:02:01.927Z\"}"

It's also happening when is_read is true.

More information:

Rails 4.1.7, Ruby 2.1.2, Postgresql

Alert

create_table "alerts", force: true do |t|
  t.string   "title"
  t.string   "subtitle"
  t.text     "body"
  t.boolean  "is_read",    default: false, null: false
  t.datetime "created_at"
  t.datetime "updated_at"
end

Script used to populate db:

Alert.populate 100 do |alert|
  alert.title = Faker::Lorem.sentence(3)
  alert.subtitle = Faker::Lorem.sentence(3)
  alert.body = Faker::Lorem.sentence(3)
  alert.is_read = false
end

Controller:

def show
  alert = Alert.find(params[:id])
  render json: alert
end

PostgreSQL

EDIT 26 Mar 2015

irb(main):013:0> Alert.find(1)
  Alert Load (0.6ms)  SELECT  "alerts".* FROM "alerts"  WHERE "alerts"."id" = $1 LIMIT 1  [["id", 1]]
=> #<Alert id: 1, title: "Test", subtitle: "waaat?", body: "heeeelp", is_read: false, created_at: "2015-03-26 15:49:32", updated_at: "2015-03-26 15:56:51">
irb(main):014:0> a.body
=> "heeeelp"
irb(main):015:0> a.title
=> "Test"
irb(main):016:0> a.is_read
=> nil
irb(main):017:0> a["is_read"]
=> false
irb(main):018:0> a.update_attributes(is_read: true)
   (0.4ms)  BEGIN
  SQL (0.5ms)  UPDATE "alerts" SET "is_read" = $1, "updated_at" = $2 WHERE "alerts"."id" = 1  [["is_read", "t"], ["updated_at", "2015-03-26 15:57:26.645210"]]
   (2.1ms)  COMMIT
=> true
irb(main):019:0> a["is_read"]
=> true

Final: it was because there was attr_reader :is_read in the model. Removing that it will result in a correct serialization. Can someone explain this?

Drenmi

Final: it was because there was attr_reader :is_read in the model. Removing that it will result in a correct serialization. Can someone explain this?

ActiveRecord helps map your database fields to methods on your Ruby class by creating a getter method for each attribute, that pulls the value from the internal attribute store (variable.)

When you define attr_reader :is_read, which is actually shorthand for:

# app/mode/alert.rb    
def is_read
  @is_read
end

Your getter method that was handily provided by ActiveRecord::Base will be masked by this newly defined method. This can be unexpected, and definitely seem like sorcery at first.

The behaviour of :attr_reader is (somewhat sparsely) documented here:

Creates instance variables and corresponding methods that return the value of each instance variable.

http://ruby-doc.org/core-1.9.3/Module.html#method-i-attr_reader

But why then it is coming as null? Could be related to render json: internals?

There are two parts to this question.

Firstly, why is the value not there? The answer to this was given above. Your model has masked the ActiveRecord getter method is_read, that was poiting to ActiveRecord's internal attribute store, with one pointing to an instance variable @is_read. Because you do not assign @is_read in your model, nil is returned.

Secondly, why is it null and not nil? The answer to this is, as you hinted at, related to render: json. In the JSON specification, you have the following allowed values:

string, number, object, array, true, false, null

To produce a valid JSON response, render: json replaces Ruby's nil, with JSON's null.

So is it safe then to say that you never want to use attr_reader, attr_accessor et. al. in your models? Not really. These can be used as virtual, or transient attributes, that are lost once the object is destroyed. One should keep in mind, though, the interactions with ActiveRecord, to avoid running into seemingly obscure bugs.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

why models.BooleanField returning bytes instead of Boolean value?

JSON_VALUE value returning NULL instead of actual value

Behavior of TOP with WHERE clause . Why is this query returning a value instead of NULL?

Getting null instead of the boolean value

json_decode is returning a null value instead of an array

Why is my code returning value of 'null' from a nested JSON array

Why isn't this if statement returning the boolean value?

Returning boolean instead of list

.getStringExtra returning null value instead String

JSON returning caret (^) in the value and returning null in the response

Returning null value in dictionary of JSON

Why is useContext() not returning the Provider value instead returning the initial value?

Why is Python returning None instead of value

Why is result returning hex instead of actual value?

Why is reduce returning a list instead of a single value?

returning ZoneAwarePromise instead of the actual value in Angular, why?

Why is function returning none instead of the value? (Python)

why is Spring Boot returning a string instead of JSON

Why is RestSharp returning text/html instead of json?

Why is my text area returning the value 'null'?

Why are my char arrays returning a null value?

Why is arr[6] returning a null value in this code?

Cannot understand why this parameter is returning a null value

Why Array helper Filter is returning a null value

Why is Kotlin's mutableListOf() returning a null value

A Boolean function returning a value

Why is to_json returning null values?

Why is json_extract_scalar returning null

why it's returning null value after parsing json + using bloc pattern