0

I have a relationship table in a rails application called edit_privileges, in which the User is the "editor" and a number of other classes are "editable". Let's say that two of those classes are Message and Comment.

My EditPrivilege model uses the following code:

belongs_to :editor, :class_name => "User"
belongs_to :editable, :polymorphic => true

And User, of course

has_many :edit_privileges, :foreign_key => "editor_id"

In order to determine if a user has edit privileges for a certain model, I can't do the normal query:

user.edit_privileges.find_by_editable_id(@message.id)

because if the user has edit privileges to edit a comment with the same id as @message, the query will return true with the wrong edit privilege record from the table.

So, I tried doing these options:

user.edit_privileges.find(:all, :conditions => ["editable_id = ? AND editable_type ?", @message.id, @message.class.to_s])
user.edit_privileges.where(:editable_id => @message.id, :editable_type => @message.class.to_s)

which works great at finding the right record, but returns an array instead of an object (an empty array [] if there is no edit privilege). This is especially problematic if I'm trying to create a method to destroy edit privileges, since you can't pass .destroy on an array.

I figure appending .first to the two above solutions returns the first object and nil if the result of the query is an empty has, but is that really the best way to do it? Are there any problems with doing it this way? (like, instead of using dynamic attribute-based finders like find_by_editabe_id_and_editable_type)

Ryan Atallah
  • 2,977
  • 26
  • 34
  • 1
    [] is not hash but array. If you want to do batch operation on an array (collection), for example if you want to destroy all records in an array, you can use `users.map(&:destroy)`. – James Chen Aug 15 '11 at 06:21

1 Answers1

3

Use find(:first, ...) instead of find(:all, ...) to get one record (note it might return nil while find will raise an RecordNotFound exception). So for your example:

user.edit_privileges.find(:first, :conditions => { :editable_id => @message.id, :editable_type => @message.class.to_s })

BTW, if you're on more edge rails version (3.x), Model.where(...).first is the new syntax:

user.edit_privileges.where(:editable_id => @message.id, :editable_type => @message.class.to_s).first
James Chen
  • 10,794
  • 1
  • 41
  • 38
  • I figured that was the way to do it, but I wasn't sure if there was an alternative 'best practice' I should be aware of, since plucking the first record from an array seems kind of strange to me. – Ryan Atallah Aug 15 '11 at 06:23
  • Is there a reason why this would be preferable to using dynamic attribute-based finders like `find_by_editabe_id_and_editable_type`? – Ryan Atallah Aug 15 '11 at 06:26
  • 1
    find_by_this_and_that is fine and quite a good dynamic method, but using `where(...).first` is more flexible in case more conditions be used. – James Chen Aug 15 '11 at 06:31