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

Ticket #844 (closed defect: duplicate)

Opened 4 years ago

Last modified 3 years ago

introspection/reflection fails due to name conflict

Reported by: anonymous Assigned to: David
Priority: high Milestone: 1.0
Component: ActiveRecord Version: 0.13.0
Severity: normal Keywords: scaffolding, reflection, dynamic, introspection
Cc:

Description

I very consistently got this error, and could not at first figure out why it occured.

 Showing usr/local/lib/ruby/gems/1.8/gems/actionpack-1.5.1/lib/action_controller/templates/scaffolds/list.rhtml where line #13 raised wrong number of arguments

10: <% for entry in instance_variable_get("@#{@scaffold_plural_name}") %>
11:   <tr>
12:   <% for column in @scaffold_class.content_columns %>
13:     <td><%= entry.send(column.name) %></td>
14:   <% end %>
15:     <td><%= link_to "Show", :action => "show#{@scaffold_suffix}", :id => entry.id %></td>
16:     <td><%= link_to "Edit", :action => "edit#{@scaffold_suffix}", :id => entry.id %></td>

Template Trace Top

(erb):13:in `system'
(erb):13:in `send'
(erb):13:in `evaluate_locals'

The spurious call to system was the key. The database in use had a column labeled 'system', and the Kernel#system was being called via the send call. I verified that using any of the following names in a database with scaffolding causes errors/malfunctions/misrenderings like this:

abort, atExit, autoload, binding, callcc, caller, catch, chomp, chop, eval, exec, exit, fail, fork, format, gets, globalVariables, gsub, lambda, load, localVariables, loop, open, p, print, printf, proc, putc, puts, raise, rand, readline, readlines, require, scan, select, setTraceFunc, singletonMethodAdded, sleep, split, sprintf, srand, sub, syscall, system, test, throw, traceVar, trap, untraceVar

It's likely that all the methods in Object, Module, and definitely anything dynamically 'include'd into Object will fall under this bug.

The worst case being a column called 'sleep', which puts the webserver to sleep forever. Other bad cases include rand, which generates a mysterious 1 or 0 in the output without other error; all io calls that don't have to have arguments either block, or generate randomish data; and fail, raise, exit which generate errors which have nothing to do with the actual error.

I'm sure that this interferes with other reflection/introspection performed in ActiveRecord, but right now do not have specific other problems.

Change History

03/15/05 17:21:01 changed by david

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

03/15/05 20:20:02 changed by anonymous

  • status changed from closed to reopened.
  • resolution deleted.
  • summary changed from scaffolding fails due to name conflict to introspection/reflection fails due to name conflict.

Ticket #556 does not work for the above cases. (It is correct in all other cases.) The send method sees the def included by the module. The attribute is shadowed by a coincidental module include. We want the dynamic attribute in those cases.

Dynamic attributes fail as well. For instance, in controller code @model.system will fail -- giving wrong number of aguments. It should have returned the column datum corresponding to the introspected 'system' column, but instead tries calling Kernel#system.

However, if I have provided a 'system' method for my model. It should use that instead of the introspected 'system' column.

03/25/05 19:12:01 changed by anonymous

  • version changed from 0.10.0 to 0.11.0.

Changing the scaffold templates to use "[column.name]" instead of "send(column.name)" fixes this, at least for columns named 'time' and 'loop'.

This is a nasty obstacle for new users/potential adopters (like myself), highly mysterious, and seemingly trivial to fix, so perhaps deserves higher priority.

A fix requires changing actionpack-1.6.0/lib/templates/scaffolds/list.rhtml and show.rhtml.

03/25/05 19:45:07 changed by anonymous

To fix fully also requires an analagous change to action_view/helpers/form_helper.rb, line 246 (value method):

def value
    object[@method_name] unless object.nil
end

Most likely the name clash results from some subtlety of ruby that I don't know, as I've only been a user for one day - but if this is in fact the right way to fix it, there appear to be several other places where it is necessary. In any case, works for me...

04/01/05 19:57:30 changed by anonymous

neither "send(name)" nor "[name]" are correct for all cases. The original post identifies why and when either will fail. A possible fix is for the ActiveRecord::Base to wipe itself of all methods, using Modle#undef_method or Module#remove_method. Here is a pseduo code outline of the fix.

#open and close the class def
#get the class object (klass)
#foreach symbol in klass { undef or remove}
#reopen the class def
#all the original code

04/12/05 02:08:20 changed by nzkoz

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

I think that #751 and #752 cover this scenario, if you disagree just reopen the bug, set the milestone to 1.0 and leave a comment explaining why

04/12/05 05:13:22 changed by anonymous

  • keywords changed from scaffolding to scaffolding, reflection, dynamic, introspection.
  • status changed from closed to reopened.
  • resolution deleted.
  • milestone set to 1.0.

#751 and #752 do cover this situation, however the proposed solution while simple -- cut and paste out of my original post the list of forbidden names -- is eliminating RAILS as an option to older applications.

Some of the forbidden names "system", "test", "split", and particularly "caller" and "exit", are used in existing DA's that would have benefited from being ported to RAILS. I have 3 DA's that could not use RAILS (in its current state) because of usage of one or more of the forbidden names. The RAILS interface has to coexist peacfully with an older interface, so the column names cannot be changed. The solution I provided above of undef-ing the model's methods should be adequate, and I will be testing this patch on my own systems. (Double underscore methods should be preserved, so send and id will still work).

Perhaps the worst feature of #751 and #752's solution is that Ruby is a dynamic language and anyone can add new methods to Object or Kernel. When a new version of Ruby appears there might be new methods in Object and Kernel. Every library that is loaded, might add a method in Object or Kernel. Or even a new version of an existing library might suddenly with that version add a method. This will break existing existing applications that now shadow the new method names. (Even RubyGems adds methods to Kernel, so potentially only the RubyGems-installations might suddenly stop working, a nasty situation indeed).

05/03/05 03:54:10 changed by anonymous

FYI - this is a royal pain of a bug. I'm new to RoR and setup a table named 'quotes' and found myself trying to figure out what on earth was wrong for 2 hrs. This needs a fix.

07/11/05 02:25:12 changed by anonymous

  • priority changed from normal to high.
  • version changed from 0.11.0 to 0.13.0.

Just as an update. #1663 show that the GLUE module is included in the root namespace, so all the methods names of the GLUE module are now forbidden names as well. The longer we wait to use the provided solution or an equivalent the more names that become forbidden. I'm upping the priority as more method names get included into the namespace, more application might break, more applications can't get built directly, and more confusion ensues as the likelihood that a clash occurs that returns wrong results, instead of clean exiting or dirty crashing.

#In the ActiveRecord::Base file

class ActiveRecord::Base; end

method_array = [] + ActiveRecord::Base.public_methods 
                  + ActiveRecord::Base.protected_methods
                  + ActiveRecord::Base.private_methods

# preserve __methods, such as __send__ and __id__ and __init__
method_array.delete_if { |method| (/^__/ =~ method.to_s)!= nil }
# other methods can be excluded if necessary

method_array.each { |method| ActiveRecord::Base.class.undef_method(method.to_sym) }

class ActiveRecord::Base
# all the old code, possibly modified to use __send__ if necessary
end

11/03/05 18:57:33 changed by leslie@camary.co.za

I had a table called "property" and my App worked great until I uploaded to TextDrive. It took me two days of searching, logging, modifying, rewriting, etc. to find these pages and finally figure out what was going on.

What I don't understand is how the Glue module gets included when my Rails app runs, can someone explain that? If I load irb, it's not there, but if I load a script/console, it's there, polluting the namespace. What loads it?

Perhaps there could be something that runs when the rails app first starts and checks if all the models have ActiveRecord::Base in their ancestors? A log entry saying "warning: this name is taken" would have saved me two days.

11/15/05 09:24:08 changed by bitsweat

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

Superseded by #752.