Rails 3.2 Validate based on different model - ruby-on-rails-3

How would I make this work? I want to validate an award nomination based on criteria from a different model.
class Award < ActiveRecord::Base
belongs_to :manager, :class_name => 'Manager', :foreign_key => 'manager_username'
def cant_be_manager
if nominee_username == Manager.username
errors.add(:nominee, "is a manager and cannot be nominated.")
end
end
end

Try this:
class Award < ActiveRecord::Base
belongs_to :manager, :class_name => 'Employee', :foreign_key => 'manager_username'
validate :cant_be_manager # <----- added this line
def cant_be_manager
if nominee_username == manager.username # <----- lower case m
errors.add(:nominee, "is a manager and cannot be nominated.")
end
end
end
But (just guessing here what your model looks like) I'm wondering if that second modified line shouldn't be:
if nominee_username == manager_username
The belongs_to line indicates that you have a manager_username field in your awards table, but it would be more common in Rails for this to be a manager_id field, with the belongs_to line looking like this:
belongs_to :manager, :class_name => 'Employee', :foreign_key => 'manager_id'
If that is indeed what you have, your code should look like this:
class Award < ActiveRecord::Base
belongs_to :manager, :class_name => 'Employee', :foreign_key => 'manager_id' # <----- changed
validate :cant_be_manager # <----- added this line
def cant_be_manager
if nominee_id == manager_id # <----- changed
errors.add(:nominee, "is a manager and cannot be nominated.")
end
end
end
This assumes that you are trying to prevent an employee from nominating his own manager, but it's okay for the employee to nominate other managers, or for managers to nominate other managers. If instead you want to prevent any managers at all from being nominated by anyone, let me know how you know if an Employee is a manager (probably an attribute or method on your Employee model) and I will update the answer.

Maybe smth. like this?
class Award < ActiveRecord::Base
validate :cant_be_manager
def cant_be_manager
.....
end
See this question too: Rails custom validation

Related

Rails 3.2 - Validate in Model Based on Another Model

I have an AWARD model - there are two forms to create an AWARD. One is for nominating EMPLOYEES, the other is for Non-Employees. The EMPLOYEE form pulls a list of active employees to populate the Nominee selection box. The Non-Employee form has only text fields to populate the Nominee field (because I have no source to populate a selection list).
To dummy-proof the app, I want to run a validation that disallows Employees from nominating themselves (because they will inevitably try to do so!). There is a hidden field on each form to set whether the form is Employee or Non: <%= f.hidden_field :employee, :value => true/false %>
So, on the Non-Employee form, if the user types in his own nominee_username, it should throw an error that says he cannot nominate himself.
I have a validation, but the error throws even if the nominee_username DOES NOT match the nominator. So, there is a problem with my validation.
Here's what I've attempted:
class Award < ActiveRecord::Base
belongs_to :nominator, :class_name => 'Employee', :foreign_key => 'nominator_id'
belongs_to :nominee, :class_name => 'Employee', :foreign_key => 'nominee_id'
validate :cant_nominate_self_non_employee_form,
:on => :create, :unless => :employee_nomination?
def employee_nomination?
self.employee == true
end
##### the validation below is not working properly - it throws error every time ####
##### only if Employee.username is equal to award.nominee_username, it should error ####
def cant_nominate_self_non_employee_form
if Employee.where(:username => nominee_username)
errors.add(:nominator, "can't nominate yourself")
end
end
end
There is an association between the Award and Employee models:
class Employee < ActiveRecord::Base
has_many :awards, :foreign_key => 'nominator_id'
has_many :awards, :foreign_key => 'nominee_id'
end

Uniqueness validation with a has_many relationship

My model structure is as follows:
class Client < ActiveRecord::Base
has_many :charts
end
class Chart < ActiveRecord::Base
belongs_to :client
has_many :chart_data
end
class ChartDatum < ActiveRecord::Base
belongs_to :chart
end
ChartDatum has an attribute called 'name' which needs to be unique for each client.
I tried using "validates_uniqueness_of :name, :scope => [:chart_id]" but this helped me getting a unique key for a particular chart but not for all charts for a particular client. I am looking for something like "validates_uniqueness_of :name, :scope => [:client_id]" but obviously with the current structure it will not work out.
Could someone please help me?
Since you need unique name for chart_data for each client, you can try writing a custom validation for name something like this :
class ChartDatum < ActiveRecord::Base
belongs_to :chart
validates :name, :uniqueness => true, unless => :unique_for_client?
def unique_for_client?
client = self.chart.client
client.charts.chart_data.pluck(:name).include?(self.name)
end
end

Many to many relationship with metadata stored in the mapping table in activerecord

I have two tables with a many to many relationship, through a third table. In the third table is a piece of data I need to assign when I build the relationships between the two tables, how can I use ActiveRecords build method to assign that?
Here is code to show what I mean:
class Company < Contact
has_many :contact_companies
has_many :people, :through => :contact_companies
accepts_nested_attributes_for :people, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class Person < Contact
has_many :contact_companies
has_many :companies, :through => :contact_companies
accepts_nested_attributes_for :companies, :allow_destroy => true
accepts_nested_attributes_for :contact_companies
end
class ContactCompany < ActiveRecord::Base
belongs_to :person
belongs_to :company
end
ContactCompany contains a data member called "position". What I want to do is something like:
c = Person.new
c.companies.build(:name => Faker::Company.name, :position => positions.sample)
EDIT:
When I try the code above I get "unknown attribute: position".
The c.companies.build line is attempting to build a Company object which does not have the position attribute (the ContactCompany does) hence the error. It looks like you are trying to set attributes on two different models, so you'll have to make sure you are setting the appropriate attribute on the right model:
# you can chain these calls but I separated them for readability
cc = c.contact_companies.build(:position => positions.sample)
cc.build_company(:name => Faker::Company.name)

Help with a complex ActiveRecord query that has too many joins

I have the following models
class User < ActiveRecord::Base
has_many :occupations, :dependent => :destroy
has_many :submitted_jobs, :class_name => 'Job', :foreign_key => 'customer_id'
has_many :assigned_jobs, :class_name => 'Job', :foreign_key => 'employee_id'
end
class Job < ActiveRecord::Base
belongs_to :customer, :class_name => 'User', :foreign_key => 'customer_id'
belongs_to :employee, :class_name => 'User', :foreign_key => 'employee_id'
belongs_to :field
end
class Occupation < ActiveRecord::Base
belongs_to :user
belongs_to :field
belongs_to :expertise
end
along with Field (just name and id) and Expertise (name and integer rank).
I need to create a filter that works like the following pseudocode
select * from jobs where employee_id == current_user_id
or employee_id == 0
and current_user has occupation where occupation.field == job.field
and if job.customer has occupation where occupation.field == job.field
current_user.occupations must include an occupation where field == job.field
and expertise.rank > job.customer.occupation.expertise.rank
You can see how I quickly exhaust my knowledge of SQL with a query this complex.
How would you do it? The proper SQL would be great, but if a Rails person can point me towards the correct way to do it with ActiveRecord methods, that's great too. Or maybe I'm not structuring my models very well; I'm open to all kinds of suggestions.
Thanks!
I might have missed something and did not look into refactoring the models but heres something that might help you to a complete solution or how to reformulate your query
The code is not tested or syntax checked
#jobs = Job.
joins(:employee,:occupation).
includes(:customer => {:occupations => :expertise}).
where(:employee_id => current_user.id).
where("occupations.field_id = jobs.field_id").all
user_occupations = current_user.occupations.include(:expertise)
user_occupations_by_field_id = user_occupations.inject({}) do |hash,oc|
hash[oc.field_id] = oc
hash
end
#jobs.reject! do |j|
common_occupations = j.customer.occupations.select do |oc|
if c = user_occupations_by_field_id[oc.field_id]
!user_occupations.select do |eoc|
c.field_id == eoc.field_id && c.expertise.rank > oc.expertise.rank
end.empty?
else
true
end
end
end

How do i create an object if it has more than one belongs_to?

I have the following:
class Org < ActiveRecord::Base
has_many :users
has_many :entries
end
class Entry < ActiveRecord::Base
belongs_to :org
belongs_to :user
validates_presence_of :entry_text
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end
I can Create Orgs and Users... How do i create an entry if there are two belongs_to? and what is this pattern called?
Double nested resources are tricky. The trick with users usually is to keep it out of your desired entry path.
Your question is kind of broad, but if you specify more information, people would be able to help you better. Also, I would recommend using the gem Devise for your user management system. Since you're using 'users' I would assume you want users from orgs to create entries. The entry created would be a part of org and the user would be the session's current user. Sorry if I am wrong to assume this.
Your routes.rb file can look something like this (assuming rails 3):
resources :orgs do
resources :entries
end
Then the create of your entry controller would look like:
#entry = #org.entries.new(params[:topic])
#entry.user = current_user #or however you are managing the current user's session.
And you'd want to set the org for the entire class by making a method that loads your current org and do a before_filter :loadOrg
def loadOrg
#org = Org.find(params[:id])
end
This is of course assuming your path is something like: /org/(id)/entry/(entry_id)
and not
/org/(id)/user/(user_id)/entry/(entry_id)
which in my opinion is unnecessary and can lead to more problems. You can always create a userpage model that calls all entries by users, but the default route doesn't necessarily have to include users in the path.
I don't see any problem.
#entry = Entry.create(:entry_text => "Hello World!")
Now questions to clarify what do you need:
Can #entry belongs both org and user at the same time? Or it can belongs to only one of them?
Should #entry belongs to at least one of them?
If #entry supposed to belong only one of them, so you should use Polymorphism
http://railscasts.com/episodes/154-polymorphic-association
class Entry < ActiveRecord::Base
belongs_to :textable, :polymorphic => true
validates_presence_of :entry_text
end
class Org < ActiveRecord::Base
has_many :users
has_many :entries, :as => :textable
end
class User < ActiveRecord::Base
belongs_to :org
has_many :entries, :as => :textable
validates_uniqueness_of :user_name
validates_presence_of :user_name, :length => { :minimum => 3 }
end