I was working on a project this weekend and came across an issue that hung me up for hours. The application will have an area to manage contacts, so I have a Person model, which has_many email_addresses, among other things. So in order to add another email_address to my form using rjs, I send a request to the ContactMethodsController with a the type param set to email_address. It renders the rjs, which renders a partial and inserts it into my form. In order to accomplish this I needed to generate a temporary ID for each email address so I could use them in a form, generate urls with them, and when/if errors occur the id would remain the same so the form could pick up the errors. I do not want to save the models unless they are filled in. This scheme works wonderfully in development, but when I push it to production, it breaks because the generated id becomes nil after an action other than index/new is run in the PeopleController. The index action can be refreshed, but as soon as another action is called it blows up. class Person < ActiveRecord::Base with_options :dependent => :destroy do |person| person.has_many :phone_numbers, :as => :phoneable person.has_many :email_addresses, :as => :emailable person.has_many :addresses, :as => :addressable end def update_contact_methods(data) result = {} [ :email_addresses, :phone_numbers, :addresses ].collect do |c| klass = c.to_s.classify.constantize x = c.to_s.singularize.split(''_'')[0] klass.transaction do unless new_record? klass.destroy_all("#{x}able_id = #{self.id} AND #{x}able_type ''#{self.class}''") end result[c] = data[c].values.collect{|v| klass.new(v) }.delete_if(&:blank?) end end update_attributes(result) end def update_from_params(person_params) contact_params = person_params.delete(:contact_data) update_attributes(person_params) reload update_contact_methods(contact_params) errors.empty? end def set_place_holder_data # give the person a temp id self.id ||= 0 # add an address it there is none self.addresses = [ self.addresses.build ] if self.addresses.empty? # add phone numbers if we have none self.email_addresses = [ self.email_addresses.build ] if self.email_addresses.empty? # add phone numbers if we have none self.phone_numbers = [ self.phone_numbers.build ] if self.phone_numbers.empty? end end Note that when set_place_holder_data is called a temporary id of 0 is set if the id is not set and we add a dummy email_address if one does not exist, so the fields show up on the form. [code=]class EmailAddress < ActiveRecord::Base ##### Associations belongs_to :emailable, :polymorphic => true ##### Attributes cattr_reader :locations @@locations = %w(HOME WORK OTHER) ##### Validation validates_length_of :address, :within => 7..80 validates_format_of :address, :with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+ [a-z]{2,})$/ ##### Class Methods ##### Instance Methods def generated_identifier @generated_identifier ||= generate_identifier end def id_with_generated_identifier new_record? ? generated_identifier : id_without_generated_identifier end alias_method_chain :id, :generated_identifier def blank? address.blank? end private def generate_identifier(len=10) chars = ("a".."z").to_a + (1..9).to_a identifier = "" 1.upto(len){|i| identifier += chars[rand(chars.size-1)].to_s } identifier end end Note that the EmailAddress model generates an ID for itself if it is a new record. this is done so I can generate urls using named routes and a dom_id for the object on a form where there may be a couple "new" EmailAddresses. For example in a view I would: <div id ="<%= dom_id(email_address) %>"> <%= link_to "Delete", person_contact_method_url(@person, email_address) + "?type=email_address", :method => :delete %> </div>[/code] The dom_id would be: email_address_894j5jksl The url would be: /person/0/contact_methods/894j5jksl? type=email_address class PeopleController < ApplicationController helper :contact_methods # GET /people def index # preload the new form @person = provider.new # set place holder data for the form @person.set_place_holder_data # get all of the contacts with eagerly loaded data @people = provider.find_roots(:all) respond_to do |format| format.html # index.rhtml format.xml { render :xml => @people.to_xml } end end # GET /people/1 def show @person = provider.find_with_includes(params[:id]) respond_to do |format| format.html # show.rhtml format.xml { render :xml => @person.to_xml } end end # POST /people def create @person = provider.new respond_to do |format| if @person.update_from_params(params[:person]) flash[:notice] = ''Contact was successfully created.'' format.js {} else format.js { render :update do |page| page[:new_person_form_errors].replace_html error_messages_for(:person) end } end end end # GET /people/1/edit def edit @person = provider.find_and_set_place_holders(params[:id]) end # PUT /people/1 def update @person = provider.find(params[:id]) respond_to do |format| if @person.update_from_params(params[:person]) flash[:notice] = ''Contact was successfully updated.'' format.js format.html { redirect_to people_url } else format.html { render :action => "edit" } end end end private def provider current_account.people end end class ContactMethodsController < ApplicationController def new @object = provider.new respond_to do |format| format.js end end def destroy if params[:id].match(/^\d+$/) @object = provider.find(params[:id]) @object.destroy end @object = provider.new respond_to do |format| format.js end end private def provider if params[:person_id].eql?(''0'') @person = current_account.people.new @person.set_place_holder_data end @person ||= current_account.people.find(params[:person_id]) case params[:type] when ''phone_number'' return @person.phone_numbers when ''email_address'' return @person.email_addresses when ''address'' return @person.addresses end end end This maybe a bit confusing, but the problem occurs when I put this code into production. The first time you load the "new" action in the PeopleController it works fine. The second time it gives me a 500 error because the ID has disappeared from the EmailAddress and I am using named route functions to generated urls which require it. At first I though it had something to do with the generated_identifier not being set the second time. Not the case, actually I put a raise statement in the aliased method id_with_generated_identifier which never gets called after the first execution. After hours of debugging I figured out that if I turn off the cache_classes setting in production/environment.rb it works great. Slow ... but great. It seems that the alias_method_chain which creates an alias from id_with_generated_identifier to id is broken after the first execution. I am not sure why this is. Does anyone have an idea why this would be happening? If not, does anyone have a better way of accomplishing this that would allow me to cache my classes again? If more clarification is needed please let me know. Thanks, Nicholas --~--~---------~--~----~------------~-------~--~----~ 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?hl=en -~----------~----~----~----~------~----~------~--~---