Default value along with uniqueness validation - ruby-on-rails

I have a Path model with name attribute as unique. I want to set default value as '/'
to the same.
I have done in the following manner.
class Path < ActiveRecord::Base
attr_accessible :name
validates :name, presence: true, uniqueness: true
before_validation :set_default_path
private
def set_default_path
self.name = name.presence || '/'
end
end
Domain model is designed as:
class Domain < ActiveRecord::Base
attr_accessible :name, path_id
validates :name, :path_id, presence: true
validates :name, uniqueness: {scope: :path_id}
end
But this doesn't work for consecutive inserts with a blank name for path.
path = Path.find_or_create_by_name('')
domain = Domain.new(name: 'stackoverflow.com')
domain.path = path
domain.save! # Fails with validation error
ActiveRecord::RecordInvalid:
Validation failed: Path can't be blank
Is there a robust way to achieve this ?

You should remove following callback
before_validation :set_default_path
and use validation for name as following:--
validates :name, presence: true, uniqueness: true, :if => 'name.present?'
and write a migration file to add default value to name attribute of paths table as either of followings:--
change_column :paths, :name, :string, :default => '/'
or
change_column_default :paths, :name, '/'

add condition on validation:
validates :name, presence: true
validates :name, uniqueness: true, unless: proc { |e| e.name == "/" }

Related

Rails inherit class shared attributes on build

assuming i have two models human and male, they both have similar attributes but not all the attributes, for example:
class Human < ApplicationRecord
has_many :males
validates :name, presence: true
validates :age, presence: true
validates :date_of_birth, presence: true
validates :identity, presence: true
end
class Male < ApplicationRecord
belongs_to :human
validates :name, presence: true
validates :age, presence: true
end
now what i want to do is when i use human.males.build i want the new male instance to inherit the shared attributes like name and age
instead of using this behavior human.males.build({attributes})
I'm thinking of overwriting the initialize (alias of build) method of Male model which will do the trick:
def initialize(attributes = {}, &block)
super(attributes, &block)
self.name = human.name unless attributes[:name].present?
self.age = human.age unless attributes[:age].present?
end
But honestly, I don't think this is a safe and generic way for the problem.

Make Rails Validation Helpers uniqueness multiple with a group of columns

How i can validate four attributes at once using method uniqueness or if I have how?
I am use MySQL:
class Product < ApplicationRecord
validates :manufacturer,:model, :color,:carrier_plan_type, :quantity, :price, presence: true
validates :quantity, numericality: {greater_than_or_equal_to: 0}
validates :price, numericality: {greater_than_or_equal_to: 0.01}
validates :manufacturer, confirmation: { case_sensitive: false }
validates :model, format: {with: /\w+\s\w{2}\s\d{2,3}GB/i }
validates :color, format: {with: /[a-zA-Z]+|[a-zA-Z]+\s[a-zA-Z]+/i}
validates :carrier_plan_type, format: {with: /p(re|os)/}
end
This is my model Product, i would like to 4 columns (manufacturer, model, color and carrier_plan_type) with method_uniqueness. So that it is only valid to insert an instance in my database if you do not have these 4 columns repeated.
I found in Rails Guide that:
See the MySQL manual for more details about multiple column indexes or the PostgreSQL manual for examples of unique constraints that refer to a group of columns.
But i didn't understand how i can to group the columns.
To add, It is a good practice to do the validations as I did up there, if you have another opinion I am open to an idea.
Try using:
validates_uniqueness_of :carrier_plan_type, scope: [:manufacturer, :model, :color]
or
validates :carrier_plan_type, uniqueness: { scope: [:manufacturer, :model, :color] }
either should give you what you want.
In reply to the comment below try:
validates_uniqueness_of :carrier_plan_type,
scope: [:manufacturer, :model, :color, :price], if: -> { quantity == 0 }
or even
validates_uniqueness_of :carrier_plan_type,
scope: [:manufacturer, :model, :color, :price], if: :quantity_zero
private
def quantity_zero
quantity == 0
end

How to validates Rails column with self other column not blank?

I have a mode named Exam.
There are some columns in exames:
:title
:subject_id
:exam_type
I want to know how to implement this:
class Exam < ApplicationRecord
validates :title, presence: true
validates :subject_id, presence: true, if: :no_exam_type?
def no_exam_type?
self.exam_type == ""
end
end
That is to say, I want to create a exam:
Exam.create(title: "first exam", exam_type: "something")
The subject_id must be exist, when exam_type is blank, such as exam_type="" or just do:
Exam.create(title: "first exam", subject_id: 3)
because exam_type has a default blank value.
But the subject_id doesn't necessary provide, when exam_type not blank, such as exam_type="something".
Exam.create(title: "first exam", exam_type: "something", subject_id: 3)
I test it, but no lucky.
How to do that? Thanks appreciate.
In Rails 5 belongs_to associations default to optional: false. Which means that the model will automatically validate the presence of the association.
class Thing < ApplicationRecord
belongs_to :other_thing
end
Thing.create!
# => ActiveRecord::RecordInvalid: Validation failed: other_thing can't be blank
So you need to set the association as optional and make sure the column is nullable.
class Exam < ApplicationRecord
belongs_to :subject, optional: true
validates :title, presence: true
validates :subject_id, presence: true, if: :no_exam_type?
def no_exam_type?
!self.exam_type.present?
end
end
Have you tried like this.
validates :subject_id, presence: true, :if => exam_type.blank?
you can refer the doc here to suite your requirement
use validates_presence_of instead.
validates_presence_of :subject_id, if: :no_exam_type?
def no_exam_type?
self.exam_type.nil?
end

FactoryGirl giving me an error

I got this model:
rails g model Absence user:references company:references from:date to:date date:date category:integer hours:decimal remarks
This also generates:
FactoryGirl.define do
factory :absence do
user nil
company nil
from nil
to nil
date nil
category 0
hours "8.00"
remarks "MyString"
end
end
I set from, to and date to nil because it's either: from and to OR a certain date.
When I try this in my spec:
#absence = create(:absence, user: #company.owner, from: "2015-09-10", to: "2015-09-10", hours: 4)
I receive this error message:
NoMethodError:
undefined method `from=' for #<Absence:0x007f81f5494b88>
What could be wrong?
Edit:
When I remove the
from nil
from the factories/absences.rb I'm getting it on the next field (to) and after removing that I'm seeing the error message on category.
Edit2:
Model:
class Absence < ActiveRecord::Base
belongs_to :user
belongs_to :company
enum type: {holiday: 0, sick: 1}
validates :from, presence: true, if: '!user_id.nil?'
validates :to, presence: true, if: '!user_id.nil?'
validates :date, presence: true, if: '!company_id.nil?'
validates :hours, presence: true, if: '!user_id.nil?'
validates :hours, :numericality => { :greater_than_or_equal_to => 0 }, if: '!user_id.nil?'
validates :category, presence: true, if: '!user_id.nil?'
validates_numericality_of :company_id, allow_nil: true
validates_numericality_of :user_id, allow_nil: true
validate :company_xor_user
validate :to_date_after_from_date
validate :hours_smaller_than_workday
validate :non_overlapping
after_save :calculate_time_checks
after_destroy :calculate_time_checks_delete
DB:
https://www.evernote.com/shard/s29/sh/e8c1429d-9fa7-475b-87e8-3dc11a3f3978/08a7e7d6dfd80c6f407339cab97734c2
FINALLY found the real cause.
At first I had the Absence model created with an attribute named 'type'. This was migrated to both the development and test database. Afterwards I changed it to category and added 'from' and 'to' as well and did a rollback and migrated again (but not on test!).
By using pry
require 'pry'; binding.pry
in the test I did Absence.columns and noticed the difference.

Strong parameters in Ruby

I'm getting the error message about strong parameters. I think it's just that rails 4 doesn't use attributes anymore. the code for my toy.rb is:
class Toy < ActiveRecord::Base
attr_accessible :name, :price, :vendor
validates :name, :presence => true
validates :price, :presence => true
validates :price, :numericality => true
validates :vendor, :presence => true
end
how can I change this to strong parameters?
EDIT: I used a different rb i changed it to employees and this is what I have:
class Employee < ActiveRecord::Base
params.require(:employee).permit(:first, :last, :salary, :salary, :ssn)
validates :first, :presence => true
validates :last, :presence => true
validates :salary, :presence => true
validates :salary, :numericality => true
validates :ssn, :presence => true
end
It's still telling me "ndefined local variable or method `params' for #"
The code you need is
params.require(:toy).permit(:name, :price, :vendor)
You will put this in your controller. Typically, you create a private method:
def create
Toy.create(toy_params)
end
private
def toy_params
params.require(:toy).permit(:name, :price, :vendor)
end
See http://guides.rubyonrails.org/getting_started.html#saving-data-in-the-controller for more information.
Edit
I think I might have misled you with my original answer. The code goes in the controller, not the model.
Strong params are designed to help your controller send specific data to your model. It's meant to protect your app against unauthorized data being passed:
#app/controllers/toys_controller.rb
Class ToysController < ActiveRecord::Base
def new
#toy = Toy.new #-> creates a blank AR object
end
def create
#toy = Toy.new(toys_params) #->creates new AR object (populating with strong params)
#toy.save
end
private
def toys_params
params.require(:toys).permit(:your, :params, :here)
end
end

Resources