how to access self.id in an association - ruby-on-rails-3

I have something like this my user model
class User < ActiveRecord::Base
has_many :received_msgs, class_name:"Message", foreign_key: "recipient_id", :conditions =>['owner_id = ?', self.id]
has_many :sent_msgs, class_name: "Message", foreign_key: "sender_id", :conditions =>['owner_id = ?', self.id]
where I want the owner_id to almost act as a secondary foreign key since each message is duplicated for independent deletion.
However, I'm getting an error saying id isn't defined for the object. How do I access self.id in this case? Or is there a better way of doing this?
Thanks

Are you sure you don't just want the foreign_key to be owner_id? That way, you won't need that conditions array at all.
Anyhow, the reason you can't access self.id in the associations definition is because of what scope the code is evaluated in. The associations are evaluated when loading the class, and not when actually calling the #received_msgs method on a User instance.
I believe you could access the self.id of the instance by wrapping the conditions array in a lambda. That way it will not be evaluated until you call it.

Related

has_many association ignores restricting condition

I'm working on a Rails 3.0.x application (actually it's Hobo 1.3.x but that's not material to this question). Among the models, there are GraphPanes, GraphLabels, and LabelSets. A GraphPane can have GraphLabels and LabelSets. GraphLabels can belong to GraphPanes or LabelSets, but not both. So if a GraphLabel belongs to a LabelSet, I'd like to keep it from being associated to a GraphPane.
I am trying to enforce that with this code in the GraphPane model:
has_many :graph_labels, :conditions => 'label_set_id = NULL'
However, I'm still able to associate GraphLabels with not-null label_set_id with GraphPanes. Why? How can I stop this?
This question is superficially similar, but my relationship isn't polymorphic, so the nominal solution there doesn't help me.
The functionality of :conditions on has_many is to filter the results that are passed back via the graph_labels, not to protect objects from being added to the association.
If you add a graph_label with no label_set_id, the association will build, but if you then ask for graph_pane.graph_labels, it will not return that non-condition-matching graph_label.
The has_many/belongs_to relationship is saved on the belongs_to model, graph_label, and so the parent/has_many/graph_pane does not stop the graph_label from writing whatever it wants to its graph_pane_id attribute. This delegation of responsibility is correct, although frustrating, I agree.
Now, as for how to stop this, I'm not sure. It sounds like you need some sort of validation on the graph_label object, something along the lines of not allowing a graph_pane_id to be set on a graph_label if that graph_label's label_set_id is nil. Since the has_many/belongs_to relationship is saved on the graph_label, you should write the validation on the graph_label. That way, the graph_label will not be able to be saved with a new graph_panel_id unless it fulfills the condition.
Thoughts? Questions?
Reference:
has_many
Alternate Solution
I've reread your question and I think want you want here is a polymorphic association.
def GraphPane < ActiveRecord::Base
has_many :label_sets
has_many :graph_labels, as: :parent
end
def LabelSet < ActiveRecord::Base
belongs_to :graph_pane
has_many :graph_labels, as: :parent
end
def GraphLabel < ActiveRecord::Base
belongs_to :parent, polymorphic: true
end
That way, a GraphLabel can only have a single parent, which is what your “spec” above requires. Is there any reason not to implement the relations in this way?

Finding a model instance with no relation in Rails

class User
has_one :settings
class Settings
belongs_to :user
I want to do something like
#user_with_no_settings = User.where(:settings => nil)
But this returns an empty relation.
How do I find all users which don't have a settings related to them? (So I will find them and create them)
Most importantly, your association is incorrect;
Change it to:
class User
has_one :setting
class Settings
belongs_to :user
The Class name is plural but the association has_one is always singular. So you can't use :settings for has_one as oppose to has_many which is always plural.
Use this:
User.where("id not in (select user_id from settings)")
The above query will give all the users which don't have a settings associated to them.
Okay. In your situation you may need some TRY this code.
User.where('id NOT IN (?)', User.joins(:settings).pluck('settings.user_id'))
But, you need to follow Rails Convention.
Change settings to setting.

Rails conditional validation based on polymorphic type

I am working with a Rails polymorphic inheritance configuration - I have the following setup:
User < ActiveRecord::Base
belongs_to :rolable, :polymorphic => true
Student < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
Teacher < ActiveRecord::Base
has_one :user, :as => :rolable
accepts_nested_attributes_for :user
I want to be able to capture email address for teachers and a username for students (who won't typically have an email address). I defined those as attributes of the User model, but now I'm stuck when I try to do validations for Student and Teacher. I didn't define them in their respective models because I'm using Devise and there will be other user types. Abstracting what is currently type to a Role pattern isn't a good fit for my particular scenario either.
Since username and email are properties of User what I basically want to do is check if the rolable_type field from the polymorphic relationship is type student and if so, make username required and email not, but in the new method that property isn't set. However Rails 'knows' this is a Student, so it feels like there's some way to check the instance type. The closest link I've found to what I'm shooting for is the third comment to the accepted answer in this question: How to apply different validation rule according to polymorphic association type (Rails)?, but I'm having trouble getting the method_missing syntax correct as I'm not experienced with metaprogramming. Am I on the right track with this? Or is there a simpler way? Or should I move the properties to the polymorphic models instead?

Using attr_accessible in a join model with has_many :through relationship

I have a USER that creates a COMPANY and become an EMPLOYEE in the process. The employees table has an :user_id and a :company_id.
class User
has_many :employees
has_many :companies, :through => :employees
class Employee
belongs_to :user
belongs_to :company
attr_accessible :active
class Company
has_many :employees
has_many :users, :through => employees
Pretty basic. But here's the thing, the resource EMPLOYEE has other attributes than its foreign keys, like the boolean :active. I would like to use attr_accessible, but this causes some problems. The attribute :user_id is set right, but :company_id is nil.
#user.companies << Company.new(...)
Employee id:1 user_id:1 company_id:nil
So my question is: if :user_id is set right, despite it is not an attr_accessible, why :company_id isn't set right just the same? It shouldn't be an attr_accessible.
I'm using Rails 3.0.8, and have also tested with 3.0.7.
There are a lot of bits working together here.
You definitely want to use attr_accessible on all models. (Google "hack rails mass assignment" and read the Rails Guide on mass assignment.)
Once you add attr_accessible to a model, all assignments from hashes (mass assignments) are disabled except those you explicitly allow. However, you can still assign values directly, one at a time.
Foreign keys seem like a good thing to exclude from mass assignment, so don't list them in attr_accessible.
The .create and .build methods are not using mass assignment so they can set the value of one foreign key association. If there are several associations, as best I can tell, you'll have to set all but the first separately.
Finally, the actual IDs for the foreign keys are created by the database, not by ActiveRecord. So you'll either have to create parent and child records simultaneously, or you'll have to save the child first before you can assign the foreign key in the parent. Otherwise there is no ID available for the assignment.
It's not clear to me from your example how Employee is getting instantiated. But since the Employee belongs to both User and Company, I think something like this might work, assuming #user already exists:
company = #user.companies.create(..) # fills in company.user_id and saves to DB
employee = #user.employees.build(..) # fills in employee.user_id but does NOT save yet
employee.company = company # fills in employee.company_id
employee.save # now save to DB
The company_id is nil simply because the Company hasn't been saved to the database yet - Company.new simply creates the object in memory without saving it yet.
If you do:
#user.companies << Company.create(..)
or
#user.companies << Company.first
They should both work. There's even a shorter method which I think should work too:
#user.companies.create(..)
It all depends at which point you want to save the association. In some cases, it may be better not to save the employee and company models straight away, and instead wait for when the parent model (User) is saved. In which case you can use:
#user.companies.build(..)
(which is similar to the code in your example).
In terms of your active boolean attribute on the Employee model, if this is a column in the database, you don't need to explicitly declare attr_accessible for it - it'll be accessible by default.

Rails: Make different references to a DB row refer to the same Ruby object

Suppose I have the following model relationship:
class Player < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :player
end
I know from this question that Rails will return me a copy of the object representing a database row, meaning that:
p = Player.find(:first)
c = p.cards[0]
c.player.object_id == p.object_id # => false
...and therefore if the Player model modifies self, and the Card model modifies self.player in the same request, then the modifications won't take any notice of each other and the last-saved one will overwrite the others.
I'd like to work around this (presumably with some form of caching), so that all requests for a Player with a given id would return the same object (identical object_ids), thereby allowing both models to edit the same object without having to perform a database save-and-reload. I have three questions:
Is there already a plugin or gem to do this?
Are there good reasons why I shouldn't do this?
Can anyone give me some pointers on how to go about doing this?
This is supported in Rails 3.x. You can use the :inverse_of option for the has_many association for example. Documentation here (search for :inverse_of and Bi-directional associations).