Rails: How to receive errors from nested models - ruby-on-rails

I am trying to validate emails given from a CSV list of emails. So I have created the invite_list virtual attribute where when given a list of emails, it will loop and create a new record in the invited_only_emails model.
Now the thing is, this works fine, but how can I catch the validation error thrown by InvitedOnlyEmail while looping in Users model so I'll be able to use that error in the controller?
This is my main model:
class Users < ActiveRecord::Base
attr_accessor :invite_list
attr_accessible :invite_list
has_many :invited_only_emails
def invite_list=(list)
list.split(",").each do |address|
self.invited_only_emails.create! :email => address
end
end
def invite_list
self.invited_only_emails.map {|email| email.email}.join(',')
end
end
And this is the invited_only_emails model:
class InvitedOnlyEmail < ActiveRecord::Base
attr_accessible :email
belongs_to :users
validates_format_of :email, :with => /^([\w\.%\+\-]+)#([\w\-]+\.)+([\w]{2,})$/i
end
Thanks!

I think you could use validates_associated method:
class User < ActiveRecord::Base
validates_associated :invited_only_emails
# ...
end
http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_associated

Have you looked at validates_associated?
http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_associated

Related

Saving Rails associations after creating User

I'm new to Rails and ActiveRecord and need some help. Basically, I have 4 models: User, Property, PropertyAccount, and AccountInvitation. Users and Properties have a many to many relationship via PropertyAccounts. AccountInvitations have a user's email and a property_id.
What I want to happen is that after a user registers on my app, his user account is automatically associated with some pre-created Properties. What I don't know how to do is write the query to get the Property objects from the AccountInvitations and save them to the User object. Please see def assign_properties for my pseudo code. Any help is welcome, thanks so much!
class User < ActiveRecord::Base
has_many :property_accounts
has_many :properties, through: :property_accounts
after_create :assign_properties
# Check to see if user has any pre-assigned properties, and if so assign them
def assign_properties
account_invitations = AccountInvitations.where(email: self.email)
if account_invitations.any?
account_invitations.each do |i|
properties += Property.find(i.property_id)
end
self.properties = properties
self.save
end
end
end
class AccountInvitation < ActiveRecord::Base
belongs_to :property
validates :property_id, presence: true
validates :email, uniqueness: {scope: :property_id}
end
class Property < ActiveRecord::Base
has_many :account_invitations
has_many :property_accounts
has_many :users, through: :property_accounts
end
class PropertyAccount < ActiveRecord::Base
belongs_to :property
belongs_to :user
end
Thanks to #wangthony , I looked at the includes method on http://apidock.com/rails/ActiveRecord/QueryMethods/includes and tweaked one of their examples in order to get this to work. Here's the solution:
def assign_property
self.properties = Property.includes(:account_invitations).where('account_invitations.email = ?', self.email).references(:account_invitations)
self.save
end
I believe you can do this:
user.properties = Property.includes(:account_invitations).where(email: user.email)
user.save

Rails 4 create model with nested attributes has_many

I have a many to many relationship with DoctorProfile and Insurance. I'd like to create these associations off of a form from a client side app. I'm sending back an array of doctor_insurances_ids and trying to create the association in one line. Is it possible to send back an array of doctor_insurances ids? If so what's the proper way to name it for mass assignment in the params?
The error I'm getting with the following code is
ActiveRecord::UnknownAttributeError: unknown attribute 'doctor_insurances_ids' for DoctorProfile.
class DoctorProfile
has_many :doctor_insurances
accepts_nested_attributes_for :doctor_insurances # not sure if needed
class Insurance < ActiveRecord::Base
has_many :doctor_insurances
class DoctorInsurance < ActiveRecord::Base
# only fields are `doctor_profile_id` and `insurance_id`
belongs_to :doctor_profile
belongs_to :insurance
def create
params = {"first_name"=>"steve",
"last_name"=>"johanson",
"email"=>"steve#ymail.com",
"password_digest"=>"password",
"specialty_id"=>262,
"doctor_insurances_ids"=>["44", "47"]}
DoctorProfile.create(params)
end
You're not putting a doctor_insurance_id in your Doctor Profile so your DoctorProfile.create(params) line isn't going to work. You could do something like this:
def create
doctor = DoctorProfile.create(doctor_profile_params)
params["doctor_insurances_ids"].each do |x|
DoctorInsurance.create(doctor_profile_id: doctor.id, insurance_id: x)
end
end
def doctor_profile_params
params.require(:doctor_profile).permit(:first_name, :last_name, :email, :password_digest, :specialty_id)
end

how to pass parameters from one model to another in Rails

Hi I have two model Company and Feed
company.rb
class Company < ActiveRecord::Base
attr_accessible :rss_url, :name
has_many :feeds
end
feed.rb
class Feed < ActiveRecord::Base
attr_accessible :guid, :name, :published_at, :summary, :url
after_create { |feed| FeedEntry.update_from_feed(feed.feed_url) }
belongs_to :company
def self.update_from_feed(rss_url) ?????
feed = Feedzirra::Feed.fetch_and_parse(rss_url) ?????
add_entries(feed.entries)
end
end
How two take RSS_URL from company to feed ?
I don't know what add_entries does, but if you want to access the rss_url of Company inside Feed, you can use the association. Define this instance method on your Feed class.
def do_something
url = self.company.rss_url
puts "This feed belongs to a company with the following rss url: #{url}"
end

How to validate existing invalid model via nested attributes?

It seems Rails will only validate an existing invalid nested model if the nested model's attributes have changed.
With the following models:
class Person < ActiveRecord::Base
has_many :addresses
accepts_nested_attributes_for :addresses
end
class Address < ActiveRecord::Base
belongs_to :person
validates_presence_of :street
end
The following code for example, will save and return true:
p = Person.first
p.update_attributes({:first_name => "Bryan", :addresses_attributes=>{"0"=>{:street=>"", :id => 1}})
Is there a way to validate the nested model as if it's attributes have changed? ( while retaining errors )
It works for me -- here's a test-case I created to prove it
require 'test_helper'
class PersonTest < ActiveSupport::TestCase
test "update address" do
expected_new_address="pandascout"
person = Person.create(name: "jwo")
address = person.addresses.create(street: "123 Elm")
person.update_attributes({:addresses_attributes=>{"0"=>{:street=>expected_new_address, :id=>address.id}}})
assert_equal expected_new_address, person.addresses.first.street
end
end
The only thing I can think of is you have a reference to "address" and you need to reload it.

Rails setting OR conditions in validate_presence_of in a model?

In a rails model, is it possible to do something like
class Example < ActiveRecord::Base
#associations
validates_presence_of :item_id, (:user_id OR :user_email)
#functions
end
Where the model has 3 columns of :item_id, :user_id, and :user_email?
I want the model to be valid as long as I have a :user_id or a :user_email.
Idea being that if the item is recommended to a person who isn't currently signed up, it can be associated via email address for when the recommended person signs up.
Or is there a different method that I can use instead?
One approach is to wrap those fields as a virtual attribute, say:
class Example < ActiveRecord::Base
validates_presence_of :referral
def referral
user_id || user_email
end
end
or you can just throw a custom validate validation method. See custom validations on the Rails API
If both user_id and user_email come from another model, perhaps it's better to add the association instead
class Example
belongs_to :user
validates_associated :user
before_validate :build_user_from_id_or_email
def build_user_from_id_or_email
# ... Find something with the parameters
end
end
validates_presence_of :item_id
validates_presence_of :user_id, :if => Proc.new{ |x| x.user_email.blank? }
validates_presence_of :user_email, :if => Proc.new{ |x| x.user_id.blank? }

Resources