title: Rails 4: How to set a default for a custom data type?
body: <p>I have a Rails 4.2 + MySQL 5.7 setup. I've created a custom data type for native JSON columns, which works fine, with one problem - if I set a default, it's not persisted.</p>
  
  <p>Specifically, if I set the default, the attribute will be set correctly on instantiation, but on instance save, the field will not recognized as changed, so it won't be saved.</p>
  
  <p>This is the data type definition (in a simplified form):</p>
  
  <pre><code>module ActiveRecord
    module Type
      class Json &lt; Type::Value
        include Type::Mutable
        def type
          :json
        end
  
        def type_cast_for_database(value)
          case value
          when Hash
            value.to_json
          else
            raise "type_cast_for_database not supported: #{value.inspect}"
          end
        end
  
        def type_cast(value)
          case value
          when nil
            {}
          when ::Hash
            value
          else
            raise "type_cast not supported: #{value.inspect}"
          end
        end
      end
    end
  end
  </code></pre>
  
  <p>Now, if I define the attribute as:</p>
  
  <pre><code>  attribute :jsfield, ActiveRecord::Type::Json.new, default: {}
  </code></pre>
  
  <p>On instantiation, the value will be set, but not saved:</p>
  
  <pre><code>  instance = MyModel.new
    instance.jsfield        #=&gt; {}
    instance.save           # the INSERT will not include the attribute :jsfield
  </code></pre>
  
  <p>This is caused by <code>ActiveRecord::Type::Mutable</code>:</p>
  
  <pre><code>  def changed_in_place?(raw_old_value, new_value)
      raw_old_value != type_cast_for_database(new_value)
    end
  </code></pre>
  
  <p>it does the correct thing, but when a default is set, <code>raw_old_value</code> is preset (to <code>{}</code> in this case), so ActiveRecord thinks that the value hasn't changed.</p>
  
  <p>I can work this around by allowing nil in <code>ActiveRecord::Type::Json#Jsontype_cast</code> - but is this the only solution?<br>
  Am I missing something?</p>