Skip to content

Instantly share code, notes, and snippets.

@bethesque bethesque/a.md
Last active Nov 30, 2015

Embed
What would you like to do?
DelegatedProperty - flattening model hierarchy - don't expose your model relationships in your form

This allows you do something like:

User = Struct.new(:address)
Address = Struct.new(:street, :suburb)

class UserForm < Reform::Form

  property :address do
    property :street
    property :suburb
  end

  delegated_property :street, to: :address, validates: {presence: true}
  delegated_property :suburb, to: :address, validates: {presence: true}

end

user = User.new(Address.new)
form = UserForm.new(user)
form.street = '13 Smith St'
form.suburb = 'Melbourne'
require 'rails_helper'
require 'reform/delegated_property'
module Reform
describe DelegatedProperty do
Parent = Struct.new(:child)
Child = Struct.new(:child_property)
class ParentForm < Reform::Form
include ::Reform::DelegatedProperty
property :child do
property :child_property
end
delegated_property :child_property, to: :child, validates: {presence: true}
end
let(:params) { {'child_property' => 'child_value'} }
let(:child) { Child.new }
let(:parent) { Parent.new(child) }
let(:form) { ParentForm.new(parent) }
it "delegates the property at the top level to the child level" do
form.validate(params)
form.sync
expect(child.child_property).to eq 'child_value'
end
context "when the child_property is missing" do
let(:params) { {} }
it "returns a validation error" do
expect(form.validate(params)).to be false
expect(form.errors[:child_property]).to_not be_empty
end
end
end
end
# Used to "flatten" the hierarchy of a parent/child relationship
# so that all the parameters can be exposed on the same level.
# Used to avoid refactoring the Provider CRUD HTML forms.
require 'reform'
module Reform
module DelegatedProperty
def self.included(base)
base.class_eval do
extend ClassMethods
end
end
module ClassMethods
def delegated_property property_name, options
options = options.dup
target = options.delete(:to) # the child form name
# Define a virtual property on the parent so Reform knows that this is a property
# needs to handle, but virtual: true so it does not try and set or get the parent value,
# as the value exists on the child, not the parent
property property_name, options.merge(virtual: true)
# define setter to set value on child form when value is set on parent form
define_method("#{property_name}=") do | value |
super value
send(target).send("#{property_name}=", value)
end
# define getter to get value from child form
define_method(property_name) do
send(target).send(property_name)
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.