Weird stuff: validate_uniqueness_of with :scope does not work

Running rails 2.3.8.

I have a two models defined like this

class Member
has_many :folders
before_save: create_default_folders

protected
def create_default_folders!
%w{Inbox Sent}.each do |folder|
unless self.folders.find_by_name(folder)
self.folders.create!(:name => folder, :deletable => false)
end
end
end
end

class Folder
belongs_to :member

folder names must be unique for each user

validates_uniqueness_of :name, :scope => [:member_id], :allow_blank
=> false,
:case_sensitive => false, :message => ‘already exists for this
member’
end

------ MembersController
def create
@member = Member.new(params[:member])

if @member.save
     ...
end

end

So as you can see, what I wish to get when I save a new Member are two
default folders colled (Inbox and Sent).
So I defined the after_save callback. I tested it in script/console
and it works whenever I build and save a Member.

However, as soon as I create a member through a controller-backed
form, the uniqueness validation gets tripped up. I get validation
error from my validates_uniqueness_of as “Folder already exists for
this member”. Which shouldn’t happen.

For some strange reason :scope gets ignored and thus uniqueness check
isn’t properly scoped by member_id…

The weird part is that it all works in script/console, but not via web
forms.

Any suggestions???

Thanks in advance!

On Sep 7, 11:55 am, Swartz [email protected] wrote:

Is there some odd interaction because you’re creating these folders
from a before_save (ie the member’s id has not yet been set)?

Fred

dont use the before_save use transactions that way it will roll back
both
actions if there is an error.
and dont use that function you have with the each and all use

find_or_create_by_name(name)

it does what you want.

On Tue, Sep 7, 2010 at 7:20 AM, Frederick C.
<[email protected]

That’s what I thought originally about id not being set… but why
does it work from script/console ?
That kind of puzzles me…

On Sep 7, 5:20 am, Frederick C. [email protected]
wrote:

On Sep 7, 11:55 am, Swartz [email protected] wrote:

Also note that I have to set at least one other folder attribute while
creating a folder ,
so this variant doesn’t help me much.

That code must’ve been 5th iteration of trying to get that call back
to work.

I’ve tried your suggested method.
I’ve so tried create/create!, save/save!.

Everything work from console.
That is I can do
m = Member.new
m.x = “value”
m.folders.find_or_create_by_name('folder_name")
m.save

It all work beautifully from console. I can test my validation, it
works.

But as soon as I do the same thing (create new Member) from controller
context via form params, the scoped validates_uniqueness_of does not
function as expected.


Swartz

I’ve created a brand new Rails 2.3.8 app.
No gems, nothing.

I created only the bare necessities:
Two models (Member and Folder)
one controller (MembersController with only new and create actions)
one view (new).

I can create one member with default folders as per my callback (the
very first one), but any subsequent members don’t get default folders
created for them.

But again, when I try the same thing from script/console everything
works (I create new member and, bam, two folders come into existence
as well)…

w t f…

watch this and see if ti helps,

check your attri_accesible, and check if you rae using a gem that uses
attr_protected, it appears you have a problem with mass assignment and
you
cant see from one controller the attribute that member_id from the
folder
controller i think you need to be able to , also note that with before
save
there is no member_id in the database to compare the validation with
thats
why i said you should use a transaction and create everything from the
member controller like this

Member.transaction do
@member.new(blah, blah)
@member.save!
params[:folder_name].nil? ? name= “Default” : name =
params[:folder_name]
@folder = Folder.find(:conditions =>{:name = name, :member_id=>
@member.id})

@folder ||= Folder.new(blah blah)
@folder.member = @member
@folder.save!
end

try that, the code inside the transaction will rollback if either to the
save fail and everything will go back to the its original state.