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:
stitches/lib/stitches/errors.rb
Line 73 in c457ed6
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