Error when updating with an image file

NOOB ALERT

Now I warned you, here’s the problem that’s been getting me mad for hours

I’ve got a model for content I’m calling “lead”. It has a title, a text and an image used as a background.

I’m not using paperclip or CarrierWave to handle the image file (and I don’t want to, one of my needs here is to learn how this all works).

I’m using this function to upload the file (it comes directly from the rails guides) :

def upload

if params[:lead][:illustration]

  uploaded_io = params[:lead][:illustration]

  File.open(Rails.root.join('app', 'assets', 'images', uploaded_io.original_filename), 'wb') do |file|

    file.write(uploaded_io.read)

  end      

  @lead.illustration = "#{uploaded_io.original_filename}"

end

end

When creating a “lead”, everything goes fine with create:

def create

@lead = Lead.new( params[:lead] )



upload

if @lead.save

  flash[:success] = "Lead successfully added."

  redirect_to @lead

else

  redirect_to root_path

end

end

The “lead” gets saved, I can use it. So far, so good.

Now with the problem (remember the NOOB ALERT).

Updating a lead without modifying the image goes fine, but updating with a new image returns this error:

NOOB ALERT

Now I warned you, here's the problem that's been getting me mad for hours

I've got a model for content I'm calling "lead". It has a title, a text and an image used as a background.

I'm not using paperclip or CarrierWave to handle the image file (and I don't want to, one of my needs here is to learn how this all works). I'm using this function to upload the file (it comes directly from the rails guides) :

  def upload     if params[:lead][:illustration]       uploaded_io = params[:lead][:illustration]

      File.open(Rails.root.join('app', 'assets', 'images', uploaded_io.original_filename), 'wb') do |file|         file.write(uploaded_io.read)       end

      @lead.illustration = "#{uploaded_io.original_filename}"     end   end

When creating a "lead", everything goes fine with create:

  def create     @lead = Lead.new( params[:lead] )

    upload

    if @lead.save       flash[:success] = "Lead successfully added."       redirect_to @lead     else       redirect_to root_path     end   end

The "lead" gets saved, I can use it. So far, so good.

Now with the problem (remember the NOOB ALERT). Updating a lead without modifying the image goes fine, but updating with a new image returns this error:

NoMethodError in LeadsController#update

undefined method `name' for nil:NilClass

and

app/controllers/leads_controller.rb:45:in `update'

The error says that you are calling method 'name' for something that is nil at line 45 in leads_controller.rb.

Here's my update function:

  def update     @lead = Lead.find_by_id( params[:id] )

    upload

    if @lead.update_attributes!(params[:lead])       flash[:success] = "Leaderboard successfully updated."       redirect_to @lead     else       render action: "edit"     end   end

Basically the same as create. I've been googling and going back and forth for hours and I didn't get to solve this. Any help would be more than welcome.

Unfortunately you have not told us which is line 45.

Colin

I supposed it was obvious… ; )

Seriously, line 45 is in the update function:

40 def update 41 @lead = Lead.find_by_id( params[:id] ) 42 43 upload 44 45 if @lead.update_attributes!(params[:lead]) 46 flash[:success] = “Leaderboard successfully updated.” 47 redirect_to @lead 48 else 49 render action: “edit” 50 end 51 end

Please don't top post, it makes it difficult to follow the thread. Insert you reply inline at appropriate points in the previous message. Thanks

I supposed it was obvious... ; )

Not sure why it would be obvious.

Seriously, line 45 is in the update function:

40 def update 41 @lead = Lead.find_by_id( params[:id] ) 42 43 upload 44 45 if @lead.update_attributes!(params[:lead])

It is also better not to use html as now the fact that you highlighted the line above is not visible. Have you checked that @lead is not nil? Have a look at the Rails Guide on Debugging to get lots of ideas on how to debug your code. Start by looking in log/development.log to check that the attributes are being passed correctly. Then, for example, use the debugger to break into the code and check what is going on or put diagnostic puts statements in to inspect the data.

Colin

Can you post your create and update methods as you now have them so I can see what you are doing.

Colin

Thanks for the advise. Logging led me to a solution, but I don’t

really understand what’s going on under the hood.

Here’s my solution:

“upload” now returns this:

return “#{uploaded_io.original_filename}”

In “create”, I set:

@lead.illustration = upload

In “update”, this wont work. I need to set:

params[:lead][:illustration] = upload

and the update goes fine.

After finding this out I tried to set

params[:lead][:illustration] = upload

instead of

@lead.illustration = upload

in “create”, and that wont work.

Can you post your create and update methods as you now have them so I

can see what you are doing.

Sure, here goes :

def create

@lead = Lead.new( params[:lead] )

@lead.illustration = upload_image

if @lead.save

flash[:success] = “Leaderboard successfully added.”

redirect_to @lead

else

redirect_to root_path

end

end

def update

@lead = Lead.find_by_id( params[:id] )

params[:lead][:illustration] = upload_image

if @lead.update_attributes( params[:lead] )

flash[:success] = “Leaderboard successfully updated.”

redirect_to @lead

else

render action: “edit”

end

end

def upload_image

if params[:lead][:illustration]

uploaded_io = params[:lead][:illustration]

File.open(Rails.root.join(‘app’, ‘assets’, ‘images’, uploaded_io.original_filename), ‘wb’) do |file|

file.write(uploaded_io.read)

end

return “#{uploaded_io.original_filename}”

end

end

> ... > Thanks for the advise. Logging led me to a solution, but I don't > really understand what's going on under the hood. > > Here's my solution: > > "upload" now returns this: > return "#{uploaded_io.original_filename}" > > In "create", I set: > @lead.illustration = upload > > In "update", this wont work. I need to set: > params[:lead][:illustration] = upload > and the update goes fine. > > After finding this out I tried to set > params[:lead][:illustration] = upload > instead of > @lead.illustration = upload > in "create", and that wont work.

Can you post your create and update methods as you now have them so I can see what you are doing.

Sure, here goes :

  def create     @lead = Lead.new( params[:lead] )

    @lead.illustration = upload_image

    if @lead.save       flash[:success] = "Leaderboard successfully added."       redirect_to @lead     else       redirect_to root_path     end   end

  def update     @lead = Lead.find_by_id( params[:id] )

    params[:lead][:illustration] = upload_image

You said that doing @lead.illustration = upload_image (presumably at this point) did not work. It should do, provided that params[:lead][:illustration] is not present. If it is then the value from upload_image will be overwritten by the value from params when you call update_attributes. Another possibility is that if the illustration is the only field being changed then update_attributes may not actually save the record (as it does not think anything has changed).

Probably leaving it as you have it is the simplest solution.

Colin

... Thanks for the advise. Logging led me to a solution, but I don't really understand what's going on under the hood.

Here's my solution:

"upload" now returns this: return "#{uploaded_io.original_filename}"

In "create", I set: @lead.illustration = upload

In "update", this wont work. I need to set: params[:lead][:illustration] = upload and the update goes fine.

After finding this out I tried to set params[:lead][:illustration] = upload instead of @lead.illustration = upload in "create", and that wont work.

Can you post your create and update methods as you now have them so I can see what you are doing.

Sure, here goes :

def create    @lead = Lead.new( params[:lead] )

   @lead.illustration = upload_image

   if @lead.save      flash[:success] = "Leaderboard successfully added."      redirect_to @lead    else      redirect_to root_path    end end

def update    @lead = Lead.find_by_id( params[:id] )

   params[:lead][:illustration] = upload_image

You said that doing @lead.illustration = upload_image (presumably at this point) did not work. It should do, provided that params[:lead][:illustration] is not present. If it is then the value from upload_image will be overwritten by the value from params when you call update_attributes. Another possibility is that if the illustration is the only field being changed then update_attributes may not actually save the record (as it does not think anything has changed).

Probably leaving it as you have it is the simplest solution.

Colin

Jean-David,

Here's a bit of code from an old project of mine:

  def update     data = params[:product].delete('data') # HashWithIndifferentAccess still needs the actual key type to .delete     begin       @product = Product.find(params[:id], :conditions => { :virtual => true })

      unless data.blank? # i.e., not replacing image         content = data.read         if content.blank?           flash[:error] = "Selected upload file was empty"           redirect_to :action => 'edit'           return         end         @product.image_size = ImageSizer.of_blob(content).size         filename = Product.get_local_image_name data.original_filename         File.open(File.join(RAILS_ROOT, 'public', filename), 'wb') do |f|           f.write content         end         @product.image = filename       end

      respond_to do |format|         if @product.update_attributes(params[:product]) …   end

Note that I am removing the 'data' key from the attributes hash params[:product] so that it isn't used in the .update_attributes call later. Perhaps you need to remove the 'illustration' since you've already handled that portion of the update.

If you know that the illustration is changed, but ActiveRecord doesn't, then perhaps you need to call @lead.illustration_will_change! to inform AR that the attribute is dirty and needs to be written out.

-Rob

Thanks for the advise. Logging led me to a solution, but I don’t

really understand what’s going on under the hood.

Here’s my solution:

“upload” now returns this:

return “#{uploaded_io.original_filename}”

In “create”, I set:

@lead.illustration = upload

In “update”, this wont work. I need to set:

params[:lead][:illustration] = upload

and the update goes fine.

After finding this out I tried to set

params[:lead][:illustration] = upload

instead of

@lead.illustration = upload

in “create”, and that wont work.

Can you post your create and update methods as you now have them so I

can see what you are doing.

Sure, here goes :

def create

@lead = Lead.new( params[:lead] )

@lead.illustration = upload_image

if @lead.save

 flash[:success] = "Leaderboard successfully added."
 redirect_to @lead

else

 redirect_to root_path

end

end

def update

@lead = Lead.find_by_id( params[:id] )

params[:lead][:illustration] = upload_image

You said that doing @lead.illustration = upload_image (presumably at

this point) did not work. It should do, provided that

params[:lead][:illustration] is not present. If it is then the value

from upload_image will be overwritten by the value from params when

you call update_attributes. Another possibility is that if the

illustration is the only field being changed then update_attributes

may not actually save the record (as it does not think anything has

changed).

Probably leaving it as you have it is the simplest solution.

Colin

Jean-David,

Here’s a bit of code from an old project of mine:

def update

data = params[:product].delete('data') # HashWithIndifferentAccess still needs the actual key type to .delete

begin

  @product = Product.find(params[:id], :conditions => { :virtual => true })



  unless data.blank?          # i.e., not replacing image

    content = data.read

    if content.blank?

      flash[:error] = "Selected upload file was empty"

      redirect_to :action => 'edit'

      return

    end

    @product.image_size = ImageSizer.of_blob(content).size

    filename = Product.get_local_image_name data.original_filename

    File.open(File.join(RAILS_ROOT, 'public', filename), 'wb') do |f|

      f.write content

    end

    @product.image = filename

  end



  respond_to do |format|

    if @product.update_attributes(params[:product])

end

Note that I am removing the ‘data’ key from the attributes hash params[:product] so that it isn’t used in the .update_attributes call later. Perhaps you need to remove the ‘illustration’ since you’ve already handled that portion of the update.

If you know that the illustration is changed, but ActiveRecord doesn’t, then perhaps you need to call @lead.illustration_will_change! to inform AR that the attribute is dirty and needs to be written out.

-Rob

Thank you Rob, I’ll try to put that in my own sauce (that’s an approximate translation for french “à ma sauce” ; )

You said that doing @lead.illustration = upload_image (presumably at

this point) did not work. It should do, provided that

params[:lead][:illustration] is not present. If it is then the value

from upload_image will be overwritten by the value from params when

you call update_attributes. Another possibility is that if the

illustration is the only field being changed then update_attributes

may not actually save the record (as it does not think anything has

changed).

Probably leaving it as you have it is the simplest solution.

Colin

I too guess it should do but I will leave it this way for now.

Thx Colin.