dsu 0.1.0.alpha.1 → 0.1.0.alpha.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,7 +2,9 @@
2
2
 
3
3
  require 'deco_lite'
4
4
  require_relative '../support/entry_group_loadable'
5
+ require_relative '../services/entry_group_deleter_service'
5
6
  require_relative '../services/entry_group_reader_service'
7
+ require_relative '../services/entry_group_writer_service'
6
8
  require_relative '../validators/entries_validator'
7
9
  require_relative '../validators/time_validator'
8
10
 
@@ -30,10 +32,16 @@ module Dsu
30
32
  end
31
33
 
32
34
  class << self
35
+ def delete(time:, options: {})
36
+ Services::EntryGroupDeleterService.new(time: time, options: options).call
37
+ end
38
+
33
39
  def exists?(time:)
34
40
  Dsu::Services::EntryGroupReaderService.entry_group_file_exists?(time: time)
35
41
  end
36
42
 
43
+ # Loads the EntryGroup from the file system and returns an
44
+ # instantiated EntryGroup object.
37
45
  def load(time: nil)
38
46
  new(**hydrated_entry_group_hash_for(time: time))
39
47
  end
@@ -51,6 +59,18 @@ module Dsu
51
59
  %i[time entries]
52
60
  end
53
61
 
62
+ def save!
63
+ validate!
64
+ Services::EntryGroupWriterService.new(entry_group: self).call
65
+ self
66
+ end
67
+
68
+ # Deletes the entry group file from the file system.
69
+ def delete
70
+ self.class.delete(time: time)
71
+ self
72
+ end
73
+
54
74
  def to_h
55
75
  super.tap do |hash|
56
76
  hash[:entries] = hash[:entries].dup
@@ -11,23 +11,27 @@ module Dsu
11
11
 
12
12
  attr_reader :default_options
13
13
 
14
- def initialize(default_options: Dsu::Support::Configuration::DEFAULT_DSU_OPTIONS)
15
- @default_options = default_options
14
+ def initialize(default_options: nil)
15
+ unless default_options.nil? ||
16
+ default_options.is_a?(Hash) ||
17
+ default_options.is_a?(ActiveSupport::HashWithIndifferentAccess)
18
+ raise ArgumentError, 'default_options must be a Hash or ActiveSupport::HashWithIndifferentAccess'
19
+ end
20
+
21
+ @default_options ||= default_options || {}
22
+ @default_options = @default_options.with_indifferent_access if @default_options.is_a?(Hash)
16
23
  end
17
24
 
18
25
  def call
19
- load_config.merge(default_options || {}).presence&.with_indifferent_access || raise('No configuration options found')
26
+ config_options.merge(default_options).with_indifferent_access
20
27
  end
21
28
 
22
29
  private
23
30
 
24
- attr_writer :default_options
31
+ def config_options
32
+ return Support::Configuration::DEFAULT_DSU_OPTIONS unless config_file?
25
33
 
26
- def load_config
27
- return {} unless config_file?
28
-
29
- yaml_options = File.read(config_file)
30
- YAML.safe_load ERB.new(yaml_options).result
34
+ @config_options ||= YAML.safe_load(ERB.new(File.read(config_file)).result)
31
35
  end
32
36
  end
33
37
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Services
5
+ class TempFileReaderService
6
+ def initialize(temp_file_path:, options: {})
7
+ raise ArgumentError, 'temp_file_path is nil' if temp_file_path.nil?
8
+ raise ArgumentError, 'temp_file_path is the wrong object type' unless temp_file_path.is_a?(String)
9
+ raise ArgumentError, 'temp_file_path is empty' if temp_file_path.empty?
10
+ raise ArgumentError, 'temp_file_path does not exist' unless File.exist?(temp_file_path)
11
+ raise ArgumentError, 'options is nil' if options.nil?
12
+ raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
13
+
14
+ @temp_file_path = temp_file_path
15
+ @options = options || {}
16
+ end
17
+
18
+ def call
19
+ raise ArgumentError, 'no block given' unless block_given?
20
+
21
+ File.foreach(temp_file_path) do |line|
22
+ yield line.strip
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :temp_file_path, :options
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+
5
+ module Dsu
6
+ module Services
7
+ class TempFileWriterService
8
+ def initialize(temp_file_content:, options: {})
9
+ raise ArgumentError, 'temp_file_content is nil' if temp_file_content.nil?
10
+ raise ArgumentError, 'temp_file_content is the wrong object type' unless temp_file_content.is_a?(String)
11
+ raise ArgumentError, 'options is nil' if options.nil?
12
+ raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
13
+
14
+ @temp_file_content = temp_file_content
15
+ @options = options || {}
16
+ end
17
+
18
+ def call
19
+ raise ArgumentError, 'no block given' unless block_given?
20
+
21
+ Tempfile.new('dsu').tap do |file|
22
+ file.write("#{temp_file_content}\n")
23
+ file.close
24
+ yield file.path
25
+ end.unlink
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :temp_file_content, :options
31
+ end
32
+ end
33
+ end
@@ -33,14 +33,44 @@ module Dsu
33
33
  desc 'init', 'Creates and initializes a .dsu file in your home folder'
34
34
  long_desc <<-LONG_DESC
35
35
  NAME
36
- \x5
37
- `dsu config init` -- will create and initialize a .dsu file
38
- in the "#{Dsu::Support::FolderLocations.root_folder}" folder.
36
+
37
+ `dsu config init` -- will create and initialize a .dsu file ("#{Dsu::Support::FolderLocations.root_folder}/.dsu") that you may edit. Otherwise, the default configuration will be used.
39
38
 
40
39
  SYNOPSIS
41
- \x5
40
+
42
41
  dsu config init
42
+
43
+ CONFIGURATION FILE OPTIONS
44
+
45
+ The following configuration file options are available:
46
+
47
+ editor:
48
+
49
+ The default editor to use when editing entry groups if the EDITOR environment variable on your system is not set. The default is 'nano'. You'll need to change the default editor on Windows systems.
50
+
51
+ entries_display_order:
52
+
53
+ The order by which entries will be displayed, 'asc' or 'desc' (ascending or descending, respectively).
54
+
55
+ Default: 'desc'
56
+
57
+ entries_file_name:
58
+
59
+ The entries file name format. It is recommended that you do not change this. The file name must include `%Y`, `%m` and `%d` `Time` formatting specifiers to make sure the file name is unique and able to be located by `dsu` functions. For example, the default file name is `%Y-%m-%d.json`; however, something like `%m-%d-%Y.json` or `entry-group-%m-%d-%Y.json` would work as well.
60
+
61
+ ATTENTION: Please keep in mind that if you change this value `dsu` will not recognize entry files using a different format. You would (at this time), have to manually rename any existing entry file names to the new format.
62
+
63
+ Default: '%Y-%m-%d.json'
64
+
65
+ entries_folder:
66
+
67
+ This is the folder where `dsu` stores entry files. You may change this to anything you want. `dsu` will create this folder for you, as long as your system's write permissions allow this.
68
+
69
+ ATTENTION: Please keep in mind that if you change this value `dsu` will not be able to find entry files in any previous folder. You would (at this time), have to manually mode any existing entry files to this new folder.
70
+
71
+ Default: "'#{Dsu::Support::FolderLocations.root_folder}/dsu/entries'"
43
72
  LONG_DESC
73
+
44
74
  def init
45
75
  create_config_file!
46
76
  end
@@ -0,0 +1,132 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require_relative '../base_cli'
5
+ require_relative '../models/entry_group'
6
+ require_relative '../services/temp_file_reader_service'
7
+ require_relative '../services/temp_file_writer_service'
8
+ require_relative '../support/time_formatable'
9
+ require_relative '../views/entry_group/edit'
10
+ require_relative '../views/entry_group/show'
11
+
12
+ module Dsu
13
+ module Subcommands
14
+ class Edit < Dsu::BaseCLI
15
+ include Support::TimeFormatable
16
+
17
+ map %w[d] => :date
18
+ map %w[n] => :today
19
+ map %w[t] => :tomorrow
20
+ map %w[y] => :yesterday
21
+
22
+ desc 'today, n',
23
+ 'Edits the DSU entries for today.'
24
+ long_desc <<-LONG_DESC
25
+ Edits the DSU entries for today.
26
+ LONG_DESC
27
+ def today
28
+ Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now)).render
29
+ end
30
+
31
+ desc 'tomorrow, t',
32
+ 'Edits the DSU entries for tomorrow.'
33
+ long_desc <<-LONG_DESC
34
+ Edits the DSU entries for tomorrow.
35
+ LONG_DESC
36
+ def tomorrow
37
+ Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now.tomorrow)).render
38
+ end
39
+
40
+ desc 'yesterday, y',
41
+ 'Edits the DSU entries for yesterday.'
42
+ long_desc <<-LONG_DESC
43
+ Edits the DSU entries for yesterday.
44
+ LONG_DESC
45
+ def yesterday
46
+ Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.now.yesterday)).render
47
+ end
48
+
49
+ desc 'date, d DATE',
50
+ 'Edits the DSU entries for DATE.'
51
+ long_desc <<-LONG_DESC
52
+ Edits the DSU entries for DATE.
53
+
54
+ \x5 #{date_option_description}
55
+ LONG_DESC
56
+ def date(date)
57
+ Views::EntryGroup::Show.new(entry_group: edit_entry_group(time: Time.parse(date))).render
58
+ rescue ArgumentError => e
59
+ say "Error: #{e.message}", ERROR
60
+ exit 1
61
+ end
62
+
63
+ private
64
+
65
+ def edit_entry_group(time:)
66
+ formatted_time = formatted_time(time: time)
67
+ unless Models::EntryGroup.exists?(time: time)
68
+ say "No DSU entries exist for #{formatted_time}"
69
+ exit 1
70
+ end
71
+
72
+ say "Editing DSU entries for #{formatted_time}..."
73
+ entry_group = Models::EntryGroup.load(time: time)
74
+
75
+ # This renders the view to a string...
76
+ output = capture_stdxxx do
77
+ Views::EntryGroup::Edit.new(entry_group: entry_group).render
78
+ end
79
+ # ...which is then written to a temp file.
80
+ Services::TempFileWriterService.new(temp_file_content: output).call do |temp_file_path|
81
+ unless system("${EDITOR:-#{configuration[:editor]}} #{temp_file_path}")
82
+ say "Failed to open temporary file in editor '#{configuration[:editor]}';" \
83
+ "the system error returned was: '#{$CHILD_STATUS}'.", ERROR
84
+ say 'Either set the EDITOR environment variable ' \
85
+ 'or set the dsu editor configuration option (`$ dsu config init`).', ERROR
86
+ say 'Run `$ dsu help config` for more information.', ERROR
87
+ system('dsu help config')
88
+ exit 1
89
+ end
90
+ entries = []
91
+ Services::TempFileReaderService.new(temp_file_path: temp_file_path).call do |temp_file_line|
92
+ # Skip comments and blank lines.
93
+ next if ['#', nil].include? temp_file_line[0]
94
+
95
+ match_data = temp_file_line.match(/(\S+)\s(.+)/)
96
+ # TODO: Error handling if match_data is nil.
97
+ entry_sha = match_data[1]
98
+ entry_description = match_data[2]
99
+
100
+ next if %w[- d delete].include?(entry_sha) # delete the entry
101
+
102
+ entry_sha = nil if %w[+ a add].include?(entry_sha) # add the new entry
103
+ entries << Models::Entry.new(uuid: entry_sha, description: entry_description)
104
+ end
105
+
106
+ entry_group.entries = entries
107
+
108
+ return entry_group.delete if entries.empty?
109
+
110
+ entry_group.save!
111
+ end
112
+ entry_group
113
+ end
114
+
115
+ # https://stackoverflow.com/questions/4459330/how-do-i-temporarily-redirect-stderr-in-ruby/4459463#4459463
116
+ def capture_stdxxx
117
+ # The output stream must be an IO-like object. In this case we capture it in
118
+ # an in-memory IO object so we can return the string value. You can assign any
119
+ # IO object here.
120
+ string_io = StringIO.new
121
+ prev_stdout, $stdout = $stdout, string_io # rubocop:disable Style/ParallelAssignment
122
+ prev_stderr, $stderr = $stderr, string_io # rubocop:disable Style/ParallelAssignment
123
+ yield
124
+ string_io.string
125
+ ensure
126
+ # Restore the previous value of stderr and stdout (typically equal to STDERR).
127
+ $stdout = prev_stdout
128
+ $stderr = prev_stderr
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_cli'
4
+
5
+ module Dsu
6
+ module Subcommands
7
+ class List < Dsu::BaseCLI
8
+ map %w[d] => :date
9
+ map %w[n] => :today
10
+ map %w[t] => :tomorrow
11
+ map %w[y] => :yesterday
12
+
13
+ desc 'today, n',
14
+ 'Displays the DSU entries for today'
15
+ long_desc <<-LONG_DESC
16
+ Displays the DSU entries for today. This command has no options.
17
+ LONG_DESC
18
+ def today
19
+ time = Time.now
20
+ sorted_dsu_times_for(times: [time, time.yesterday]).each do |t|
21
+ view_entry_group(time: t)
22
+ puts
23
+ end
24
+ end
25
+
26
+ desc 'tomorrow, t',
27
+ 'Displays the DSU entries for tomorrow'
28
+ long_desc <<-LONG_DESC
29
+ Displays the DSU entries for tomorrow. This command has no options.
30
+ LONG_DESC
31
+ def tomorrow
32
+ time = Time.now
33
+ sorted_dsu_times_for(times: [time.tomorrow, time]).each do |t|
34
+ view_entry_group(time: t)
35
+ puts
36
+ end
37
+ end
38
+
39
+ desc 'yesterday, y',
40
+ 'Displays the DSU entries for yesterday'
41
+ long_desc <<-LONG_DESC
42
+ Displays the DSU entries for yesterday. This command has no options.
43
+ LONG_DESC
44
+ def yesterday
45
+ time = Time.now
46
+ sorted_dsu_times_for(times: [time.yesterday, time.yesterday.yesterday]).each do |t|
47
+ view_entry_group(time: t)
48
+ puts
49
+ end
50
+ end
51
+
52
+ desc 'date, d DATE',
53
+ 'Displays the DSU entries for DATE'
54
+ long_desc <<-LONG_DESC
55
+ Displays the DSU entries for DATE.
56
+ \x5 #{date_option_description}
57
+ LONG_DESC
58
+ def date(date)
59
+ time = Time.parse(date)
60
+ sorted_dsu_times_for(times: [time, time.yesterday]).each do |t|
61
+ view_entry_group(time: t)
62
+ puts
63
+ end
64
+ rescue ArgumentError => e
65
+ say "Error: #{e.message}", ERROR
66
+ exit 1
67
+ end
68
+ end
69
+ end
70
+ end
@@ -18,11 +18,16 @@ module Dsu
18
18
 
19
19
  # rubocop:disable Style/StringHashKeys - YAML writing/loading necessitates this
20
20
  DEFAULT_DSU_OPTIONS = {
21
+ # The default editor to use when editing entry groups if the EDITOR
22
+ # environment variable on your system is not set. On nix systmes,
23
+ # the default editor is`nano`. You need to change this default on
24
+ # Windows systems.
25
+ 'editor' => 'nano',
21
26
  # The order by which entries should be displayed by default:
22
27
  # asc or desc, ascending or descending, respectively.
23
28
  'entries_display_order' => 'desc',
24
29
  'entries_file_name' => '%Y-%m-%d.json',
25
- 'entries_folder' => "#{FolderLocations.root_folder}/dsu/entries",
30
+ 'entries_folder' => "#{FolderLocations.root_folder}/dsu/entries"
26
31
  }.freeze
27
32
  # rubocop:enable Style/StringHashKeys
28
33
 
@@ -43,6 +48,7 @@ module Dsu
43
48
  delete_config_file config_file: config_file
44
49
  end
45
50
 
51
+ # TODO: Move this to a view (e.g. views/configuration/show.rb)
46
52
  def print_config_file
47
53
  if config_file?
48
54
  say "Config file (#{config_file}) contents:", SUCCESS
@@ -50,6 +56,11 @@ module Dsu
50
56
  say hash.to_yaml.gsub("\n-", "\n\n-"), SUCCESS
51
57
  else
52
58
  say "Config file (#{config_file}) does not exist.", WARNING
59
+ say ''
60
+ say 'The default configuration is being used:'
61
+ DEFAULT_DSU_OPTIONS.each_with_index do |config_entry, index|
62
+ say "#{index + 1}. #{config_entry[0]}: '#{config_entry[1]}'"
63
+ end
53
64
  end
54
65
  end
55
66
 
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Support
5
+ module EntryGroupViewable
6
+ module_function
7
+
8
+ def view_entry_group(time:)
9
+ entry_group = if Models::EntryGroup.exists?(time: time)
10
+ entry_group_json = Services::EntryGroupReaderService.new(time: time).call
11
+ Services::EntryGroupHydratorService.new(entry_group_json: entry_group_json).call
12
+ else
13
+ Models::EntryGroup.new(time: time)
14
+ end
15
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
16
+ end
17
+ end
18
+ end
19
+ end
@@ -13,29 +13,23 @@ module Dsu
13
13
  def formatted_time(time:)
14
14
  time = time.localtime if time.utc?
15
15
 
16
- today_yesterday_or_tomorrow = if today?(time: time)
16
+ today_yesterday_or_tomorrow = if time.today?
17
17
  'Today'
18
- elsif yesterday?(time: time)
18
+ elsif time.yesterday?
19
19
  'Yesterday'
20
- elsif tomorrow?(time: time)
20
+ elsif time.tomorrow?
21
21
  'Tomorrow'
22
22
  end
23
23
 
24
- return time.strftime('%A, %Y-%m-%d') unless today_yesterday_or_tomorrow
24
+ time_zone = timezone_for(time: time)
25
25
 
26
- time.strftime("%A, (#{today_yesterday_or_tomorrow}) %Y-%m-%d")
27
- end
28
-
29
- def today?(time:)
30
- time.strftime('%Y%m%d') == Time.now.strftime('%Y%m%d')
31
- end
26
+ return time.strftime("%A, %Y-%m-%d #{time_zone}") unless today_yesterday_or_tomorrow
32
27
 
33
- def yesterday?(time:)
34
- time.strftime('%Y%m%d') == 1.day.ago(Time.now).strftime('%Y%m%d')
28
+ time.strftime("%A, (#{today_yesterday_or_tomorrow}) %Y-%m-%d #{time_zone}")
35
29
  end
36
30
 
37
- def tomorrow?(time:)
38
- time.strftime('%Y%m%d') == 1.day.from_now(Time.now).strftime('%Y%m%d')
31
+ def timezone_for(time:)
32
+ time.zone
39
33
  end
40
34
  end
41
35
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Support
5
+ module TimesSortable
6
+ module_function
7
+
8
+ def times_sort(times:, entries_display_order: nil)
9
+ entries_display_order ||= 'asc'
10
+ unless %w[asc desc].include? entries_display_order
11
+ raise "Invalid entries_display_order: #{entries_display_order}"
12
+ end
13
+
14
+ if entries_display_order == 'asc'
15
+ times.sort # sort ascending
16
+ elsif entries_display_order == 'desc'
17
+ times.sort.reverse # sort descending
18
+ end
19
+ end
20
+
21
+ def times_for(times:)
22
+ start_date = times.max
23
+ return times unless start_date.monday? || start_date.on_weekend?
24
+
25
+ # (0..3).map { |num| start_date - num.days }
26
+ # (start_date..-start_date.friday?).map { |time| time }
27
+ # (0..3).map { |num| start_date - num.days if start_date.on_weekend? || start_date.monday? }
28
+ # If the start_date is a weekend or a Monday, then we need to include
29
+ # start_date along with all the dates up to and including the previous
30
+ # Monday.
31
+ (0..3).filter_map do |num|
32
+ time = start_date - num.days
33
+ next unless time == start_date || time.on_weekend? || time.friday?
34
+
35
+ time
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -12,14 +12,14 @@ module Dsu
12
12
  def validate(record)
13
13
  raise 'options[:fields] is not defined.' unless options.key? :fields
14
14
  raise 'options[:fields] is not an Array.' unless options[:fields].is_a? Array
15
- raise 'options[:fields] elements are not Symbols.' unless options[:fields].all? { |field| field.is_a? Symbol }
15
+ raise 'options[:fields] elements are not Symbols.' unless options[:fields].all?(Symbol)
16
16
 
17
17
  options[:fields].each do |field|
18
18
  entries = record.send(field)
19
19
 
20
20
  unless entries.is_a?(Array)
21
21
  record.errors.add(field, 'is the wrong object type. ' \
22
- "\"Array\" was expected, but \"#{entries.class}\" was received.")
22
+ "\"Array\" was expected, but \"#{entries.class}\" was received.")
23
23
  next
24
24
  end
25
25
 
@@ -35,7 +35,7 @@ module Dsu
35
35
  next if entry.is_a? Dsu::Models::Entry
36
36
 
37
37
  record.errors.add(field, 'entry Array element is the wrong object type. ' \
38
- "\"Entry\" was expected, but \"#{entry.class}\" was received.",
38
+ "\"Entry\" was expected, but \"#{entry.class}\" was received.",
39
39
  type: Dsu::Support::FieldErrors::FIELD_TYPE_ERROR)
40
40
 
41
41
  next
@@ -47,16 +47,13 @@ module Dsu
47
47
 
48
48
  entry_objects = entries.select { |entry| entry.is_a?(Dsu::Models::Entry) }
49
49
 
50
- entry_objects.map(&:uuid).tap do |uuids|
51
- return if uuids.uniq.length == uuids.length
52
- end
50
+ uuids = entry_objects.map(&:uuid)
51
+ return if uuids.uniq.length == uuids.length
53
52
 
54
- entry_objects.map(&:uuid).tap do |uuids|
55
- non_unique_uuids = uuids.select{ |element| uuids.count(element) > 1 }.uniq
56
- if non_unique_uuids.any?
57
- record.errors.add(field, "contains duplicate UUIDs: #{non_unique_uuids.join(', ')}.",
58
- type: Dsu::Support::FieldErrors::FIELD_DUPLICATE_ERROR)
59
- end
53
+ non_unique_uuids = uuids.select { |element| uuids.count(element) > 1 }.uniq
54
+ if non_unique_uuids.any?
55
+ record.errors.add(field, "contains duplicate UUIDs: #{non_unique_uuids.join(', ')}.",
56
+ type: Dsu::Support::FieldErrors::FIELD_DUPLICATE_ERROR)
60
57
  end
61
58
  end
62
59
  end
data/lib/dsu/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Dsu
4
- VERSION = '0.1.0.alpha.1'
4
+ VERSION = '0.1.0.alpha.3'
5
5
  end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require 'active_support/core_ext/numeric/time'
5
+ require_relative '../../models/entry_group'
6
+ require_relative '../../support/colorable'
7
+ require_relative '../../support/say'
8
+ require_relative '../../support/time_formatable'
9
+
10
+ module Dsu
11
+ module Views
12
+ module EntryGroup
13
+ class Edit
14
+ include Support::Colorable
15
+ include Support::Say
16
+ include Support::TimeFormatable
17
+
18
+ def initialize(entry_group:, options: {})
19
+ raise ArgumentError, 'entry_group is nil' if entry_group.nil?
20
+ raise ArgumentError, 'entry_group is the wrong object type' unless entry_group.is_a?(Models::EntryGroup)
21
+ raise ArgumentError, 'options is nil' if options.nil?
22
+ raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
23
+
24
+ @entry_group = entry_group
25
+ @options = options || {}
26
+ end
27
+
28
+ def call
29
+ # Just in case the entry group is invalid, we'll
30
+ # validate it before displaying it.
31
+ entry_group.validate!
32
+ render_entry_group!
33
+ rescue ActiveModel::ValidationError
34
+ puts "Error(s) encountered: #{entry_group.errors.full_messages}"
35
+ raise
36
+ end
37
+ alias render call
38
+
39
+ private
40
+
41
+ attr_reader :entry_group, :options
42
+
43
+ def render_entry_group!
44
+ say "# Editing DSU Entries for #{formatted_time(time: entry_group.time)}"
45
+ say('(no entries available for this day)') and return if entry_group.entries.empty?
46
+
47
+ say ''
48
+ say '# [SHA/COMMAND] [DESCRIPTION]'
49
+
50
+ entry_group.entries.each do |entry|
51
+ say "#{entry.uuid} #{entry.description}"
52
+ end
53
+
54
+ say ''
55
+ say '# INSTRUCTIONS:'
56
+ say '# ADD a DSU entry: use one of the following commands: [+|a|add] ' \
57
+ 'followed by the description.'
58
+ say '# EDIT a DSU entry: change the description.'
59
+ say '# DELETE a DSU entry: delete the entry or replace the sha with one ' \
60
+ 'of the following commands: [-|d|delete].'
61
+ say '# NOTE: deleting all the entries will delete the entry group file; '
62
+ say '# this is preferable if this is what you want to do :)'
63
+ say '# REORDER a DSU entry: reorder the DSU entries in order preference.'
64
+ say '#'
65
+ say '# *** When you are done, save and close your editor ***'
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end