Search code examples
crystal-langamber-framework

Save empty strings as NULL in the database. AmberFramework / Granite


It is possible to force Granite to save empty strings as NULL in the DB?

I tried this code:

before_save :nilify_blanks

def nilify_blanks
  self.to_h.keys.each do |column|
    if self[column].is_a?(String) && self[column].empty?
      self[column] = nil 
    end
  end
end

But the compiler gives me an error:

 223 | if self[column].is_a?(String) && self[column].empty?
              ^
Error: undefined method '[]' for Foo

Solution

  • Finally I used a macro:

    class Pet < Granite::Base
      connection pg
      table pets
    
      column id : Int64, primary: true
      column name : String?
      column breed : String?
      column age : Int32?
      timestamps
        
    
      before_save :nilify_blanks
    
      def nilify_blanks
        {% for column in @type.instance_vars.select { |ivar| ivar.annotation(Granite::Column) } %}
          {% if column.type.id == Union(String | Nil).id %}
            col = self.{{column.name}}
            if col && col.strip.empty?
              self.{{column.name}} = nil
            end
          {% end %}
        {% end %}
      end
        
    end
    

    The macro will generate this code:

      def nilify_blanks
        col = self.name
        if col && col.strip.empty?
          self.name = nil
        end
    
        col = self.breed
        if col && col.strip.empty?
          self.breed = nil
        end
    
        ...
      end