stitchfix / stitches

Create a Microservice in Rails with minimal ceremony

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Issue with model validation and controller valid? check

sqlninja opened this issue · comments

I was reviewing the details from the Error Handling page, but it does seem to indicate the best way to implement validations in the model.

I have a model that contains a file attachment called :object. I have a validation that checks the file size based on file type:

def ojbect_size_valid
   errors.add(:object, 'should be less than or equal to 5MB for images') if object_file_size > 5.megabytes && image_type?
   errors.add(:object, 'should be less than or equal to 15MB for gifs and videos') if object_file_size > 15.megabytes && (gif_type? || video_type?)
end

This validation is catching files correctly but in the create method of the controller it is throwing an error:

def create
    mp = medium_params
    @medium = Medium.create(mp)

    if @medium.valid?
       ...
    else
      render json: {
        errors: Stitches::Errors.from_active_record_object(@medium)
      }, status: :unprocessable_entity
    end
  end

undefined method `full_messages' for {}:Hash

app/controllers/api/v1/media_controller.rb, line 108

  103         )
  104       else
  105   
  106   
  107         render json: {
> 108           errors: Stitches::Errors.from_active_record_object(@medium)
  109         }, status: :unprocessable_entity
  110       end
  111     end
  112   

What am I doing wrong here to get the errors to render?

This is weird. Is your class an ActiveRecord?

This is how the method is implemented:

https://github.com/stitchfix/stitches/blob/master/lib/stitches/errors.rb#L71

what it's doing is basically iterating over all the active record errors on your object and, if the field with an error itself is an active record, get those errors.

it could be that the way it does this isn't quite right. The code:

message = if object.send(field).respond_to?(:errors)

is assuming that if the field's value responds to errors, it's therefore an ActiveRecord and thus errors will return an active record errors object, not a hash.

It sounds like in your case, errors on either your object, or an object your object references has an errors method that returns a hash and not an active record errors object.

The class is inheriting from ApplicationRecord

class Medium < ApplicationRecord
  ...

  validate :ojbect_size_valid

 ...

  def ojbect_size_valid
    errors.add(:object, 'should be less than or equal to 5MB for images') if object_file_size > 5.megabytes && image_type?
    errors.add(:object, 'should be less than or equal to 15MB for gifs and videos') if object_file_size > 15.megabytes && (gif_type? || video_type?)
  end

  private

  def image_type?
    object_content_type =~ /jpeg/ || object_content_type =~ /jpg/ || object_content_type =~ /png/
  end

  def gif_type?
    object_content_type =~ /gif/
  end

  def video_type?
    object_content_type =~ /video/
  end

  def pdf_type?
    object_content_type =~ /pdf/
  end
end

The AppilcationRecord is just inheriting from ActiveRecord

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

Hm. My guess is that the code is detecting that .object returns a value that responds to errors but that errors is actually a hash. What is the type of object on this class?

Whoops meant to leave that in:
has_attached_file :object, styles: ->(f) { f.instance.check_file_type }

object is a paperclip attachment to the medium

https://github.com/thoughtbot/paperclip/blob/master/lib/paperclip/attachment.rb#L83

Stitches assumes if the object responds to #errors it's an active record. This is probably a bug in stitches.

To solve your immediate problem, you could inline the implementation of from_active_record and fix it so it doesn't break. I probably can't look into a fix today, but maybe could this week. I think it's as simple as checking if errors is an ActiveModel::Errors

Believe I figured it out...
So the "field" isn't really object, but instead object_file_size
Paperclip basically yields a wrapper to the various metadata about the object