summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md141
-rw-r--r--data/actions.txt65
-rw-r--r--family-tree.pngbin0 -> 269964 bytes
-rw-r--r--lib/action_file_executor.rb3
-rw-r--r--lib/family_tree.rb6
-rw-r--r--spec/family_tree_spec.rb8
6 files changed, 162 insertions, 61 deletions
diff --git a/README.md b/README.md
index ea09d83..0e2cc09 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,141 @@
# shippit-coding-challenge
-My Submission to the Shippit Coding Challenge
+
+My submission to the Shippit Coding Challenge, implemented in Ruby v3.3.5, is a small application to manage the following family tree of King Arthur and Queen Margaret:
+
+![Image of the Family Tree](./family-tree.png)
+
+The application accepts an action file where each line denotes an action to perform on the family tree.
+
+Example: `./ruby family_tree.rb /path/to/actions.txt`
+
+**Supported Actions**
+
+- `ADD_CHILD [MOTHER'S NAME] [CHILD'S NAME] [CHILD'S GENDER]`
+- `GET_RELATIONSHIP [PERSON'S NAME] [RELATIONSHIP TYPE]`
+
+> Note that you can only add a child via the mother
+
+**Supported Genders**
+
+- `MALE`
+- `FEMAL`
+
+**Supported Relationship Types**
+
+- `MOTHER`
+- `FATHER`
+- `SIBLINGS`
+- `CHILD`
+- `DAUGHTER`
+- `SON`
+- `PATERNAL-UNCLE`
+- `MATERNAL-UNCLE`
+- `PATERNAL-AUNT`
+- `MATERNAL-AUNT`
+- `SISTER-IN-LAW`
+- `BROTHER-IN-LAW`
+
+**Comments and Blank Lines**
+
+Blank lines and lines starting with `#` are ignored.
+
+**Example `actions.txt` File**
+
+Based on the family tree of King Arthur and Queen Margaret, here is an example action file:
+
+```txt
+# Adding Children
+ADD_CHILD "Queen Margaret" "Jonathan" "Male"
+ADD_CHILD Ginerva "St John" "Male"
+
+# Getting relationships
+GET_RELATIONSHIP Remus Maternal-Aunt
+```
+
+> Note that quotation marks are only required if the name of the person has a space in it, if quotation marks are ommitted the action will fail.\
+> Also, note that the **gender** and **relationship-types** are **case-insensitive**.
+
+**Output**
+
+Based on the family tree, here are the expected outputs for commoon scenarios:
+
+| Action | Output | Comments |
+| ----------------------------------------------- | ----------------------- | --------------------------- |
+| `ADD_CHILD "Queen Margaret" "Jonathan" "Male"` | `CHILD_ADDED` | Successful |
+| `ADD_CHILD "Betty" "Jonathan" "Male"` | `PERSON_NOT_FOUND` | Betty doesn't exist |
+| `ADD_CHILD "Queen Margaret" "Jonathan" "Other"` | `CHILD_ADDITION_FAILED` | Invalid gender |
+| `ADD_CHILD "King Arthur" "Jonathan" "Other"` | `CHILD_ADDITION_FAILED` | Father's name provided |
+| `ADD_CHILD "King Arthur"` | _no output_ | Invalid number of arguments |
+| `ADD_CHILD "King Arthur" "Jonathan"` | _no output_ | Invalid number of arguments |
+| `ADD_PET "King Arthur" "Poodles"` | _no output_ | Invalid action, `ADD_PET` |
+| `GET_RELATIONSHIP "Percy" "Child"` | `Molly Lucy` | Names of children |
+| `GET_RELATIONSHIP "Charlie" "Child"` | `NONE` | Charlie has no children |
+| `GET_RELATIONSHIP "Betty" "Child"` | `PERSON_NOT_FOUND` | Betty doesn't exist |
+| `GET_RELATIONSHIP "King Arthur" "Pets"` | _no output_ | Invalid relationship type |
+| `GET_RELATIONSHIP "King Arthur"` | _no output_ | Invalid number of arguments |
+
+## Approach
+
+### Data Model
+
+The data model revolves around four primary classes: `FamilyTree`, `FamilyFactory`, `Family`, and `Person`.
+
+#### `FamilyTree`
+
+Represents the entire family tree and ensures a single instance throughout the application using the Singleton pattern.\\
+It manages a collection of `Family` objects and the retrieval of various family relationships such as parents, uncles, aunts, and children.
+
+#### `FamilyFactory`
+
+Responsible for creating and initializing predefined families. It uses helper methods to find or create individuals and establish relationships between them.
+
+#### `RelationshipManager`
+
+Manages the relationships between individuals, ensuring that connections like spouses, parents, and siblings are correctly established and maintained.
+
+#### `Family`
+
+Represents a family unit which is made up of a mother (`Person`), father (`Person`), and children (an array of `Person` objects).
+
+#### `Person`
+
+Represents an individual within the family tree, holding personal information and a reference to their spouse. A `Person` can exist in multiple families - as a **child** of one and as a **parent** (mother or father) of another.
+
+## Dependencies
+
+This project requires the following dependencies:
+
+- Ruby 3.3.5
+- Bundler (for managing gem dependencies)
+
+Make sure to install Bundler if you haven't already:
+
+```sh
+gem install bundler
+```
+
+## Usage
+
+To run the application with a custom `actions.txt` file, you can specify the path to your custom file. For example:
+
+```sh
+ruby ./bin/family_tree.rb ./path/to/your/actions.txt
+```
+
+This will execute the actions specified in your custom `actions.txt` file.
+
+Alternatively, you can use the Rake task `rake run` to run the application with the included `./data/actions.txt` file:
+
+```sh
+rake run
+```
+
+## Testing
+
+To run the tests, you can use the Rake tasks defined in the `Rakefile`. Use the following command to run all tests:
+
+```sh
+rake test
+```
+
+This will execute all the RSpec tests in the `spec` directory.
diff --git a/data/actions.txt b/data/actions.txt
index be79572..5f1ffe1 100644
--- a/data/actions.txt
+++ b/data/actions.txt
@@ -1,57 +1,16 @@
+ADD_CHILD "Queen Margaret" "Jonathan" "Male"
+ADD_CHILD "Betty" "Jonathan" "Male"
+ADD_CHILD "Queen Margaret" "Jonathan" "Other"
+ADD_CHILD "King Arthur" "Jonathan" "Other"
-GET_RELATIONSHIP "Hugo" "Paternal-Uncle"
-GET_RELATIONSHIP "Hugo" "Paternal-Aunt"
-#
-GET_RELATIONSHIP "Albus" "Maternal-Uncle"
-GET_RELATIONSHIP "Remus" "Maternal-Aunt"
-#
-GET_RELATIONSHIP "Ted" "Sister-in-Law"
-GET_RELATIONSHIP "Percy" "Sister-in-Law"
-GET_RELATIONSHIP "Charlie" "Sister-in-Law"
-#
-GET_RELATIONSHIP "Percy" "Brother-in-Law"
-GET_RELATIONSHIP "Charlie" "Brother-in-Law"
-GET_RELATIONSHIP "Ted" "Brother-in-Law"
-#
-GET_RELATIONSHIP "King Arthur" "Mother"
-GET_RELATIONSHIP "Bill" "Mother"
-GET_RELATIONSHIP "Charlie" "Mother"
-GET_RELATIONSHIP "Percy" "Mother"
-GET_RELATIONSHIP "Ronald" "Mother"
-GET_RELATIONSHIP "Ginerva" "Mother"
-GET_RELATIONSHIP "Flora" "Mother"
-GET_RELATIONSHIP "Victoire" "Mother"
-GET_RELATIONSHIP "Dominique" "Mother"
-GET_RELATIONSHIP "Louis" "Mother"
-GET_RELATIONSHIP "Ted" "Mother"
-GET_RELATIONSHIP "Remus" "Mother"
-GET_RELATIONSHIP "Audrey" "Mother"
-GET_RELATIONSHIP "Molly" "Mother"
-GET_RELATIONSHIP "Lucy" "Mother"
-GET_RELATIONSHIP "Helen" "Mother"
-GET_RELATIONSHIP "Rose" "Mother"
-GET_RELATIONSHIP "Hugo" "Mother"
-GET_RELATIONSHIP "Malfoy" "Mother"
-GET_RELATIONSHIP "Draco" "Mother"
-GET_RELATIONSHIP "Aster" "Mother"
-GET_RELATIONSHIP "Harry" "Mother"
-GET_RELATIONSHIP "James" "Mother"
-GET_RELATIONSHIP "Albus" "Mother"
-GET_RELATIONSHIP "Lily" "Mother"
-GET_RELATIONSHIP "Darcy" "Mother"
-GET_RELATIONSHIP "William" "Mother"
-GET_RELATIONSHIP "Alice" "Mother"
-GET_RELATIONSHIP "Ron" "Mother"
-GET_RELATIONSHIP "Ginny" "Mother"
-GET_RELATIONSHIP "Molly" "Father"
+ADD_CHILD "King Arthur"
+ADD_CHILD "King Arthur" "Jonathan"
+ADD_PET "King Arthur" "Poodles"
-GET_RELATIONSHIP "Bill" "Siblings"
+GET_RELATIONSHIP "Percy" "Child"
+GET_RELATIONSHIP "Charlie" "Child"
-GET_RELATIONSHIP "King Arthur" "Child"
+GET_RELATIONSHIP "Betty" "Child"
-GET_RELATIONSHIP "King Arthur" "Son"
-GET_RELATIONSHIP "King Arthur" "Daughter"
-GET_RELATIONSHIP "Charlie" "Son"
-#
-ADD_CHILD "Victoire" "Fred" "Male"
-ADD_CHILD "Ted" "Fred" "Male"
+GET_RELATIONSHIP "King Arthur" "Pets"
+GET_RELATIONSHIP "King Arthur" \ No newline at end of file
diff --git a/family-tree.png b/family-tree.png
new file mode 100644
index 0000000..2a89364
--- /dev/null
+++ b/family-tree.png
Binary files differ
diff --git a/lib/action_file_executor.rb b/lib/action_file_executor.rb
index 2354dd7..701ad02 100644
--- a/lib/action_file_executor.rb
+++ b/lib/action_file_executor.rb
@@ -72,7 +72,8 @@ class ActionFileExecutor
when 'ADD_CHILD'
puts FamilyTree.instance.add_child(*params)
when 'GET_RELATIONSHIP'
- puts FamilyTree.instance.get_relationship(*params)
+ result = FamilyTree.instance.get_relationship(*params)
+ result && puts(result)
end
end
diff --git a/lib/family_tree.rb b/lib/family_tree.rb
index 5ba8c72..9850280 100644
--- a/lib/family_tree.rb
+++ b/lib/family_tree.rb
@@ -38,6 +38,8 @@ class FamilyTree
parent = result[:person]
parent_of_family = result[:parent_of_family]
+ return 'PERSON_NOT_FOUND' if parent.is_a?(NilPerson)
+
if parent_of_family.nil? || parent_of_family.mother.is_a?(NilPerson) || parent_of_family.father.eql?(parent)
return 'CHILD_ADDITION_FAILED'
end
@@ -82,7 +84,7 @@ class FamilyTree
when 'brother-in-law'
handle_brother_in_law_relationship(person, child_of_family)
else
- 'UNSUPPORTED_RELATIONSHIP'
+ false
end
end
@@ -130,7 +132,7 @@ class FamilyTree
daughters = children.select { |child| child.gender == Gender::FEMALE }
daughters.empty? ? 'NONE' : daughters.map(&:name).join(' ')
else
- 'UNSUPPORTED_RELATIONSHIP'
+ false
end
end
diff --git a/spec/family_tree_spec.rb b/spec/family_tree_spec.rb
index 90d08b3..4dbe83e 100644
--- a/spec/family_tree_spec.rb
+++ b/spec/family_tree_spec.rb
@@ -51,10 +51,10 @@ RSpec.describe FamilyTree do
end
context 'when the mother is not present' do
- it 'fails to add a child and returns CHILD_ADDITION_FAILED' do
+ it 'fails to add a child and returns PERSON_NOT_FOUND' do
FamilyTree.instance.families.clear # Clear existing families
result = FamilyTree.instance.add_child('Unknown Mother', 'Charlie', Gender::MALE)
- expect(result).to eq('CHILD_ADDITION_FAILED')
+ expect(result).to eq('PERSON_NOT_FOUND')
end
end
end
@@ -149,8 +149,8 @@ RSpec.describe FamilyTree do
end
context 'invalid relationships' do
- it 'returns UNSUPPORTED_RELATIONSHIP for unsupported types' do
- expect(FamilyTree.instance.get_relationship('Anna', 'uncle')).to eq('UNSUPPORTED_RELATIONSHIP')
+ it 'returns false for unsupported types' do
+ expect(FamilyTree.instance.get_relationship('Anna', 'uncle')).to eq(false)
end
end
end