RoR: Validation of field through model-level validation - validation

In Ruby on Rails, I have a table called Person. It has a field name. The table already has many rows. Some of them also have name as nil. I am fine with the records that already have nil in the name field but moving forward I want to mandate filling of name field when someone creates a new Person entry. I am using Active Record Validation to implement this:
in app/models/person.rb
class Person < ActiveRecord::Base
validates :name, :presence => true
end
Will this mess up my Person table, since I already have some entries with nil in the name field.

Validate only while creating new entry
validates :name, :presence => true, on::create

Related

Why won't my DataMapper record save when I pass it the user it belongs to?

I have set up a simple has-many and belongs-to association using DataMapper with Sinatra. My User model has many 'peeps', and my Peep model belongs to user. See below for the classes....
I am able to successfully create a new peep which belongs to a particular user, by passing the user_id directly into the peep on initialization, like this:
Method 1
new_peep = Peep.create(content: params[:content], user_id: current_user.id)
This adds 1 to Peep to Peep.count.
However, my understanding is that I should be able to create the association by assigning the current_user to new_peep.user. But when I try that, the peep object won't save.
I've tried this:
Method 2
new_peep = Peep.create(content: params[:content], user: current_user)
Current user here is User.get(session[:current_user_id])
The resulting new_peep has an id of nil, but does have user_id set to the current_user's id. New_peep looks exactly like the new_peep that successfully gets created using Method 1, except it has no id because it hasn't successfully saved. I've tried calling new_peep.save separately, but I still get the below for the peep object:
<Peep #id=nil #content="This is a test peep" #created_at=#<DateTime: 2016-05-08T12:42:52+01:00 ((2457517j,42172s,0n),+3600s,2299161j)> #user_id=1>, #errors={}
Note that there are no validation errors. Most problems other people seem to have had with saving records come down to a validation criteria not being met.
I assumed this was something to do with the belongs_to association not working, but I can (after creating new_peep using Method 1 above) still call new_peep.user and access the correct user. So it seems to me the belongs_to is working as a reader but not a setter.
This problem also means I cannot create a peep by adding one into the user.peeps collection then saving user, which means there's virtually no point in peep belonging to user.
I've seen other people have had problems saving records that don't have any changes to save - but this is a completely new record, so all its attributes are being updated.
I'd really like to know what's going on - this has baffled me for too long!
Here are my classes:
class Peep
include DataMapper::Resource
property :id, Serial
property :content, Text
property :created_at, DateTime
belongs_to :user, required: false
def created_at_formatted
created_at.strftime("%H:%M, %A %-d %b %Y")
end
end
class User
include DataMapper::Resource
include BCrypt
attr_accessor :password_confirmation
attr_reader :password
property :id, Serial
property :email, String, unique: true, required: true
property :username, String, unique: true, required: true
property :name, String
property :password_hash, Text
def self.authenticate(params)
user = first(email: params[:email])
if user && Password.new(user.password_hash) == params[:password]
user
else
false
end
end
def password=(actual_password)
#password = actual_password
self.password_hash = Password.create(actual_password)
end
validates_confirmation_of :password
validates_presence_of :password
has n, :peeps
end
When you create the Peep you don't create it through User, maybe that's why it has no primary id, since it belongs to User. Also you're assigning it the foreign key user_id as if you have a property defined as such. Although the database has it, in DataMapper you don't pass in the foreign key id, it does it for you.
Try replacing
new_peep = Peep.create(content: params[:content], user: current_user)
with:
new_peep = current_user.peeps.create(content: params[:content], created_at: Time.now)

Specific validation

I have models User and Product in my project. User have some products. I want user products to have unique title only for this user.
For example user1 has a product titled "product". In this case user2 also can have a product titled "product". First "product" differs from second "product". In table both products have the same name. But the user may not have both of these products at the same time.
That is my Product.rb:
validates :title, :description, presence: true
validate :uniq_of_product_title
def uniq_of_product_title
if Product.where(user_id: user_id).find_by_title(title)
errors.add(:title, "Product already exists")
end
end
So it works. Problem appears then I edit description and try to update product. Validator finds created product in table and gives a error.
The question is how can I make validates best way?
You can probably use regular Rails' validates method for this case, try something like:
validates :title, uniqueness: true

How to validate a mongoid field based on another mongoid field?

I made the switch to nosql and am playing with Mongoid with Sinatra. I'm still new to this, and I"m stumped on this problem.
class Item
field :name, type: String
field :category, type: Moped::BSON::ObjectId # holds _id of the category object
field :cover_type, type: String
validates_presence_of :category
validates_inclusion_of :cover_type, in:["Hardcover", "Softcover"]
end
class Category
field :name
validates_inclusion_of :name, in:["Books", "Movies", "Ebooks"]
end
Imagine a store that sells Books, Movies, and Ebooks and each item sold belongs to one of the three categories. If an Item is listed under the category "Books", then the Item is required to have a field called cover_type. Furthermore, cover_type can only be either "Hardcover" or "Softcover".
When saving an Item, how do I piece together the validate in the Item class for when the Item is in the Book category and therefore requires the presence of the field cover_type, which is also validated as being "Hardcover" or "Softcover"?
If the Item isn't a Book, then cover_type can be null.
Mongoid models inherit ActiveModel validations. You can just write a custom validator method:
class Item
validates :cover_type_must_be_valid, if: :book?
def book?
Category.find(category).name == 'Books'
end
def cover_type_must_be_valid
errors.add(:cover_type, 'must be Hardcover or Softcover') unless %w{Hardcover Softcover}.include? cover_type
end
end
The book? method is unpleasant; why not use belongs_to :category on Item and has_many :items on Category?
EDIT: here’s what book? could look like if you used the has_many and belongs_to:
def book?
category.name == 'Books'
end
Not that different, but you’ll surely be accessing item.category all over your application, I can’t see why you wouldn’t want to make it easier.

Pass Validation on Form Fields Before Friendly ID Creation - Custom

I currently have two models
Location and Product
I have it configured that when a record is created in my production model, it creates a custom url based on the information it has gather from the location selected during the product creation
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
def should_generate_new_friendly_id?
name_changed? or location_id_changed?
end
def slug_candidates
[
[location.media_type, location.state, location.name, :name, :sku]
]
end
What I am currently testing is when a user decides NOT to fill out those very important fields, for it to throw an error message before creating
validates :name, presence: true
validates :sku, presence: true
validates :location_id, presence: true
What is happening in my case, is that it overlooks that validator and first tries to create the slug. If I remove the custom attributes to the url creation and list as
def slug_candidates
[
[:name, :sku]
]
end
it will work fine, running the field validators first. Assuming because those two are attributes on the given model directly.
Does anyone know why this is happening? I need for the validators to be picked up first since it contains all the relevant information for the url.
Solved
def slug_candidates
if self.location_id.nil?
self.errors.add(:location_id)
else
[
[location.media_type, location.state, location.name, :name, :sku]
]
end
end

activerecord validation - validates_associated

I'm unclear on what this method actually does or when to use it.
Lets say I have these models:
Person < ...
# id, name
has_many :phone_numbers
end
PhoneNumber < ...
# id, number
belongs_to :person
validates_length_of :number, :in => 9..12
end
When I create phone numbers for a person like this:
#person = Person.find(1)
#person.phone_numbers.build(:number => "123456")
#person.phone_numbers.build(:number => "12346789012")
#person.save
The save fails because the first number wasn't valid. This is a good thing, to me. But what I don't understand is if its already validating the associated records what is the function validates_associated?
You can do has_many :phone_numbers, validate: false and the validation you're seeing wouldn't happen.
Why use validates_associated then? You might want to do validates_associated :phone_numbers, on: :create and skip validation on update (e.g. if there was already bad data in your db and you didn't want to hassle existing users about it).
There are other scenarios. has_one according to docs is validate: false by default. So you need validates_associated to change that.

Resources