dsu 2.0.8 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -2
  3. data/Gemfile.lock +9 -9
  4. data/README.md +178 -69
  5. data/lib/core/ruby/color_theme_mode.rb +1 -1
  6. data/lib/dsu/base_cli.rb +3 -17
  7. data/lib/dsu/cli.rb +44 -72
  8. data/lib/dsu/command_services/add_entry_service.rb +1 -1
  9. data/lib/dsu/env.rb +4 -0
  10. data/lib/dsu/models/color_theme.rb +2 -2
  11. data/lib/dsu/models/configuration.rb +3 -1
  12. data/lib/dsu/models/entry_group.rb +1 -1
  13. data/lib/dsu/presenters/color_theme_presenter.rb +4 -2
  14. data/lib/dsu/presenters/color_theme_show_presenter.rb +9 -3
  15. data/lib/dsu/presenters/configuration_presenter.rb +2 -2
  16. data/lib/dsu/presenters/entry_group_presenter.rb +2 -2
  17. data/lib/dsu/services/entry_group/counter_service.rb +32 -0
  18. data/lib/dsu/services/entry_group/deleter_service.rb +35 -0
  19. data/lib/dsu/services/entry_group/editor_service.rb +7 -11
  20. data/lib/dsu/subcommands/base_subcommand.rb +0 -2
  21. data/lib/dsu/subcommands/config.rb +17 -74
  22. data/lib/dsu/subcommands/delete.rb +107 -0
  23. data/lib/dsu/subcommands/edit.rb +16 -30
  24. data/lib/dsu/subcommands/list.rb +26 -96
  25. data/lib/dsu/subcommands/theme.rb +50 -79
  26. data/lib/dsu/support/ask.rb +1 -1
  27. data/lib/dsu/support/command_help_colorizeable.rb +7 -0
  28. data/lib/dsu/support/command_hookable.rb +2 -0
  29. data/lib/dsu/support/command_options/dsu_times.rb +10 -10
  30. data/lib/dsu/support/command_options/time.rb +1 -0
  31. data/lib/dsu/support/command_options/time_mnemonic.rb +108 -0
  32. data/lib/dsu/support/command_options/{time_mneumonics.rb → time_mnemonics.rb} +2 -1
  33. data/lib/dsu/support/time_formatable.rb +13 -0
  34. data/lib/dsu/validators/description_validator.rb +2 -0
  35. data/lib/dsu/validators/entries_validator.rb +1 -0
  36. data/lib/dsu/version.rb +1 -1
  37. data/lib/dsu/views/color_theme/show.rb +1 -0
  38. data/lib/dsu/views/entry_group/edit.rb +2 -1
  39. data/lib/dsu/views/entry_group/shared/no_entries_to_display.rb +4 -1
  40. data/lib/dsu/views/shared/model_errors.rb +1 -0
  41. data/lib/dsu.rb +5 -1
  42. data/lib/locales/en/active_record.yml +8 -0
  43. data/lib/locales/en/commands.yml +136 -0
  44. data/lib/locales/en/miscellaneous.yml +23 -0
  45. data/lib/locales/en/presenters.yml +19 -0
  46. data/lib/locales/en/services.yml +10 -0
  47. data/lib/locales/en/subcommands.yml +348 -0
  48. metadata +26 -27
  49. data/exe/dsu_migrate.rb +0 -43
  50. data/lib/dsu/support/command_options/time_mneumonic.rb +0 -108
  51. data/lib/dsu/support/subcommand_help_colorizeable.rb +0 -27
@@ -12,50 +12,36 @@ module Dsu
12
12
  map %w[t] => :tomorrow
13
13
  map %w[y] => :yesterday
14
14
 
15
- desc 'today, n',
16
- 'Edits the DSU entries for today.'
17
- long_desc <<-LONG_DESC
18
- Edits the DSU entries for today.
19
- LONG_DESC
15
+ desc I18n.t('subcommands.edit.date.desc'), I18n.t('subcommands.edit.date.usage')
16
+ long_desc I18n.t('subcommands.edit.date.long_desc', date_option_description: date_option_description)
17
+ def date(date)
18
+ entry_group = Models::EntryGroup.edit(time: Time.parse(date))
19
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
20
+ rescue ArgumentError => e
21
+ puts apply_theme(I18n.t('errors.error', message: e.message), theme_color: color_theme.error)
22
+ exit 1
23
+ end
24
+
25
+ desc I18n.t('subcommands.edit.today.desc'), I18n.t('subcommands.edit.today.usage')
26
+ long_desc I18n.t('subcommands.edit.today.long_desc')
20
27
  def today
21
28
  entry_group = Models::EntryGroup.edit(time: Time.now)
22
29
  Views::EntryGroup::Show.new(entry_group: entry_group).render
23
30
  end
24
31
 
25
- desc 'tomorrow, t',
26
- 'Edits the DSU entries for tomorrow.'
27
- long_desc <<-LONG_DESC
28
- Edits the DSU entries for tomorrow.
29
- LONG_DESC
32
+ desc I18n.t('subcommands.edit.tomorrow.desc'), I18n.t('subcommands.edit.tomorrow.usage')
33
+ long_desc I18n.t('subcommands.edit.tomorrow.long_desc')
30
34
  def tomorrow
31
35
  entry_group = Models::EntryGroup.edit(time: Time.now.tomorrow)
32
36
  Views::EntryGroup::Show.new(entry_group: entry_group).render
33
37
  end
34
38
 
35
- desc 'yesterday, y',
36
- 'Edits the DSU entries for yesterday.'
37
- long_desc <<-LONG_DESC
38
- Edits the DSU entries for yesterday.
39
- LONG_DESC
39
+ desc I18n.t('subcommands.edit.yesterday.desc'), I18n.t('subcommands.edit.yesterday.usage')
40
+ long_desc I18n.t('subcommands.edit.yesterday.long_desc')
40
41
  def yesterday
41
42
  entry_group = Models::EntryGroup.edit(time: Time.now.yesterday)
42
43
  Views::EntryGroup::Show.new(entry_group: entry_group).render
43
44
  end
44
-
45
- desc 'date, d DATE',
46
- 'Edits the DSU entries for DATE.'
47
- long_desc <<-LONG_DESC
48
- Edits the DSU entries for DATE.
49
-
50
- \x5 #{date_option_description}
51
- LONG_DESC
52
- def date(date)
53
- entry_group = Models::EntryGroup.edit(time: Time.parse(date))
54
- Views::EntryGroup::Show.new(entry_group: entry_group).render
55
- rescue ArgumentError => e
56
- puts apply_theme("Error: #{e.message}", theme_color: color_theme.error)
57
- exit 1
58
- end
59
45
  end
60
46
  end
61
47
  end
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../services/entry_group/counter_service'
3
4
  require_relative '../support/command_options/dsu_times'
4
- require_relative '../support/command_options/time_mneumonic'
5
+ require_relative '../support/command_options/time_mnemonic'
5
6
  require_relative '../support/time_formatable'
6
7
  require_relative '../views/entry_group/shared/no_entries_to_display'
7
8
  require_relative '../views/shared/error'
@@ -10,7 +11,7 @@ require_relative 'base_subcommand'
10
11
  module Dsu
11
12
  module Subcommands
12
13
  class List < BaseSubcommand
13
- include Support::CommandOptions::TimeMneumonic
14
+ include Support::CommandOptions::TimeMnemonic
14
15
  include Support::TimeFormatable
15
16
 
16
17
  map %w[d] => :date
@@ -19,57 +20,39 @@ module Dsu
19
20
  map %w[t] => :tomorrow
20
21
  map %w[y] => :yesterday
21
22
 
22
- desc 'today, n',
23
- 'Displays the DSU entries for today'
24
- long_desc <<-LONG_DESC
25
- Displays the DSU entries for today. This command has no options.
26
- LONG_DESC
27
- option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
23
+ desc I18n.t('subcommands.list.today.desc'), I18n.t('subcommands.list.today.usage')
24
+ long_desc I18n.t('subcommands.list.today.long_desc')
28
25
  def today
29
26
  time = Time.now
30
27
  times = sorted_dsu_times_for(times: [time.yesterday, time])
31
28
  view_list_for(times: times, options: options)
32
29
  end
33
30
 
34
- desc 'tomorrow, t',
35
- 'Displays the DSU entries for tomorrow'
36
- long_desc <<-LONG_DESC
37
- Displays the DSU entries for tomorrow. This command has no options.
38
- LONG_DESC
39
- option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
31
+ desc I18n.t('subcommands.list.tomorrow.desc'), I18n.t('subcommands.list.tomorrow.usage')
32
+ long_desc I18n.t('subcommands.list.tomorrow.long_desc')
40
33
  def tomorrow
41
34
  time = Time.now
42
35
  times = sorted_dsu_times_for(times: [time, time.tomorrow])
43
36
  view_list_for(times: times, options: options)
44
37
  end
45
38
 
46
- desc 'yesterday, y',
47
- 'Displays the DSU entries for yesterday'
48
- long_desc <<-LONG_DESC
49
- Displays the DSU entries for yesterday. This command has no options.
50
- LONG_DESC
51
- option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
39
+ desc I18n.t('subcommands.list.yesterday.desc'), I18n.t('subcommands.list.yesterday.usage')
40
+ long_desc I18n.t('subcommands.list.yesterday.long_desc')
52
41
  def yesterday
53
42
  time = Time.now
54
43
  times = sorted_dsu_times_for(times: [time.yesterday, time.yesterday.yesterday])
55
44
  view_list_for(times: times, options: options)
56
45
  end
57
46
 
58
- desc 'date, d DATE|MNEUMONIC',
59
- 'Displays the DSU entries for the DATE or MNEUMONIC provided'
60
- long_desc <<-LONG_DESC
61
- Displays the DSU entries for the DATE or MNEUMONIC provided.
62
-
63
- #{date_option_description}
64
-
65
- #{mneumonic_option_description}
66
- LONG_DESC
67
- option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
68
- def date(date_or_mneumonic)
69
- time = if time_mneumonic?(date_or_mneumonic)
70
- time_from_mneumonic(command_option: date_or_mneumonic)
47
+ desc I18n.t('subcommands.list.date.desc'), I18n.t('subcommands.list.date.usage')
48
+ long_desc I18n.t('subcommands.list.date.long_desc',
49
+ date_option_description: date_option_description,
50
+ mnemonic_option_description: mnemonic_option_description)
51
+ def date(date_or_mnemonic)
52
+ time = if time_mnemonic?(date_or_mnemonic)
53
+ time_from_mnemonic(command_option: date_or_mnemonic)
71
54
  else
72
- Time.parse(date_or_mneumonic)
55
+ Time.parse(date_or_mnemonic)
73
56
  end
74
57
  times = sorted_dsu_times_for(times: [time, time.yesterday])
75
58
  view_list_for(times: times, options: options)
@@ -77,67 +60,13 @@ module Dsu
77
60
  Views::Shared::Error.new(messages: e.message).render
78
61
  end
79
62
 
80
- desc 'dates|dd OPTIONS',
81
- 'Displays the DSU entries for the OPTIONS provided'
82
- long_desc <<~LONG_DESC
83
- NAME
84
-
85
- $ dsu dates|dd OPTIONS -- will display the DSU entries for the OPTIONS provided.
86
-
87
- SYNOPSIS
88
-
89
- $ dsu dates|dd OPTIONS
90
-
91
- OPTIONS
92
-
93
- -a|--include-all true|false: If true, all DSU dates within the specified range will be displayed. If false, DSU dates between the first and last DSU dates that have NO entries will NOT be displayed.. The default is taken from the dsu configuration setting :include_all, see `dsu config info`.
94
-
95
- -f|--from DATE|MNEMONIC: The DATE or MNEUMONIC that represents the start of the range of DSU dates to display. If a relative mneumonic is used (+/-n, e.g +1, -1, etc.), the date calculated will be relative to the current date (e.g. `<MNEUMONIC>.to_i.days.from_now(Time.now)`).
96
-
97
- -t|--to DATE|MNEMONIC: The DATE or MNEUMONIC that represents the end of the range of DSU dates to display. If a relative mneumonic is used (+/-n, e.g +1, -1, etc.), the date calculated will be relative to the date that resulting from the `--from` option date calculation.
98
-
99
- #{date_option_description}
100
-
101
- #{mneumonic_option_description}
102
-
103
- EXAMPLES
104
-
105
- NOTE: All examples are subject to the `--include-all` option.
106
-
107
- The below will display the DSU entries for the range of dates from 1/1 to 1/4:
108
-
109
- $ dsu list dates --from 1/1 --to +3
110
-
111
- This will display the DSU entries for the range of dates from 1/2 to 1/5:
112
-
113
- $ dsu list dates --from 1/5 --to -3
114
-
115
- This (assuming "today" is 1/10) will display the DSU entries for the last week 1/10 to 1/3:
116
-
117
- $ dsu list dates --from today --to -7
118
-
119
- This (assuming "today" is 5/23) will display the DSU entries for the last week 5/16 to 5/22.
120
- This example simply illustrates the fact that you can use relative mneumonics for
121
- both `--from` and `--to` options; this doesn't mean you should do so...
122
-
123
- While you can use relative mneumonics for both `--from` and `--to` options,
124
- there is always a more intuitive way. The below example basically lists one week
125
- of DSU entries back 1 week from yesterday's date:
126
-
127
- $ dsu list dates --from -7 --to +6
128
-
129
- The above can be accomplished MUCH easier by simply using the `yesterday` mneumonic...
130
-
131
- This (assuming "today" is 5/23) will display the DSU entries back 1 week from yesterday's date 5/16 to 5/22:
132
-
133
- $ dsu list dates --from yesterday --to -6
134
- LONG_DESC
135
- # -f, --from FROM [DATE|MNEMONIC] (e.g. -f, --from 1/1[/yyy]|n|t|y|today|tomorrow|yesterday)
63
+ desc I18n.t('subcommands.list.dates.desc'), I18n.t('subcommands.list.dates.usage')
64
+ long_desc I18n.t('subcommands.list.dates.long_desc',
65
+ date_option_description: date_option_description,
66
+ mnemonic_option_description: mnemonic_option_description)
136
67
  option :from, type: :string, required: true, aliases: '-f', banner: 'DATE|MNEMONIC'
137
- # -t, --to TO [DATE|MNEMONIC] (e.g. -t, --to 1/1[/yyy]|n|t|y|today|tomorrow|yesterday)
138
68
  option :to, type: :string, required: true, aliases: '-t', banner: 'DATE|MNEMONIC'
139
- # Include dates that have no DSU entries.
140
- option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
69
+ option :include_all, default: false, type: :boolean, aliases: '-a', desc: I18n.t('options.include_all')
141
70
  def dates
142
71
  options = configuration.to_h.merge(self.options).with_indifferent_access
143
72
  times, errors = Support::CommandOptions::DsuTimes.dsu_times_for(from_option: options[:from], to_option: options[:to]) # rubocop:disable Layout/LineLength
@@ -149,9 +78,10 @@ module Dsu
149
78
  # NOTE: special sort here, unlike the other commands where rules for
150
79
  # displaying DSU entries are applied; this is more of a list command.
151
80
  times = times_sort(times: times, entries_display_order: options[:entries_display_order])
152
- view_entry_groups(times: times, options: options) do |total_entry_groups, _total_entry_groups_not_shown|
153
- # nothing_to_display_banner_for(times) if total_entry_groups.zero?
154
- Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options) if total_entry_groups.zero?
81
+ view_entry_groups(times: times, options: options) do |_total_entry_groups, _total_entry_groups_not_shown|
82
+ if Services::EntryGroup::CounterService.new(times: times).call.zero?
83
+ Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
84
+ end
155
85
  end
156
86
  rescue ArgumentError => e
157
87
  Views::Shared::Error.new(messages: e.message).render
@@ -11,142 +11,108 @@ require_relative 'base_subcommand'
11
11
  module Dsu
12
12
  module Subcommands
13
13
  class Theme < BaseSubcommand
14
- map %w[c] => :create if Dsu.env.test?
15
- map %w[d] => :delete if Dsu.env.test?
14
+ map %w[c] => :create if Dsu.env.local?
15
+ map %w[d] => :delete if Dsu.env.local?
16
16
  map %w[l] => :list
17
17
  map %w[s] => :show
18
18
  map %w[u] => :use
19
19
 
20
- if Dsu.env.test?
21
- desc 'create THEME_NAME [OPTIONS]',
22
- 'Creates a dsu color theme named THEME_NAME.'
23
- long_desc <<-LONG_DESC
24
- Create a dsu color theme named THEME_NAME in the #{Support::Fileable.themes_folder} folder.
25
-
26
- SYNOPSIS
27
-
28
- `dsu create THEME_NAME [-d|--description DESCRIPTION]`
29
-
30
- OPTIONS:
31
-
32
- -d|--description DESCRIPTION: Creates the dsu color theme with having DESCRIPTION as the color theme description.
33
-
34
- DESCRIPTION:
35
-
36
- Must be be between 2 and 256 characters (inclusive) in length.
37
- LONG_DESC
20
+ if Dsu.env.local?
21
+ desc I18n.t('subcommands.theme.create.desc'), I18n.t('subcommands.theme.create.usage')
22
+ long_desc I18n.t('subcommands.theme.create.long_desc', themes_folder: Support::Fileable.themes_folder)
38
23
  option :description, type: :string, aliases: '-d', banner: 'DESCRIPTION'
39
24
  option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
40
25
  def create(theme_name)
41
26
  if Models::ColorTheme.exist?(theme_name: theme_name)
42
- Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" already exists.").render
27
+ message = I18n.t('subcommands.theme.create.errors.already_exists', theme_name: theme_name)
28
+ Views::Shared::Error.new(messages: message).render
43
29
  return false
44
30
  end
45
- prompt = color_theme.prompt_with_options(prompt: "Create color theme \"#{theme_name}\"?", options: %w[y N])
31
+ prompt_string = I18n.t('subcommands.theme.create.prompts.create_theme', theme_name: theme_name)
32
+ prompt = color_theme.prompt_with_options(prompt: prompt_string, options: %w[y N])
46
33
  if yes?(prompt, options: options)
47
34
  theme_hash = Models::ColorTheme::DEFAULT_THEME.dup
48
- theme_hash[:description] = options[:description] || "#{theme_name.capitalize} color theme"
35
+ theme_hash[:description] = options[:description] || I18n.t('subcommands.theme.generic.color_theme',
36
+ theme_name: theme_name.capitalize)
49
37
  Models::ColorTheme.new(theme_name: theme_name, theme_hash: theme_hash).save!
50
- Views::Shared::Info.new(messages: "\nCreated color theme \"#{theme_name}\".").render
38
+ message = I18n.t('subcommands.theme.create.messages.created', theme_name: theme_name)
39
+ Views::Shared::Info.new(messages: "\n#{message}").render
51
40
  true
52
41
  else
53
- Views::Shared::Info.new(messages: "\nCanceled.").render
42
+ message = I18n.t('subcommands.theme.create.messages.canceled')
43
+ Views::Shared::Info.new(messages: "\n#{message}").render
54
44
  false
55
45
  end
56
46
  end
57
47
 
58
- desc 'delete THEME_NAME',
59
- 'Deletes the existing dsu color theme THEME_NAME.'
60
- long_desc <<-LONG_DESC
61
- NAME
62
-
63
- `dsu delete [THEME_NAME]` -- will delete the dsu color theme named THEME_NAME located in the #{Support::Fileable.themes_folder} folder.
64
- LONG_DESC
48
+ desc I18n.t('subcommands.theme.delete.desc'), I18n.t('subcommands.theme.delete.usage')
49
+ long_desc I18n.t('subcommands.theme.delete.long_desc', themes_folder: Support::Fileable.themes_folder)
65
50
  option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
66
- def delete(theme_name)
51
+ def delete(theme_name) # rubocop:disable Metrics/MethodLength
67
52
  display_dsu_header
68
53
 
69
54
  if theme_name == Models::ColorTheme::DEFAULT_THEME_NAME
70
- display_dsu_header
71
- Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" cannot be deleted.").render
55
+ message = I18n.t('subcommands.theme.delete.errors.cannot_delete', theme_name: theme_name)
56
+ Views::Shared::Error.new(messages: message).render
72
57
  return
73
58
  end
74
59
 
75
60
  unless Models::ColorTheme.exist?(theme_name: theme_name)
76
- Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" does not exist.").render
61
+ message = I18n.t('subcommands.theme.generic.errors.does_not_exist', theme_name: theme_name)
62
+ Views::Shared::Error.new(messages: message).render
77
63
  return
78
64
  end
79
65
 
80
- prompt = color_theme.prompt_with_options(prompt: "Delete color theme \"#{theme_name}\"?",
81
- options: %w[y N])
82
- if yes?(prompt, options: options)
66
+ prompt_string = I18n.t('subcommands.theme.delete.prompts.delete_theme', theme_name: theme_name)
67
+ prompt = color_theme.prompt_with_options(prompt: prompt_string, options: %w[y N])
68
+ message = if yes?(prompt, options: options)
83
69
  Models::ColorTheme.delete!(theme_name: theme_name)
84
- Views::Shared::Info.new(messages: "\nDeleted color theme \"#{theme_name}\".").render
70
+ change_theme
71
+ I18n.t('subcommands.theme.delete.messages.deleted', theme_name: theme_name)
85
72
  else
86
- Views::Shared::Info.new(messages: "\nCanceled.").render
73
+ I18n.t('subcommands.theme.delete.messages.canceled')
87
74
  end
75
+ Views::Shared::Info.new(messages: "\n#{message}").render
88
76
  end
89
77
  end
90
78
 
91
- desc 'list',
92
- 'Lists the available dsu color themes.'
93
- long_desc <<-LONG_DESC
94
- NAME
95
-
96
- `dsu list` -- lists the available dsu color themes located in the #{Support::Fileable.themes_folder} folder.
97
- LONG_DESC
79
+ desc I18n.t('subcommands.theme.list.desc'), I18n.t('subcommands.theme.list.usage')
80
+ long_desc I18n.t('subcommands.theme.list.long_desc', themes_folder: Support::Fileable.themes_folder)
98
81
  def list
99
82
  Views::ColorTheme::Index.new.render
100
83
  end
101
84
 
102
- desc 'use THEME_NAME',
103
- 'Sets THEME_NAME as the current DSU color theme.'
104
- long_desc <<-LONG_DESC
105
- NAME
106
-
107
- `dsu theme use [THEME_NAME]` -- sets the dsu color theme to THEME_NAME.
108
-
109
- SYNOPSIS
110
-
111
- If THEME_NAME is not provided, the default theme will be used.
112
- If THEME_NAME does not exist, you will be given the option to create a new theme.
113
-
114
- LONG_DESC
85
+ desc I18n.t('subcommands.theme.use.desc'), I18n.t('subcommands.theme.use.usage')
86
+ long_desc I18n.t('subcommands.theme.use.long_desc')
115
87
  option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
116
88
  def use(theme_name = Models::ColorTheme::DEFAULT_THEME_NAME)
117
- if Dsu.env.test? && !Models::ColorTheme.exist?(theme_name: theme_name)
118
- display_dsu_header
119
- return unless create(theme_name)
120
- end
89
+ display_dsu_header
90
+
91
+ return if Dsu.env.local? && !Models::ColorTheme.exist?(theme_name: theme_name) && !create(theme_name)
121
92
 
122
93
  unless Models::ColorTheme.exist?(theme_name: theme_name)
123
- display_dsu_header
124
- Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" does not exist.").render
94
+ message = I18n.t('subcommands.theme.generic.errors.does_not_exist', theme_name: theme_name)
95
+ Views::Shared::Error.new(messages: message).render
125
96
  return
126
97
  end
127
98
 
128
- configuration.theme_name = theme_name
129
- configuration.save!
99
+ change_theme theme_name: theme_name
130
100
  # We need to display the header after the theme is updated so that it is displayed in the
131
101
  # correct theme color.
132
- display_dsu_header
133
- Views::Shared::Info.new(messages: "Using color theme \"#{theme_name}\".").render
102
+ message = I18n.t('subcommands.theme.use.messages.using_color_theme', theme_name: theme_name)
103
+ Views::Shared::Info.new(messages: message).render
134
104
  end
135
105
 
136
- desc 'show THEME_NAME',
137
- 'Displays the dsu color theme.'
138
- long_desc <<-LONG_DESC
139
- NAME
140
-
141
- `dsu show THEME_NAME` -- displays the dsu color theme for THEME_NAME.
142
- LONG_DESC
106
+ desc I18n.t('subcommands.theme.show.desc'), I18n.t('subcommands.theme.show.usage')
107
+ long_desc I18n.t('subcommands.theme.show.long_desc')
143
108
  def show(theme_name = configuration.theme_name)
144
109
  if Dsu::Models::ColorTheme.exist?(theme_name: theme_name)
145
110
  Views::ColorTheme::Show.new(theme_name: theme_name).render
146
111
  return
147
112
  end
148
113
 
149
- Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" does not exist.").render
114
+ message = I18n.t('subcommands.theme.generic.errors.does_not_exist', theme_name: theme_name)
115
+ Views::Shared::Error.new(messages: message).render
150
116
  end
151
117
 
152
118
  private
@@ -154,6 +120,11 @@ module Dsu
154
120
  def display_dsu_header
155
121
  self.class.display_dsu_header
156
122
  end
123
+
124
+ def change_theme(theme_name: Models::ColorTheme::DEFAULT_THEME_NAME)
125
+ configuration.theme_name = theme_name
126
+ configuration.save!
127
+ end
157
128
  end
158
129
  end
159
130
  end
@@ -23,7 +23,7 @@ module Dsu
23
23
  def auto_prompt(prompt, options)
24
24
  prompt = Utils.strip_escapes(prompt)
25
25
  @auto_prompt ||= begin
26
- value = options.dig('prompts', prompt)
26
+ value = options.dig('prompts', prompt) || options.dig('prompts', 'any')
27
27
  value = (value == 'true' unless value.nil?)
28
28
  value
29
29
  end
@@ -12,11 +12,18 @@ module Dsu
12
12
  end
13
13
 
14
14
  module ClassMethods
15
+ # Handles general help colorization.
15
16
  def help(shell, subcommand = false) # rubocop:disable Style/OptionalBooleanParameter
16
17
  help_text = Services::StdoutRedirectorService.call { super }
17
18
  puts apply_theme(help_text, theme_color: color_theme.help)
18
19
  end
19
20
 
21
+ # Handles sub-command help colorization.
22
+ def command_help(shell, subcommand = false) # rubocop:disable Style/OptionalBooleanParameter
23
+ help_text = Services::StdoutRedirectorService.call { super }
24
+ puts apply_theme(help_text, theme_color: color_theme.help)
25
+ end
26
+
20
27
  def color_theme
21
28
  @color_theme ||= Models::ColorTheme.current_or_default
22
29
  end
@@ -35,11 +35,13 @@ module Dsu
35
35
  def suspend_header?(args, _options)
36
36
  return false unless args.count > 1
37
37
 
38
+ # TODO: I18n?
38
39
  true if args[0] == 'theme' && %w[use delete].include?(args[1])
39
40
  end
40
41
 
41
42
  def display_dsu_footer
42
43
  puts apply_theme('_' * 35, theme_color: color_theme.dsu_footer)
44
+ # TODO: I18n.
43
45
  footer = apply_theme("Theme: #{color_theme.theme_name}", theme_color: color_theme.dsu_footer)
44
46
  puts footer
45
47
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'time'
4
- require_relative 'time_mneumonic'
4
+ require_relative 'time_mnemonic'
5
5
 
6
6
  module Dsu
7
7
  module Support
@@ -11,14 +11,14 @@ module Dsu
11
11
 
12
12
  # Returns an array of Time objects. The first element is the "from" time.
13
13
  # The second element is the "to" time. Both arguments are expected to be
14
- # command options that are time strings, time or relative time mneumonics.
14
+ # command options that are time strings, time or relative time mnemonics.
15
15
  def dsu_times_for(from_option:, to_option:)
16
16
  from_time = dsu_from_time_for(from_option: from_option)
17
17
  to_time = dsu_to_time_for(to_option: to_option, from_time: from_time)
18
18
 
19
19
  errors = []
20
- errors << "Option -f, [--from=DATE|MNEMONIC] value is invalid [\"#{from_option}\"]" if from_time.nil?
21
- errors << "Option -t, [--to=DATE|MNEMONIC] value is invalid [\"#{to_option}\"]" if to_time.nil?
20
+ errors << I18n.t('errors.from_option_invalid', from_option: from_option) if from_time.nil?
21
+ errors << I18n.t('errors.to_option_invalid', to_option: to_option) if to_time.nil?
22
22
  return [[], errors] if errors.any?
23
23
 
24
24
  min_time, max_time = [from_time, to_time].sort
@@ -28,17 +28,17 @@ module Dsu
28
28
  def dsu_from_time_for(from_option:)
29
29
  return if from_option.nil?
30
30
 
31
- from_time = if TimeMneumonic.time_mneumonic?(from_option)
32
- TimeMneumonic.time_from_mneumonic(command_option: from_option)
31
+ from_time = if TimeMnemonic.time_mnemonic?(from_option)
32
+ TimeMnemonic.time_from_mnemonic(command_option: from_option)
33
33
  end
34
34
  from_time || Time.time_from_date_string(command_option: from_option)
35
35
  end
36
36
 
37
37
  def dsu_to_time_for(to_option:, from_time:)
38
- to_time = if TimeMneumonic.relative_time_mneumonic?(to_option)
39
- TimeMneumonic.time_from_mneumonic(command_option: to_option, relative_time: from_time)
40
- elsif TimeMneumonic.time_mneumonic?(to_option)
41
- TimeMneumonic.time_from_mneumonic(command_option: to_option)
38
+ to_time = if TimeMnemonic.relative_time_mnemonic?(to_option)
39
+ TimeMnemonic.time_from_mnemonic(command_option: to_option, relative_time: from_time)
40
+ elsif TimeMnemonic.time_mnemonic?(to_option)
41
+ TimeMnemonic.time_from_mnemonic(command_option: to_option)
42
42
  end
43
43
  to_time || Time.time_from_date_string(command_option: to_option)
44
44
  end
@@ -67,6 +67,7 @@ module Dsu
67
67
 
68
68
  def valid_time!(time_parts:)
69
69
  time_string = time_string_for(time_parts: time_parts)
70
+ # TODO: I18n.
70
71
  Date.strptime(time_string, '%Y/%m/%d').to_time
71
72
  end
72
73
 
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'time_mnemonics'
4
+
5
+ module Dsu
6
+ module Support
7
+ module CommandOptions
8
+ # The purpose of this module is to take a command option that is a string and return a Time object.
9
+ # The command option is expected to be a time mneumoic.
10
+ module TimeMnemonic
11
+ include TimeMnemonics
12
+
13
+ module_function
14
+
15
+ def time_from_mnemonic(command_option:, relative_time: nil)
16
+ time_from_mnemonic!(command_option: command_option, relative_time: relative_time)
17
+ rescue ArgumentError
18
+ nil
19
+ end
20
+
21
+ # command_option: is expected to me a time mnemonic. If relative_time is NOT nil, all
22
+ # time mnemonics are relative to relative_time. Otherwise, they are relative to Time.now.
23
+ # relative_time: is a Time object that is required IF command_option is expected to be
24
+ # a relative time mnemonic. Otherwise, it is optional.
25
+ def time_from_mnemonic!(command_option:, relative_time: nil)
26
+ validate_argument!(command_option: command_option, command_option_name: :command_option)
27
+ unless relative_time.nil? || relative_time.is_a?(::Time)
28
+ raise ArgumentError, "relative_time is not a Time object: \"#{relative_time}\""
29
+ end
30
+
31
+ relative_time ||= ::Time.now
32
+
33
+ time_for_mnemonic(mnemonic: command_option, relative_time: relative_time)
34
+ end
35
+
36
+ # This method returns true if mnemonic is a valid mnemonic OR
37
+ # a relative time mnemonic.
38
+ def time_mnemonic?(mnemonic)
39
+ mnemonic?(mnemonic) || relative_time_mnemonic?(mnemonic)
40
+ end
41
+
42
+ # This method returns true if mnemonic is a valid relative
43
+ # time mnemonic.
44
+ def relative_time_mnemonic?(mnemonic)
45
+ return false unless mnemonic.is_a?(String)
46
+
47
+ mnemonic.match?(RELATIVE_REGEX)
48
+ end
49
+
50
+ # Add private_class_methods here.
51
+
52
+ # Returns a Time object from a mnemonic.
53
+ def time_for_mnemonic(mnemonic:, relative_time:)
54
+ time = relative_time
55
+ if today_mnemonic?(mnemonic)
56
+ time
57
+ elsif tomorrow_mnemonic?(mnemonic)
58
+ time.tomorrow
59
+ elsif yesterday_mnemonic?(mnemonic)
60
+ time.yesterday
61
+ elsif relative_time_mnemonic?(mnemonic)
62
+ relative_time_for(days_from_now: mnemonic, time: time)
63
+ end
64
+ end
65
+
66
+ def relative_time_for(days_from_now:, time:)
67
+ days_from_now.to_i.days.from_now(time)
68
+ end
69
+
70
+ # This method returns true if mnemonic is a valid time mnemonic.
71
+ # This method will return false if mnemonic is an invalid mnemonic
72
+ # OR if mnemonic is a relative time mnemonic.
73
+ def mnemonic?(mnemonic)
74
+ today_mnemonic?(mnemonic) ||
75
+ tomorrow_mnemonic?(mnemonic) ||
76
+ yesterday_mnemonic?(mnemonic)
77
+ end
78
+
79
+ def today_mnemonic?(mnemonic)
80
+ TODAY.include?(mnemonic)
81
+ end
82
+
83
+ def tomorrow_mnemonic?(mnemonic)
84
+ TOMORROW.include?(mnemonic)
85
+ end
86
+
87
+ def yesterday_mnemonic?(mnemonic)
88
+ YESERDAY.include?(mnemonic)
89
+ end
90
+
91
+ def validate_argument!(command_option:, command_option_name:)
92
+ raise ArgumentError, "#{command_option_name} cannot be nil." if command_option.nil?
93
+ raise ArgumentError, "#{command_option_name} cannot be blank." if command_option.blank?
94
+ unless command_option.is_a?(String)
95
+ raise ArgumentError, "#{command_option_name} must be a String: \"#{command_option}\""
96
+ end
97
+ unless time_mnemonic?(command_option)
98
+ raise ArgumentError, "#{command_option_name} is an invalid mnemonic: \"#{command_option}\"."
99
+ end
100
+ end
101
+
102
+ private_class_method :time_for_mnemonic, :relative_time_for,
103
+ :mnemonic?, :today_mnemonic?, :tomorrow_mnemonic?,
104
+ :yesterday_mnemonic?, :validate_argument!
105
+ end
106
+ end
107
+ end
108
+ end