dsu 1.2.1 → 2.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (105) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +55 -21
  3. data/Gemfile.lock +7 -7
  4. data/README.md +28 -35
  5. data/bin/console +23 -1
  6. data/bin/dsu +3 -0
  7. data/bin/setup +14 -3
  8. data/exe/dsu +23 -1
  9. data/exe/dsu_migrate.rb +43 -0
  10. data/lib/core/ruby/color_theme_colors.rb +16 -0
  11. data/lib/core/ruby/color_theme_mode.rb +42 -0
  12. data/lib/core/ruby/not_today.rb +7 -0
  13. data/lib/core/ruby/wrap_and_join.rb +31 -0
  14. data/lib/dsu/base_cli.rb +19 -23
  15. data/lib/dsu/cli.rb +47 -37
  16. data/lib/dsu/command_services/add_entry_service.rb +10 -21
  17. data/lib/dsu/crud/json_file.rb +139 -0
  18. data/lib/dsu/crud/raw_json_file.rb +51 -0
  19. data/lib/dsu/env.rb +21 -0
  20. data/lib/dsu/migration/service.rb +196 -0
  21. data/lib/dsu/migration/version.rb +7 -0
  22. data/lib/dsu/models/color_theme.rb +270 -0
  23. data/lib/dsu/models/configuration.rb +160 -0
  24. data/lib/dsu/models/entry.rb +6 -2
  25. data/lib/dsu/models/entry_group.rb +143 -42
  26. data/lib/dsu/models/migration_version.rb +48 -0
  27. data/lib/dsu/presenters/base_presenter.rb +32 -0
  28. data/lib/dsu/presenters/color_theme_presenter.rb +50 -0
  29. data/lib/dsu/presenters/color_theme_show_presenter.rb +49 -0
  30. data/lib/dsu/presenters/configuration_presenter.rb +45 -0
  31. data/lib/dsu/presenters/entry_group_presenter.rb +35 -0
  32. data/lib/dsu/presenters/entry_presenter.rb +25 -0
  33. data/lib/dsu/services/color_theme/hydrator_service.rb +42 -0
  34. data/lib/dsu/services/configuration/hydrator_service.rb +42 -0
  35. data/lib/dsu/services/entry/hydrator_service.rb +33 -0
  36. data/lib/dsu/services/entry_group/editor_service.rb +107 -0
  37. data/lib/dsu/services/entry_group/hydrator_service.rb +37 -0
  38. data/lib/dsu/services/migration_version/hydrator_service.rb +36 -0
  39. data/lib/dsu/services/stderr_redirector_service.rb +27 -0
  40. data/lib/dsu/services/temp_file/reader_service.rb +33 -0
  41. data/lib/dsu/services/temp_file/writer_service.rb +35 -0
  42. data/lib/dsu/subcommands/base_subcommand.rb +14 -0
  43. data/lib/dsu/subcommands/config.rb +92 -32
  44. data/lib/dsu/subcommands/edit.rb +3 -3
  45. data/lib/dsu/subcommands/list.rb +70 -93
  46. data/lib/dsu/subcommands/theme.rb +159 -0
  47. data/lib/dsu/support/ask.rb +14 -19
  48. data/lib/dsu/support/color_themable.rb +34 -0
  49. data/lib/dsu/support/command_help_colorizeable.rb +27 -0
  50. data/lib/dsu/support/command_hookable.rb +60 -0
  51. data/lib/dsu/support/command_options/dsu_times.rb +32 -21
  52. data/lib/dsu/support/command_options/time.rb +7 -1
  53. data/lib/dsu/support/command_options/time_mneumonic.rb +7 -1
  54. data/lib/dsu/support/descriptable.rb +6 -4
  55. data/lib/dsu/support/entry_group_viewable.rb +28 -4
  56. data/lib/dsu/support/fileable.rb +94 -0
  57. data/lib/dsu/support/presentable.rb +11 -0
  58. data/lib/dsu/support/subcommand_help_colorizeable.rb +27 -0
  59. data/lib/dsu/support/time_comparable.rb +19 -0
  60. data/lib/dsu/support/time_formatable.rb +12 -0
  61. data/lib/dsu/support/times_sortable.rb +48 -14
  62. data/lib/dsu/support/utils.rb +11 -0
  63. data/lib/dsu/validators/color_theme_validator.rb +74 -0
  64. data/lib/dsu/validators/entries_validator.rb +4 -8
  65. data/lib/dsu/validators/version_validator.rb +29 -0
  66. data/lib/dsu/version.rb +2 -1
  67. data/lib/dsu/views/color_theme/index.rb +62 -0
  68. data/lib/dsu/views/color_theme/show.rb +106 -0
  69. data/lib/dsu/views/configuration/show.rb +41 -0
  70. data/lib/dsu/views/entry_group/edit.rb +3 -5
  71. data/lib/dsu/views/entry_group/shared/no_entries_to_display.rb +41 -0
  72. data/lib/dsu/views/entry_group/show.rb +16 -15
  73. data/lib/dsu/views/shared/error.rb +17 -0
  74. data/lib/dsu/views/shared/info.rb +17 -0
  75. data/lib/dsu/views/shared/message.rb +85 -0
  76. data/lib/dsu/views/shared/model_errors.rb +31 -0
  77. data/lib/dsu/views/shared/success.rb +17 -0
  78. data/lib/dsu/views/shared/warning.rb +17 -0
  79. data/lib/dsu.rb +22 -1
  80. data/lib/seed_data/themes/cherry.json +79 -0
  81. data/lib/seed_data/themes/default.json +79 -0
  82. data/lib/seed_data/themes/lemon.json +79 -0
  83. data/lib/seed_data/themes/matrix.json +79 -0
  84. data/lib/seed_data/themes/whiteout.json +79 -0
  85. metadata +70 -25
  86. data/lib/dsu/core/ruby/not_today.rb +0 -11
  87. data/lib/dsu/services/ai/tense_translator_service.rb +0 -63
  88. data/lib/dsu/services/configuration_loader_service.rb +0 -55
  89. data/lib/dsu/services/entry_group_deleter_service.rb +0 -31
  90. data/lib/dsu/services/entry_group_editor_service.rb +0 -96
  91. data/lib/dsu/services/entry_group_hydrator_service.rb +0 -43
  92. data/lib/dsu/services/entry_group_reader_service.rb +0 -36
  93. data/lib/dsu/services/entry_group_writer_service.rb +0 -46
  94. data/lib/dsu/services/entry_hydrator_service.rb +0 -35
  95. data/lib/dsu/services/temp_file_reader_service.rb +0 -31
  96. data/lib/dsu/services/temp_file_writer_service.rb +0 -33
  97. data/lib/dsu/support/colorable.rb +0 -14
  98. data/lib/dsu/support/configurable.rb +0 -15
  99. data/lib/dsu/support/configuration.rb +0 -112
  100. data/lib/dsu/support/entry_group_fileable.rb +0 -49
  101. data/lib/dsu/support/entry_group_loadable.rb +0 -49
  102. data/lib/dsu/support/folder_locations.rb +0 -21
  103. data/lib/dsu/support/say.rb +0 -40
  104. data/lib/dsu/views/edited_entries/shared/errors.rb +0 -39
  105. data/lib/dsu/views/shared/messages.rb +0 -56
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../base_cli'
4
+ require_relative '../support/ask'
5
+ require_relative '../support/subcommand_help_colorizeable'
6
+
7
+ module Dsu
8
+ module Subcommands
9
+ class BaseSubcommand < Dsu::BaseCLI
10
+ include Support::Ask
11
+ include Support::SubcommandHelpColorizable
12
+ end
13
+ end
14
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
4
- require_relative '../support/configuration'
3
+ require_relative '../models/configuration'
4
+ require_relative '../views/configuration/show'
5
+ require_relative '../views/shared/model_errors'
6
+ require_relative '../views/shared/success'
7
+ require_relative '../views/shared/warning'
8
+ require_relative 'base_subcommand'
5
9
 
6
10
  module Dsu
7
11
  module Subcommands
8
- class Config < ::Thor
9
- include Dsu::Support::Configuration
10
-
12
+ class Config < BaseSubcommand
11
13
  default_command :help
12
14
 
13
15
  class << self
@@ -19,59 +21,117 @@ module Dsu
19
21
  desc 'info', 'Displays information about this gem configuration'
20
22
  long_desc <<-LONG_DESC
21
23
  NAME
22
- \x5
24
+
23
25
  `dsu config info` -- Displays information about this gem configuration.
24
26
 
25
27
  SYNOPSIS
26
- \x5
28
+
27
29
  dsu config info
28
30
  LONG_DESC
29
31
  def info
30
- print_config_file
32
+ configuration = Models::Configuration.new
33
+ Views::Configuration::Show.new(config: configuration).call
31
34
  end
32
35
 
33
- desc 'init', 'Creates and initializes a .dsu file in your home folder'
34
- long_desc <<-LONG_DESC
35
- NAME
36
+ if ENV['DEV_ENV']
37
+ desc 'init', 'Creates and initializes a .dsu file in your home folder'
38
+ long_desc <<-LONG_DESC
39
+ NAME
36
40
 
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.
41
+ `dsu config init` -- will create and initialize a .dsu file ("#{Dsu::Support::Fileable.root_folder}/.dsu") that you may edit. Otherwise, the default configuration will be used.
38
42
 
39
- SYNOPSIS
43
+ SYNOPSIS
40
44
 
41
- dsu config init
45
+ dsu config init
42
46
 
43
- CONFIGURATION FILE OPTIONS
47
+ CONFIGURATION FILE OPTIONS
44
48
 
45
- The following configuration file options are available:
49
+ The following configuration file options are available:
46
50
 
47
- editor:
51
+ version:
48
52
 
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.
53
+ The configuration version - DO NOT ALTER THIS VALUE!
50
54
 
51
- entries_display_order:
55
+ editor:
52
56
 
53
- The order by which entries will be displayed, 'asc' or 'desc' (ascending or descending, respectively).
57
+ 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.
54
58
 
55
- Default: 'desc'
59
+ entries_display_order:
56
60
 
57
- entries_file_name:
61
+ The order by which entries will be displayed, :asc or :desc (ascending or descending, respectively).
58
62
 
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.
63
+ Default: :desc
60
64
 
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.
65
+ entries_file_name:
62
66
 
63
- Default: '%Y-%m-%d.json'
67
+ 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.
64
68
 
65
- entries_folder:
69
+ 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.
66
70
 
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.
71
+ Default: '%Y-%m-%d.json'
68
72
 
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.
73
+ entries_folder:
70
74
 
71
- Default: "'#{Dsu::Support::FolderLocations.root_folder}/dsu/entries'"
72
- LONG_DESC
73
- def init
74
- create_config_file!
75
+ 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.
76
+
77
+ 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.
78
+
79
+ Default: "'#{Dsu::Support::Fileable.root_folder}/dsu/entries'"
80
+ LONG_DESC
81
+ def init
82
+ exit 1 if configuration_errors_or_wanings?
83
+
84
+ Models::Configuration.default.tap do |configuration|
85
+ configuration.save!
86
+ messages = ["Configuration file (#{Models::Configuration.config_file}) created."]
87
+ Views::Shared::Success.new(messages: messages).render
88
+ Views::Configuration::Show.new(config: configuration).render
89
+ end
90
+ end
91
+
92
+ desc 'delete', 'Deletes the configuration file'
93
+ long_desc <<-LONG_DESC
94
+ NAME
95
+
96
+ `dsu config delete` -- Deletes the configuration.
97
+
98
+ SYNOPSIS
99
+
100
+ dsu config delete
101
+
102
+ NOTES
103
+
104
+ Deleting the dsu configuration file will simply cause dsu to use the default configuration options (`Dsu::Models::Configuration::DEFAULT_CONFIGURATION`).
105
+ LONG_DESC
106
+ def delete
107
+ unless Models::Configuration.exist?
108
+ messages = ["Configuration file (#{Models::Configuration.config_file}) does not exist."]
109
+ Views::Shared::Warning.new(messages: messages).render
110
+ exit 1
111
+ end
112
+ Models::Configuration.delete!
113
+ messages = ["Configuration file (#{Models::Configuration.config_file}) deleted."]
114
+ Views::Shared::Success.new(messages: messages).render
115
+ end
116
+ end
117
+
118
+ private
119
+
120
+ def configuration_errors_or_wanings?
121
+ if Models::Configuration.exist?
122
+ messages = ["Configuration file (#{Models::Configuration.config_file}) already exists"]
123
+ Views::Shared::Warning.new(messages: messages).render
124
+ elsif !Dir.exist?(Models::Configuration.config_folder)
125
+ messages = ["Destination folder for configuration file (#{Models::Configuration.config_folder}) does not exist"] # rubocop:disable Layout/LineLength
126
+ Views::Shared::Error.new(messages: messages).render
127
+ else
128
+ configuration = Models::Configuration.default
129
+ return false if configuration.valid?
130
+
131
+ Views::Shared::ModelErrors.new(model: configuration).render
132
+ end
133
+
134
+ true
75
135
  end
76
136
  end
77
137
  end
@@ -1,12 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base_cli'
3
+ require_relative 'base_subcommand'
4
4
  require_relative '../models/entry_group'
5
5
  require_relative '../views/entry_group/show'
6
6
 
7
7
  module Dsu
8
8
  module Subcommands
9
- class Edit < Dsu::BaseCLI
9
+ class Edit < BaseSubcommand
10
10
  map %w[d] => :date
11
11
  map %w[n] => :today
12
12
  map %w[t] => :tomorrow
@@ -53,7 +53,7 @@ module Dsu
53
53
  entry_group = Models::EntryGroup.edit(time: Time.parse(date))
54
54
  Views::EntryGroup::Show.new(entry_group: entry_group).render
55
55
  rescue ArgumentError => e
56
- say "Error: #{e.message}", ERROR
56
+ puts apply_theme("Error: #{e.message}", theme_color: color_theme.error)
57
57
  exit 1
58
58
  end
59
59
  end
@@ -1,13 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../base_cli'
4
3
  require_relative '../support/command_options/dsu_times'
4
+ require_relative '../support/command_options/time_mneumonic'
5
5
  require_relative '../support/time_formatable'
6
+ require_relative '../views/entry_group/shared/no_entries_to_display'
7
+ require_relative '../views/shared/error'
8
+ require_relative 'base_subcommand'
6
9
 
7
10
  module Dsu
8
11
  module Subcommands
9
- class List < Dsu::BaseCLI
10
- include Support::CommandOptions::DsuTimes
12
+ class List < BaseSubcommand
13
+ include Support::CommandOptions::TimeMneumonic
11
14
  include Support::TimeFormatable
12
15
 
13
16
  map %w[d] => :date
@@ -21,10 +24,11 @@ module Dsu
21
24
  long_desc <<-LONG_DESC
22
25
  Displays the DSU entries for today. This command has no options.
23
26
  LONG_DESC
27
+ option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
24
28
  def today
25
29
  time = Time.now
26
- times = sorted_dsu_times_for(times: [time, time.yesterday])
27
- view_list_for(times: times)
30
+ times = sorted_dsu_times_for(times: [time.yesterday, time])
31
+ view_list_for(times: times, options: options)
28
32
  end
29
33
 
30
34
  desc 'tomorrow, t',
@@ -32,10 +36,11 @@ module Dsu
32
36
  long_desc <<-LONG_DESC
33
37
  Displays the DSU entries for tomorrow. This command has no options.
34
38
  LONG_DESC
39
+ option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
35
40
  def tomorrow
36
41
  time = Time.now
37
- times = sorted_dsu_times_for(times: [time.tomorrow, time])
38
- view_list_for(times: times)
42
+ times = sorted_dsu_times_for(times: [time, time.tomorrow])
43
+ view_list_for(times: times, options: options)
39
44
  end
40
45
 
41
46
  desc 'yesterday, y',
@@ -43,10 +48,11 @@ module Dsu
43
48
  long_desc <<-LONG_DESC
44
49
  Displays the DSU entries for yesterday. This command has no options.
45
50
  LONG_DESC
51
+ option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
46
52
  def yesterday
47
53
  time = Time.now
48
54
  times = sorted_dsu_times_for(times: [time.yesterday, time.yesterday.yesterday])
49
- view_list_for(times: times)
55
+ view_list_for(times: times, options: options)
50
56
  end
51
57
 
52
58
  desc 'date, d DATE|MNEUMONIC',
@@ -54,130 +60,101 @@ module Dsu
54
60
  long_desc <<-LONG_DESC
55
61
  Displays the DSU entries for the DATE or MNEUMONIC provided.
56
62
 
57
- \x5
58
63
  #{date_option_description}
59
64
 
60
- \x5
61
65
  #{mneumonic_option_description}
62
66
  LONG_DESC
63
- def date(param)
64
- time = if time_mneumonic?(param)
65
- time_from_mneumonic(command_option: param)
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)
66
71
  else
67
- Time.parse(param)
72
+ Time.parse(date_or_mneumonic)
68
73
  end
69
74
  times = sorted_dsu_times_for(times: [time, time.yesterday])
70
- view_list_for(times: times)
75
+ view_list_for(times: times, options: options)
71
76
  rescue ArgumentError => e
72
- say "Error: #{e.message}", ERROR
73
- exit 1
77
+ Views::Shared::Error.new(messages: e.message).render
74
78
  end
75
79
 
76
- desc 'dates, dd OPTIONS',
80
+ desc 'dates|dd OPTIONS',
77
81
  'Displays the DSU entries for the OPTIONS provided'
78
82
  long_desc <<~LONG_DESC
79
83
  NAME
80
- \x5
81
- `dsu dates|dd OPTIONS` -- will display the DSU entries for the OPTIONS provided.
84
+
85
+ $ dsu dates|dd OPTIONS -- will display the DSU entries for the OPTIONS provided.
82
86
 
83
87
  SYNOPSIS
84
- \x5
85
- `dsu dates|dd OPTIONS`
88
+
89
+ $ dsu dates|dd OPTIONS
86
90
 
87
91
  OPTIONS
88
- \x5
92
+
89
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`.
90
94
 
91
- \x5
92
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)`).
93
96
 
94
- \x5
95
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.
96
98
 
97
- \x5
98
99
  #{date_option_description}
99
100
 
100
- \x5
101
101
  #{mneumonic_option_description}
102
102
 
103
- \x5
104
103
  EXAMPLES
105
104
 
106
- \x5
107
105
  NOTE: All examples are subject to the `--include-all` option.
108
106
 
109
- \x5
110
- # The below will display the DSU entries for the range of dates from 1/1 to 1/4.
111
- \x5`dsu list dates --from 1/1 --to +3`
112
-
113
- \x5
114
- # will display the DSU entries for the range of dates from 1/2 to 1/5.
115
- \x5`dsu list dates --from 1/5 --to -3`
116
-
117
- \x5
118
- # (assuming "today" is 1/10) will display the DSU entries for the last week 1/10 to 1/3.
119
- \x5`dsu list dates --from today --to -7`
120
-
121
- \x5
122
- # (assuming "today" is 5/23) will display the DSU entries for the last week 5/16 to 5/22.
123
- #
124
- # This example simply illustrates the fact that you can use relative mneumonics for
125
- # both `--from` and `--to` options; this doesn't mean you should do so...
126
- #
127
- # While you can use relative mneumonics for both `--from` and `--to` options,
128
- # there is always a more intuitive way. The below example basically lists one week
129
- # of DSU entries back 1 week from yesterday's date:
130
- \x5`dsu list dates --from -7 --to +6`
131
-
132
- \x5
133
- # This can MUCH easily be accomplished by simply using the `yesterday` mneumonic:
134
- # (assuming "today" is 5/23) will display the DSU entries back 1 week from yesterday's date 5/16 to 5/22.
135
- \x5`dsu list dates --from yesterday --to -6`
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
136
134
  LONG_DESC
137
135
  # -f, --from FROM [DATE|MNEMONIC] (e.g. -f, --from 1/1[/yyy]|n|t|y|today|tomorrow|yesterday)
138
- option :from, type: :string, aliases: '-f', banner: 'DATE|MNEMONIC'
136
+ option :from, type: :string, required: true, aliases: '-f', banner: 'DATE|MNEMONIC'
139
137
  # -t, --to TO [DATE|MNEMONIC] (e.g. -t, --to 1/1[/yyy]|n|t|y|today|tomorrow|yesterday)
140
- option :to, type: :string, aliases: '-t', banner: 'DATE|MNEMONIC'
141
-
138
+ option :to, type: :string, required: true, aliases: '-t', banner: 'DATE|MNEMONIC'
142
139
  # Include dates that have no DSU entries.
143
- option :include_all, type: :boolean, aliases: '-a'
140
+ option :include_all, type: :boolean, aliases: '-a', desc: 'Include dates that have no DSU entries'
144
141
  def dates
145
- options = configuration.merge(self.options)
146
- times = dsu_times_from!(from_command_option: options[:from], to_command_option: options[:to])
147
- # Note special sort here, unlike the other commands where rules for
148
- # displaying DSU entries are applied; this is more of a list command.
149
- times = times_sort(times: times, entries_display_order: entries_display_order)
150
- view_entry_groups(times: times, options: options) do |total_entry_groups|
151
- nothing_to_display_banner_for(times) if total_entry_groups.zero?
142
+ options = configuration.to_h.merge(self.options).with_indifferent_access
143
+ times, errors = Support::CommandOptions::DsuTimes.dsu_times_for(from_option: options[:from], to_option: options[:to]) # rubocop:disable Layout/LineLength
144
+ if errors.any?
145
+ Views::Shared::Error.new(messages: errors).render
146
+ return
152
147
  end
153
- rescue ArgumentError => e
154
- say "Error: #{e.message}", ERROR
155
- exit 1
156
- end
157
-
158
- private
159
-
160
- def nothing_to_display_banner_for(entry_group_times)
161
- entry_group_times.sort!
162
- time_range = "#{formatted_time(time: entry_group_times.first)} " \
163
- "through #{formatted_time(time: entry_group_times.last)}"
164
- say "(nothing to display for #{time_range})", INFO
165
- end
166
148
 
167
- # This method will unconditionally display the FIRST and LAST entry groups
168
- # associated with the times provided by the <times> argument. All other
169
- # entry groups will be conditionally displayed based on the :include_all
170
- # value in the <options> argument.
171
- def view_list_for(times:)
172
- options = configuration.merge(self.options)
173
- times_first_and_last = [times.first, times.last]
174
- times.each do |time|
175
- view_options = options.dup
176
- view_options[:include_all] = true if times_first_and_last.include?(time)
177
- view_entry_group(time: time, options: view_options) do
178
- puts
179
- end
149
+ # NOTE: special sort here, unlike the other commands where rules for
150
+ # displaying DSU entries are applied; this is more of a list command.
151
+ 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?
180
155
  end
156
+ rescue ArgumentError => e
157
+ Views::Shared::Error.new(messages: e.message).render
181
158
  end
182
159
  end
183
160
  end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../env'
4
+ require_relative '../support/fileable'
5
+ require_relative '../views/color_theme/index'
6
+ require_relative '../views/color_theme/show'
7
+ require_relative '../views/shared/error'
8
+ require_relative '../views/shared/info'
9
+ require_relative 'base_subcommand'
10
+
11
+ module Dsu
12
+ module Subcommands
13
+ class Theme < BaseSubcommand
14
+ map %w[c] => :create if Dsu.env.test?
15
+ map %w[d] => :delete if Dsu.env.test?
16
+ map %w[l] => :list
17
+ map %w[s] => :show
18
+ map %w[u] => :use
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
38
+ option :description, type: :string, aliases: '-d', banner: 'DESCRIPTION'
39
+ option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
40
+ def create(theme_name)
41
+ if Models::ColorTheme.exist?(theme_name: theme_name)
42
+ Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" already exists.").render
43
+ return false
44
+ end
45
+ prompt = color_theme.prompt_with_options(prompt: "Create color theme \"#{theme_name}\"?", options: %w[y N])
46
+ if yes?(prompt, options: options)
47
+ theme_hash = Models::ColorTheme::DEFAULT_THEME.dup
48
+ theme_hash[:description] = options[:description] || "#{theme_name.capitalize} color theme"
49
+ Models::ColorTheme.new(theme_name: theme_name, theme_hash: theme_hash).save!
50
+ Views::Shared::Info.new(messages: "\nCreated color theme \"#{theme_name}\".").render
51
+ true
52
+ else
53
+ Views::Shared::Info.new(messages: "\nCanceled.").render
54
+ false
55
+ end
56
+ end
57
+
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
65
+ option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
66
+ def delete(theme_name)
67
+ display_dsu_header
68
+
69
+ 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
72
+ return
73
+ end
74
+
75
+ unless Models::ColorTheme.exist?(theme_name: theme_name)
76
+ Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" does not exist.").render
77
+ return
78
+ end
79
+
80
+ prompt = color_theme.prompt_with_options(prompt: "Delete color theme \"#{theme_name}\"?",
81
+ options: %w[y N])
82
+ if yes?(prompt, options: options)
83
+ Models::ColorTheme.delete!(theme_name: theme_name)
84
+ Views::Shared::Info.new(messages: "\nDeleted color theme \"#{theme_name}\".").render
85
+ else
86
+ Views::Shared::Info.new(messages: "\nCanceled.").render
87
+ end
88
+ end
89
+ end
90
+
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
98
+ def list
99
+ Views::ColorTheme::Index.new.render
100
+ end
101
+
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
115
+ option :prompts, type: :hash, default: {}, hide: true, aliases: '-p'
116
+ 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
121
+
122
+ 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
125
+ return
126
+ end
127
+
128
+ configuration.theme_name = theme_name
129
+ configuration.save!
130
+ # We need to display the header after the theme is updated so that it is displayed in the
131
+ # correct theme color.
132
+ display_dsu_header
133
+ Views::Shared::Info.new(messages: "Using color theme \"#{theme_name}\".").render
134
+ end
135
+
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
143
+ def show(theme_name = configuration.theme_name)
144
+ if Dsu::Models::ColorTheme.exist?(theme_name: theme_name)
145
+ Views::ColorTheme::Show.new(theme_name: theme_name).render
146
+ return
147
+ end
148
+
149
+ Views::Shared::Error.new(messages: "Color theme \"#{theme_name}\" does not exist.").render
150
+ end
151
+
152
+ private
153
+
154
+ def display_dsu_header
155
+ self.class.display_dsu_header
156
+ end
157
+ end
158
+ end
159
+ end
@@ -5,34 +5,29 @@ require 'thor'
5
5
  module Dsu
6
6
  module Support
7
7
  module Ask
8
- ASK_YES = %w[y yes].freeze
9
- # ASK_NO = %w[n no].freeze
10
- # ASK_CANCEL = %w[c cancel].freeze
11
- # ASK_YES_NO_CANCEL = ASK_YES.concat(ASK_NO).concat(ASK_CANCEL).freeze
12
-
13
8
  def ask(prompt)
14
9
  options = {}
15
10
  Thor::LineEditor.readline(prompt, options)
16
11
  end
17
12
 
18
- def yes?(prompt, color = nil)
19
- Thor::Base.shell.new.yes?(prompt, color)
20
- end
13
+ def yes?(prompt, options: {})
14
+ auto_prompt = auto_prompt(prompt, options)
21
15
 
22
- # def no?(prompt)
23
- # ask_with(prompt: prompt, values: ASK_NO)
24
- # end
16
+ return auto_prompt unless auto_prompt.nil?
25
17
 
26
- # def yes_no_cancel(prompt)
27
- # ask_with(prompt: prompt, values: ASK_YES_NO_CANCEL)
28
- # end
18
+ Thor::Base.shell.new.yes?(prompt)
19
+ end
29
20
 
30
- # private
21
+ private
31
22
 
32
- # def ask_with(prompt:, values:)
33
- # p "#{prompt}"
34
- # values.include? STDIN.gets.chomp
35
- # end
23
+ def auto_prompt(prompt, options)
24
+ prompt = Utils.strip_escapes(prompt)
25
+ @auto_prompt ||= begin
26
+ value = options.dig('prompts', prompt)
27
+ value = (value == 'true' unless value.nil?)
28
+ value
29
+ end
30
+ end
36
31
  end
37
32
  end
38
33
  end