association_proxy does not allow AR instance to rescue NoMethodError

Hey guys,

I'm using method_missing in one of my AR models, like this:

class Item < ActiveRecord::Base   # Helper for finding properties, eg item.size   def method_missing(symbol, *args)     super   rescue NoMethodError => e     eval("properties.find_#{symbol}") || raise(NoMethodError, e)   end end

Appears that when NoMethodError is raised by association_proxy, it can longer be rescued. This happens when rendering a view (trace below).

Odd thing is that it works from script/console:

Cart.first.items.first.size

=> #<Property id: 2, property_type_id: 1, position: 4, code: "01", g1_code: "01", name: "small", webname: "small", description: nil>

Here's the trace:

Processing CartController#show (for 127.0.0.1 at 2008-10-17 09:24:29) [GET]   Session ID: BAh7DDoMY2FydF9pZGltOhB3aXNobGlzdF9pZEY6DnJldHVybl90byILL2FkbWluOgxjc3JmX2lkIiVhNmI0MDcyZjYwZWU5YzRjZGYzNjY0ZWY1NmQyMmE1NzoLbG9jYWxlIgplbi1BVSIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7ADoMdXNlcl9pZDA=--98880aad2d821bfad6d0e05eecf321bece536d02   Parameters: {"action"=>"show", "controller"=>"cart"}   SQL (0.1ms) SET NAMES 'utf8'   SQL (0.1ms) SET SQL_AUTO_IS_NULL=0   SQL (0.1ms) SET NAMES 'utf8'   SQL (0.1ms) SET SQL_AUTO_IS_NULL=0   User Columns (2.9ms) SHOW FIELDS FROM `users`   User Load (0.8ms) SELECT * FROM `users` WHERE (`users`.`remember_token` = '2e11465956f10ef80e1e1f156d19b834b782aef0') LIMIT 1   Cart Columns (3.1ms) SHOW FIELDS FROM `item_collections`   Cart Load (0.4ms) SELECT * FROM `item_collections` WHERE (`item_collections`.`id` = 104) AND ( (`item_collections`.`type` = 'Cart' ) )   User Load (0.3ms) SELECT * FROM `users` WHERE (`users`.`id` IS NULL) LIMIT 1   CACHE (0.0ms) SELECT * FROM `users` WHERE (`users`.`remember_token` = '2e11465956f10ef80e1e1f156d19b834b782aef0') LIMIT 1   LineItem Columns (3.2ms) SHOW FIELDS FROM `line_items`   SQL (0.5ms) SELECT count(*) AS count_all FROM `line_items` WHERE (`line_items`.item_collection_id = 104)   CACHE (0.0ms) SELECT count(*) AS count_all FROM `line_items` WHERE (`line_items`.item_collection_id = 104) Rendering template within layouts/application Rendering cart/show   LineItem Load (0.5ms) SELECT * FROM `line_items` WHERE (`line_items`.item_collection_id = 104) ORDER BY line_items.id   Item Columns (2.9ms) SHOW FIELDS FROM `items`   Item Load (0.4ms) SELECT * FROM `items` WHERE (`items`.`id` = 6016)   Clothing Columns (2.4ms) SHOW FIELDS FROM `clothing`   Clothing Load (0.4ms) SELECT * FROM `clothing` WHERE (`clothing`.`id` = 27)   Style Columns (2.5ms) SHOW FIELDS FROM `styles`   Style Load (0.4ms) SELECT * FROM `styles` WHERE (`styles`.`id` = 1)   FabricColour Columns (2.3ms) SHOW FIELDS FROM `fabric_colours`   FabricColour Load (0.3ms) SELECT * FROM `fabric_colours` WHERE (`fabric_colours`.`id` = 189)   Fabric Columns (2.4ms) SHOW FIELDS FROM `fabrics`   Fabric Load (0.4ms) SELECT * FROM `fabrics` WHERE (`fabrics`.`id` = 50)   Colour Columns (2.2ms) SHOW FIELDS FROM `colours`   Colour Load (0.3ms) SELECT * FROM `colours` WHERE (`colours`.`id` = 29)

ActionView::TemplateError (NoMethodError) on line #6 of app/views/ line_items/_line_item.html.haml: 3: %td.top{:style => "padding:15px 5px;"} 4: -# display the style, colour and size icons if this is clothing 5: - if line_item.clothing? 6: = item_icons_table line_item.item 7: - else 8: = cart_icon line_item.item, "small" 9: %td.top{:style => "padding-top:10px"}

    vendor/rails/activerecord/lib/active_record/associations/ association_proxy.rb:209:in `method_missing'     app/helpers/cart_helper.rb:34:in `item_icons_table'     app/views/line_items/_line_item.html.haml:6     vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb: 313:in `fields_for'     app/views/line_items/_line_item.html.haml:1     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `send'     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `render'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 20:in `render'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 26:in `benchmark'     vendor/rails/activesupport/lib/active_support/core_ext/ benchmark.rb:8:in `realtime'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 26:in `benchmark'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 19:in `render'     vendor/rails/actionpack/lib/action_view/template.rb:73:in `render_template'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 45:in `render_partial'     vendor/rails/actionpack/lib/action_view/partials.rb:184:in `render_partial_collection'     vendor/rails/activerecord/lib/active_record/associations/ association_collection.rb:353:in `method_missing_without_paginate'     vendor/rails/activerecord/lib/active_record/associations/ association_proxy.rb:212:in `method_missing'     vendor/rails/activerecord/lib/active_record/associations/ association_proxy.rb:212:in `map'     vendor/rails/activerecord/lib/active_record/associations/ association_proxy.rb:212:in `send'     vendor/rails/activerecord/lib/active_record/associations/ association_proxy.rb:212:in `method_missing'     vendor/rails/activerecord/lib/active_record/associations/ association_collection.rb:353:in `method_missing_without_paginate'     vendor/plugins/will_paginate/lib/will_paginate/finder.rb:164:in `method_missing'     vendor/rails/actionpack/lib/action_view/partials.rb:179:in `render_partial_collection'     vendor/rails/actionpack/lib/action_view/partials.rb:159:in `render_partial'     vendor/rails/actionpack/lib/action_view/base.rb:268:in `render_without_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:5:in `render'     vendor/plugins/haml/lib/haml/helpers.rb:57:in `non_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:5:in `render'     app/views/item_collections/_item_collection.html.haml:16     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `send'     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `render'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 20:in `render'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 26:in `benchmark'     vendor/rails/activesupport/lib/active_support/core_ext/ benchmark.rb:8:in `realtime'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 26:in `benchmark'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 19:in `render'     vendor/rails/actionpack/lib/action_view/template.rb:73:in `render_template'     vendor/rails/actionpack/lib/action_view/renderable_partial.rb: 45:in `render_partial'     vendor/rails/actionpack/lib/action_view/partials.rb:152:in `render_partial'     vendor/rails/actionpack/lib/action_view/base.rb:268:in `render_without_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:5:in `render'     vendor/plugins/haml/lib/haml/helpers.rb:57:in `non_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:5:in `render'     app/views/cart/show.html.haml:3     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:157:in `call'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:157:in `form_for'     vendor/plugins/haml/lib/haml/helpers.rb:392:in `call'     vendor/plugins/haml/lib/haml/helpers.rb:392:in `haml_bind_proc'     vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb: 313:in `fields_for'     vendor/rails/actionpack/lib/action_view/helpers/form_helper.rb: 253:in `form_for_without_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:161:in `form_for'     app/views/cart/show.html.haml:1     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `send'     vendor/rails/actionpack/lib/action_view/renderable.rb:34:in `render'     vendor/rails/actionpack/lib/action_view/template.rb:73:in `render_template'     vendor/rails/actionpack/lib/action_view/base.rb:266:in `render_without_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:6:in `render'     vendor/rails/actionpack/lib/action_view/base.rb:372:in `_render_with_layout'     vendor/rails/actionpack/lib/action_view/base.rb:260:in `render_without_haml'     vendor/plugins/haml/lib/haml/helpers/action_view_mods.rb:6:in `render'     vendor/rails/actionpack/lib/action_controller/base.rb:1127:in `render_for_file'     vendor/rails/actionpack/lib/action_controller/base.rb:886:in `render_without_benchmark'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 51:in `render_without_trace_View____determine_metric_path__Rendering'     vendor/rails/activesupport/lib/active_support/core_ext/ benchmark.rb:8:in `realtime'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 51:in `render_without_trace_View____determine_metric_path__Rendering'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 118:in `render'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 38:in `trace_method_execution'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 117:in `render'     vendor/rails/actionpack/lib/action_controller/base.rb:858:in `render_without_benchmark'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 51:in `render_without_trace_View____determine_metric_path__Rendering'     vendor/rails/activesupport/lib/active_support/core_ext/ benchmark.rb:8:in `realtime'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 51:in `render_without_trace_View____determine_metric_path__Rendering'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 118:in `render'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 38:in `trace_method_execution'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 117:in `render'     vendor/rails/actionpack/lib/action_controller/base.rb:1179:in `default_render'     vendor/rails/actionpack/lib/action_controller/base.rb:1185:in `perform_action_without_filters'     vendor/rails/actionpack/lib/action_controller/filters.rb:617:in `call_filters'     vendor/rails/actionpack/lib/action_controller/filters.rb:610:in `perform_action_without_benchmark'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 68:in `perform_action_without_rescue'     /opt/local/lib/ruby/1.8/benchmark.rb:293:in `measure'     vendor/rails/actionpack/lib/action_controller/benchmarking.rb: 68:in `perform_action_without_rescue'     vendor/rails/actionpack/lib/action_controller/rescue.rb:136:in `perform_action_without_caching'     vendor/rails/actionpack/lib/action_controller/caching/sql_cache.rb: 13:in `perform_action_without_newrelic_trace'     vendor/rails/activerecord/lib/active_record/connection_adapters/ abstract/query_cache.rb:48:in `cache'     vendor/rails/activerecord/lib/active_record/query_cache.rb:8:in `cache'     vendor/rails/actionpack/lib/action_controller/caching/sql_cache.rb: 12:in `perform_action_without_newrelic_trace'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/instrumentation/ action_controller.rb:39:in `perform_action'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 38:in `trace_method_execution'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/instrumentation/ action_controller.rb:22:in `perform_action'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 38:in `trace_method_execution'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/instrumentation/ action_controller.rb:16:in `perform_action'     vendor/rails/actionpack/lib/action_controller/base.rb:533:in `send'     vendor/rails/actionpack/lib/action_controller/base.rb:533:in `process_without_filters'     vendor/rails/actionpack/lib/action_controller/filters.rb:606:in `process_without_session_management_support'     vendor/rails/actionpack/lib/action_controller/ session_management.rb:134:in `sass_old_process'     vendor/plugins/haml/lib/sass/plugin/rails.rb:19:in `process'     vendor/rails/actionpack/lib/action_controller/base.rb:401:in `process'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:179:in `handle_request'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:107:in `dispatch_unlocked'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:120:in `dispatch'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:119:in `synchronize'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:119:in `dispatch'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:129:in `dispatch_cgi'     vendor/rails/actionpack/lib/action_controller/dispatcher.rb:36:in `dispatch_without_trace_Rails_HTTP_Dispatch'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 126:in `dispatch'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 38:in `trace_method_execution'     vendor/plugins/newrelic_rpm/lib/newrelic/agent/method_tracer.rb: 125:in `dispatch'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ rails.rb:76:in `process'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ rails.rb:74:in `synchronize'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ rails.rb:74:in `process'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 159:in `process_client'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 158:in `each'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 158:in `process_client'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 285:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 285:in `initialize'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 285:in `new'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 285:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 268:in `initialize'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 268:in `new'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel.rb: 268:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ configurator.rb:282:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ configurator.rb:281:in `each'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ configurator.rb:281:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails: 128:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/lib/mongrel/ command.rb:212:in `run'     /opt/local/lib/ruby/gems/1.8/gems/mongrel-1.1.5/bin/mongrel_rails: 281     vendor/rails/activesupport/lib/active_support/dependencies.rb: 141:in `load_without_new_constant_marking'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 141:in `load'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 507:in `new_constants_in'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 141:in `load'     vendor/rails/railties/lib/commands/servers/mongrel.rb:64     /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'     /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 148:in `require'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 507:in `new_constants_in'     vendor/rails/activesupport/lib/active_support/dependencies.rb: 148:in `require'     vendor/rails/railties/lib/commands/server.rb:49     /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require'     /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'     script/server:3

Rendered /Users/zubin/ww/g2/vendor/rails/actionpack/lib/ action_controller/templates/rescues/_trace (63.0ms) Rendered /Users/zubin/ww/g2/vendor/rails/actionpack/lib/ action_controller/templates/rescues/_request_and_response (1.8ms) Rendering /Users/zubin/ww/g2/vendor/rails/actionpack/lib/ action_controller/templates/rescues/layout.erb (internal_server_error)

Hey guys,

I'm using method_missing in one of my AR models, like this:

class Item < ActiveRecord::Base # Helper for finding properties, eg item.size def method_missing(symbol, *args)    super rescue NoMethodError => e    eval("properties.find_#{symbol}") || raise(NoMethodError, e) end end

Just a minor stylistic nitpick, you should really avoid rescuing and re-raising that exception like that, check ahead of time.

Appears that when NoMethodError is raised by association_proxy, it can longer be rescued. This happens when rendering a view (trace below).

Odd thing is that it works from script/console:

Cart.first.items.first.size

=> #<Property id: 2, property_type_id: 1, position: 4, code: "01", g1_code: "01", name: "small", webname: "small", description: nil>

Can you reproduce this breakage in a stand-alone application or as a patch the the rails unit tests? It's pretty hard for me to tell what's going on based on this trace and this email.

Did you remembert to override respond_to? when you overrode method_missing? If those two don't align there's no guarantee you'll get the behaviour you expect.