views:

617

answers:

4

I'm having a very strange problem. I'm getting a collection of Rails ActiveRecord models back from the database but the first model in the collection does not have the model attributes/methods, just the standard ActiveRecord base methods. The rest have all the attributes. And it's only doing this on my production debian server using Passenger. It works find on OS X and cygwin.

Any ideas?

Thanks, Kevin

A: 

It could be bad data in your production database.

I would turn up your log level (the production environment does not log SQL queries), find the query being generated, and then open up your production database and run the query and see what it returns.

Luke Francl
@Luke, bad data in the DB should not and will not affect individual object attribute accessor methods. These are welded to the class, and affect models in an all-or-nothing way.
vladr
Good point. I thought perhaps the attributes were returning nil, but if they're simply not there...That's a different story.
Luke Francl
@Luke, actually I take that back for Rails 2.2... looking at the 2.2 source code it appears it actually checks against not the columns, but the columns that were actually find'ed (pardon my french) from the db.
vladr
True, but if the columns are in the result set for one row they would be in the result set for all the rows, wouldn't they? I think you are still correct.
Luke Francl
We don't know how (form what sources) Kevin is building his @records; I'm not too sure if the "first record is different" is still a problem, we'll see
vladr
A: 

It's hard to be certain about this without doing some debugging, but one thing I might consider investigating is threading issues. ActiveRecord, I believe, defines these methods on the fly as they're needed, and it's possible that you're accessing the first model while it's in the process of doing so. Similar issues can arise with Ruby's "require" statement - if you require something in one thread, it's possible that the class won't be fully defined yet when you try to access it in another thread.

Greg Campbell
A: 

Hello,

When you say that the first model in the collection does not have the model attributes/methods, is it because you get a NoMethodError exception when you attempt to invoke one of them?

It is normal for a model object not to have the attribute methods (e.g. when dumping the output of object.methods) until you attempt to actually call them. When you attempt to call one of the missing methods in question, ActiveRecord's method_missing handler is triggered and, provided that the method name matches the name of one of the columns defined in the database, the method will be created dynamically and no exception will be risen. If that does not happen it is either because the object is of the wrong class (does not match the model you are expecting to be operating on), or because a plugin or gem is interfering by misbehaving in the method_missing handling chain.

Can you dump into the logs (logger.info or logger.info, depending on your log level) what the attribute-less object instance's self.class.name equal to in? Can you also perform an audit of the gems (and corresponding versions) installed in production vs. locally?

Cheers, V.

vladr
Okay, that makes sense that the first record does not yet have the methods/attributes until it is called. I'm using a aggregating class that has a collection of activerecord models and calling a method and summing up the results. See below in next comment
Kevin Colyar
Kevin Colyar
sanity check: can you stick in there a logger call to validate the type of record and whether the record actually has the relevant column?
vladr
@records.inject(0) {|sum, record| logger.info("#{record.class}: #{record.columns.join(',')}"); sum + (eval("record.#{method.to_s}") || 0) }
vladr
I couldn't do that for the record itself but I could for the class: MyClass.column_names and it has all the columns I need.I am running ruby 1.8.5 on my debian production server. It's just strange that it's just two attributes it's puking on.
Kevin Colyar
I did make it so i'm doing a send(method) instead of eval-ing and now I'm getting a ActiveRecord::MissingAttributeError.
Kevin Colyar
@Kevin, you must really make sure that all your items in @records are of the same class. Also, are they per-chance polymorphic or not (do you have subclasses for the type that @records members should be? is there a class_name column in there?)
vladr
can you add the modified logger code and share the result? logger.info("#{record.class}: #{record.class.columns.join(',')}"); sum + ...
vladr
Class: MonthlyConnectedLoadRevenue Columns: id,report_date,new_residential_quantity,...Class: MonthlyConnectedLoadRevenue Columns: (same as above)Class: MonthlyConnectedLoadRevenue Columns: (same as above)Class: MonthlyConnectedLoadRevenue Columns: (same as above)...
Kevin Colyar
Nope, no polymophic classes here. Would there be some reason those attributes wouldn't be selected in a find?
Kevin Colyar
..and I assume that the problematic attributes *are* listed in the output above (id,report_date,new_residential_quantity,...) Even if the attributes were not selected, the accessors should exist because they are based on <model>.class.columns (IIRC ActiveRecord's source code)
vladr
BTW, looking at the Rails code now to see under which conditions, exactly, is the exception risen - what Rails version do you use? 2.2? 2.1.x?
vladr
OK, in Rails 2.2 it *will* actually raise the exception if an attribute is not selected in a find, as it uses a model's @attributes (not model.class's @columns) to check whether a missing accessor is kosher or not. Can you see the generated SQL queries and see if the missing attributes are SELECTed?
vladr
Also, what are the names of the mysteriously missing attribute names? Just curious... And I don't suppose you use the :select option in your relationship definition and/or find statement used to populate @records?
vladr
BTW, instead of __send__'ing, you can just do "sum + (record.read_attribute(method) || 0)" -- this won't crash, but you still have a functional issue if data is not retrieved from the DB
vladr
Using rails 2.1.0. I'm not using :select, just a find(:all, :conditions => [some conditions]). Offending columns are conversion_wall_heater_quantity and new_residential_electric_heat_revenue.
Kevin Colyar
Okay, I've figured out my problem. The attribute names on the records are being truncated to 30 characters, yet when I call ClassName.column_names I get the full attribute names back. I'm thinking it's a odbc problem on the production server.
Kevin Colyar
A: 

The problem was that I wan't using the correct freetds version for SQL Server 2005. I was using an old protocol version that didn't support column names over 30 characters, which was why it couldn't find the attribute I was calling. It was being truncated. Changing it in my freetds.conf fixed the problem. Thanks Vlad for all your troubleshooting help.

Kevin Colyar
You're welcome. :)
vladr