# Composite Pattern
https://refactoring.guru/design-patterns/composite
## Applicability
1. Use the Composite pattern when you have to implement a **tree-like** object structure.
2. Use the pattern when you want the client code to **treat both simple and complex elements uniformly**.
## Examples

A box could even add some extra cost to the final price, such as packaging cost.
## Structure

## Apply to OA Form Field rendering


```ruby
class FormComponent
attr_accessor :parent
def add(child)
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
def render
raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
end
end
class GroupComponent < FormComponent
def initialize(title:)
@title = title
@children = []
end
def add(child_component)
@children += Array.wrap(child_component)
end
def render
puts ""
puts "----- Render Group Component --------"
@children.each(&:render)
end
def render_json
{
group_title: @title,
children: @children.map(&:render_json)
}
end
end
class FieldComponent < FormComponent
attr_reader :record, :school_field
def initialize(record, school_field)
@record = record
@school_field = school_field
end
def render
case school_field
when ColumnNavigation
puts ""
puts "========= #{school_field.title} ========="
else
puts "#{school_field.label}: #{record.read_field(school_field)}"
end
end
def render_json
case school_field
when ColumnNavigation
{
name: school_field.name,
label: school_field.label,
value: school_field.title,
}
else
{
name: school_field.name,
label: school_field.label,
value: record.read_field(school_field),
}
end
end
end
# return [Array<FormComponent>]
def decorate_school_field(record, school_field)
if school_field.class < FieldGroup
records = record.read_field(school_field)
components = records.map do |child_record|
component = GroupComponent.new(title: school_field.label)
school_field.children.each do |child_field|
child_component = decorate_school_field(child_record, child_field)
component.add(child_component)
end
component
end
return components
else
return FieldComponent.new(record, school_field)
end
end
student = Student.find(726)
application_form = student.application_form
form_component = GroupComponent.new(title: application_form.title)
application_form.fields.map do |field|
components = decorate_school_field(student, field)
form_component.add(components)
end
# form_component.render
File.open(Rails.root.join("result.json"), "w") do |f|
f.write form_component.render_json.to_json
end
```
$ rails runner xx.rb