Testing association through aliases - ruby-on-rails

I'm new to TDD and Rails.
I'm trying to test an 'aliased' association (don't know if this is a valid term).
My test:
it 'an order should always have a customer' do
o = Order.new
o.should_not be_valid
end
My model:
class Order < ActiveRecord::Base
belongs_to :customer, :class_name => Person
validates_associated :customer
end
I don't know if validates_associated does what It implies. My test still counts orders with 'custumer_id: nil' as valid.
The Person model:
class Person < ActiveRecord::Base
attr_accessible :first_name, :last_name, :middle_name
validates_presence_of :first_name, :last_name
has_many :addresses
has_many :orders, :as => :customer
end
I want to make sure that every Order saved has an associated custumer (a Person object).
Am I missing something?

validates_associated only tells the Model that validations should be run on associated objects as well (have a look at the docs here). You still need to add a validation on Order. Have a look at this SO question. I could be wrong, but I think it will point you in the direction of what you're looking for.

Related

Data modeling of Grandparent, Parent, and Child relationships in Rails

Is it a bad practice to set a model (table) association between both parent and child AND grandparent and child? For example, if I want to easily query a user's projects or a user's tasks, is the following setup recommended? If so, is there a good way to ensure both foreign keys always point to the same user?
Any help will be greatly appreciated.
class User < ActiveRecord::Base
attr_accessible :email, :first_name, :last_name
has_many :projects
has_many :tasks
end
class Project < ActiveRecord::Base
attr_accessible :name, :status
belongs_to :user
has_many :tasks
end
class Task < ActiveRecord::Base
attr_accessible :name, :status
belongs_to :user
belongs_to :project
end
There is nothing wrong with what you are trying to do, in fact rails has already predefined some association methods to help you. Just a couple of modifications and you are good to go
class User < ActiveRecord::Base
attr_accessible :email, :first_name, :last_name
has_many :projects
has_many :tasks, through: :projects
end
By adding the through: :projects to your tasks now means you can access all of a users tasks like so
$user = User.first #or whatever
$user.tasks
=> [AR array of all tasks]
You can play around with it in irb. In addition you don't need belongs_to :user in your Task model. It's already taken care of.
Look at section 2.4 for more details
EDIT: I have made the assumption (based upon your description) that a task belongs to a project, and a project belongs_to a user, and that user's don't have tasks directly, but through projects. If that was wrong, let me know and we'll figure it out from there.

Has-Many and Belongs-to-one Relation

I'm trying to keep track of how many signup_conversions a user creates.
Therefore, I have those two following models:
signup_conversion.rb
class SignupConversion < ActiveRecord::Base
belongs_to :user
belongs_to :convertee, :class_name => "User", :foreign_key => 'convertee_id'
attr_accessible :convertee_id
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
belongs_to :signup_conversion
has_many :signup_conversions
end
Would this work that way? Or am I missing something crucial here?
I haven't tried the code, but I give you some tips that hope you find useful.
I think every has_many/has_one statement should have its correspondent belongs_to, so three belongs_to and one has_many doesn't look good.
I'm not sure has_one :signup_conversion and has_many :signup_conversions would play well together, so I'd rather change the names. I changed the other names as well to try to make the associations clearer although I'm not sure I fully understand the real concepts they represent. You will probably come up with better names.
By default, the foreign key is guessed adding the suffix _id to the association name, so you don't need to specify it in this case. Also, I don't think you need to make that attribute accessible, at least not for the association to work.
signup_conversion.rb
class SignupConversion < ActiveRecord::Base
belongs_to :owner , :class_name => "User"
belongs_to :convertee, :class_name => "User"
end
user.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_one :owned_signup_conversion , :class_name => "SignupConversion"
has_many :triggered_signup_conversions, :class_name => "SignupConversion"
end
You have more code than you need here; you only need
class SignupConversion < ActiveRecord::Base
belongs_to :user
end
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_many :signup_conversions
end
Then you need a user_id column in your signup_conversions table, and you call
#user.signup_conversions
in your views or controller.

validating a belongs_to association

I'm trying to validate a model Student like this;
class Student < ActiveRecord::Base
belongs_to :room
end
I want to ensure that Room is a valid model, and with that I can only save a student only if the room is valid.
I tried to change the association to:
belongs_to :room, :validate => true
But it didnt change the behaviour..
API says:
:validate
If false, don’t validate the associated objects when saving the parent object. false by default.
So I changed the validation to room:
class Room < ActiveRecord::Base
has_many :students, :validate => true
end
but neither options solve for me
any ideas???
Give this a try...
class Student < ActiveRecord::Base
belongs_to :room
validates_associated :room
end
I'm looking at this portion of the API docs: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_associated
Also, be careful not to use that validation on both sides of the association!

Validating a self-referential association doesn't link back to the original instance in rails

I have a many-to-many model, following the example in this great railscast
My model links authors to each other. I'd like to validate that an author cannot friend himself. I know I can handle this at the UI level, but I'd love to have a validation in place to prevent a bug in the UI from allowing it. I've tried validates_exclusion_of, but it doesn't work. Here's my model for the relationship:
class Friendship < ActiveRecord::Base
# prevent duplicates
validates_uniqueness_of :friend_id, :scope => :author_id
# prevent someone from following themselves (doesn't work)
validates_exclusion_of :friend_id, :in => [:author_id]
attr_accessible :author_id, :friend_id
belongs_to :author
belongs_to :friend, :class_name => "Author"
end
You'll have to use a custom validation:
class Friendship < ActiveRecord::Base
# ...
validate :disallow_self_referential_friendship
def disallow_self_referential_friendship
if friend_id == author_id
errors.add(:friend_id, 'cannot refer back to the author')
end
end
end

has_one and has_many in same model. How does rails track them?

I a little confused about how this work even if it works properly. I have a model that has two association to the same other model.
Company has an owner and company has many employees of the class users.
here is my company model:
class Company < ActiveRecord::Base
validates_presence_of :name
has_many :employee, :class_name => 'User'
has_one :owner, :class_name => 'User'
accepts_nested_attributes_for :owner, :allow_destroy => true
end
here is my user model:
class User < ActiveRecord::Base
include Clearance::User
attr_accessible :lastname, :firstname #other attr are whitelisted in clearance gem
validates_presence_of :lastname, :firstname
belongs_to :company
end
Now assuming I have 3 employee of this company including the owner. When I first create the company I set the owner to the employee with id 1 and the two others (2,3) are added to the employee list by setting their company_id (user.company=company). All three have their company_id set to the company id which we can assume is 1
when I ask for company.owner, I get the right user and when I do company.employee, I get all three.
If I change the owner to user 2, it removes user 1 from the employees automatically by setting it's company_id to nil. This is fine and if I add him back as a simple employee all is still good.
How the heck does rails know which is which? What I mean is how does it know that an employee is owner and not just an employee? Nothing in the schema defines this.
I have a feeling I should reverse the owner association and make company belong_to a user.
As you have it now, there's nothing to distinguish owners from employees. Which means you're going to run into problems once you start removing people or try to change ownership.
As François points out, you're just lucking out in that the owner is the user that belongs to company with the lowest ID.
To fix the problem I would have my models relate in the following maner.
class Company < ActiveRecord::Base
belongs_to :owner, :class_name => "user"
has_many :employees, :class_name => "user"
validates_presence_of :name
accepts_nested_attributes_for :owner, :allow_destroy => true
end
class User < ActiveRecord::Base
include Clearance::User
attr_accessible :lastname, :firstname #other attr are whitelisted in clearance gem
validates_presence_of :lastname, :firstname
belongs_to :company
has_one :company, :foreign_key => :owner_id
end
You'll have to add another column called owner_id to the Companies table, but this more clearly defines your relationships. And will avoid any troubles associated with changing the owner. Take note that there might be a cyclical dependency if you go this route and have your database set so that both users.company_id and companies.owner_id cannot be null.
I'm not quite sure how well accepts_nested_attributes_for will play with a belongs_to relationship.
has_one is syntactic sugar for:
has_many :whatevers, :limit => 1
has one adds the :limit => 1 bit, thereby ensuring only 1 record is ever returned. In your has one declaration, make sure you have an :order clause, to return the right record in all circumstances. In this instance, I'd put a flag on the Employee to signify who is the owner, and sort by this column to get the right record 1st.
Your question about how come Rails knows this is because most databases will return records in their primary key order. So, the 1st added employee has ID 1, thereby will be returned 1st.
You could have a model called ownership -
ownership belongs_to company
ownership belongs_to user
user has_many ownerships
company has_one ownership

Resources