Open akz92 opened 3 years ago
Hm, this is interesting. As you might've seen in Reform's source, all #sync!
does is calling model.file= <here is the File object>
. So the problem must be in the attachment object. What has changed here from Rails 6.0 to 6.1? The blob.identify_without_saving
is called in the constructor, so this can't be the root of the problem?!
I believe blob.identify_without_saving
is the root of the problem as it tries to find_or_build_blob
and that only accepts one of: ActiveStorage::Blob
, ActionDispatch::Http::UploadedFile
, Rack::Test::UploadedFile
, Hash
or String
. For an unchanged attachment we pass in an ActiveStorage::Attachable::One
.
To be honest I feel that calling model.file = model.file
should not throw any errors (it does!), but that seems to be Rails's desired API as submitting the same form without Reform does work (as it does not reassign the attachment).
Am I correct to assume that the relevant #sync!
comes from Disposable::Twin::Sync? Do you think it may be possible to add a guard clause here skipping if the value has not changed?
Actually, I believe that Disposable only calls the setter if the value has changed, but I might be wrong. In your case, whatsoever, the problem seems to be the collection
of file
s and here the setter gets always called. I'm pretty sure the problem wouldn't arise if it was just one property
field for the upload. Let me check in the Disposable gem how we could solve this.
Or, wait a minute, did you say that model.file = model.file
also breaks?
Yes, calling model.file = model.file
throws the same error I reported here. On Rails 6.0 the assignment call succeeds but the subsequent .save
call throws said error. That's why I believe the addition to the constructor is what causes this issue.
@akz92 could you try assigning file.blob
to model.file
?
@yogeshjain999 yes that does work as file.blob
returns an instance of ActiveStorage::Blob
which is one of the supported classes.
Cool 🍻 Closing this issue.
@yogeshjain999 I'm sorry if it sounded like that solves the initial issue. The discussion about model.file = model.file
was tangential and that call can't be changed on the application level. This call is made by Reform's #sync!
and since the scenario where this happens is when the attachment hasn't changed we can't use, lets say, a populator to fix it.
@akz92 Could you try this? https://github.com/apotonick/disposable/blob/master/lib/disposable/twin/sync.rb#L133
This module included in the FileForm
will prevent #sync!
from writing if the property hasn't changed.
@apotonick good call, that does in fact solve the issue! Please let me know if/how I can help either document or fix it for good.
Thank you both very much for the prompt attention to this issue!
I'm afraid I jumped to conclusions too soon. Adding feature Sync::SkipUnchanged
does in fact prevent that failure but at the cost of ignoring any changed files so it seems that alone isn't a viable solution.
Hm, that sounds like unintended behavior (a "bug"). Can you check disposable's tests if we cover collections where only one property is changed in combo with SkipUnchanged
?
Complete Description of Issue
Given a form with a nested collection where each element of the collection has an ActiveStorage attachment, calling
.sync
or.save
fails on update without changing at least one of the attached files.This started happening with Rails 6.1 and is possibly linked to this addition
Steps to reproduce
I have created a tiny Rails app that reproduces this in isolation, the steps to try it are in the README.
Simplified scenario that would cause the issue:
Expected behavior
The record should be synced/saved.
Actual behavior
Fails with
Could not find or build blob: expected attachable, got ActiveStorage::Attachable::One
System configuration
Reform version: 2.6.0 Rails version: 6.1.4
Full Backtrace of Exception (if any)