Has_many with :uniq not working for me

Hi all,

I have a relationship (no really!)

class RiskMatrix < ActiveRecord::Base
has_many :severities, :order => :position, :uniq => true
end

class RiskFactor < ActiveRecord::Base

belongs_to :risk_matrix

validates_presence_of :descriptor, :example
validates_uniqueness_of :descriptor, :example, :scope=> 

:risk_matrix_id
end

class Severity < RiskFactor
acts_as_list :scope => :risk_matrix
end

I don’t want any duplicates for a given risk matrix of a risk factor so
I
wrote a couple of tests. Both of them fail but I’m not sure why.

def test_no severity should be duplicated for a risk_matrix
sev = @rm.severities.find(:first ) # @rm is a risk matrix set during
setup
@rm.severities << sev
assert_equal 1, @rm.severities.select{ |s| s == sev }.size
end

def test_descriptor should be uniq for a given risk matrix
@sev = Severity.find(:first )
@rm = @sev.risk_matrix
@rm.severities << @sev.dup
assert_equal 1, @rm.severities.select{|s| s.descriptor == @
sev.descriptor }.size
end

The uniq option doesn’t seem to be doing anything. Am I using it
incorrectly?

Thanx to anyone who can help me.

Cheers
Daniel

Hi all… Sorry to bump but this is driving me nuts.

Daniel ----- wrote:

class RiskMatrix < ActiveRecord::Base
has_many :severities, :order => :position, :uniq => true
end

class RiskFactor < ActiveRecord::Base

belongs_to :risk_matrix

validates_presence_of :descriptor, :example
validates_uniqueness_of :descriptor, :example, :scope=> 

:risk_matrix_id
end

class Severity < RiskFactor
acts_as_list :scope => :risk_matrix
end

I don’t want any duplicates for a given risk matrix of a risk factor so
I wrote a couple of tests. Both of them fail but I’m not sure why.

def test_no severity should be duplicated for a risk_matrix
sev = @rm.severities.find(:first ) # @rm is a risk matrix set during setup
@rm.severities << sev
assert_equal 1, @rm.severities.select{ |s| s == sev }.size
end

def test_descriptor should be uniq for a given risk matrix
@sev = Severity.find(:first )
@rm = @sev.risk_matrix
@rm.severities << @sev.dup
assert_equal 1, @rm.severities.select{|s| s.descriptor == @
sev.descriptor }.size
end

The uniq option doesn’t seem to be doing anything. Am I using it
incorrectly?

The :uniq option is really only useful for has_many :through
associations. How else would you get the same record in a has_many
association more than once? I don’t fully understand your modelling
problem, but I’m guessing if you are worried about dupes you might be
wanting to use a many-to-many mapping, like a has_many :through. If not,
and you just have a has_many, you shouldn’t need to use :uniq.

What’s going on in your tests is that you are adding the sev object to
the in-memory collection, then checking to see if it’s there, which it
of course is. You haven’t saved anything to the database, or updated the
in-memory collection with new content from the database. And since you
aren’t saving your model, the validations don’t run, so the
validates_uniqueness_of check won’t catch anything. Remember,
ActiveRecord is an ORM system, and it only does something useful when
you are either reading from or writing to the database.

If you want some examples of :uniq with has_many :through, search in my
blog for “uniq”. And you’re on the right track for the validation,
you’ll just need to adjust for the new association.


Josh S.
http://blog.hasmanythrough.com

On 12/6/06, Josh S. [email protected] wrote:

I wrote a couple of tests. Both of them fail but I’m not sure why.
@rm = @sev.risk_matrix
association more than once? I don’t fully understand your modelling
ActiveRecord is an ORM system, and it only does something useful when
you are either reading from or writing to the database.

If you want some examples of :uniq with has_many :through, search in my
blog for “uniq”. And you’re on the right track for the validation,
you’ll just need to adjust for the new association.


Josh S.
http://blog.hasmanythrough.com

Right on the money as usual. Thanx

My test ended up as follows:

specify “no severity should be duplicated for a risk_matrix” do
sev = @rm.severities.find(:first )
@rm.severities << sev
@rm.save
@rm.severities.reset
assert_equal 1, @rm.severities.select{ |s| s == sev }.size
end

And
specify “descriptor should be uniq for a given risk matrix” do
@sev = Severity.find(:first )
@rm = @sev.risk_matrix
sev = Severity.new( :descriptor => @sev.descriptor, :example =>
“this is
some example”, :risk_matrix => @rm )
assert !sev.valid?
end

I think it must’ve been a bit late for the second one last night because
that seems really easy now.

The first test though seems to smell a bit. I don’t think I would
remember
to

@rm.severities << sev
@rm.save
@rm.severities.reset

everywhere in my code when I want to add a severity to the matrix.

Prior to this snippet, the RiskMatrix in @rm is read from the DB.

I also thought that the << would automagically save the association.
Do I
need to do this everytime I want to add to the collecion?

Cheers
Daniel

On 12/6/06, Daniel N [email protected] wrote:

acts_as_list :scope => :risk_matrix
assert_equal 1, @ rm.severities.select{ |s| s == sev }.size
The uniq option doesn’t seem to be doing anything. Am I using it
the in-memory collection, then checking to see if it’s there, which it

sev = @rm.severities.find(:first )
sev = Severity.new ( :descriptor => @sev.descriptor, :example => "this
@rm.severities << sev

Cheers
Daniel

For posterities sake, the test

specify “no severity should be duplicated for a risk_matrix” do
sev = @rm.severities.find(:first )
@rm.severities << sev
@rm.save
@rm.severities.reset
assert_equal 1, @ rm.severities.select{ |s| s == sev }.size
end

is a pain because I’ve selected the severities into an array. That’s
why
the severities need to be reset first. Instead, by using count on the
association collection

specify “no severity should be duplicated for a risk_matrix” do
sev = @rm.severities.find(:first )
initial_severities = @rm.severities.count
@rm.severities << sev
@rm.save
assert_equal initial_severities, @rm.severities.count
end

works as expected.