dsu 0.1.0.alpha.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c27f3abb1daf923976963a1612f08258fd8a0460d672c1674dc2c149aa69ed03
4
- data.tar.gz: 8611ccb51518e24f9497ffc0c02a7b1dd567321419728f727969718620da5b59
3
+ metadata.gz: fa1b6f69c4de21d556a38cd96f29ba9ad8b7b4b2a9f5d34cda7fccb61f15d63f
4
+ data.tar.gz: 7376022ead177228164448d6fa4b8ee554a35611d45450955c586ed1bf84462a
5
5
  SHA512:
6
- metadata.gz: d1b1bcd4f9222bf2629ffa0d02434b429280f20e760676f2cde2eaca40860f56952995ce7a69628981cc0f7caf941a48c07cca91a9c1f96b4334e872d5a7374a
7
- data.tar.gz: 0436e614b146dbfd71ca098cc699d7b6de99cafe47cfbad8141ac6268853b6f7d68285d8222053796bb2db729cf86c14e03e3fa3f2dca97554fd78998449fbbe
6
+ metadata.gz: 7128d03202e5f9536e6fda51c2dab009dd0ed32f56f00e17b6d1c96cb5420daa8dd0ab71c69d25f811ee41acf14cda05d133211f3b9480b70907736201d7162b
7
+ data.tar.gz: bc276d357add40377ec88b79b8379fad502cea936440c31a81e7e0ed18c1ad766244f0a09b9eb33a1536a0d60d2c7140b429d5643aa6a3d51b2f362cdec60673
data/.rubocop.yml CHANGED
@@ -10,7 +10,7 @@ AllCops:
10
10
  - '.idea/**/*'
11
11
  - 'init/*'
12
12
  - 'Rakefile'
13
- # - '*.gemspec'
13
+ - '*.gemspec'
14
14
  # - 'spec/**/*'
15
15
  - 'vendor/**/*'
16
16
  - 'scratch*.rb'
data/CHANGELOG.md CHANGED
@@ -1,3 +1,20 @@
1
+ ## [1.0.0] - 2023-05-18
2
+ * First official release.
3
+ * NOTE: If you have been using the alpha version of `dsu`, you will need to delete the `entries` folder (e.g. `/Users/<whoami>/dsu/entries` on a nix os) as the old entries .json files are incompatible with this official release.
4
+ * Changes from the alpha version
5
+ - When editing an entry group, editor commands are no longer necessary. Simply add, remove or change entries as needed. See the README.md for more information.
6
+ - When editing an entry group and saving the results, take note of the folowing behavior:
7
+ - Entering duplicate entries are not allowed, only one entry with a given description is allowed per entry group.
8
+ - Entering entries whose descriptions are < 2 or > 256 characters will fail validation and will not be saved.
9
+ - When editing and encountering any of the aforementioned, the errors will be displayed to the console after the editor file is saved.
10
+ - Sha's are no longer being used, as I've not found a good use (currently) to use them. I may add them back in the future if I find a good use for them (tracking entries across entry groups, etc.).
11
+ - When adding an entry (`dsu add OPTION`), it used to be that after the entry was added, the entry group for the date being edited would be displayed, as well as "yesterday's" date. This is no longer the case; now, only the entry group for the date being edited is displayed.
12
+ ## [0.1.0.alpha.5] - 2023-05-12
13
+ * Changes
14
+ - `dsu edit SUBCOMMAND` will now allow editing of an entry group for a date that does not yet exist. This will allow you to add entries in the editor using `+|a|add DESCRIPTION`. Be sure to follow the instructions in the editor when editing entry group entries.
15
+ - `dsu edit SUBCOMMAND` will gracefully display an error if the entry sha (Entry#uuid) or entry discription (Entry#description) are not unique. In this case, the entry will not be added to the entry group.
16
+ NOTE: Not all edge cases are being handled currently by `dsu edit SUBCOMMAND`.
17
+ - `dsu add OPTION` will raise an error if the entry discription (Entry#description) are not unique. This will be handled gracefully in a future release.
1
18
  ## [0.1.0.alpha.4] - 2023-05-09
2
19
  * Changes
3
20
  - Gemfile gemspec description changes.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dsu (0.1.0.alpha.4)
4
+ dsu (1.0.0)
5
5
  activesupport (~> 7.0, >= 7.0.4)
6
6
  colorize (~> 0.8.1)
7
7
  deco_lite (~> 1.3)
data/README.md CHANGED
@@ -9,8 +9,6 @@
9
9
  ## About
10
10
  `dsu` is little gem that helps manage your Agile DSU (Daily Stand Up) participation. How? by providing a simple command line interface (CLI) which allows you to create, read, update, and delete (CRUD) noteworthy activities that you performed during your day. During your DSU, you can then easily recall and share these these activities with your team. Activities are grouped by day and can be viewed in simple text format from the command line. When viewing a particular day, `dsu` will automatically display the previous day's activities as well. This is useful for remembering what you did yesterday, so you can share your "Today" and "Yesterday" activities with your team during your DSU. If the day you are trying to view falls on a weekend or Monday, `dsu` will display back to, and including the weekend and previous Friday inclusive, so that you can share what you did over the weekend (if anything) and the previous Friday.
11
11
 
12
- **NOTE:** This gem is in development (alpha version). Please see the [WIP Notes](#wip-notes) section for current `dsu` features.
13
-
14
12
  ## Help
15
13
 
16
14
  After installation (`gem install dsu`), the first thing you may want to do is run the `dsu` help:
@@ -19,7 +17,7 @@ After installation (`gem install dsu`), the first thing you may want to do is ru
19
17
  ```shell
20
18
  #=>
21
19
  Commands:
22
- dsu add, -a [OPTIONS] DESCRIPTION # Adds a DSU entry having DESCRIPTION to the date associated with the given OPTION
20
+ dsu add, -a [OPTIONS] DESCRIPTION # Adds a DSU entry...
23
21
  dsu config, -c SUBCOMMAND # Manage configuration...
24
22
  dsu edit, -e SUBCOMMAND # Edit DSU entries...
25
23
  dsu help [COMMAND] # Describe available...
@@ -35,7 +33,7 @@ The next thing you may want to do is `add` some DSU activities (entries) for a p
35
33
  ## Adding DSU Entries
36
34
  `dsu add [OPTIONS] DESCRIPTION`
37
35
 
38
- Adding DSU entry using this command will _add_ the DSU entry for the given day (or date, `-d`), and also _display_ the given day's (or date's, `-d`) DSU entries, as well as the DSU entries for the previous day relative to the given day or date (`-d`).
36
+ Adding DSU entry using this command will _add_ the DSU entry for the given day (or date, `-d`), and also _display_ the given day's (or date's, `-d`) DSU entries, as well as the DSU entries for the previous day relative to the given day or date (`-d`). *NOTE: You cannot add duplicate entry group entries; that is, the entry DESCRIPTION needs to be unique within an entry group.*
39
37
 
40
38
  ### Today
41
39
  If you need to add a DSU entry to the current day (today), you can use the `-n`|`--today` option. Today (`-n`) is the default; therefore, the `-n` flag is optional when adding DSU entries for the current day:
@@ -97,7 +95,7 @@ Friday, (Yesterday) 2023-05-05
97
95
  ```
98
96
  ## Editing DSU Entries
99
97
 
100
- You can edit DSU entry groups by date. `dsu` will allow you to edit a DSU entry group using the `dsu edit SUBCOMMAND` date (today|tomorrow|yesterday|date DATE) you specify. `dsu edit` will open your DSU entry group entries in your editor, where you'll be able to perform editing functions against one or all of the entries.
98
+ You can edit DSU entry groups by date. `dsu` will allow you to edit a DSU entry group using the `dsu edit SUBCOMMAND` date (today|tomorrow|yesterday|date DATE) you specify. `dsu edit` will open your DSU entry group entries in your editor, where you'll be able to perform editing functions against one or all of the entries. If no entries exist in the entry group the the date, the editor will allow you to add entries for that date. *NOTE: duplicate entries are not allowed; that is, the entry DESCRIPTION need to be unique within an entry group. Non-unique entries will not be added to the entry group. The same holds true for entries whose DESCRIPTION that do not pass validation (between 2 and 256 characters (inclusive) in length).*
101
99
 
102
100
  Note: See the "[Customizing the `dsu` Configuration File](#customizing-the-dsu-configuration-file)"" section to configure `dsu` to use the editor of your choice.
103
101
 
@@ -116,20 +114,19 @@ The following will edit your DSU entry group entries for "Today", where `Time.no
116
114
  ```shell
117
115
  #=> In your editor, you will see...
118
116
  # Editing DSU Entries for Tuesday, (Today) 2023-05-09 EDT
119
-
120
- # [SHA/COMMAND] [DESCRIPTION]
121
- 3849f0c0 Interative planning meeting 11:00AM.
122
- 586478f5 Pair with Chad on ticket 31211.
123
- 9e5f82c8 Investigate spike ticket 31255.
124
- 59b25ca7 Review Kelsey's PR ticket 30721.
117
+ # [ENTRY DESCRIPTION]
118
+ Interative planning meeting 11:00AM.
119
+ Pair with Chad on ticket 31211.
120
+ Investigate spike ticket 31255.
121
+ Review Kelsey's PR ticket 30721.
125
122
 
126
123
  # INSTRUCTIONS:
127
- # ADD a DSU entry: use one of the following commands: [+|a|add] followed by the description.
128
- # EDIT a DSU entry: change the description.
129
- # DELETE a DSU entry: delete the entry or replace the sha with one of the following commands: [-|d|delete].
130
- # NOTE: deleting all the entries will delete the entry group file;
131
- # this is preferable if this is what you want to do :)
132
- # REORDER a DSU entry: reorder the DSU entries in order preference.
124
+ # ADD a DSU entry: type an ENTRY DESCRIPTION on a new line.
125
+ # EDIT a DSU entry: change the existing ENTRY DESCRIPTION.
126
+ # DELETE a DSU entry: delete the ENTRY DESCRIPTION.
127
+ # NOTE: deleting all of the ENTRY DESCRIPTIONs will delete the entry group file;
128
+ # this is preferable if this is what you want to do :)
129
+ # REORDER a DSU entry: reorder the ENTRY DESCRIPTIONs in order preference.
133
130
  #
134
131
  # *** When you are done, save and close your editor ***
135
132
  ```
@@ -140,32 +137,28 @@ Simply change the entry descripton text.
140
137
 
141
138
  For example...
142
139
  ```
143
- from: 3849f0c0 Interative planning meeting 11:00AM.
144
- to: 3849f0c0 Interative planning meeting 12:00AM.
140
+ from: Interative planning meeting 11:00AM.
141
+ to: Interative planning meeting 12:00AM.
145
142
  ```
146
143
 
147
144
  #### Add an Entry
148
145
 
149
- Replace the entry `sha` one of the add commands: `[+|a|add]`.
146
+ Simply type a new entry on a separate line. *Note: any entry that starts with a `#` in the first character position will be ignored.*
150
147
 
151
148
  For example...
152
149
  ```
153
- + Add me to this entry group.
150
+ Add me to this entry group.
154
151
  ```
155
152
 
156
153
  #### Delete an Entry
157
154
 
158
- Simply delete the entry or replace the entry `sha` one of the delete commands: `[-|d|delete]`.
155
+ Simply delete the entry.
159
156
 
160
157
  For example...
161
158
  ```
162
159
  # Delete this this entry from the editor file
163
- 3849f0c0 Interative planning meeting 11:00AM.
164
- ```
165
- or
166
- ```
167
- from: 3849f0c0 Interative planning meeting 11:00AM.
168
- to: - Interative planning meeting 11:00AM.
160
+ from: Interative planning meeting 11:00AM.
161
+ to: <deleted>
169
162
  ```
170
163
 
171
164
  #### Reorder Entries
@@ -174,14 +167,14 @@ Simply reorder the entries in the editor.
174
167
 
175
168
  For example...
176
169
  ```
177
- from: 3849f0c0 Interative planning meeting 11:00AM.
178
- 586478f5 Pair with Chad on ticket 31211.
179
- 9e5f82c8 Investigate spike ticket 31255.
180
- 59b25ca7 Review Kelsey's PR ticket 30721.
181
- to: 59b25ca7 Review Kelsey's PR ticket 30721.
182
- 9e5f82c8 Investigate spike ticket 31255.
183
- 586478f5 Pair with Chad on ticket 31211.
184
- 3849f0c0 Interative planning meeting 11:00AM.
170
+ from: Interative planning meeting 11:00AM.
171
+ Pair with Chad on ticket 31211.
172
+ Investigate spike ticket 31255.
173
+ Review Kelsey's PR ticket 30721.
174
+ to: Review Kelsey's PR ticket 30721.
175
+ Investigate spike ticket 31255.
176
+ Pair with Chad on ticket 31211.
177
+ Interative planning meeting 11:00AM.
185
178
  ```
186
179
 
187
180
  ## Customizing the `dsu` Configuration File
@@ -242,9 +235,6 @@ These notes apply to anywhere DATE is used...
242
235
 
243
236
  DATE may be any date string that can be parsed using `Time.parse`. Consequently, you may use also use '/' as date separators, as well as omit the year if the date you want to display is the current year (e.g. <month>/<day>, or 1/31). For example: `require 'time'; Time.parse('2023-01-02'); Time.parse('1/2') # etc.`
244
237
 
245
- ## WIP Notes
246
- This gem is in development (alpha release).
247
-
248
238
  ## Installation
249
239
 
250
240
  $ gem install dsu
data/lib/dsu/base_cli.rb CHANGED
@@ -15,9 +15,6 @@ require_relative 'version'
15
15
  require_relative 'views/entry_group/show'
16
16
 
17
17
  module Dsu
18
- #
19
- # The `dsu` command.
20
- #
21
18
  class BaseCLI < ::Thor
22
19
  include Support::Colorable
23
20
  include Support::EntryGroupViewable
data/lib/dsu/cli.rb CHANGED
@@ -30,14 +30,21 @@ module Dsu
30
30
  \x5
31
31
  -d DATE: Adds a DSU entry having DESCRIPTION to the DATE.
32
32
 
33
- \x5 #{date_option_description}
33
+ \x5
34
+ #{date_option_description}
34
35
 
35
- \x5 -n: Adds a DSU entry having DESCRIPTION to today's date (`Time.now`).
36
+ \x5
37
+ -n: Adds a DSU entry having DESCRIPTION to today's date (`Time.now`).
36
38
 
37
- \x5 -t: Adds a DSU entry having DESCRIPTION to tomorrow's date (`Time.new.tomorrow`).
39
+ \x5
40
+ -t: Adds a DSU entry having DESCRIPTION to tomorrow's date (`Time.new.tomorrow`).
38
41
 
39
- \x5 -y: Adds a DSU entry having DESCRIPTION to yesterday's date (`Time.new.yesterday`).
42
+ \x5
43
+ -y: Adds a DSU entry having DESCRIPTION to yesterday's date (`Time.new.yesterday`).
40
44
 
45
+ DESCRIPTION:
46
+ \x5
47
+ Must be be between 2 and 256 characters (inclusive) in length.
41
48
  LONG_DESC
42
49
  option :date, type: :string, aliases: '-d'
43
50
  option :tomorrow, type: :boolean, aliases: '-t'
@@ -45,52 +52,50 @@ module Dsu
45
52
  option :today, type: :boolean, aliases: '-n', default: true
46
53
 
47
54
  def add(description)
48
- times = if options[:date].present?
49
- time = Time.parse(options[:date])
50
- [time, time.yesterday]
55
+ time = if options[:date].present?
56
+ Time.parse(options[:date])
51
57
  else
52
- time = Time.now
53
58
  if options[:tomorrow].present?
54
- [time.tomorrow, time.tomorrow.yesterday]
59
+ Time.now.tomorrow
55
60
  elsif options[:yesterday].present?
56
- [time.yesterday, time.yesterday.yesterday]
61
+ Time.now.yesterday
57
62
  elsif options[:today].present?
58
- [time, time.yesterday]
63
+ Time.now
59
64
  end
60
65
  end
61
66
  entry = Models::Entry.new(description: description)
62
- # NOTE: We need to add the Entry to the date that is the furthest in the future
63
- # (time.max) because this is the DSU entry that the user specified.
64
- CommandServices::AddEntryService.new(entry: entry, time: times.max).call
65
- sorted_dsu_times_for(times: times).each do |t|
66
- view_entry_group(time: t)
67
- puts
68
- end
67
+ CommandServices::AddEntryService.new(entry: entry, time: time).call
68
+ view_entry_group(time: time)
69
69
  end
70
70
 
71
+ # def add(description)
72
+ # times = if options[:date].present?
73
+ # time = Time.parse(options[:date])
74
+ # [time, time.yesterday]
75
+ # else
76
+ # time = Time.now
77
+ # if options[:tomorrow].present?
78
+ # [time.tomorrow, time.tomorrow.yesterday]
79
+ # elsif options[:yesterday].present?
80
+ # [time.yesterday, time.yesterday.yesterday]
81
+ # elsif options[:today].present?
82
+ # [time, time.yesterday]
83
+ # end
84
+ # end
85
+ # entry = Models::Entry.new(description: description)
86
+ # # NOTE: We need to add the Entry to the date that is the furthest in the future
87
+ # # (time.max) because this is the DSU entry that the user specified.
88
+ # CommandServices::AddEntryService.new(entry: entry, time: times.max).call
89
+ # sorted_dsu_times_for(times: times).each do |t|
90
+ # view_entry_group(time: t)
91
+ # puts
92
+ # end
93
+ # end
94
+
71
95
  desc 'list, -l SUBCOMMAND',
72
96
  'Displays DSU entries for the given SUBCOMMAND'
73
97
  subcommand :list, Subcommands::List
74
98
 
75
- # TODO: Implement this.
76
- # desc 'interactive', 'Opens a DSU interactive session'
77
- # long_desc ''
78
- # option :next_day, type: :boolean, aliases: '-n'
79
- # option :previous_day, type: :boolean, aliases: '-p'
80
- # option :today, type: :boolean, aliases: '-t'
81
-
82
- # # https://stackoverflow.com/questions/4604905/interactive-prompt-with-thor
83
- # def interactive
84
- # exit_commands = %w[x q exit quit]
85
- # display_interactive_help
86
- # loop do
87
- # command = ask('dsu > ')
88
- # display_interactive_help if command == 'h'
89
- # break if exit_commands.include? command
90
- # end
91
- # say 'Done.'
92
- # end
93
-
94
99
  desc 'config, -c SUBCOMMAND',
95
100
  'Manage configuration file for this gem'
96
101
  subcommand :config, Subcommands::Config
@@ -104,17 +109,5 @@ module Dsu
104
109
  def version
105
110
  say VERSION
106
111
  end
107
-
108
- private
109
-
110
- def display_interactive_help
111
- say 'Interactive Mode Commands:'
112
- say '---'
113
- say '[h]: show this help screen'
114
- say '[t]: next day'
115
- say '[y]: previous day'
116
- say '[n]: today'
117
- say '[x|q|exit|quit]: Exit interactive mode'
118
- end
119
112
  end
120
113
  end
@@ -1,60 +1,60 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../services/entry_group_writer_service'
4
3
  require_relative '../models/entry'
4
+ require_relative '../services/entry_group_writer_service'
5
+ require_relative '../support/colorable'
6
+ require_relative '../support/descriptable'
5
7
  require_relative '../support/entry_group_loadable'
6
8
  require_relative '../support/folder_locations'
9
+ require_relative '../support/say'
10
+ require_relative '../views/shared/messages'
7
11
 
8
12
  module Dsu
9
13
  module CommandServices
10
- # This class adds (does NOT update) an entry to an entry group.
14
+ # This class adds (does NOT update) an entry to an entry group by
15
+ # writing it to the appropriate entry group json file.
11
16
  class AddEntryService
12
- include Dsu::Support::EntryGroupLoadable
13
- include Dsu::Support::FolderLocations
17
+ include Support::Colorable
18
+ include Support::Descriptable
19
+ include Support::EntryGroupLoadable
20
+ include Support::FolderLocations
21
+ include Support::Say
14
22
 
15
23
  attr_reader :entry, :entry_group, :time
16
24
 
25
+ delegate :description, to: :entry
26
+
17
27
  # :entry is an Entry object
18
28
  # :time is a Time object; the time of the entry group.
19
29
  def initialize(entry:, time:)
20
30
  raise ArgumentError, 'entry is nil' if entry.nil?
21
- raise ArgumentError, 'entry is the wrong object type' unless entry.is_a?(Dsu::Models::Entry)
31
+ raise ArgumentError, 'entry is the wrong object type' unless entry.is_a?(Models::Entry)
22
32
  raise ArgumentError, 'time is nil' if time.nil?
23
33
  raise ArgumentError, 'time is the wrong object type' unless time.is_a?(Time)
24
34
 
25
35
  @entry = entry
26
36
  @time = time
27
- @entry_group = Dsu::Models::EntryGroup.load(time: time)
37
+ @entry_group = Models::EntryGroup.load(time: time)
28
38
  end
29
39
 
30
40
  def call
31
41
  entry.validate!
32
42
  save_entry_group!
33
- entry.uuid
34
- rescue ActiveModel::ValidationError
35
- puts "Error(s) encountered: #{entry.errors.full_messages}"
36
- raise
43
+ entry
44
+ rescue ActiveModel::ValidationError => e
45
+ header = 'An error was encountered; the entry could not be added added:'
46
+ Views::Shared::Messages.new(messages: e.message, message_type: :error, options: { header: header }).render
37
47
  end
38
48
 
39
49
  private
40
50
 
41
51
  attr_writer :entry, :entry_group, :time
42
52
 
43
- def entry_exists?
44
- @entry_exists ||= entry_group.entries.include? entry.uuid
45
- end
46
-
47
- def entry_group_hash
48
- @entry_group_hash ||= entry_group_hash_for time: time
49
- end
50
-
51
53
  def save_entry_group!
52
- raise "Entry #{entry.uuid} already exists in entry group #{time}" if entry_exists?
53
-
54
54
  entry_group.entries << entry
55
55
  entry_group.validate!
56
56
 
57
- Dsu::Services::EntryGroupWriterService.new(entry_group: entry_group).call
57
+ Services::EntryGroupWriterService.new(entry_group: entry_group).call
58
58
  end
59
59
  end
60
60
  end
@@ -1,39 +1,56 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'deco_lite'
3
+ require 'active_model'
4
4
  require 'securerandom'
5
+ require_relative '../support/descriptable'
6
+ require_relative '../validators/description_validator'
5
7
 
6
8
  module Dsu
7
9
  module Models
8
- class Entry < DecoLite::Model
9
- validates :uuid, presence: true, format: {
10
- with: /\A[0-9a-f]{8}\z/i,
11
- message: 'is the wrong format. ' \
12
- '0-9, a-f, and 8 characters were expected.' \
13
- }
14
- validates :description, presence: true, length: { minimum: 2, maximum: 256 }
15
-
16
- def initialize(description:, uuid: nil)
17
- raise ArgumentError, 'description is nil' if description.nil?
10
+ # This class represents something someone might want to share at their
11
+ # daily standup (DSU).
12
+ class Entry
13
+ include ActiveModel::Model
14
+ include Support::Descriptable
15
+
16
+ validates_with Validators::DescriptionValidator
17
+
18
+ attr_reader :description
19
+
20
+ def initialize(description:)
18
21
  raise ArgumentError, 'description is the wrong object type' unless description.is_a?(String)
19
- raise ArgumentError, 'uuid is the wrong object type' unless uuid.is_a?(String) || uuid.nil?
20
22
 
21
- uuid ||= SecureRandom.uuid[0..7]
23
+ self.description = description
24
+ end
25
+
26
+ class << self
27
+ def clean_description(description)
28
+ return if description.nil?
22
29
 
23
- super(hash: {
24
- uuid: uuid,
25
- description: description
26
- })
30
+ description.strip.gsub(/\s+/, ' ')
31
+ end
27
32
  end
28
33
 
29
- def required_fields
30
- %i[uuid description]
34
+ def description=(description)
35
+ @description = self.class.clean_description description
31
36
  end
32
37
 
38
+ def to_h
39
+ { description: description }
40
+ end
41
+
42
+ # Override == and hash so that we can compare Entry objects based
43
+ # on description alone. This is useful for comparing entries in
44
+ # an array, for example.
33
45
  def ==(other)
34
46
  return false unless other.is_a?(Entry)
35
47
 
36
- uuid == other.uuid && description == other.description
48
+ description == other.description
49
+ end
50
+ alias eql? ==
51
+
52
+ def hash
53
+ description.hash
37
54
  end
38
55
  end
39
56
  end
@@ -1,41 +1,53 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'deco_lite'
4
- require_relative '../support/entry_group_loadable'
3
+ require 'active_model'
4
+ require_relative '../services/entry_group_editor_service'
5
5
  require_relative '../services/entry_group_deleter_service'
6
6
  require_relative '../services/entry_group_reader_service'
7
7
  require_relative '../services/entry_group_writer_service'
8
+ require_relative '../support/entry_group_loadable'
9
+ require_relative '../support/time_formatable'
8
10
  require_relative '../validators/entries_validator'
9
11
  require_relative '../validators/time_validator'
12
+ require_relative 'entry'
10
13
 
11
14
  module Dsu
12
15
  module Models
13
- class EntryGroup < DecoLite::Model
16
+ # This class represents a group of entries for a given day. IOW,
17
+ # things someone might want to share at their daily standup (DSU).
18
+ class EntryGroup
19
+ include ActiveModel::Model
14
20
  extend Support::EntryGroupLoadable
21
+ include Support::TimeFormatable
15
22
 
16
- validates_with Validators::EntriesValidator, fields: [:entries]
17
- validates_with Validators::TimeValidator, fields: [:time]
23
+ attr_accessor :time
24
+ attr_reader :entries
25
+
26
+ validates_with Validators::EntriesValidator
27
+ validates_with Validators::TimeValidator
18
28
 
19
29
  def initialize(time: nil, entries: [])
20
30
  raise ArgumentError, 'time is the wrong object type' unless time.is_a?(Time) || time.nil?
21
- raise ArgumentError, 'entries is the wrong object type' unless entries.is_a?(Array) || entries.nil?
22
-
23
- time ||= Time.now
24
- time = time.localtime if time.utc?
25
31
 
26
- entries ||= []
27
-
28
- super(hash: {
29
- time: time,
30
- entries: entries
31
- })
32
+ @time = ensure_local_time(time)
33
+ self.entries = entries || []
32
34
  end
33
35
 
34
36
  class << self
35
- def delete(time:, options: {})
37
+ def delete!(time:, options: {})
36
38
  Services::EntryGroupDeleterService.new(time: time, options: options).call
37
39
  end
38
40
 
41
+ def edit(time:, options: {})
42
+ # NOTE: Uncomment this line to prohibit edits on
43
+ # Entry Groups that do not exist (i.e. have no entries).
44
+ # return new(time: time) unless exists?(time: time)
45
+
46
+ load(time: time).tap do |entry_group|
47
+ Services::EntryGroupEditorService.new(entry_group: entry_group, options: options).call
48
+ end
49
+ end
50
+
39
51
  def exists?(time:)
40
52
  Dsu::Services::EntryGroupReaderService.entry_group_file_exists?(time: time)
41
53
  end
@@ -43,47 +55,63 @@ module Dsu
43
55
  # Loads the EntryGroup from the file system and returns an
44
56
  # instantiated EntryGroup object.
45
57
  def load(time: nil)
46
- new(**hydrated_entry_group_hash_for(time: time))
58
+ load_entry_group_file_for(time: time)
47
59
  end
48
60
 
49
61
  # This function returns a hash whose :time and :entries
50
62
  # key values are hydrated with instantiated Time and Entry
51
63
  # objects.
52
- def hydrated_entry_group_hash_for(time:)
53
- entry_group_hash = entry_group_hash_for(time: time)
54
- hydrate_entry_group_hash(entry_group_hash: entry_group_hash, time: time)
64
+ def load_entry_group_for(time:)
65
+ load_entry_group_file_for(time: time)
55
66
  end
56
67
  end
57
68
 
58
- def required_fields
59
- %i[time entries]
69
+ def valid_unique_entries
70
+ entries&.select(&:valid?)&.uniq(&:description)
60
71
  end
61
72
 
62
- def save!
63
- validate!
64
- Services::EntryGroupWriterService.new(entry_group: self).call
65
- self
73
+ def clone
74
+ clone = super
75
+
76
+ clone.entries = clone.entries.map(&:clone)
77
+ clone
78
+ end
79
+
80
+ def entries=(entries)
81
+ entries ||= []
82
+
83
+ raise ArgumentError, 'entries is the wrong object type' unless entries.is_a?(Array)
84
+ raise ArgumentError, 'entries contains the wrong object type' unless entries.all?(Entry)
85
+
86
+ @entries = entries.map(&:clone)
66
87
  end
67
88
 
68
89
  # Deletes the entry group file from the file system.
69
- def delete
70
- self.class.delete(time: time)
90
+ def delete!
91
+ self.class.delete!(time: time)
92
+ self.entries = []
93
+ self
94
+ end
95
+
96
+ def save!
97
+ delete! and return if entries.empty?
98
+
99
+ validate!
100
+ Services::EntryGroupWriterService.new(entry_group: self).call
71
101
  self
72
102
  end
73
103
 
74
104
  def to_h
75
- super.tap do |hash|
76
- hash[:entries] = hash[:entries].dup
77
- hash[:entries].each_with_index do |entry, index|
78
- hash[:entries][index] = entry.to_h
79
- end
80
- end
105
+ {
106
+ time: time.dup,
107
+ entries: entries.map(&:to_h)
108
+ }
81
109
  end
82
110
 
83
- def to_h_localized
84
- to_h.tap do |hash|
85
- hash[:time] = hash[:time].localtime
86
- end
111
+ private
112
+
113
+ def ensure_local_time(time)
114
+ time.nil? ? Time.now : time.dup.localtime
87
115
  end
88
116
  end
89
117
  end