· 8 years ago · Jun 20, 2018, 12:50 PM
1# Caren Entities
2
3## User
4This is the entity that represents the individual who has the ability to 'login' using a device.
5
6#### Generator
7The scaffolding for this entity is currently being generated by the Devise gem.
8
9```bash
10rails g devise:install
11```
12
13However, we do need some extra fields for our user, namely, `first_name` and `last_name`. For this we need to modify the `devise_create_users` migration. Add:
14
15```ruby
16#Custom
17t.string :first_name
18t.string :last_name
19```
20
21#### Associations
22```ruby
23has_many :circles, foreign_key: "super_admin"
24has_many :positions
25```
26The `has_many :circles` association gives the `User` the following functionality.
27
28```ruby
29# returns an array of Circle objects.
30user = User.first
31user.circles
32```
33
34This functionality is used to identify which circles that user has created. This, however, is dependent on the `Circle` being set up correctly.
35
36The `has_many :positions` associates provides the following functionality:
37
38```ruby
39# returns an array of Position objects
40user = User.first
41user.positions
42```
43
44## Circle
45This is the entity that represents the 'project'. It is the glue that connects the users, tasks, posts, and everything else associated with taking care a single individual. This cannot exist without a `User` having created it first. Other users can then be associated with it.
46
47#### Generator
48The `User` who creates the `Circle` is known as the <b>Super Admin</b>. We want a `Circle` object to have the following functionality:
49
50```ruby
51# return a single user
52circle = Circle.first
53circle.super_admin
54```
55We need to do some manual tweaking to achieve this. First, run the generator:
56
57```bash
58rails g scaffold circle super_admin_id:integer
59```
60
61In the generated migration add the following line after the `change` block:
62
63```ruby
64add_foreign_key :circles, :users, column: :super_admin_id
65```
66The super_admin column is now a foreign key that points to the users table.
67
68#### Associations
69```ruby
70belongs_to :super_admin, class_name: 'User'
71has_many :positions
72has_many :task_generators
73has_many :important_info_pieces
74has_many :posts
75has_many :tasks
76```
77The `Circle` entity has a `belongs_to :super_admin` association. However, it needs to point to the `User` model, hence the following line.
78
79```ruby
80belongs_to :super_admin, class_name: 'User'
81```
82
83As it is a foreign key and a `super_admin` is required, a `Circle` must be created with an associated `User`. It must be created in the following manner or it will fail validation:
84
85```ruby
86#create a new circle using the logged in user as super admin.
87user = current_user
88Circle.create(super_admin: user)
89```
90
91We can now retrieve the super admin of a specific `Circle`:
92
93```ruby
94# return single super_admin
95Circle.first.super_admin
96```
97
98It also has a `has_many: positions` association. This gives us the ability to list all associated `Users` and `Roles`. That would look something like the following:
99
100```ruby
101# return an array of Position objects
102Circle.first.positions
103
104# return the role and name of users associated with a circle.
105Circle.first.positions.last.role.name
106Circle.first.positions.last.user.name
107
108# "Captain"
109# "Joey JoJo Jr. Shabadoo"
110```
111
112## Role
113This is the entity that represents a certain level of authorization. These are really just titles and nothing more. They are useful because they can be associated with a position (and by extension a user) and with authorization parameters. Users will not be able to create or modify these in anyway. They are created by the development team. Authorization is currently being handled through the use of the Pundit gem.
114
115#### Generator
116```bash
117rails g scaffold role name:string
118```
119
120#### Associations
121Roles are not dependent on anything. They are associated with positions but there is no real need to see every position associated with a role (`role.positions`) so there is currently no association given.
122
123## Category
124This entity is another 'title-only' item. What gives it its usefulness is the association it can have with `Task` objects and `ImportantInfoPiece` objects.
125
126```bash
127rails g scaffold category name:string
128```
129
130## Position
131This is the entity that represents a specific user and the role he/she plays when it comes to a `Circle`. This is not to be confused with a `Role`. A `Position` is a single, unique item that is associates a `User` with a `Role` under a specific `Circle`.
132
133#### Generator
134```bash
135rails g scaffold position role:references circle:references user:references
136```
137
138#### Associations
139```ruby
140belongs_to :role
141belongs_to :user, optional: true
142belongs_to :circle
143
144has_one :invitation
145```
146
147Some of the Position associations are a bit counter-intuitive. Specifically the `belongs_to :role` association. It's tempting to think that a role `belongs_to` a position or that a position `has_one` role. However, because we want the position to have a role method (`position.role`) the `role_id` attribute must be given to the position. This attribute is what dictates the the `belongs_to: role` association.
148
149Create a position like so:
150
151```ruby
152role = Role.first
153circle = Circle.first
154user = User.first # optional
155
156Position.create(user: user, role:role, circle: circle)
157
158# returns an array of positions
159Circle.first.positions
160```
161
162Also, take note of the `optional: true` argument for the `user` association. It is likely that role will be created for a user who has not yet created an account. This optional parameter allows a position object to be created without having to specify the user immediately.
163
164The `Position` also `belongs_to :circle`. This is here for the sake of an `Invitation` which references a `Position`. When a user gets an invitation to fulfill a specific position, we have access to the `Circle` that position belongs to.
165
166As well, we can get information about a positions invitation (like whether or not it has been seen, rejected, etc) through the `has_one :invitation` association.
167
168```ruby
169# returns an Invitation object
170position = Position.first
171positon.invitation
172```
173
174## Invitation
175This entity represents the association of two `Users` and a `Circle`. It does this through a specific `position` (which belongs to a `Circle`.) When created, a recieving `User` will have the abiliy to accept the invitation. In doing so, his/her user account will then be associated with a specific `Position` object.
176
177#### Generator
178Creating the proper migration for this entity is a little more tricky than most as we need attributes labeled `sender_id` and `recipient_id` that are both foreign keys for a `User`. Creating a normal migration for this won't work as Rails expects the column name to simply be `user_id`.
179
180Additionally, we have an `email` field that should never be empty This email address field is used to identify if any newly signed up user has any existing invitations.
181Because we are using Postgres we can get away with adding a database constraint (`null: false`)that prevents this field from being empty when a new invitation is created.
182
183Rather than using the `references` datatype, create the column name and type manually.
184
185```bash
186rails g scaffold invitation accepted:boolean rejected:boolean position:references sender_id:integer recipient_id:integer email:string
187```
188The created migration file will need to be modified. Open it and add the following lines.
189```ruby
190# The default values
191t.boolean :accepted, default: false
192t.boolean :rejected, default: false
193
194# The email constraint
195t.string :email, null: false
196```
197
198And added to the bottom of the `change` method, after the `create_table` block:
199
200```ruby
201add_foreign_key :invitations, :users, column: :sender_id
202add_foreign_key :invitations, :users, column: :recipient_id
203```
204
205Those lines inform Rails that the invitations table is to use the `sender_id` and `recipient_id` columns as foreign keys to the `users` table.
206
207The migration is now possible but if we try and use it in this state, Rails will end up looking for `Sender` and `Recipient` classes. To prevent this we need the following:
208
209#### Associations
210```ruby
211belongs_to :position
212belongs_to :sender, class_name: 'User'
213belongs_to :recipient, class_name: 'User', optional: true
214```
215
216An `Invitation` is created like this:
217```ruby
218# recipient DOES NOT yet exist
219position = Position.first
220user = User.first
221
222Invitation.create(
223 position: position,
224 sender: user,
225 email: "caren@carecrew.ca"
226)
227
228# recipient DOES exist
229recipient = User.last
230
231Invitation.create(
232 position: position,
233 sender: user,
234 email: recipient.email,
235 recipient: recipient
236)
237```
238
239## Task Generator
240This entity is responsible for creating new `Task` objects based on the values provided by the `User`. It is different than a task in that it should be thought of as a 'task factory' that creates task objects for reoccuring tasks.
241
242#### Generator
243```bash
244rails g scaffold task_generator description:text category:references circle:references created_by_id:integer mandatory:boolean every_n:integer sun:boolean mon:boolean tues:boolean wed:boolean thurs:boolean fri:boolean sat:boolean part_of_day:integer custom_time:time last_run:timestamp look_ahead:integer
245```
246
247Modify the migration file as before to turn the `created_by` column into a foreign key:
248
249```ruby
250add_foreign_key :task_generators, :users, column: :created_by_id
251```
252
253There are also a number of default values and a required description field as seen below.
254
255```ruby
256t.text :description, null: false
257t.references :category, foreign_key: true
258t.references :circle, foreign_key: true
259t.integer :created_by_id
260t.boolean :mandatory, default: false
261t.integer :every_n, default: 1
262t.boolean :sun, default: false
263t.boolean :mon, default: false
264t.boolean :tues, default: false
265t.boolean :wed, default: false
266t.boolean :thurs, default: false
267t.boolean :fri, default: false
268t.boolean :sat, default: false
269t.integer :part_of_day, default: 1
270t.time :custom_time
271t.timestamp :last_run
272t.integer :look_ahead, default: 7
273
274t.timestamps
275```
276#### Associations
277The assoications are as seen below.
278
279```ruby
280belongs_to :category
281belongs_to :circle
282belongs_to :created_by, class_name: 'User'
283```
284
285We now have the following functionality:
286
287```ruby
288# Create a new TaskGenerator object.
289TaskGenerator.create(
290 description: 'Rake leaves',
291 category: Cateory.first,
292 circle: current_circle,
293 created_by: current_user
294)
295
296# Retrieve all TaskGenerators for a given Circle
297Circle.first.task_generators
298
299# Identify the user who created a specific Task Generator
300TaskGenerator.first.created_by
301```
302
303
304## Important Info Piece
305This entity represents the pieces of critical information each `User` within a given `Circle` needs to be aware of. The list of `Important Info Piece` objects for a `Circle` will be consolidated under the 'about' tab, or something more meaningful such as 'Need To Know'.
306
307It also has its own array (thanks to PostgreSQL) that contains the ids of the Users who have acknowledged the newly added piece of information.
308
309#### Generator
310```bash
311rails g scaffold important_info_piece description:text category:references circle:references created_by_id:integer seen_by:integer
312```
313The migration will need changes to require the description field and add the array functionality for the `seen_by` attribute.
314
315```ruby
316t.string :description, null: false
317t.integer :seen_by, array: true, default: []
318```
319
320As usual, it also needs the foreign key constraint added.
321```
322add_foreign_key :important_info_pieces, :users, column: :created_by_id
323```
324#### Associations
325```ruby
326belongs_to :category
327belongs_to :circle
328belongs_to :created_by, class_name: 'User'
329```
330
331This gives us the following functionality:
332
333```ruby
334ImportantInfoPiece.create(
335 description: 'Has fragile bones',
336 category: Category.last,
337 created_by: User.last,
338 circle: current_circle
339)
340
341# retrieve an array of important info pieces for a specific circle.
342circle = Circle.all[3]
343circle.important_info_pieces
344```
345## Post
346This is the entity previously refered to as 'log'. I'm using `Post` as it is more descriptive of the entity's actual use. Currently, it represents some text and can be tagged as a 'medical' related post. The `User` who posted it and the time are also indicated.
347
348#### Generator
349```bash
350rails g scaffold post description:text circle:references user:references medical:boolean
351```
352
353The obligatory changes to the migration file:
354
355```ruby
356t.text :description, null: false
357t.boolean :medical, default: false
358```
359
360#### Associations
361Nice and simple.
362
363```ruby
364belongs_to :circle
365belongs_to :user
366```
367
368We now have the functionality to see all posts associated with a given `Circle`, and also, if need be, to display `Post` objects by a given user, regardless of circle.
369
370```ruby
371Post.create(
372 description: 'I love posting things.',
373 circle: current_circle,
374 user: current_user
375)
376
377# retrieve an array of posts for a given circle
378Circle.all[14].posts
379
380# retrive an array of posts for a specific user
381User.find(first_name: 'Timmy').posts
382```
383
384## Task
385This entity represents the literal, single-time task that is to be performed. A single `Task` object can be created directly by a `User` or a by a `TaskGenerator` (if it is a reoccuring task).
386
387Once marked complete by a user the `Task` object will remain editable (the only thing editable is the `complete` attribute) for a period of time (24 hours after the 'updated_at' timestamp). After that, the task is archived/deleted and a `TaskReport` object is generated. A `Task` object is also removed and reported if it expires.
388
389#### Generator
390```bash
391rails g scaffold task description:text expiration_date:datetime completed:boolean completed_by_id:integer created_by_id:integer category:references task_generator:references circle:references
392```
393
394Migrations file alterations:
395
396```ruby
397t.text :description, null: false
398t.boolean :completed, default: false
399
400# outside the change block
401add_foreign_key :tasks, :users, column: :created_by_id
402add_foreign_key :tasks, :users, column: :completed_by_id
403```
404
405#### Associations
406```ruby
407belongs_to :category
408belongs_to :task_generator, optional: true
409belongs_to :circle
410belongs_to :created_by, class_name: 'User'
411belongs_to :completed_by, class_name: 'User', optional: true
412```
413
414The following functionality now exists:
415
416```ruby
417Task.create(
418 description: 'Clean the dishes',
419 category: Category.first,
420 circle: Circle.first,
421 created_by: User.first
422)
423
424# retrieve an array of task objects for a given circle.
425Circle.first.tasks
426```
427
428/////The following are unrefined////
429## User Report
430
431This entity represents a User that is meant to persist beyond a User's account. In the case a User delete's his/her account, a record of the User's UUID and other critical data will be kept intact.
432
433Thought needs to be given as what information needs to be exactly. What happens when a User edits their profile for example and changes their name or email?
434
435```bash
436rails g scaffold user_report uuid:string
437```
438
439### Task Report
440
441This entity is generated when a Task has been marked completed for 24 hours or it expires.
442Currently it will be a simple description of the task and if/when it was completed. The data associated with a Task needs to be stringified to prevent changes (eg. the User who completed the task needs to be have their name stringified so if they delete their account the data still remains).
443
444```bash
445rails g scaffold task_report description:text
446```
447
448
449### Cirle Report
450
451This entity represents a Circle that is meant to persist beyond the actual circle. It should
452reference the unique circle along with all Users involved.
453
454Again, thought needs to be given to exactly what information needs to be saved and what happens when data is edited.
455
456```bash
457rails g scaffold circle_report uuid:string
458```