Question

I have a activerecord model with a validates :body, presence: true. When a form is filled and submited, I get {:body=>["can't be blank"]} even though the body is not actually blank. If I remove the validation from the model, and re-submit, the record is successfully created. Why is the validation saying the body field is blank when it is not blank.

The controller.

class SqlTemplatesController < ApplicationController

  def create
    @sql_template = SqlTemplate.new(sql_template_params)

     respond_to do |format|
        if @sql_template.save
         format.html { redirect_to @sql_template, notice: 'Sql template was successfully created.' }
    end
   end

   private
   def sql_template_params
      params.require(:sql_template).permit(:body, :path, :format, :locale, :handler, :partial)
   end

end

The Model

class SqlTemplate < ActiveRecord::Base
  validates :body, :path, presence: true
  validates :format, inclusion: Mime::SET.symbols.map(&:to_s)
  validates :locale, inclusion: I18n.available_locales.map(&:to_s)
  validates :handler, inclusion: ActionView::Template::Handlers.extensions.map(&:to_s)

  def to_liquid
    SqlTemplateDrop.new(self)
  end

 def body=(text)
    if self[:handler] == 'liquid'
      @template = Liquid::Template.parse(text)
      self[:body] = Marshal.dump(@template)
    end
 end

 def render(options = {})
   template.render options
 end

 private

 def template
   return @body unless @body.nil?
   @body = Marshal.load(self[:body])
 end

end

In the rails console If create a new record and set the body field to either a string body => "message body" or a liquid markup eg body => "{{body}}, it will raise the error {:body=>["can't be blank"]} but if remove the validation they work.

irb(main):016:0> a = SqlTemplate.create(:body => "{{body}", path => "mail/liquid_database_template", :format => "html", :locale => "en", :handler => "liquid", :partial => false)

(0.1ms)  begin transaction
(0.1ms)  rollback transaction

irb(main):016:0> a.errors.messages
 => {:body=>["can't be blank"]}

If I remove the validation and submit a form, it all works, as shown below:

 Started POST "/sql_templates" for 127.0.0.1 at 2014-03-11 15:28:14 +0000
  Processing by SqlTemplatesController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"GVsRbsCKSlcEMiL1AzXE0tT8LBCNhhoK6wSGzvnB80A=", "sql_template"=>{"body"=>"{{body}}", "path"=>"customer_mail/liquid_database_template", "format"=>"html", "locale"=>"en", "handler"=>"liquid", "partial"=>"0"}, "commit"=>"Create Sql template"}
  #<SqlTemplate id: nil, body: nil, path: "customer_mail/liquid_database_template", format: "html", locale: "en", handler: "liquid", partial: false, created_at: nil, updated_at: nil>
 (0.1ms)  begin transaction
 SQL (9.4ms)  INSERT INTO "sql_templates" ("created_at", "format", "handler", "locale", "path", "updated_at") VALUES (?, ?, ?, ?, ?, ?)  [["created_at", "2014-03-11 15:28:14.869619"], ["format", "html"], ["handler", "liquid"], ["locale", "en"], ["path", "customer_mail/liquid_database_template"], ["updated_at", "2014-03-11 15:28:14.869619"]]
 (621.4ms)  commit transaction
 Redirected to http://localhost:3000/sql_templates/7
 Completed 302 Found in 662ms (ActiveRecord: 630.9ms)

Started GET "/sql_templates/7" for 127.0.0.1 at 2014-03-11 15:28:15 +0000
  Processing by SqlTemplatesController#show as HTML
  Parameters: {"id"=>"7"}
  SqlTemplate Load (3.1ms)  SELECT  "sql_templates".* FROM "sql_templates"  WHERE  "sql_templates"."id" = ? LIMIT 1  [["id", 7]]
  Rendered sql_templates/show.html.erb within layouts/application (11.4ms)
  Completed 200 OK in 52ms (Views: 46.0ms | ActiveRecord: 3.1ms)

If I add the validation back and submit it fails as shown below:

Started POST "/sql_templates" for 127.0.0.1 at 2014-03-11 14:34:22 +0000
 ActiveRecord::SchemaMigration Load (0.3ms)  SELECT "schema_migrations".* FROM  "schema_migrations"
 Processing by SqlTemplatesController#create as HTML
  Parameters: {"utf8"=>"✓",  "authenticity_token"=>"GVsRbsCKSlcEMiL1AzXE0tT8LBCNhhoK6wSGzvnB80A=", "sql_template"=> {"body"=>"{{body}}", "path"=>"customer_mail/liquid_database_template", "format"=>"html",  "locale"=>"en", "handler"=>"liquid", "partial"=>"0"}, "commit"=>"Create Sql template"}
    #<SqlTemplate id: nil, body: nil, path: "customer_mail/liquid_database_template", format: "html", locale: "en", handler: "liquid", partial: false, created_at: nil, updated_at: nil>
   (0.2ms)  begin transaction
 (0.2ms)  rollback transaction
  Rendered sql_templates/_form.html.erb (32.6ms)
  Rendered sql_templates/new.html.erb within layouts/application (57.1ms)
  Completed 200 OK in 208ms (Views: 143.0ms | ActiveRecord: 1.4ms)
Was it helpful?

Solution 2

I resolved it myself. The problem was with the setter method for the body field. Normally rails automatically defines this but if it is being overridden to do some extra things which is the case here, I still needed to set self[:body] = text at the top before everything else. Note that text is the value passed in to the field through the form. Without that validates presence will fail.

def body=(text)
  self[:body] = text
end

OTHER TIPS

Your body setter is getting called before the setter for handler. So self[:handler] will be nil when it goes into the body= method

You can try one of these

i) Change the order of your hash

a = SqlTemplate.create(:handler => "liquid", :body => "{{body}", path =>   "mail/liquid_database_template", :format => "html", :locale => "en", :partial => false)

ii) Set the body later after setting the handler

a = SqlTemplate.new(:handler => "liquid", path => "mail/liquid_database_template", :format => "html", :locale => "en", :partial => false)
a.body = "{{body}}"
a.save
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top