How do I force a rails ActiveRecord string to only accept strings?

I have a class Post

with a fieldtitle:string

post = Post.create!
#=> Post created
post.title = "foo"
post.save! 
# success
post.title = 1
post.save! 
# succeeds, but I want it to throw a type mismatch exception

      

How can I get the last salvation! cause type mismatch?

+3


source to share


6 answers


Ruby is a duck language and has no type mismatches. If the object responds to to_s

, Rails will call on it to_s

and place it in the string field.

It sounds like you really want to check the format of the value you put in the field (is a string a "2"

valid value?), Which is what it is for validates_format_of

. For example, if you want to make sure that at least one non-digit character has a value, you can do this:

class Post < ActiveRecord::Base
  validates_format_of :title, with: /\D/
end

      

Of course, you really want to tweak this regex a bit.



If you really can't stand the duck printing and want to make sure that only the true string is in that column, you can override title=

:

class Post < ActiveRecord::Base
  def title=(value)
    throw "`title' must be a String" unless value.is_a?(String)
    super
  end
end

      

Note that this will not be called when you do this for example. post[:title] = ...

or post.write_attribute(:title, ...)

, but it should cover 95% of situations.

+1


source


This really goes against what Ruby is. If it is a string column, the database layer will render this value as a string and store it.

The concept of type is different from Ruby than in other languages. Generally, if an object is supported to_s

, then it is considered strict enough to be used, the basic tenet of Duck Typing .

If you pass in something that cannot be represented as a string, you will get an exception. For example:



post.title = Post.find(5)

      

If you want your fields to be non-numeric, you must add validation that enforces a specific format.

+3


source


A new gem has been created to help validate types in rails and an explanatory blog post exists to answer the "why" question that was created in the first place.

In this library, your code will be simple:

class Post < ActiveRecord::Base
  validates_type :title, :string
end

      

This will throw an exception when an integer value is assigned title

instead of quietly assigning to the title

string and saving it.

+2


source


To answer the exact question, if you want only String objects to be stored in the field, you must add the following custom validation.

  validate :must_be_string

  def must_be_string
    errors.add(:base, 'The field must be String') unless title.class == String
  end

      

0


source


You can define a custom validator.

validates :title, type: String

      

and then:

class TypeValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    record.errors.add attribute, (options[:message] || "is not of class #{options[:with]}") unless
      value.class == options[:with]
  end
end

      

0


source


add this line to your post model.

  validates_format_of :title, with: /^[a-zA-Z\s]*$/

      

-1


source







All Articles