doto 0.0.1.pre.alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. checksums.yaml +7 -0
  2. data/.env.test +1 -0
  3. data/.reek.yml +20 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +206 -0
  6. data/.ruby-version +1 -0
  7. data/CHANGELOG.md +7 -0
  8. data/CODE_OF_CONDUCT.md +84 -0
  9. data/Gemfile +30 -0
  10. data/Gemfile.lock +179 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +38 -0
  13. data/Rakefile +16 -0
  14. data/bin/console +36 -0
  15. data/bin/doto +3 -0
  16. data/bin/setup +18 -0
  17. data/exe/doto +33 -0
  18. data/lib/core/ruby/color_theme_colors.rb +16 -0
  19. data/lib/core/ruby/color_theme_mode.rb +42 -0
  20. data/lib/core/ruby/wrap_and_join.rb +31 -0
  21. data/lib/doto/base_cli.rb +56 -0
  22. data/lib/doto/cli.rb +131 -0
  23. data/lib/doto/command_services/add_entry_service.rb +50 -0
  24. data/lib/doto/crud/json_file.rb +161 -0
  25. data/lib/doto/env.rb +44 -0
  26. data/lib/doto/migration/base_service.rb +118 -0
  27. data/lib/doto/migration/migrator.rb +24 -0
  28. data/lib/doto/migration/raw_helpers/color_theme_hash.rb +13 -0
  29. data/lib/doto/migration/raw_helpers/configuration_hash.rb +15 -0
  30. data/lib/doto/migration/raw_helpers/entry_group_hash.rb +13 -0
  31. data/lib/doto/migration/raw_json_file.rb +15 -0
  32. data/lib/doto/migration/raw_json_files.rb +56 -0
  33. data/lib/doto/migration/v20230613121411/service.rb +94 -0
  34. data/lib/doto/migration/v20240210161248/service.rb +148 -0
  35. data/lib/doto/migration/version.rb +7 -0
  36. data/lib/doto/models/color_theme.rb +224 -0
  37. data/lib/doto/models/configuration.rb +185 -0
  38. data/lib/doto/models/entry.rb +63 -0
  39. data/lib/doto/models/entry_group.rb +223 -0
  40. data/lib/doto/models/migration_version.rb +49 -0
  41. data/lib/doto/models/project.rb +295 -0
  42. data/lib/doto/presenters/base_presenter.rb +32 -0
  43. data/lib/doto/presenters/base_presenter_ex.rb +15 -0
  44. data/lib/doto/presenters/color_theme_presenter.rb +52 -0
  45. data/lib/doto/presenters/color_theme_show_presenter.rb +55 -0
  46. data/lib/doto/presenters/configuration_presenter.rb +50 -0
  47. data/lib/doto/presenters/entry_group/list/date_presenter.rb +77 -0
  48. data/lib/doto/presenters/entry_group/list/dates_presenter.rb +60 -0
  49. data/lib/doto/presenters/entry_group/list/messages.rb +15 -0
  50. data/lib/doto/presenters/entry_group/list/nothing_to_list.rb +15 -0
  51. data/lib/doto/presenters/entry_group_presenter.rb +35 -0
  52. data/lib/doto/presenters/entry_presenter.rb +25 -0
  53. data/lib/doto/presenters/export/all_presenter.rb +44 -0
  54. data/lib/doto/presenters/export/dates_presenter.rb +55 -0
  55. data/lib/doto/presenters/import/all_presenter.rb +57 -0
  56. data/lib/doto/presenters/import/dates_presenter.rb +70 -0
  57. data/lib/doto/presenters/import/import_entry.rb +22 -0
  58. data/lib/doto/presenters/import/import_file.rb +33 -0
  59. data/lib/doto/presenters/project/create_presenter.rb +44 -0
  60. data/lib/doto/presenters/project/defaultable.rb +15 -0
  61. data/lib/doto/presenters/project/delete_by_number_presenter.rb +54 -0
  62. data/lib/doto/presenters/project/delete_presenter.rb +53 -0
  63. data/lib/doto/presenters/project/list_presenter.rb +24 -0
  64. data/lib/doto/presenters/project/rename_by_number_presenter.rb +63 -0
  65. data/lib/doto/presenters/project/rename_presenter.rb +57 -0
  66. data/lib/doto/presenters/project/use_by_number_presenter.rb +57 -0
  67. data/lib/doto/presenters/project/use_presenter.rb +56 -0
  68. data/lib/doto/services/color_theme/hydrator_service.rb +42 -0
  69. data/lib/doto/services/configuration/hydrator_service.rb +42 -0
  70. data/lib/doto/services/entry/hydrator_service.rb +33 -0
  71. data/lib/doto/services/entry_group/browse_service.rb +100 -0
  72. data/lib/doto/services/entry_group/counter_service.rb +32 -0
  73. data/lib/doto/services/entry_group/deleter_service.rb +35 -0
  74. data/lib/doto/services/entry_group/editor_service.rb +103 -0
  75. data/lib/doto/services/entry_group/exporter_service.rb +98 -0
  76. data/lib/doto/services/entry_group/hydrator_service.rb +37 -0
  77. data/lib/doto/services/entry_group/importer_service.rb +117 -0
  78. data/lib/doto/services/migration_version/hydrator_service.rb +36 -0
  79. data/lib/doto/services/project/hydrator_service.rb +40 -0
  80. data/lib/doto/services/project/rename_service.rb +70 -0
  81. data/lib/doto/services/stderr_redirector_service.rb +27 -0
  82. data/lib/doto/services/stdout_redirector_service.rb +27 -0
  83. data/lib/doto/services/temp_file/reader_service.rb +33 -0
  84. data/lib/doto/services/temp_file/writer_service.rb +35 -0
  85. data/lib/doto/subcommands/base_subcommand.rb +12 -0
  86. data/lib/doto/subcommands/browse.rb +49 -0
  87. data/lib/doto/subcommands/config.rb +81 -0
  88. data/lib/doto/subcommands/delete.rb +108 -0
  89. data/lib/doto/subcommands/edit.rb +48 -0
  90. data/lib/doto/subcommands/export.rb +62 -0
  91. data/lib/doto/subcommands/import.rb +72 -0
  92. data/lib/doto/subcommands/list.rb +95 -0
  93. data/lib/doto/subcommands/project.rb +146 -0
  94. data/lib/doto/subcommands/theme.rb +131 -0
  95. data/lib/doto/support/ask.rb +44 -0
  96. data/lib/doto/support/color_themable.rb +36 -0
  97. data/lib/doto/support/command_help_colorizeable.rb +34 -0
  98. data/lib/doto/support/command_hookable.rb +71 -0
  99. data/lib/doto/support/command_options/doto_times.rb +48 -0
  100. data/lib/doto/support/command_options/time.rb +84 -0
  101. data/lib/doto/support/command_options/time_mnemonic.rb +108 -0
  102. data/lib/doto/support/command_options/time_mnemonics.rb +16 -0
  103. data/lib/doto/support/descriptable.rb +29 -0
  104. data/lib/doto/support/entry_group_browsable.rb +104 -0
  105. data/lib/doto/support/field_errors.rb +11 -0
  106. data/lib/doto/support/fileable.rb +136 -0
  107. data/lib/doto/support/presentable.rb +11 -0
  108. data/lib/doto/support/project_file_system.rb +118 -0
  109. data/lib/doto/support/short_string.rb +24 -0
  110. data/lib/doto/support/time_comparable.rb +21 -0
  111. data/lib/doto/support/time_formatable.rb +65 -0
  112. data/lib/doto/support/times_sortable.rb +71 -0
  113. data/lib/doto/support/transform_project_name.rb +24 -0
  114. data/lib/doto/support/utils.rb +11 -0
  115. data/lib/doto/validators/color_theme_validator.rb +74 -0
  116. data/lib/doto/validators/description_validator.rb +51 -0
  117. data/lib/doto/validators/entries_validator.rb +77 -0
  118. data/lib/doto/validators/project_name_validator.rb +58 -0
  119. data/lib/doto/validators/time_validator.rb +25 -0
  120. data/lib/doto/validators/version_validator.rb +29 -0
  121. data/lib/doto/version.rb +6 -0
  122. data/lib/doto/views/base_list_view.rb +41 -0
  123. data/lib/doto/views/color_theme/index.rb +62 -0
  124. data/lib/doto/views/color_theme/show.rb +107 -0
  125. data/lib/doto/views/configuration/show.rb +41 -0
  126. data/lib/doto/views/entry_group/edit.rb +121 -0
  127. data/lib/doto/views/entry_group/list.rb +23 -0
  128. data/lib/doto/views/entry_group/shared/no_entries_to_display.rb +53 -0
  129. data/lib/doto/views/entry_group/shared/no_entries_to_display_for_month_of.rb +32 -0
  130. data/lib/doto/views/entry_group/shared/no_entries_to_display_for_week_of.rb +33 -0
  131. data/lib/doto/views/entry_group/shared/no_entries_to_display_for_year_of.rb +33 -0
  132. data/lib/doto/views/entry_group/show.rb +63 -0
  133. data/lib/doto/views/export.rb +82 -0
  134. data/lib/doto/views/import.rb +105 -0
  135. data/lib/doto/views/import_dates.rb +17 -0
  136. data/lib/doto/views/project/create.rb +87 -0
  137. data/lib/doto/views/project/delete.rb +96 -0
  138. data/lib/doto/views/project/delete_by_number.rb +19 -0
  139. data/lib/doto/views/project/list.rb +115 -0
  140. data/lib/doto/views/project/rename.rb +98 -0
  141. data/lib/doto/views/project/rename_by_number.rb +21 -0
  142. data/lib/doto/views/project/use.rb +97 -0
  143. data/lib/doto/views/project/use_by_number.rb +19 -0
  144. data/lib/doto/views/shared/error.rb +17 -0
  145. data/lib/doto/views/shared/info.rb +17 -0
  146. data/lib/doto/views/shared/message.rb +85 -0
  147. data/lib/doto/views/shared/model_errors.rb +32 -0
  148. data/lib/doto/views/shared/success.rb +17 -0
  149. data/lib/doto/views/shared/warning.rb +17 -0
  150. data/lib/doto.rb +33 -0
  151. data/lib/locales/en/active_record.yml +17 -0
  152. data/lib/locales/en/commands.yml +165 -0
  153. data/lib/locales/en/miscellaneous.yml +29 -0
  154. data/lib/locales/en/presenters.yml +19 -0
  155. data/lib/locales/en/services.yml +14 -0
  156. data/lib/locales/en/subcommands.yml +786 -0
  157. data/lib/seed_data/0/.todo +5 -0
  158. data/lib/seed_data/20230613121411/.doto +8 -0
  159. data/lib/seed_data/20230613121411/doto/migration_version.json +3 -0
  160. data/lib/seed_data/20230613121411/doto/themes/cherry.json +79 -0
  161. data/lib/seed_data/20230613121411/doto/themes/christmas.json +79 -0
  162. data/lib/seed_data/20230613121411/doto/themes/default.json +79 -0
  163. data/lib/seed_data/20230613121411/doto/themes/lemon.json +79 -0
  164. data/lib/seed_data/20230613121411/doto/themes/light.json +79 -0
  165. data/lib/seed_data/20230613121411/doto/themes/matrix.json +79 -0
  166. data/lib/seed_data/20230613121411/doto/themes/whiteout.json +79 -0
  167. data/lib/seed_data/20240210161248/.doto +9 -0
  168. data/lib/seed_data/20240210161248/doto/current_project.json +4 -0
  169. data/lib/seed_data/20240210161248/doto/migration_version.json +3 -0
  170. data/lib/seed_data/20240210161248/doto/projects/default/project.json +5 -0
  171. data/lib/seed_data/20240210161248/doto/themes/cherry.json +79 -0
  172. data/lib/seed_data/20240210161248/doto/themes/christmas.json +79 -0
  173. data/lib/seed_data/20240210161248/doto/themes/default.json +79 -0
  174. data/lib/seed_data/20240210161248/doto/themes/lemon.json +79 -0
  175. data/lib/seed_data/20240210161248/doto/themes/light.json +79 -0
  176. data/lib/seed_data/20240210161248/doto/themes/matrix.json +79 -0
  177. data/lib/seed_data/20240210161248/doto/themes/whiteout.json +79 -0
  178. data/sig/dsu.rbs +4 -0
  179. metadata +406 -0
@@ -0,0 +1,295 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+
5
+ require_relative '../crud/json_file'
6
+ require_relative '../migration/version'
7
+ require_relative '../models/configuration'
8
+ require_relative '../services/project/hydrator_service'
9
+ require_relative '../services/project/rename_service'
10
+ require_relative '../support/descriptable'
11
+ require_relative '../support/fileable'
12
+ require_relative '../support/project_file_system'
13
+ require_relative '../validators/description_validator'
14
+ require_relative '../validators/project_name_validator'
15
+ require_relative '../validators/version_validator'
16
+
17
+ module Doto
18
+ module Models
19
+ # This class represents a project. A project is a collection of entry groups.
20
+ class Project
21
+ include ActiveModel::Model
22
+ include Support::Descriptable
23
+ include Support::Fileable
24
+ include Support::ProjectFileSystem
25
+
26
+ VERSION = Migration::VERSION
27
+ MIN_PROJECT_NAME_LENGTH = 2
28
+ MAX_PROJECT_NAME_LENGTH = 32
29
+ MIN_DESCRIPTION_LENGTH = 2
30
+ MAX_DESCRIPTION_LENGTH = 64
31
+
32
+ attr_reader :project_name, :current_project_file, :description, :version, :options
33
+
34
+ validates_with Validators::DescriptionValidator
35
+ validates_with Validators::ProjectNameValidator
36
+ validates_with Validators::VersionValidator
37
+
38
+ def initialize(project_name:, description: nil, version: nil, options: {})
39
+ raise ArgumentError, 'project_name is blank' if project_name.blank?
40
+ raise ArgumentError, 'version is the wrong object type' unless version.is_a?(Integer) || version.nil?
41
+
42
+ self.project_name = project_name
43
+ self.description = description
44
+ self.version = version || VERSION
45
+ self.options = options || {}
46
+ end
47
+
48
+ # Override == and hash so that we can compare Entry Group objects.
49
+ def ==(other)
50
+ other.is_a?(Project) &&
51
+ project_name == other.project_name &&
52
+ description == other.description &&
53
+ version == other.version
54
+ end
55
+ alias eql? ==
56
+
57
+ def can_delete?
58
+ self.class.can_delete?(project_name: project_name)
59
+ end
60
+
61
+ def create
62
+ self.class.create(project_name: project_name, description: description)
63
+ end
64
+ alias save create
65
+
66
+ def create!
67
+ self.class.create!(project_name: project_name, description: description)
68
+ end
69
+ alias save! create!
70
+
71
+ def current_project?
72
+ self.class.current_project?(project_name: project_name)
73
+ end
74
+
75
+ def default!
76
+ return if default_project?
77
+
78
+ self.class.default!(project: self)
79
+ end
80
+
81
+ def default_project?
82
+ self.class.default_project?(project_name: project_name)
83
+ end
84
+
85
+ def delete
86
+ self.class.delete(project_name: project_name)
87
+ end
88
+
89
+ def delete!
90
+ self.class.delete!(project_name: project_name)
91
+ end
92
+
93
+ def hash
94
+ [project_name, description, version].map(&:hash).hash
95
+ end
96
+
97
+ def project_file
98
+ self.class.project_file(project_name: project_name)
99
+ end
100
+
101
+ def project_folder
102
+ self.class.project_folder(project_name: project_name)
103
+ end
104
+
105
+ def rename!(new_project_name:, new_project_description: nil)
106
+ self.class.rename!(project_name: project_name,
107
+ new_project_name: new_project_name, new_project_description: new_project_description, options: options)
108
+ end
109
+
110
+ def to_h
111
+ {
112
+ version: version,
113
+ project_name: project_name,
114
+ description: description
115
+ }
116
+ end
117
+
118
+ # def update
119
+ # self.class.update(project_name: project_name, description: description, version: version, options: options)
120
+ # end
121
+
122
+ # def update!
123
+ # self.class.update!(project_name: project_name, description: description, version: version, options: options)
124
+ # end
125
+
126
+ def use!
127
+ return if current_project?
128
+
129
+ self.class.use!(project: self)
130
+ end
131
+
132
+ class << self
133
+ delegate :project_file_for, :project_folder_for, to: Support::Fileable
134
+
135
+ def all
136
+ project_metadata.map do |metadata|
137
+ find(project_name: metadata[:project_name])
138
+ end
139
+ end
140
+
141
+ def can_delete?(project_name:)
142
+ exist?(project_name: project_name) &&
143
+ # Cannot delete the last project.
144
+ count > 1 &&
145
+ # Do not allow the project to be deleted if it
146
+ # is currently the default project.
147
+ # The user needs to change to another default
148
+ # project before they can delete this project.
149
+ !default_project?(project_name: project_name)
150
+ end
151
+
152
+ def count
153
+ project_metadata.count
154
+ end
155
+
156
+ def create(project_name:, description: nil, options: {})
157
+ Models::Project.new(project_name: project_name, description: description, options: options).tap do |project|
158
+ project.validate!
159
+ initialize_project(project_name: project_name)
160
+ Crud::JsonFile.write!(file_data: project.to_h,
161
+ file_path: project_file_for(project_name: project_name))
162
+ end
163
+ end
164
+ alias update create
165
+
166
+ def create!(project_name:, description: nil, options: {})
167
+ if exist?(project_name: project_name)
168
+ raise I18n.t('models.project.errors.already_exists', project_name: project_name)
169
+ end
170
+
171
+ create(project_name: project_name, description: description, options: options)
172
+ end
173
+ alias update! create!
174
+
175
+ def current_project
176
+ find(project_name: current_project_name)
177
+ end
178
+
179
+ def current_project?(project_name:)
180
+ current_project_name == project_name
181
+ end
182
+
183
+ def default!(project:)
184
+ project.validate!
185
+
186
+ Models::Configuration.new.tap do |configuration|
187
+ configuration.default_project = project.project_name
188
+ configuration.save!
189
+ end
190
+ end
191
+
192
+ def default_project
193
+ find(project_name: default_project_name)
194
+ end
195
+
196
+ def default_project?(project_name:)
197
+ project_name == default_project_name
198
+ end
199
+
200
+ def delete(project_name:)
201
+ return false unless can_delete?(project_name: project_name)
202
+
203
+ project_folder = project_folder_for(project_name: project_name)
204
+ FileUtils.rm_rf(project_folder)
205
+
206
+ true
207
+ end
208
+
209
+ def delete!(project_name:)
210
+ unless exist?(project_name: project_name)
211
+ raise I18n.t('models.project.errors.does_not_exist', project_name: project_name)
212
+ end
213
+
214
+ raise I18n.t('models.project.errors.delete_only_project', project_name: project_name) unless count > 1
215
+
216
+ if default_project?(project_name: project_name)
217
+ raise I18n.t('models.project.errors.delete_default_project', project_name: project_name)
218
+ end
219
+
220
+ delete(project_name: project_name)
221
+ end
222
+
223
+ def find(project_name:)
224
+ unless project_folder_exist?(project_name: project_name)
225
+ raise I18n.t('models.project.errors.does_not_exist', project_name: project_name)
226
+ end
227
+
228
+ project_file = project_file_for(project_name: project_name)
229
+
230
+ unless project_file_exist?(project_name: project_name)
231
+ raise I18n.t('models.project.errors.project_file_not_exist', project_file: project_file)
232
+ end
233
+
234
+ project_hash = Crud::JsonFile.read!(file_path: project_file)
235
+ Services::Project::HydratorService.new(project_hash: project_hash).call
236
+ end
237
+
238
+ # project_number is 1 based.
239
+ def find_by_number(project_number:)
240
+ project = project_metadata.find do |metadata|
241
+ metadata[:project_number] == project_number.to_i
242
+ end
243
+ return unless project
244
+
245
+ find(project_name: project[:project_name])
246
+ end
247
+
248
+ # def find_or_create(project_name:)
249
+ # find_or_initialize(project_name: project_name).tap do |project|
250
+ # project.save! unless project.persisted?
251
+ # end
252
+ # end
253
+
254
+ def find_or_initialize(project_name:)
255
+ return Models::Project.new(project_name: project_name) unless project_file_exist?(project_name: project_name)
256
+
257
+ project_file = project_file_for(project_name: project_name)
258
+ project_hash = Crud::JsonFile.read!(file_path: project_file)
259
+ Services::Project::HydratorService.new(project_hash: project_hash).call
260
+ end
261
+
262
+ def rename!(project_name:, new_project_name:, new_project_description: nil, options: {})
263
+ Services::Project::RenameService.new(from_project_name: project_name,
264
+ to_project_name: new_project_name, to_project_description: new_project_description, options: options).call
265
+ end
266
+
267
+ def use!(project:)
268
+ project.validate!
269
+
270
+ current_project_hash = { version: project.version, project_name: project.project_name }
271
+ Crud::JsonFile.write!(file_data: current_project_hash, file_path: current_project_file)
272
+ end
273
+ end
274
+
275
+ private
276
+
277
+ attr_writer :current_project_file, :options, :version
278
+
279
+ def description=(value)
280
+ @description = if value.blank?
281
+ "#{project_name} project"
282
+ else
283
+ value
284
+ end
285
+ end
286
+
287
+ def project_name=(value)
288
+ @project_name = begin
289
+ @current_project_file = project_folder_for(project_name: value)
290
+ value
291
+ end
292
+ end
293
+ end
294
+ end
295
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+ require_relative '../models/color_theme'
5
+ require_relative '../support/color_themable'
6
+
7
+ module Doto
8
+ module Presenters
9
+ class BasePresenter < SimpleDelegator
10
+ include Support::ColorThemable
11
+
12
+ attr_reader :color_theme
13
+
14
+ def initialize(object, options: {})
15
+ super(object)
16
+
17
+ @options = options || {}
18
+ theme_name = options.fetch(:theme_name, Models::Configuration.new.theme_name)
19
+ @color_theme = Models::ColorTheme.find(theme_name: theme_name)
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :options
25
+
26
+ def formatted_index(index:)
27
+ apply_theme("#{format('%02s', index + 1)}. ",
28
+ theme_color: color_theme.index)
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doto
4
+ module Presenters
5
+ class BasePresenterEx
6
+ def initialize(options: {})
7
+ @options = options&.dup || {}
8
+ end
9
+
10
+ private
11
+
12
+ attr_accessor :options
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_presenter'
4
+
5
+ module Doto
6
+ module Presenters
7
+ class ColorThemePresenter < BasePresenter
8
+ attr_reader :color_theme
9
+
10
+ def initialize(color_theme, options: {})
11
+ super
12
+
13
+ @color_theme = color_theme
14
+ end
15
+
16
+ def header
17
+ header = I18n.t('presenters.color_theme_presenter.headers.color_themes')
18
+ apply_theme(header, theme_color: color_theme.subheader)
19
+ end
20
+
21
+ def footer
22
+ header = I18n.t('presenters.color_theme_presenter.headers.current_theme')
23
+ apply_theme(header, theme_color: color_theme.footer)
24
+ end
25
+
26
+ def detail
27
+ "#{apply_theme(theme_name_formatted, theme_color: color_theme.body)} - " \
28
+ "#{apply_theme(description, theme_color: color_theme.body)}"
29
+ end
30
+
31
+ def detail_with_index(index:)
32
+ "#{formatted_index(index: index)} #{detail}"
33
+ end
34
+
35
+ private
36
+
37
+ def theme_name_formatted
38
+ return theme_name unless default_color_theme?
39
+
40
+ "*#{theme_name}"
41
+ end
42
+
43
+ def default_color_theme?
44
+ theme_name == default_color_theme.theme_name
45
+ end
46
+
47
+ def default_color_theme
48
+ @default_color_theme ||= Models::ColorTheme.current_or_default
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_presenter'
4
+
5
+ module Doto
6
+ module Presenters
7
+ class ColorThemeShowPresenter < BasePresenter
8
+ def initialize(color_theme, options: {})
9
+ super(color_theme, options: options.merge(theme_name: color_theme.theme_name))
10
+ end
11
+
12
+ def detail
13
+ headers = [I18n.t('presenters.color_theme_show_presenter.headers.number'),
14
+ I18n.t('presenters.color_theme_show_presenter.headers.color'),
15
+ I18n.t('presenters.color_theme_show_presenter.headers.values')]
16
+ puts_detail(*headers, header: true)
17
+
18
+ Models::ColorTheme::DEFAULT_THEME_COLORS.keys.each_with_index do |color_key, index|
19
+ index = formatted_index(index: index)
20
+ color_hash = color_theme.public_send(color_key)
21
+ puts_detail(index, color_key, color_hash)
22
+ end
23
+ end
24
+
25
+ def detail_with_index(index:)
26
+ "#{formatted_index(index: index)} #{detail}"
27
+ end
28
+
29
+ def footer
30
+ header = I18n.t('presenters.color_theme_show_presenter.headers.footer_example')
31
+ apply_theme(header, theme_color: color_theme.footer)
32
+ end
33
+
34
+ def header
35
+ header = I18n.t('presenters.color_theme_show_presenter.headers.viewing_color_theme',
36
+ theme_name: color_theme.theme_name)
37
+ apply_theme(header, theme_color: color_theme.subheader)
38
+ end
39
+
40
+ private
41
+
42
+ def puts_detail(index, color_key, color_hash, header: false)
43
+ if header
44
+ puts "#{apply_theme(index.to_s.ljust(4), theme_color: color_theme.index.bold!)} " \
45
+ "#{apply_theme(color_key.to_s.ljust(15), theme_color: color_theme.index.bold!)} " \
46
+ "#{apply_theme(color_hash.to_s.ljust(10), theme_color: color_theme.index.bold!)}"
47
+ else
48
+ puts "#{apply_theme(index.to_s.ljust(4), theme_color: color_theme.index)} " \
49
+ "#{apply_theme(color_key.to_s.ljust(15), theme_color: color_hash)} " \
50
+ "#{apply_theme(color_hash.to_s.ljust(10), theme_color: color_theme.body)}"
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../env'
4
+ require_relative '../support/color_themable'
5
+ require_relative 'base_presenter'
6
+
7
+ module Doto
8
+ module Presenters
9
+ class ConfigurationPresenter < BasePresenter
10
+ attr_reader :config
11
+
12
+ def initialize(config, options: {})
13
+ super
14
+
15
+ @config = config
16
+ end
17
+
18
+ def configuration_header
19
+ header = I18n.t('presenters.configuration_presenter.headers.file_contents', config_path: config_path)
20
+ apply_theme(header, theme_color: color_theme.header)
21
+ end
22
+
23
+ def configuration_details
24
+ to_h.each_with_index.filter_map do |config_entry, index|
25
+ formatted_config_entry_with_index(config_entry, index: index, theme_color: color_theme.body)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def config_path
32
+ @config_path ||= if Doto.env.screen_shot_mode?
33
+ "/Users/#{Doto.env.screen_shot_username}/.doto"
34
+ else
35
+ config.file_path
36
+ end
37
+ end
38
+
39
+ def formatted_config_entry_with_index(config_entry, index:, theme_color:)
40
+ "#{formatted_index(index: index)} #{formatted_config_entry(config_entry: config_entry,
41
+ theme_color: theme_color)}"
42
+ end
43
+
44
+ def formatted_config_entry(config_entry:, theme_color:)
45
+ config_entry = "#{config_entry[0]}: '#{config_entry[1]}'"
46
+ apply_theme(config_entry, theme_color: theme_color)
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../models/configuration'
4
+ require_relative '../../../models/entry_group'
5
+ require_relative '../../../views/entry_group/shared/no_entries_to_display'
6
+ require_relative '../../base_presenter_ex'
7
+ require_relative 'messages'
8
+ require_relative 'nothing_to_list'
9
+
10
+ module Doto
11
+ module Presenters
12
+ module EntryGroup
13
+ module List
14
+ class DatePresenter < BasePresenterEx
15
+ include Messages
16
+ include NothingToList
17
+
18
+ def initialize(times:, options: {})
19
+ raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
20
+ raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
21
+
22
+ super(options: options)
23
+
24
+ @times = times
25
+ end
26
+
27
+ def render
28
+ return if nothing_to_list?
29
+
30
+ entry_groups.each do |entry_group|
31
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
32
+ puts
33
+ end
34
+ end
35
+
36
+ def display_nothing_to_list_message
37
+ # This presenter will ALWAYS have something to list (display) since the first
38
+ # and last (if different) entry groups will always be displayed.
39
+ raise 'display_nothing_to_list_message called when there are entries to display'
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :times
45
+
46
+ def entry_groups
47
+ @entry_groups ||= begin
48
+ options = configuration.to_h.merge(self.options).with_indifferent_access
49
+
50
+ times.filter_map do |time|
51
+ view_options = options.dup
52
+ # Always show the first and last entry groups.
53
+ view_options[:include_all] = true if times_min_max.include?(time)
54
+
55
+ next unless show_entry_group?(time: time, options: view_options)
56
+
57
+ Models::EntryGroup.find_or_initialize(time: time)
58
+ end
59
+ end
60
+ end
61
+
62
+ def times_min_max
63
+ @times_min_max ||= times.minmax
64
+ end
65
+
66
+ def configuration
67
+ @configuration ||= Models::Configuration.new
68
+ end
69
+
70
+ def show_entry_group?(time:, options:)
71
+ Models::EntryGroup.exist?(time: time) || options[:include_all]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../models/entry_group'
4
+ require_relative '../../../views/entry_group/shared/no_entries_to_display'
5
+ require_relative '../../base_presenter_ex'
6
+ require_relative 'messages'
7
+ require_relative 'nothing_to_list'
8
+
9
+ module Doto
10
+ module Presenters
11
+ module EntryGroup
12
+ module List
13
+ class DatesPresenter < BasePresenterEx
14
+ include Messages
15
+ include NothingToList
16
+
17
+ def initialize(times:, options: {})
18
+ raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
19
+ raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
20
+
21
+ super(options: options)
22
+
23
+ @times = times
24
+ end
25
+
26
+ def render
27
+ return if nothing_to_list?
28
+
29
+ entry_groups.each do |entry_group|
30
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
31
+ puts
32
+ end
33
+ end
34
+
35
+ def display_nothing_to_list_message
36
+ raise 'display_nothing_to_list_message called when there are entries to display' unless nothing_to_list?
37
+
38
+ Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :times
44
+
45
+ def entry_groups
46
+ @entry_groups ||= times.filter_map do |time|
47
+ next unless show_entry_group?(time: time, options: options)
48
+
49
+ Models::EntryGroup.find_or_initialize(time: time)
50
+ end
51
+ end
52
+
53
+ def show_entry_group?(time:, options:)
54
+ Models::EntryGroup.exist?(time: time) || options[:include_all]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doto
4
+ module Presenters
5
+ module EntryGroup
6
+ module List
7
+ module Messages
8
+ def display_nothing_to_list_message
9
+ raise NotImplementedError
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Doto
4
+ module Presenters
5
+ module EntryGroup
6
+ module List
7
+ module NothingToList
8
+ def nothing_to_list?
9
+ entry_groups.none?
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end