Why ActiveModel::Type::String attribute returns "f" instead of "false" when set boolean false value?

Hi, I’m using ActiveModel::Attributes and ActiveModel::Type for json data from some API systems. And got into trouble by casting of ActiveModel::Type::String

here is the rails code.

activemodel/lib/active_model/type/string.rb

      private
        def cast_value(value)
          case value
          when ::String then ::String.new(value)
          when true then "t"
          when false then "f"
          else value.to_s
          end
        end

and here is my sample code

class Test
  include ActiveModel::Model
  include ActiveModel::Attributes

  attribute :flag, :string
end

test = Test.new(flag: false)
test.flag
=> "f"

My expectation is that when I set boolean false to the string attribute :flag, it will return "false" as string value. This is because simple to_s method does like below.

false.to_s
=> "false"

what’s the reason or any background to cast "f" instead of "false"?

I would like to propose to change this casting to be same as to_s and returns "false". ("true" too.)

This is just a guess, but it might be to save bytes, as ‘f’ is shorter (3 bytes) than ‘false’ (7 bytes).

Looking at the git log, there’s been some recent work on this. Check out Fix string type cast with boolean serialization for MySQL · rails/rails@19839ff · GitHub

2 Likes

Thanks. I noted the boolean casting will be customizable by giving arguments true: and false:.

Out of curiosity, what does the data from your API look like? Could it be a boolean or string? If so, you may want to consider using a JSON type so you can correctly serialize and deserialize data of different types.

Thanks for your reply.

The API(which is not ours) provides/accept string “true/false”. I cannot change the request/response data type itself. So my attribute type is also string.

The background of my question is that I’m working on version up of legacy Rails, including a migration from old gem Virtus to ActiveModel::Attributes. Then Rails code set boolean true/false to a model(created by Virtus) in a business logic, Virtus casts a boolean value to string as it is. true becomes “true” and false becomes “false”. This behavior also make sense for me.

but after migrating to ActiveModel::Attributes, boolean true/false became “t” and “f” which caused errors.

As a workaround, I created custom type of ActiveModel::Type for API connection to cast “true”/“false”.

Awesome - that sounds like the correct solution to me.