1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
|
# frozen_string_literal: true
require 'singleton'
require_relative 'person'
require_relative 'family_factory'
# FamilyTree class representing a family tree.
# This class follows the Singleton pattern to ensure there is only one instance
# of the family tree throughout the application. It uses the FamilyFactory to
# seed the initial family tree with predefined families.
class FamilyTree
include Singleton
attr_accessor :families
# Initializes the FamilyTree instance.
# Seeds the initial family tree using the FamilyFactory.
def initialize
@families = FamilyFactory.new.create_families
end
# Adds a family to the family tree.
#
# @param family [Family] The family to add.
# @return [void]
def add_family(family)
@families << family unless @families.include?(family)
end
# Adds a child to a family based on the mother's name.
#
# @param mothers_name [String] The name of the mother.
# @param name [String] The name of the child.
# @param gender [String] The gender of the child.
# @return [String] 'CHILD_ADDED' if the child is added successfully, 'CHILD_ADDITION_FAILED' otherwise.
def add_child(mothers_name, name, gender)
result = find_person_in_families(mothers_name)
parent = result[:person]
parent_of_family = result[:parent_of_family]
if parent_of_family.nil? || parent_of_family.mother.is_a?(NilPerson) || parent_of_family.father.eql?(parent)
return 'CHILD_ADDITION_FAILED'
end
return 'CHILD_ADDITION_FAILED' if parent_of_family.children.any? { |child| child.name.casecmp(name).zero? }
child = Person.new(name, gender)
parent_of_family.add_child(child)
'CHILD_ADDED'
end
# Retrieves the relationship of a person based on their name and the specified relationship.
#
# @param name [String] The name of the person.
# @param relationship [String] The type of relationship to retrieve.
# @return [String] The name(s) of the related person(s) or an appropriate message if not found.
def get_relationship(name, relationship)
result = find_person_in_families(name)
person = result[:person]
parent_of_family = result[:parent_of_family]
child_of_family = result[:child_of_family]
return 'PERSON_NOT_FOUND' if person.is_a?(NilPerson)
case relationship.downcase
when 'mother', 'father'
handle_parent_relationship(child_of_family, relationship)
when 'siblings'
handle_siblings_relationship(child_of_family, name)
when 'child', 'daughter', 'son'
handle_children_relationship(parent_of_family, relationship)
when 'paternal-uncle'
handle_uncle_relationship(child_of_family, 'paternal')
when 'maternal-uncle'
handle_uncle_relationship(child_of_family, 'maternal')
when 'paternal-aunt'
handle_aunt_relationship(child_of_family, 'paternal')
when 'maternal-aunt'
handle_aunt_relationship(child_of_family, 'maternal')
when 'sister-in-law'
handle_sister_in_law_relationship(person, child_of_family)
when 'brother-in-law'
handle_brother_in_law_relationship(person, child_of_family)
else
'UNSUPPORTED_RELATIONSHIP'
end
end
private
# Handles the parent relationship retrieval.
#
# @param child_of_family [Family] The family of the child.
# @param relationship [String] The type of parent relationship ('mother' or 'father').
# @return [String] The name of the parent or 'PERSON_NOT_FOUND' if not found.
def handle_parent_relationship(child_of_family, relationship)
parent = relationship == 'mother' ? child_of_family&.mother : child_of_family&.father
return 'PERSON_NOT_FOUND' if parent.nil? || parent.is_a?(NilPerson)
parent.name
end
# Handles the siblings relationship retrieval.
#
# @param child_of_family [Family] The family of the child.
# @param name [String] The name of the child.
# @return [String] The names of the siblings or 'NONE' if not found.
def handle_siblings_relationship(child_of_family, name)
siblings = find_siblings(child_of_family, name)
siblings.empty? ? 'NONE' : siblings.map(&:name).join(' ')
end
# Handles the children relationship retrieval.
#
# @param parent_of_family [Family] The family of the parent.
# @param relationship [String] The type of children relationship ('child', 'son', or 'daughter').
# @return [String] The names of the children or 'NONE' if not found.
def handle_children_relationship(parent_of_family, relationship)
return 'NONE' if parent_of_family.nil?
children = parent_of_family.children
case relationship.downcase
when 'child'
children.empty? ? 'NONE' : children.map(&:name).join(' ')
when 'son'
sons = children.select { |child| child.gender == Gender::MALE }
sons.empty? ? 'NONE' : sons.map(&:name).join(' ')
when 'daughter'
daughters = children.select { |child| child.gender == Gender::FEMALE }
daughters.empty? ? 'NONE' : daughters.map(&:name).join(' ')
else
'UNSUPPORTED_RELATIONSHIP'
end
end
# Handles the sibling relationship retrieval for uncles and aunts.
#
# @param child_of_family [Family] The family of the child.
# @param type [String] The type of sibling relationship ('uncle' or 'aunt').
# @param side [String] The side of the family ('paternal' or 'maternal').
# @return [String] The names of the uncles or aunts or 'NONE' if not found.
def handle_sibling_relationship(child_of_family, type, side)
return 'NONE' if child_of_family.nil?
parent = side == 'paternal' ? child_of_family.father : child_of_family.mother
return 'NONE' if parent.nil? || parent.is_a?(NilPerson)
# Find the family where the parent is a child
parent_result = find_person_in_families(parent.name)
parent_family = parent_result[:child_of_family]
return 'NONE' if parent_family.nil?
# Find the siblings of the parent
siblings = find_siblings(parent_family, parent.name)
# Select based on sibling gender
relatives = case type
when 'uncle'
siblings.select { |sibling| sibling.gender == Gender::MALE }
when 'aunt'
siblings.select { |sibling| sibling.gender == Gender::FEMALE }
else
[]
end
relatives.empty? ? 'NONE' : relatives.map(&:name).join(' ')
end
# Handles the uncle relationship retrieval.
#
# @param child_of_family [Family] The family of the child.
# @param type [String] The type of uncle relationship ('paternal' or 'maternal').
# @return [String] The names of the uncles or 'NONE' if not found.
def handle_uncle_relationship(child_of_family, type)
handle_sibling_relationship(child_of_family, 'uncle', type)
end
# Handles the aunt relationship retrieval.
#
# @param child_of_family [Family] The family of the child.
# @param type [String] The type of aunt relationship ('paternal' or 'maternal').
# @return [String] The names of the aunts or 'NONE' if not found.
def handle_aunt_relationship(child_of_family, type)
handle_sibling_relationship(child_of_family, 'aunt', type)
end
# Handles the sister-in-law relationship retrieval.
#
# @param person [Person] The person whose sister-in-law is to be found.
# @param child_of_family [Family] The family of the child.
# @return [String] The names of the sisters-in-law or 'NONE' if not found.
def handle_sister_in_law_relationship(person, child_of_family)
find_in_laws(person, child_of_family, Gender::FEMALE)
end
# Handles the brother-in-law relationship retrieval.
#
# @param person [Person] The person whose brother-in-law is to be found.
# @param child_of_family [Family] The family of the child.
# @return [String] The names of the brothers-in-law or 'NONE' if not found.
def handle_brother_in_law_relationship(person, child_of_family)
find_in_laws(person, child_of_family, Gender::MALE)
end
# Finds the in-laws of a person based on their gender.
#
# @param person [Person] The person whose in-laws are to be found.
# @param child_of_family [Family] The family of the child.
# @param gender [String] The gender of the in-laws to find.
# @return [String] The names of the in-laws or 'NONE' if not found.
def find_in_laws(person, child_of_family, gender)
in_laws = []
# Check for siblings of the spouse
if person.spouse.is_a?(Person)
spouse_result = find_person_in_families(person.spouse.name)
spouse_family = spouse_result[:child_of_family]
if spouse_family
spouse_siblings = find_siblings(spouse_family, person.spouse.name)
in_laws.concat(spouse_siblings.select { |sibling| sibling.gender == gender }.map(&:name))
end
end
unless child_of_family.nil?
# Check for spouses of siblings of the person
siblings = find_siblings(child_of_family, person.name)
siblings.each do |sibling|
in_laws << sibling.spouse.name if sibling.spouse && sibling.spouse.gender == gender
end
end
in_laws.uniq!
in_laws.empty? ? 'NONE' : in_laws.join(' ')
end
# Finds a person in the families by name.
#
# @param name [String] The name of the person to find.
# @return [Hash] A hash containing the person, parent_of_family, and child_of_family.
def find_person_in_families(name)
result = { person: NilPerson.new, parent_of_family: nil, child_of_family: nil }
families.each do |family|
# Check for mother
if family.mother&.name&.casecmp(name)&.zero?
result[:person] = family.mother
result[:parent_of_family] = family
end
# Check for father
if family.father&.name&.casecmp(name)&.zero?
result[:person] = family.father
result[:parent_of_family] = family
end
family.children.each do |child|
next if child.nil? || child.name.nil?
if child.name.casecmp(name).zero? && family != result[:parent_of_family]
result[:person] = child
result[:child_of_family] = family
end
end
end
result
end
# Finds the siblings of a person in a family.
#
# @param family [Family] The family of the person.
# @param name [String] The name of the person.
# @return [Array<Person>] An array of siblings.
def find_siblings(family, name)
family.children.reject { |child| child.name.casecmp(name).zero? }
end
# Checks if a person is a child of a family.
#
# @param family [Family] The family to check.
# @param name [String] The name of the person.
# @return [Boolean] True if the person is a child of the family, false otherwise.
def child_of_family?(family, name)
return false unless family.is_a?(Family)
family.children.any? do |child|
child.name.casecmp(name).zero?
end
end
end
|