My report.rb class contains the following code:
def create
@start_term = ::SchoolTerm.find(1705265)
@end_term = ::SchoolTerm.currently_enrolling
current = 0
total = all_terms.size
@terms = {}
all_terms.each do |t|
@terms[t.id] = Business::Sales::RevenueByWeek::Term.new(t)
Rails.logger.info("#{@terms} is @terms in report#create")
current += 1
self.progress = 100.0 * (Float(current) / Float(total))
end
end
def all_terms
::SchoolTerm.between(@start_term, @end_term) - RevenueGoal.terms_without
end
def each
Rails.logger.info("#{@terms} is @terms in report.each")
all_terms.each do |t|
yield @terms[t.id] if @terms[t.id]
end
end
开发者_Go百科
The logger line inside create shows that @terms has the correct values while looping through different school terms. However, the logger line in the each method shows @terms as empty. When I try to load up the report, I get this error:
You have a nil object when you didn't expect it!
You might have expected an instance of ActiveRecord::Base.
The error occurred while evaluating nil.[]
Extracted source (around line #24):
</tr>
</thead>
<% report.each do |term| %>
<tbody class="term_<%= term.id %>">
<tr class="term">
<th><%= term.term %></th>
I have verified that Business::Sales::RevenueByWeek::Term.new returns the correct data. Any ideas why I'm getting this nil object error? I thought that instance variables retained their values, but somehow this one is getting lost.
Thanks.
I'm assuming report.rb
is an active record model. In that framework, #create
isn't meant to be a constructor, it only creates rows in the underlying table. I see you set @terms = {}
there, however.
If your report instance is queried out of the database, #create
is never run, and so your member is never initialized.
I suggest you hide uses of @terms
(even internal ones) behind a method named terms
(that is, unless you've already declared it as an AR attribute, or similar.) Your method can lazy-initialize the member as needed.
report.rb is not an active record model - no database tables are used. The create method is used to create an instance of the report, with cascading calls to other classes needed to get the values for the report, such as the Term.new call.
After the create method finishes, a view is rendered - the error above comes from one of the partials in that view.
I have placed various logger calls in the create method to verify that it is indeed getting called and that the @terms variable in the create method is what I expect it to be. What I can't figure out is why the filled-in @terms hash isn't available in the each method. The create method logger calls show up before the each method logger calls, and @terms is okay in create but empty or nil in each. Shouldn't @terms retain its value for the whole instance - and do it without hiding things in another method?
I solved the problem. What was happening was that the @terms variable was supposed to be written to a cache. We are using memcached, and the @terms data was more than 1 MB is size, so the Cache.write statement was failing. Then when @terms needed to be pulled from the cache in the 'each' method, there was nothing there. The quick-and-dirty solution was to cut down the size of the @term data so that the Cache.write was successful. A long term solution is to replace memcached with Resque, which has no such limitations on the size of the data corresponding to one key.
精彩评论