Rails custom validation on 3 attributes - ruby-on-rails

I want to create a validation that makes sure no other object in a table has the same combination of 3 attributes.
So say my code looks like the below:
class Dog
attr_accessor :color, :name, :height, :weight
end
I want to create a custom rails validation on the Dog class that makes sure there is no other dog in the database that has the same color, name, and weight. Is something like this possible?
Thanks!

You could use validates_uniqueness_of:
validates_uniqueness_of :color, scope: [:name, :weight]

Related

How to save 2 models in one transaction using ActiveModel?

I have a little class that works with 4 models. I'd like to do things the "right" way. Basically, I want to create 2 Address models, 1 Shipment (w/ 2 address_ids, and 1 Parcel that belongs to Shipment.
At this point, I'm confused. I need to get past this and onto the next big milestone. Does this look promising, or do you recommends saving 2 records in the controller, then use an after_create, or something similar? Thank you.
class Quote
include ActiveModel::Model
attr_accessor address_to: {
:name, :company, :street1, :street2, :street3, :city, :state,
:zip, :country, :phone, :email},
address_from: {
:name, :company, :street1, :street2, :street3, :city, :state,
:zip, :country, :phone, :email
}
def save
return false if invalid?
ActiveRecord::Base.transaction do
user = User.find_by(id: user_id)
user.addresses.create!([{address_from, address_to}]) # how to do this?
end
end
end
If you are using Model association through the tags :belongs_to and :has_many,
you could use accepts_nested_attributes_for :another_child_model for example. It will automatically create or define this association if you permit those params on the controller.
Rails guide for nested attributes
Pay attention on permitting those attributes on the controller.
class YourController < ApplicationController
...
def your_params
params.require(:entity).permit(:etc, :etc, child_model_attributes: [:id, :name, :etc, :etc])
end
...
end
Never use callbacks on models. Hard to test models, unpredictable behavior.
U can use accept_nested_attributes.
If u will have a custom logic with that, u can use Service Objects pattern and put all logic there.
But if u want do it like u do then try to use user.addresses.create!([address_from, address_to])
If you are using Rails 6 or a higher version - there is a method called insert_all to insert multiple records to the database in one INSERT statement.
Check it out on apidock: insert_all
Example:
Book.insert_all([
{ id: 1, title: "Rework", author: "David" },
{ id: 1, title: "Eloquent Ruby", author: "Russ" }
])
(example from the docs)

How do you save an attribute to the database that is not coming from the form params?

I am basically trying to save a key that I am generating in the model. It is not something the user is filling out in a form. I keep getting the error when I go to /model/new
undefined method `presentation_url=' for #<Class:0x007fc3c7d8ca38>
here is a general idea of what I am doing in the model.
class Product < ActiveRecord::Base
attr_accessible :description, :name, :price, :pdf, :banner
self.presentation_url = "a generated url that is not coming from the form"
end
I already generated and ran the migration for the presentation_url attribute and checked to see that the column does exist.
The error is telling you that the class Product has no method named presenteation_url=. The method should exist on instances of class Product, if it's made available by activerecord based on column name. Therefor you should use the method presentation_url= in some instance method and not at class level or class methods.
Use a callback in your model, something like this:
class Product < ActiveRecord::Base
before_save :presentation_url
attr_accessible :description, :name, :price, :pdf, :banner
def default_presentation_url
self.presentation_url ||= "a generated url that is not coming from the form"
end
end

option_groups_from_collection_for_select in single table

I need to group option-tags, but the groups and values are in the same table.
Lets say i have the model "Person" with the methods :age and :name and i want to group by age and list all names for the corresponding age. This would be something like:
option_groups_from_collection_for_select(#people, :name, :age, :id, :name, 1)
Obviously this does not work because :name is a single value. Is there a simple solution? Maybe by creating a method ":names" for Person, but how do i set the connection to :age?
-Bump (Noone an idea? Maybe i can just build a string with all those groups and options?)
I think your are missing the group_label_method parameter of the option_groups_from_collection_for_select method as shown here.
Try:
option_groups_from_collection_for_select(#people, :name, :name, :age, :id, :name, 1)

Virtual attributes and mass-assignment

developers! I can't understand next situation
For Example I have model
class Pg::City < ActiveRecord::Base
belongs_to :country
#virtual accessors
attr_accessor :population
#attr_accessible :city, :isdisabled, :country_id
end
I can use code like this:
c = Pg::City.new({:population=>1000})
puts c.population
1000
But if I uncomment attr_accessible code above throw warning
WARNING: Can't mass-assign protected attributes: population
How can I use virtual attributes for mass-assigmnment together with model attributes?
Thanks!
Using attr_accessor to add a variable does not automatically add it to attr_accessible. If you are going to use attr_accessible, then you will need to add :population to the list:
attr_accessor :population
attr_accessible :city, :isdisabled, :country_id, :population

Rails, How can I combine multiple model attributes to create a unique permalink using permalink_fu?

Can Permalink_fu combine 2 or more model attributes to create a unique permalink?
Let's say I have a Business Model, this model contains :name, :address, :phone, :city, :state, :country etc. attributes.
Right now I have permalink set up in this model only for :name
has_permalink :name
So I would get "/biz/name". However I would like to combine the Business name, city, and a incremental number if there are more than 1 location in the city for that business.
For example I would like to use:
"/biz/joes-coffee-shack-chicago" for the permalink
or if a multple location business
"/biz/starbucks-chicago-92"
Is this possible with the current permalink_fu plugin or some fork of permalink_fu? Or will this require some modification to the permalink_fu plugin?
You can set the attributes as an array:
has_permalink [:one, :two, :three]
They will be automatically joined by -. Permalink_fu also automatically adds a suffix if there's already a record with that permalink.
Add a virtual attribute to your Business model.
class Business < ActiveRecord::Base
attr_accessor :perma_link_attr
has_permalink :perma_link_attr
def perma_link_attr
suffix = 1
[:name, :city, suffix].join("-")
end
end

Resources