When creating and updating boolean type columns with Rails API, I want to play types other than boolean type
$ rails -v
Rails 6.0.3.2
$ rails g model Post body:text opened:boolean
app/models/post.rb
class Post < ApplicationRecord
validates :opened, inclusion: { in: [true, false]}
end
If you do this, you should be able to play anything other than boolean ...
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "moge"}'
{"status":"success","data":{"id":1,"body":"hoge","opened":true,"created_at":"2020-08-16T01:31:14.277Z","updated_at":"2020-08-16T01:31:14.277Z"}}
Specify "moge" for boolean open column
Expectation: It's not boolean, so it's an error and it's played Actual condition: Cast to true and saved without error
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "0"}'
{"status":"SUCCESS","data":{"id":2,"body":"hoge","opened":false,"created_at":"2020-08-16T01:31:28.498Z","updated_at":"2020-08-16T01:31:28.498Z"}}
Expectation: Since it is not boolean, it will be played with an error Actual condition: Cast to false and saved without error
Apparently it seems that the value will be passed in the cast state
Make a custom validation
app/validators/boolean_validator.rb
class BooleanValidator < ActiveModel::EachValidator
def validate_each(record, attr, _value)
before_value = record.send("#{attr}_before_type_cast")
record.errors.add(attr, "is invalid") unless %w[true false].include?(before_value.to_s.downcase)
end
end
app/models/post.rb
class Post < ApplicationRecord
validates :opened, boolean: true
end
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "moge"}'
{"status":"error","data":{"opened":["is invalid"]}}
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "0"}'
{"status":"error","data":{"opened":["is invalid"]}}
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "true"}'
{"status":"success","data":{"id":3,"body":"hoge","opened":true,"created_at":"2020-08-16T02:25:18.211Z","updated_at":"2020-08-16T02:25:18.211Z"}}
$ curl localhost:3000/posts -X POST -H "Content-Type: application/json" -d '{"body": "hoge", "opened": "false"}'
{"status":"success","data":{"id":4,"body":"hoge","opened":false,"created_at":"2020-08-16T02:25:30.700Z","updated_at":"2020-08-16T02:25:30.700Z"}}
As expected, all but true and false are now played
How to validate a boolean type column in Rails and return it as an error if the type is different If you implement it with the above URL, it will get caught in rubocop in various ways, so I am writing this article