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.