james
james

Reputation: 4049

Phone is defined as string in db schema, but integers are saving as valid in Ruby

I feel like I'm missing something very simple here...

My db schema:

create_table "plans", force: true do |t|
  t.string   "phone1"
  ...
end

Here's a snippet from my console:

@plan = Plan.create(a bunch of params)
@plan.phone1 = "123"
@plan.valid?
# => true

# above is great, here's where the problem comes in:
@plan.update_attribute("phone1", 123)
@plan.phone1
# => 123
@plan.valid?
# => true

This is not making my model tests very happy. Nor me for that matter. From my model, here are all the relevant validations:

validates :phone1, presence: true
validates :phone1, length: { is: 3 }

Upvotes: 3

Views: 78

Answers (2)

max
max

Reputation: 102134

ActiveRecord looks at your schema.rb and creates setters which typecast based on the column value.

class Plan < ActiveRecord::Base
  # "Automagically" creating by Active Record.
  # def phone1= val
  #   @phone1 = val.to_s
  # end
end

So when you call .valid on @plan the 'phone1' attribute is a string. I'm not sure what your test looks like but if your are doing:

plan = Plan.new(123)
expect(plan.valid?).to be_falsy

Expecting plan to be invalid solely because it's passed a number than your have simply misunderstood how rails works.

Given:

$ rails g model plan phone1:string ends:datetime
$ rails g migrate

irb(main):004:0>@plan = Plan.create(ends: Date.tomorrow, phone1: 123)
   (0.3ms)  begin transaction
  SQL (1.2ms)  INSERT INTO "plans" ("ends", "phone1", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["ends", "2015-06-24 00:00:00.000000"], ["phone1", "123"], ["created_at", "2015-06-23 02:21:39.236332"], ["updated_at", "2015-06-23 02:21:39.236332"]]
   (1.2ms)  commit transaction
=> #<Plan id: 2, phone1: "123", ends: "2015-06-24 00:00:00", created_at: "2015-06-23 02:21:39", updated_at: "2015-06-23 02:21:39">
irb(main):005:0> @plan.phone1 = 123456
=> 123456
irb(main):006:0> @plan.phone1.class
=> String
irb(main):007:0> @plan.update_attribute("phone1", 123)
   (0.8ms)  begin transaction
   (0.3ms)  commit transaction
=> true
irb(main):008:0> @plan.phone1.class
=> String
irb(main):013:0> @plan.ends = "2015-06-23"
=> "2015-06-23"
irb(main):014:0> @plan.ends
=> Tue, 23 Jun 2015 00:00:00 UTC +00:00
irb(main):015:0> 

Upvotes: 1

Myst
Myst

Reputation: 19221

You could write a custom validation method to check that phone1 is a String*:

class Plan
   validates :phone1, presence: true
   validates :phone1, length: { is: 3 }
   validates :special_validations

   def special_validations
      errors.add(:phone1, "Must be String") unless phone1.is_a? String
      # add whatever you feel like
      true
   def
end

On the other hand, if you get a numerical field when loading the data from the database, than your database's field type isn't a string. Maybe an older setting persists?


* I'm not too savvy as far as the Rails specialty features go, so there might be a shortcut to this...

Upvotes: 0

Related Questions