Skip to content

Instantly share code, notes, and snippets.

@bethesque
Last active November 30, 2015 00:07
Show Gist options
  • Save bethesque/310f7426dc9176b6a156 to your computer and use it in GitHub Desktop.
Save bethesque/310f7426dc9176b6a156 to your computer and use it in GitHub Desktop.
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