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!

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.

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.

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

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.

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.

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.

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,

http://railscasts.com/episodes/196-nested-model-form-part-1