I''ve been pulling my hair our over this same problem for the last few hours.... Did you ever figure out the resolution? I found this thread here: http://lists.rubyonrails.org/pipermail/rails/2005-December/007657.html but see at the time you were not able to get an answer from the list. Any suggestion greatly appreciated! Chris I''ve got a really strange problem using belongs_to. I apologize in advance for the length... this is going to take a while to explain. Basic idea: Creating a User requires that the user enter an email address and activation key that matches an existing PendingUser. After creating the user successfully, that pending user should be marked as "used". The problem: When I add a belongs_to :pending_user declaration to the User class, attempting to save a User yields this exception (taken here from the output of a simple test case -- code below.) You''ll note some lines in the User class with two hashes ''##'' preceding them -- those are alternate statements that avoid using the .pending_user attribute. If you comment out the belongs_to :pending_user and use the alternate statements (which just work with the pending_user_id field), everything seems to work fine. 1) Error: test_create_with_valid_pending_user(UserTest): NoMethodError: undefined method `updated?'' for #<PendingUser:0x8d09310> /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/base.rb:1498:in `method_missing'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/callbacks.rb:341:in `callback'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/callbacks.rb:335:in `callback'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/callbacks.rb:330:in `each'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/callbacks.rb:330:in `callback'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/callbacks.rb:248:in `create_or_update'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/base.rb:1226:in `save_without_validation'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/validations.rb:698:in `save_without_transactions'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/transactions.rb:126:in `save'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/transactions.rb:126:in `transaction'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/transactions.rb:91:in `transaction'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/transactions.rb:118:in `transaction'' /usr/local/lib/ruby/gems/1.8/gems/activerecord-1.13.2/lib/ active_record/transactions.rb:126:in `save'' test/unit/user_test.rb:62:in `test_create_with_valid_pending_user'' I''m not calling the ''updated?'' method anywhere in my code, but looking in ${PREFIX}/lib/ruby/gems/1.8/doc/activerecord-1.13.2/rdoc/ classes/ActiveRecord/Associations/ClassMethods.src, it seems that belongs_to calls the .updated? method. Any light you can shed would be greatly appreciated -- I''ve been banging my head against this for a few days, and no one on #rubyonrails on freenode has been able to figure out what''s going on. -Marshall Pierce Code follows... Test case: require File.dirname(__FILE__) + ''/../test_helper'' class UserTest < Test::Unit::TestCase fixtures :accounts, :pending_users # we don''t need the users fixture -- all we''re testing here is creating a new user # and the corresponding validation of the associated pending user and account def setup @new = User.new # give it a valid email address @new.email_address = pending_users(:ok_req).email_address @new.password = @new.password_confirmation = ''doesntmatter'' @new.activation_key = ''activationkey'' @new.account_id = pending_users(:ok_req).account_id end def test_create_with_valid_pending_user assert @new.save end end SQL definitions for the tables for the classes involved (postgresql): DROP TABLE generic_users CASCADE; CREATE TABLE generic_users ( password_hash text NOT NULL , email_address text UNIQUE NOT NULL , created_at timestamp with time zone NOT NULL , updated_at timestamp with time zone NOT NULL ); DROP TABLE users CASCADE; CREATE TABLE users ( LIKE generic_users INCLUDING DEFAULTS , id SERIAL PRIMARY KEY , account_id int NOT NULL REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE , is_account_admin bool NOT NULL DEFAULT false , email_validation_key text NOT NULL , email_validated bool NOT NULL DEFAULT false , pending_user_id int NOT NULL REFERENCES pending_users(id) ON UPDATE CASCADE ON DELETE CASCADE ); DROP TABLE pending_users CASCADE; CREATE TABLE pending_users ( id SERIAL PRIMARY KEY , account_id int NOT NULL REFERENCES accounts(id) ON UPDATE CASCADE ON DELETE CASCADE , email_address text UNIQUE NOT NULL , activation_key_hash text NOT NULL , created_at timestamp with time zone NOT NULL , updated_at timestamp with time zone NOT NULL , used bool NOT NULL DEFAULT false ); The class definitions: (yeah, I know -- re-implementing login logic yet again is dumb, but this isn''t near completion yet, so don''t give me too much grief about it. :) class GenericUser < ActiveRecord::Base validates_presence_of :password, :password_confirmation, :email_address validates_uniqueness_of :email_address validates_confirmation_of :password # password must be at least 8 characters long validates_format_of :password, :with => %r{^.{8,}$}, :message => "must be at least 8 characters" validates_format_of :email_address, :with => MiscUtils::EMAIL_REGEX # user can only supply these params attr_accessible :password, :password_confirmation, :email_address # class variable -- see koz''s post http://www.koziarski.net/ archives/2005/07/16/environment cattr_accessor :current_user # non-db variables attr_accessor :password def before_save #breakpoint("generic user before_save") # save the new hash if the password is set # XXX - only set password_hash during methods that can change password self.password_hash = MiscUtils.hexdigest(@password) unless @password.nil? end def after_save @password = nil @password_confirmation = nil end def try_to_login self.class.lookup(self.email_address, @password) end # Class methods def self.lookup(email_address, password, additional_conds = '''') hashed_password = MiscUtils.hexdigest(password) begin find( :first, :conditions => ["email_address = ? and password_hash = ?" + additional_conds, email_address, hashed_password]) rescue return nil end end end -------------------- class User < GenericUser set_table_name "users" belongs_to :account belongs_to :pending_user validates_presence_of :activation_key attr_accessible :activation_key # non-db variable attr_accessor :activation_key # XXX: need to make sure that hypothetical user-editing tools don''t allow malicious queries # to change their account_id or anything like that def validate_on_create # we use an artificial, non db-backed parameter to hold the activation key, # then perform lookup here so we have access to errors.add, etc. @pending_user = PendingUser.lookup(self.email_address, @activation_key) if @pending_user.nil? errors.add_to_base("Invalid email address or activation key") return false; elsif @pending_user.used == true # XXX : only for debug. TMI. errors.add_to_base("User request already used") end begin self.account = Account.find(@pending_user.account_id) rescue errors.add(:account_id, "is invalid") end end def before_create self.email_address = @pending_user.email_address self.account = @pending_user.account ##self.pending_user = @pending_user self.pending_user_id = @pending_user.id # XXX: send an email with the activation key here... self.email_validation_key = MiscUtils.random_string(80) # XXX: since we''re not sending email, manually set the validation flag self.email_validated = true #breakpoint("user before_create finish") end def after_create # mark the pending user as used # XXX - handle failure better ##self.pending_user.used = true @pending_user.used = true ##unless self.pending_user.save unless @pending_user.save ##raise "couldn''t save pending user #{self.pending_user.id}" raise "couldn''t save pending user #{@pending_user.id}" end end def self.lookup(email_address, password) super(email_address, password, "and email_validated = true") end end ----------- class PendingUser < ActiveRecord::Base validates_presence_of :email_address, :account_id validates_uniqueness_of :email_address validates_format_of :email_address, :with => MiscUtils::EMAIL_REGEX attr_accessible :email_address, :account_id attr :activation_key belongs_to :account has_one :user # XXX: eventually we''ll want to programmatically set which account the pending user will # be tied to, and remove the accessible, etc symbols. (that is, pull it from the current # user''s info.) def validate begin self.account = Account.find(self.account_id) rescue errors.add(:account_id) end end # need to create an activation key and store its hash def before_create #randkey = MiscUtils.random_string(20) # XXX: right now we''re not sending emails, so we won''t know what the un-hashed key was... randkey = "foo" self.activation_key_hash = MiscUtils.hexdigest(randkey) end # simple wrapper to do the digest''ing of the key def self.lookup(email_address, key) hashed_key = MiscUtils.hexdigest(key) begin find( :first, :conditions => ["email_address = ? and activation_key_hash = ? and used = false", email_address, hashed_key]) rescue return nil end end end -------------- next part -------------- A non-text attachment was scrubbed... Name: smime.p7s Type: application/pkcs7-signature Size: 2357 bytes Desc: not available Url : http://wrath.rubyonrails.org/pipermail/rails/attachments/20051229/946e583c/smime.bin --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk -~----------~----~----~----~------~----~------~--~---
Mark Reginald James
2006-Sep-01 06:12 UTC
Re: belongs_to causing NoMethodError exceptions ... ?
Chris Eagan wrote:> I''ve been pulling my hair our over this same problem for the last few > hours.... Did you ever figure out the resolution? I found this thread > here: > http://lists.rubyonrails.org/pipermail/rails/2005-December/007657.html > but see at the time you were not able to get an answer from the list. > > Any suggestion greatly appreciated!See this thread: http://lists.rubyonrails.org/pipermail/rails/2006-March/025508.html -- We develop, watch us RoR, in numbers too big to ignore. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk -~----------~----~----~----~------~----~------~--~---