diff options
| -rw-r--r-- | README.md | 141 | ||||
| -rw-r--r-- | data/actions.txt | 65 | ||||
| -rw-r--r-- | family-tree.png | bin | 0 -> 269964 bytes | |||
| -rw-r--r-- | lib/action_file_executor.rb | 3 | ||||
| -rw-r--r-- | lib/family_tree.rb | 6 | ||||
| -rw-r--r-- | spec/family_tree_spec.rb | 8 |
6 files changed, 162 insertions, 61 deletions
@@ -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: + + + +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 Binary files differnew file mode 100644 index 0000000..2a89364 --- /dev/null +++ b/family-tree.png 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 |
