reform
reform copied to clipboard
Reform assigns nested properties in twins in a wrong way
Reform version: 2.2.1
Given: I have a very complex data structure which I compose into a Twin with nested properties. The Twin then serves as model for a Form:
require 'disposable'
require 'reform'
require 'rails'
require 'reform/rails'
require 'reform/form/active_model/validations'
Reform::Form.class_eval do
include Reform::Form::ActiveModel::Validations
end
User = Struct.new(:email, :password, :profile) { def save; end }
Profile = Struct.new(:name) { def save; end }
class ProjectTwin < Disposable::Twin
feature Save
feature Sync
property :email
property :password
property :profile do
property :name
end
end
class ProjectForm < Reform::Form
property :email
property :password
property :profile do
property :name
end
end
user = User.new('[email protected]', '123456', Profile.new('Test Name'))
twin = ProjectTwin.new(user)
form = ProjectForm.new(twin)
params = { email: '[email protected]', profile: { name: 'New Name' } }
form.validate(params)
What I expect: On form.save
(or form.sync
and then twin.sync
), the form writes the values from the params hash to the twin and the twin then writes the values to the nested objects without reassigning the nested properties.
What actually happens: Reform assigns the nested Twin (i.e. an instance of the anonymous class it generates to wrap the nested model) to the model:
# before save:
user.profile.class
# => Profile ... and should stay that way
# now we save:
form.save
# what happened to the nested object?
user.profile.class
# => #<Class:0x007fb403211358>
# ...which is the anonymous class generated by the twin to wrap the nested profile model:
twin.profile.class
# => #<Class:0x007fb403211358>
Additional info: If you don't use a form but assign the values to the twin manually, twin.sync
will work as expected:
user = User.new('[email protected]', '123456', Profile.new('Test Name'))
twin = ProjectTwin.new(user)
twin.email = '[email protected]'
twin.profile.name = 'New Name'
twin.sync
user.profile.class
# => Profile
So the problematic behavior is in Reform.