Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #6720 (closed defect: fixed)

Opened 2 years ago

Last modified 2 years ago

Instance Methods Disappearing

Reported by: stmpjmpr Assigned to: Ulysses
Priority: normal Milestone: 1.2 regressions
Component: ActiveResource Version: edge
Severity: normal Keywords: NoMethodError TemplateError
Cc: mislav

Description

I have a model User that belongs_to EmailAddress (I know that seems backwards, but that's the way it really is). In one of the views, I'm calling @user.email_address.verified?, where the verified? method is indeed defined in the EmailAddress class. I've been working on a checkout of Edge Rails (rev. 5379), where this works, as it does in 1.1.6. When running 1.2RC, it gives a NoMethodError on verified? only AFTER the first time. So, it works once, but if you refresh the page, it dies. When I log the methods on the instance, the first time around, I get:

==, ===, =~, [], []=, id, send, `, after_create, after_destroy, after_save, after_update, after_validation, after_validation_on_create, after_validation_on_update, allow_concurrency, allow_concurrency=, attribute_names, attribute_present?, attributes, attributes=, attributes_before_type_cast, attributes_from_column_definition, attributes_from_column_definition_with_lock, b64encode, before_create, before_destroy, before_save, before_update, before_validation, before_validation_on_create, before_validation_on_update, blank?, build_registration_invitation, build_user, class, clear_aggregation_cache, clear_association_cache, clone, colorize_logging, colorize_logging=, column_for_attribute, configurations, configurations=, connection, copy_instance_variables_from, create, create_or_update, create_or_update_with_callbacks, create_registration_invitation, create_user, create_with_callbacks, create_with_timestamps, create_without_timestamps, daemonize, dclone, decode64, decode_b, decrement, decrement!, default_timezone, default_timezone=, destroy, destroy_with_callbacks, destroy_with_transactions, destroy_without_callbacks, destroy_without_transactions, display, dup, enable_warnings, encode64, eql?, equal?, errors, extend, extend_with_included_modules_from, extended_by, freeze, frozen?, generate_key, generate_read_methods, generate_read_methods=, has_attribute?, has_registration_invitation?, has_registration_invitation_with_deprecation?, has_registration_invitation_without_deprecation?, has_user?, has_user_with_deprecation?, has_user_without_deprecation?, hash, id, id=, id_before_type_cast, increment, increment!, initialize, initialize_with_callbacks, inspect, instance_eval, instance_exec, instance_of?, instance_values, instance_variable_get, instance_variable_set, instance_variables, is_a?, kind_of?, load, lock!, lock_optimistically, lock_optimistically=, locking_enabled?, logger, logger=, method, methods, new_record?, nil?, object_id, pluralize_table_names, pluralize_table_names=, primary_key_prefix_type, primary_key_prefix_type=, private_methods, protected_methods, public_methods, quote, quote_with_deprecation, quoted_id, readonly!, readonly?, record_timestamps, record_timestamps=, registration_invitation, registration_invitation=, registration_invitation?, registration_invitation_with_deprecation?, registration_invitation_without_deprecation?, reload, remove_subclasses_of, require, require_gem, require_gem_with_options, require_library_or_gem, respond_to?, respond_to_without_attributes?, returning, save, save!, save_with_transactions, save_with_transactions!, save_with_validation, save_with_validation!, save_without_transactions, save_without_transactions!, save_without_validation, save_without_validation!, schema_format, schema_format=, send, set_registration_invitation_target, set_user_target, silence_stderr, silence_stream, silence_warnings, singleton_methods, subclasses_of, suppress, table_name_prefix, table_name_prefix=, table_name_suffix, table_name_suffix=, taguri, taguri=, taint, tainted?, to_a, to_json, to_param, to_s, to_xml, to_yaml, to_yaml_properties, to_yaml_style, toggle, toggle!, transaction, type, unloadable, untaint, update, update_attribute, update_attribute_with_validation_skipping, update_attribute_without_validation_skipping, update_attributes, update_attributes!, update_with_callbacks, update_with_lock, update_with_timestamps, update_without_callbacks, update_without_timestamps, user, user=, user?, user_with_deprecation?, user_without_deprecation?, valid?, valid_with_callbacks?, valid_without_callbacks?, validate, validate_on_create, validate_on_update, verification_timeout, verification_timeout=, verified?, verify_key, verify_key!, with_options

You can clearly see the verified method there. The very next time through, I get:

==, ===, =~, [], []=, id, send, `, after_create, after_destroy, after_save, after_update, after_validation, after_validation_on_create, after_validation_on_update, allow_concurrency, allow_concurrency=, attribute_names, attribute_present?, attributes, attributes=, attributes_before_type_cast, attributes_from_column_definition, attributes_from_column_definition_with_lock, b64encode, before_create, before_destroy, before_save, before_update, before_validation, before_validation_on_create, before_validation_on_update, blank?, class, clear_aggregation_cache, clear_association_cache, clone, colorize_logging, colorize_logging=, column_for_attribute, configurations, configurations=, connection, copy_instance_variables_from, create, create_or_update, create_or_update_with_callbacks, create_with_callbacks, create_with_timestamps, create_without_timestamps, daemonize, dclone, decode64, decode_b, decrement, decrement!, default_timezone, default_timezone=, destroy, destroy_with_callbacks, destroy_with_transactions, destroy_without_callbacks, destroy_without_transactions, display, dup, enable_warnings, encode64, eql?, equal?, errors, extend, extend_with_included_modules_from, extended_by, freeze, frozen?, generate_read_methods, generate_read_methods=, has_attribute?, hash, id, id=, id_before_type_cast, increment, increment!, initialize, initialize_with_callbacks, inspect, instance_eval, instance_exec, instance_of?, instance_values, instance_variable_get, instance_variable_set, instance_variables, is_a?, kind_of?, load, lock!, lock_optimistically, lock_optimistically=, locking_enabled?, logger, logger=, method, methods, new_record?, nil?, object_id, pluralize_table_names, pluralize_table_names=, primary_key_prefix_type, primary_key_prefix_type=, private_methods, protected_methods, public_methods, quote, quote_with_deprecation, quoted_id, readonly!, readonly?, record_timestamps, record_timestamps=, reload, remove_subclasses_of, require, require_gem, require_gem_with_options, require_library_or_gem, respond_to?, respond_to_without_attributes?, returning, save, save!, save_with_transactions, save_with_transactions!, save_with_validation, save_with_validation!, save_without_transactions, save_without_transactions!, save_without_validation, save_without_validation!, schema_format, schema_format=, send, silence_stderr, silence_stream, silence_warnings, singleton_methods, subclasses_of, suppress, table_name_prefix, table_name_prefix=, table_name_suffix, table_name_suffix=, taguri, taguri=, taint, tainted?, to_a, to_json, to_param, to_xml, to_yaml, to_yaml_properties, to_yaml_style, toggle, toggle!, transaction, type, unloadable, untaint, update, update_attribute, update_attribute_with_validation_skipping, update_attribute_without_validation_skipping, update_attributes, update_attributes!, update_with_callbacks, update_with_lock, update_with_timestamps, update_without_callbacks, update_without_timestamps, valid?, valid_with_callbacks?, valid_without_callbacks?, validate, validate_on_create, validate_on_update, verification_timeout, verification_timeout=, with_options

Others on rails-talk have seen the same.

Attachments

dependencies.log (14.6 kB) - added by supernate on 12/09/06 07:24:39.
log file with dependices - requested by Ulysses
development.log (76.5 kB) - added by supernate on 12/11/06 07:11:53.
development log - after patch applied

Change History

11/28/06 20:26:01 changed by supernate

  • keywords set to NoMethodError TemplateError.

I have experienced this same problem on Rails 1.2 RC1 and can't pin down what is wrong. On the second request after a server restart, instance methods seem to disappear with no explanation. Even the association-based instance methods are not working. This effectively breaks my application because only one page loads before everything throws a NoMethodError or TemplateError. Strangely, I can't reproduce it in unit tests -- so all the models must be fine.

After rolling back to Rails 1.1.6, everything works just fine.

Tried using both Ruby 1.8.4 and 1.8.5 on Ubuntu Linux with the same result.

Development log:

ActionView::TemplateError (undefined method `containers' for #<HomePage:0xb724649c>) on line #1 of app/views/shared/page_of_wamlets.rhtml:
1: <%= render :partial => 'container/container', :collection => @page.containers %>
2: <%= javascript_tag "Droppables.add(\"canvas\", {
3:       onDrop: function(element){#{remote_function(:url => {:controller => 'container', :action => 'move'},
4:         :with => "'id=' + getContainerId(element.id) + '&xpos=' + parseInt(Element.getStyle(element,'left') || '0') + '&ypos=' +
parseInt(Element.getStyle(element,'top') || '0')",
/usr/local/lib/ruby/gems/1.8/gems/activerecord-1.14.4.5618/lib/active_record/associations/association_proxy.rb:123:in `send'
/usr/local/lib/ruby/gems/1.8/gems/activerecord-1.14.4.5618/lib/active_record/associations/association_proxy.rb:123:in `method_missing'
#{RAILS_ROOT}/app/views/shared/page_of_wamlets.rhtml:1:in `_run_rhtml_47app47views47shared47page_of_wamlets46rhtml'
#...

Here, in fact 'containers' is defined as an association in my app:

class Page < ActiveRecord::Base
  has_many :containers
  # ...
end

class HomePage < Page ; end

Also some strange output from Mongrel that I have never seen before:

#<NameError: cannot remove Object::LOCALIZED_STRINGS>
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:416:in `remove_const'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:416:in `send'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:416:in `remove_constant'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:270:in `remove_unloadable_constants!'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:270:in `each'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:270:in `remove_unloadable_constants!'
/usr/local/lib/ruby/gems/1.8/gems/activesupport-1.3.1.5618/lib/active_support/dependencies.rb:73:in `clear'
/usr/local/lib/ruby/gems/1.8/gems/rails-1.1.6.5618/lib/dispatcher.rb:63:in `reset_application!'
/usr/local/lib/ruby/gems/1.8/gems/rails-1.1.6.5618/lib/dispatcher.rb:115:in `reset_after_dispatch'
/usr/local/lib/ruby/gems/1.8/gems/rails-1.1.6.5618/lib/dispatcher.rb:51:in `dispatch'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/rails.rb:84:in `process'
/usr/local/lib/ruby/1.8/sync.rb:229:in `synchronize'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/rails.rb:83:in `process'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:580:in `process_client'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:579:in `each'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:579:in `process_client'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:686:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:686:in `initialize'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:686:in `new'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:686:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:673:in `initialize'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:673:in `new'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel.rb:673:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/configurator.rb:267:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/configurator.rb:266:in `each'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/configurator.rb:266:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/bin/mongrel_rails:127:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/lib/mongrel/command.rb:211:in `run'
/usr/local/lib/ruby/gems/1.8/gems/mongrel-0.3.13.4/bin/mongrel_rails:231
/usr/local/bin/mongrel_rails:18:in `load'
/usr/local/bin/mongrel_rails:18 

11/28/06 20:35:34 changed by jonathan_viney

This may be related to the stricter autoloading of dependencies in 1.2. Make sure you have one class per file and that all the files are named correctly, eg: MyClass => my_class.rb. Also, remove any unnecessary require statements that load classes that could be autoloaded, or if an explicit require is necessary, change them to require_dependency.

11/28/06 22:18:37 changed by bitsweat

  • keywords changed from NoMethodError TemplateError to NoMethodError TemplateError 1.2regression.
  • owner changed from David to Ulysses.

11/28/06 23:42:58 changed by supernate

After nearly an entire day trying to debug this, I discovered this in app/config/environments/development.rb

# In the development environment your application's code is reloaded on
# every request.  This slows down response time but is perfect for development
# since you don't have to restart the webserver when you make code changes.
config.cache_classes     = false

I changed it to

config.cache_classes     = true

... and it works again! No more strange NoMethodErrors on any request.

Now can somebody explain to me what has changed in 1.2 that requires this?

11/29/06 15:30:59 changed by mow

caching classes isn't really a solution unless you are accepting to restart your development server after each change ;-(

I had similar problems and found by activating debug output in activesupport/lib/active_support/dependencies.rb and studying the output that it had to do (in my case) with the inclusion of Magick (that is, I had require "RMagick' and include Magick in my controllers).

The (new) dependency code seems to have a problem when one loaded and included a c module. When I changed my code to leave out the `include Magick' (which means to use apropriate module prefixes when using RMagick-Classes), everything was fine again.

The problem can be seen by a test application with some model defining some function eg. class Item < ActiveRecord::Base def foo

'foo'

end

end

and a controller require 'RMagick' include Magick

class ItemsController < ApplicationController

def list

@item = Item.new

end

end

and a view accessing @item.foo.

The first request will be ok (no unloding so far), the second fails (undefined method `foo' for #<Item:0x40f784d4>).

See also my mail to the rails mailing list, e.g. http://comments.gmane.org/gmane.comp.lang.ruby.rails/109534

I guess it would be helpful if the other having this problem could check if they load and include c modules as well (doesn't need to be RMagick).

regards

Morus

11/30/06 00:06:02 changed by supernate

Morus, thanks for your suggestion.

I am using RMagick in my app, and I removed all includes like you suggested. I think the problem is not with RMagick though, but the Localization module in my case. I have include Localization at the top of application_helper.rb.

When I remove it, the localization method l doesn't work in my models anymore. I couldn't get it to work by including the module prefixes either. I'm not sure why.

I think this is a bug that the Localization module is not being unloaded if cached classes are off. Evidenced by my Mongrel log included in my first comment of this ticket.

For now, I am working around this problem by just turning on cached classes.

11/30/06 07:13:08 changed by mow

  • component changed from ActiveRecord to ActiveResource.

Ok, so probably I'm wrong blaming just c modules for that. Probably any included module breaks the dependency code.

I took a second look at dependencies.rb. One can fix the symptoms of the problem (though I doubt that this is a good fix) by catching errors in dependencies.rb method remove_constant (~line 405). If you change

  def remove_constant(const)
    return false unless qualified_const_defined? const
    
    names = const.split('::')
    if names.size == 1 || names.first.empty? # It's under Object
      parent = Object
    else
      parent = (names[0..-2] * '::').constantize
    end
    
    log "removing constant #{const}"
    parent.send :remove_const, names.last
    return true
  end

to

 def remove_constant(const)
    return false unless qualified_const_defined? const
    begin
      names = const.split('::')
      if names.size == 1 || names.first.empty? # It's under Object
        parent = Object
      else
        parent = (names[0..-2] * '::').constantize
      end
      
      log "removing constant #{const}"
      parent.send :remove_const, names.last
    rescue => err
      log err.inspect
    end
    return true
  end

the problem goes away (for me). The error catched (and ignored) is something like #<NameError: cannot remove Object::OrQuantumOperator> which is understandable, since OrQuantumOperator is a constant in the Magick module, not in object.

But I guess the deeper cause of this problem is somewhere in the object loading logic, where these constants are tracked and added to the autoloaded_constants array. I don't understand enough of that. I hope some dependecy guru will have a look at this and fix it properly.

I changed the component to 'ActiveResource', since I'm pretty sure, that the problems arise there.

regards

Morus

12/09/06 05:21:34 changed by ulysses

(In [5710]) Fix remove_constant to correctly handle constant names of the form "::A::...". References #6720.

(follow-up: ↓ 10 ) 12/09/06 05:24:57 changed by ulysses

The above change might have solved your issue Morus. Please let me know if so. If the issue remains, then I'm going to need some more help in tracking it down. Perhaps one of you could set Dependencies.log_activity = true to provide more information to guide me?

Thanks, Ulysses

12/09/06 07:24:39 changed by supernate

  • attachment dependencies.log added.

log file with dependices - requested by Ulysses

(in reply to: ↑ 9 ) 12/09/06 07:27:22 changed by supernate

Ulysses,

I turned on Dependencies logging, and uploaded my log file as an attachment .

Let me know if I can help any more.

Nate

(follow-up: ↓ 13 ) 12/10/06 21:18:53 changed by ulysses

Hmm, I still haven't been able to reproduce. Any chance you could apply this patch and paste the logs again?

Index: activesupport/lib/active_support/dependencies.rb
===================================================================
--- activesupport/lib/active_support/dependencies.rb	(revision 5710)
+++ activesupport/lib/active_support/dependencies.rb	(working copy)
@@ -403,7 +403,11 @@
   end
   
   def remove_constant(const)
-    return false unless qualified_const_defined? const
+    log_call(const)
+    unless qualified_const_defined? const
+      log "qualified_const_defined? #{const.inspect} => false"
+      return false
+    end
     
     const = $1 if /\A::(.*)\Z/ =~ const.to_s
     names = const.split('::')
@@ -413,7 +417,7 @@
       parent = (names[0..-2] * '::').constantize
     end
     
-    log "removing constant #{const}"
+    log "removing constant #{const} from #{parent}"
     parent.send :remove_const, names.last
     return true
   end

Thanks! :-)

12/11/06 07:11:53 changed by supernate

  • attachment development.log added.

development log - after patch applied

12/11/06 07:14:57 changed by supernate

Ulysses,

I applied the patch and uploaded the newest log here This is the full detail log of two GET requests, both to HomeController#index. The first request runs fine, but the second request quickly fails. I hope this is enough detail to pin down the problem.

My workaround has been to keep cached classes turned on, then the problem goes away.

(in reply to: ↑ 11 ) 12/11/06 08:05:22 changed by mow

Replying to ulysses:

Hmm, I still haven't been able to reproduce. Any chance you could apply this patch and paste the logs again?

Unfortunately it does not help. The remove constant is called with a simple name, no prefix. E.g. SubtractCompositeOp. So there is no chance to see if that's a constant in object or not at this point (at least not from the name).

Adding your changes and extending the last log in remove_constant a bit further (original name is a copy of the argument to remove_constanct) I get

Dependencies: called remove_constant("SubtractCompositeOp")

Dependencies: removing constant SubtractCompositeOp from Object (original name SubtractCompositeOp)

(If you think another full log would be helpful let me know)

If I look at the problem in irb doing a

require 'RMagick'
include Magick

Object has a constant 'SubtractCompositeOp':

Object.constants.index('SubtractCompositeOp')
=> 176
Object.const_defined? 'SubtractCompositeOp'
=> true

But I cannot unload it:

Object.send :remove_const, "SubtractCompositeOp"
NameError: cannot remove Object::SubtractCompositeOp
        from (irb):4:in `remove_const'
        from (irb):4:in `send'
        from (irb):4

If I try to unload it from magick, it works:

Magick.send :remove_const, "SubtractCompositeOp"
=> SubtractComposit=50
Object.constants.index('SubtractCompositeOp')
=> nil

Unfortunately I don't know how to tell, whether some constant stems from an included module or not. Except looking through all included modules and looking at the constants defined there:

      parent.included_modules.each do | mod |
        mod.send :remove_const, names.last if mod.const_defined? names.last
      end
      parent.send :remove_const, names.last if parent.const_defined? names.last

instead of

parent.send :remove_const, names.last

does work for me. Don't know what the performance impact is (probably one could return on the first constant removed).

12/18/06 14:18:59 changed by mislav

  • cc set to mislav.

12/24/06 14:53:36 changed by ulysses

(In [5782]) Update load once paths to prevent nested once constants from being detected and claimed by an external non-once load. References #6720

(follow-up: ↓ 17 ) 12/31/06 18:28:40 changed by david

  • status changed from new to closed.
  • resolution set to fixed.

(in reply to: ↑ 16 ; follow-up: ↓ 18 ) 01/08/07 07:21:42 changed by supernate

  • status changed from closed to reopened.
  • resolution deleted.

This is not fixed for me. If I turn cached classes off in development mode, I am still getting the exact same behavior as stated before.

I am now running on Edge rails revision 5874

(in reply to: ↑ 17 ) 01/08/07 09:29:31 changed by mow

Replying to supernate:

This is not fixed for me. If I turn cached classes off in development mode, I am still getting the exact same behavior as stated before. I am now running on Edge rails revision 5874

I can confirm that. My testcase in comment #5 still fails.

01/08/07 22:43:08 changed by ulysses

  • status changed from reopened to closed.
  • resolution set to fixed.

Closing since this is a different issue and deserves a new ticket. (And I'm tired of scrolling.)

Please see #6951 -- is that an accurate description?

03/04/07 13:47:19 changed by bitsweat

  • keywords changed from NoMethodError TemplateError 1.2regression to NoMethodError TemplateError.
  • milestone changed from 1.2 to 1.2 regressions.