dsu 1.2.1 → 2.0.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -21
- data/Gemfile.lock +7 -7
- data/README.md +28 -35
- data/bin/console +23 -1
- data/bin/dsu +3 -0
- data/bin/setup +14 -3
- data/exe/dsu +23 -1
- data/exe/dsu_migrate.rb +43 -0
- data/lib/core/ruby/color_theme_colors.rb +16 -0
- data/lib/core/ruby/color_theme_mode.rb +42 -0
- data/lib/core/ruby/not_today.rb +7 -0
- data/lib/core/ruby/wrap_and_join.rb +31 -0
- data/lib/dsu/base_cli.rb +19 -23
- data/lib/dsu/cli.rb +47 -37
- data/lib/dsu/command_services/add_entry_service.rb +10 -21
- data/lib/dsu/crud/json_file.rb +139 -0
- data/lib/dsu/crud/raw_json_file.rb +51 -0
- data/lib/dsu/env.rb +21 -0
- data/lib/dsu/migration/service.rb +196 -0
- data/lib/dsu/migration/version.rb +7 -0
- data/lib/dsu/models/color_theme.rb +270 -0
- data/lib/dsu/models/configuration.rb +160 -0
- data/lib/dsu/models/entry.rb +6 -2
- data/lib/dsu/models/entry_group.rb +143 -42
- data/lib/dsu/models/migration_version.rb +48 -0
- data/lib/dsu/presenters/base_presenter.rb +32 -0
- data/lib/dsu/presenters/color_theme_presenter.rb +50 -0
- data/lib/dsu/presenters/color_theme_show_presenter.rb +49 -0
- data/lib/dsu/presenters/configuration_presenter.rb +45 -0
- data/lib/dsu/presenters/entry_group_presenter.rb +35 -0
- data/lib/dsu/presenters/entry_presenter.rb +25 -0
- data/lib/dsu/services/color_theme/hydrator_service.rb +42 -0
- data/lib/dsu/services/configuration/hydrator_service.rb +42 -0
- data/lib/dsu/services/entry/hydrator_service.rb +33 -0
- data/lib/dsu/services/entry_group/editor_service.rb +107 -0
- data/lib/dsu/services/entry_group/hydrator_service.rb +37 -0
- data/lib/dsu/services/migration_version/hydrator_service.rb +36 -0
- data/lib/dsu/services/stderr_redirector_service.rb +27 -0
- data/lib/dsu/services/temp_file/reader_service.rb +33 -0
- data/lib/dsu/services/temp_file/writer_service.rb +35 -0
- data/lib/dsu/subcommands/base_subcommand.rb +14 -0
- data/lib/dsu/subcommands/config.rb +92 -32
- data/lib/dsu/subcommands/edit.rb +3 -3
- data/lib/dsu/subcommands/list.rb +70 -93
- data/lib/dsu/subcommands/theme.rb +159 -0
- data/lib/dsu/support/ask.rb +14 -19
- data/lib/dsu/support/color_themable.rb +34 -0
- data/lib/dsu/support/command_help_colorizeable.rb +27 -0
- data/lib/dsu/support/command_hookable.rb +60 -0
- data/lib/dsu/support/command_options/dsu_times.rb +32 -21
- data/lib/dsu/support/command_options/time.rb +7 -1
- data/lib/dsu/support/command_options/time_mneumonic.rb +7 -1
- data/lib/dsu/support/descriptable.rb +6 -4
- data/lib/dsu/support/entry_group_viewable.rb +28 -4
- data/lib/dsu/support/fileable.rb +94 -0
- data/lib/dsu/support/presentable.rb +11 -0
- data/lib/dsu/support/subcommand_help_colorizeable.rb +27 -0
- data/lib/dsu/support/time_comparable.rb +19 -0
- data/lib/dsu/support/time_formatable.rb +12 -0
- data/lib/dsu/support/times_sortable.rb +48 -14
- data/lib/dsu/support/utils.rb +11 -0
- data/lib/dsu/validators/color_theme_validator.rb +74 -0
- data/lib/dsu/validators/entries_validator.rb +4 -8
- data/lib/dsu/validators/version_validator.rb +29 -0
- data/lib/dsu/version.rb +2 -1
- data/lib/dsu/views/color_theme/index.rb +62 -0
- data/lib/dsu/views/color_theme/show.rb +106 -0
- data/lib/dsu/views/configuration/show.rb +41 -0
- data/lib/dsu/views/entry_group/edit.rb +3 -5
- data/lib/dsu/views/entry_group/shared/no_entries_to_display.rb +41 -0
- data/lib/dsu/views/entry_group/show.rb +16 -15
- data/lib/dsu/views/shared/error.rb +17 -0
- data/lib/dsu/views/shared/info.rb +17 -0
- data/lib/dsu/views/shared/message.rb +85 -0
- data/lib/dsu/views/shared/model_errors.rb +31 -0
- data/lib/dsu/views/shared/success.rb +17 -0
- data/lib/dsu/views/shared/warning.rb +17 -0
- data/lib/dsu.rb +22 -1
- data/lib/seed_data/themes/cherry.json +79 -0
- data/lib/seed_data/themes/default.json +79 -0
- data/lib/seed_data/themes/lemon.json +79 -0
- data/lib/seed_data/themes/matrix.json +79 -0
- data/lib/seed_data/themes/whiteout.json +79 -0
- metadata +68 -23
- data/lib/dsu/core/ruby/not_today.rb +0 -11
- data/lib/dsu/services/ai/tense_translator_service.rb +0 -63
- data/lib/dsu/services/configuration_loader_service.rb +0 -55
- data/lib/dsu/services/entry_group_deleter_service.rb +0 -31
- data/lib/dsu/services/entry_group_editor_service.rb +0 -96
- data/lib/dsu/services/entry_group_hydrator_service.rb +0 -43
- data/lib/dsu/services/entry_group_reader_service.rb +0 -36
- data/lib/dsu/services/entry_group_writer_service.rb +0 -46
- data/lib/dsu/services/entry_hydrator_service.rb +0 -35
- data/lib/dsu/services/temp_file_reader_service.rb +0 -31
- data/lib/dsu/services/temp_file_writer_service.rb +0 -33
- data/lib/dsu/support/colorable.rb +0 -14
- data/lib/dsu/support/configurable.rb +0 -15
- data/lib/dsu/support/configuration.rb +0 -112
- data/lib/dsu/support/entry_group_fileable.rb +0 -49
- data/lib/dsu/support/entry_group_loadable.rb +0 -49
- data/lib/dsu/support/folder_locations.rb +0 -21
- data/lib/dsu/support/say.rb +0 -40
- data/lib/dsu/views/edited_entries/shared/errors.rb +0 -39
- data/lib/dsu/views/shared/messages.rb +0 -56
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'colorize'
|
|
4
|
+
|
|
5
|
+
# rubocop:disable Layout/LineLength
|
|
6
|
+
module Dsu
|
|
7
|
+
module Validators
|
|
8
|
+
class ColorThemeValidator < ActiveModel::Validator
|
|
9
|
+
def validate(record)
|
|
10
|
+
default_theme_colors = record.class::DEFAULT_THEME_COLORS
|
|
11
|
+
|
|
12
|
+
# return unless validate_color_theme_keys!(record, default_theme_colors)
|
|
13
|
+
|
|
14
|
+
default_theme_colors.each_key do |theme_color_key|
|
|
15
|
+
theme_colors_hash = record.public_send(theme_color_key)
|
|
16
|
+
|
|
17
|
+
next unless validate_theme_color_type!(record, theme_color_key, theme_colors_hash)
|
|
18
|
+
|
|
19
|
+
if theme_colors_hash.empty?
|
|
20
|
+
record.errors.add(:base, ":#{theme_color_key} colors Hash is empty")
|
|
21
|
+
next
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
validate_theme_colors!(record, theme_colors_hash)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def colors
|
|
31
|
+
@colors ||= String.colors
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def modes
|
|
35
|
+
@modes ||= String.modes
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def validate_theme_color_type!(record, theme_color_key, theme_colors_hash)
|
|
39
|
+
return true if theme_colors_hash.is_a?(Hash)
|
|
40
|
+
|
|
41
|
+
record.errors.add(:base, ":#{theme_color_key} value is the wrong object type. " \
|
|
42
|
+
"\"Hash\" was expected, but \"#{theme_colors_hash.class}\" was received.")
|
|
43
|
+
false
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def validate_theme_colors!(record, theme_colors_hash)
|
|
47
|
+
unless colors.include?(theme_colors_hash[:color])
|
|
48
|
+
value = theme_color_value_to_s(theme_colors_hash[:color])
|
|
49
|
+
record.errors.add(:base, ":color key value #{value} in theme color Hash #{theme_colors_hash} is not a valid color. " \
|
|
50
|
+
"One of #{colors.wrap_and_join(wrapper: [':', ''])} was expected, but #{value} was received.")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
unless theme_colors_hash[:mode].nil? || modes.include?(theme_colors_hash[:mode])
|
|
54
|
+
value = theme_color_value_to_s(theme_colors_hash[:mode])
|
|
55
|
+
record.errors.add(:base, ":mode key value #{value} in theme color Hash #{theme_colors_hash} is not a valid mode value. " \
|
|
56
|
+
"One of #{modes.wrap_and_join(wrapper: [':', ''])} was expected, but #{value} was received.")
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
unless theme_colors_hash[:background].nil? || colors.include?(theme_colors_hash[:background])
|
|
60
|
+
value = theme_color_value_to_s(theme_colors_hash[:background])
|
|
61
|
+
record.errors.add(:base, ":background key value #{value} in theme color Hash #{theme_colors_hash} is not a valid color. " \
|
|
62
|
+
"One of #{colors.wrap_and_join(wrapper: [':', ''])} was expected, but #{value} was received.")
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def theme_color_value_to_s(theme_color_value)
|
|
67
|
+
return ":#{theme_color_value}" if theme_color_value.is_a?(Symbol)
|
|
68
|
+
|
|
69
|
+
"'#{theme_color_value}'"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
# rubocop:enable Layout/LineLength
|
|
@@ -17,8 +17,8 @@ module Dsu
|
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
return if validate_entry_types record
|
|
20
|
-
return if validate_unique_entry record
|
|
21
20
|
|
|
21
|
+
validate_unique_entry record
|
|
22
22
|
validate_entries record
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -45,9 +45,9 @@ module Dsu
|
|
|
45
45
|
return if descriptions.uniq.length == descriptions.length
|
|
46
46
|
|
|
47
47
|
non_unique_descriptions = descriptions.select { |description| descriptions.count(description) > 1 }.uniq
|
|
48
|
-
|
|
49
|
-
record.errors.add(:entries,
|
|
50
|
-
|
|
48
|
+
non_unique_descriptions.each do |non_unique_description|
|
|
49
|
+
record.errors.add(:entries,
|
|
50
|
+
"array contains duplicate entry: \"#{short_description(non_unique_description)}\".",
|
|
51
51
|
type: Support::FieldErrors::FIELD_DUPLICATE_ERROR)
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -67,10 +67,6 @@ module Dsu
|
|
|
67
67
|
end
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
-
def format_non_unique_descriptions(non_unique_descriptions)
|
|
71
|
-
non_unique_descriptions.map { |description| "\"#{short_description(description)}\"" }.join(', ')
|
|
72
|
-
end
|
|
73
|
-
|
|
74
70
|
def short_description(description)
|
|
75
71
|
Models::Entry.short_description(string: description)
|
|
76
72
|
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# https://guides.rubyonrails.org/active_record_validations.html#validates-with
|
|
4
|
+
module Dsu
|
|
5
|
+
module Validators
|
|
6
|
+
class VersionValidator < ActiveModel::Validator
|
|
7
|
+
def validate(record)
|
|
8
|
+
version = record.version
|
|
9
|
+
|
|
10
|
+
if version.nil?
|
|
11
|
+
record.errors.add(:version, 'is nil')
|
|
12
|
+
return
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
unless version.is_a?(Integer)
|
|
16
|
+
record.errors.add(:version, 'is the wrong object type. ' \
|
|
17
|
+
"\"Integer\" was expected, but \"#{version.class}\" was received.")
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# TODO: This validation should check the configuration version
|
|
22
|
+
# against the current migration version and they should match.
|
|
23
|
+
# unless version == record.class::VERSION
|
|
24
|
+
# record.errors.add(:version, "\"#{version}\" is not the correct version: \"#{record.class::VERSION}\"")
|
|
25
|
+
# end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/dsu/version.rb
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../models/configuration'
|
|
4
|
+
require_relative '../../models/color_theme'
|
|
5
|
+
|
|
6
|
+
module Dsu
|
|
7
|
+
module Views
|
|
8
|
+
module ColorTheme
|
|
9
|
+
class Index
|
|
10
|
+
include Support::ColorThemable
|
|
11
|
+
|
|
12
|
+
def render
|
|
13
|
+
render!
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
|
|
18
|
+
def render!
|
|
19
|
+
presenter = color_theme.presenter
|
|
20
|
+
puts presenter.header
|
|
21
|
+
puts
|
|
22
|
+
render_theme_details
|
|
23
|
+
puts
|
|
24
|
+
puts presenter.footer
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render_theme_details
|
|
28
|
+
themes_folder = Models::Configuration.new.themes_folder
|
|
29
|
+
theme_file_names = Dir.glob("#{themes_folder}/*").map { |theme_path| File.basename(theme_path, '.*') }
|
|
30
|
+
theme_file_names << default_theme_name unless theme_file_names.include?(default_theme_name)
|
|
31
|
+
theme_file_names.sort.each_with_index do |theme_file, index|
|
|
32
|
+
color_theme = if theme_file == default_theme_name
|
|
33
|
+
default_theme
|
|
34
|
+
else
|
|
35
|
+
Models::ColorTheme.find(theme_name: theme_file)
|
|
36
|
+
end
|
|
37
|
+
presenter = color_theme.presenter
|
|
38
|
+
puts presenter.detail_with_index(index: index)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# When getting the default theme, if it exists on disk, use that; otherwise,
|
|
43
|
+
# use the in-memory default theme.
|
|
44
|
+
def default_theme
|
|
45
|
+
if Models::ColorTheme.exist?(theme_name: default_theme_name)
|
|
46
|
+
Models::ColorTheme.find(theme_name: default_theme_name)
|
|
47
|
+
else
|
|
48
|
+
Models::ColorTheme.default
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def default_theme_name
|
|
53
|
+
Dsu::Models::ColorTheme::DEFAULT_THEME_NAME
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def color_theme
|
|
57
|
+
@color_theme ||= Models::ColorTheme.current_or_default
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../models/color_theme'
|
|
4
|
+
require_relative '../../models/configuration'
|
|
5
|
+
require_relative '../entry_group/show'
|
|
6
|
+
require_relative '../shared/error'
|
|
7
|
+
require_relative '../shared/info'
|
|
8
|
+
require_relative '../shared/success'
|
|
9
|
+
require_relative '../shared/warning'
|
|
10
|
+
|
|
11
|
+
module Dsu
|
|
12
|
+
module Views
|
|
13
|
+
module ColorTheme
|
|
14
|
+
class Show
|
|
15
|
+
include Support::ColorThemable
|
|
16
|
+
|
|
17
|
+
def initialize(theme_name:, options: {})
|
|
18
|
+
@theme_name = theme_name
|
|
19
|
+
@options = options || {}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render
|
|
23
|
+
render!
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
attr_reader :theme_name, :options
|
|
29
|
+
|
|
30
|
+
def color_theme
|
|
31
|
+
@color_theme ||= Models::ColorTheme.find_or_initialize(theme_name: theme_name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def presenter
|
|
35
|
+
@presenter ||= Dsu::Presenters::ColorThemeShowPresenter.new(color_theme, options: options)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def render!
|
|
39
|
+
puts presenter.header
|
|
40
|
+
puts
|
|
41
|
+
|
|
42
|
+
presenter.detail
|
|
43
|
+
puts
|
|
44
|
+
|
|
45
|
+
display_entry_group_example
|
|
46
|
+
puts
|
|
47
|
+
|
|
48
|
+
display_configuration_example
|
|
49
|
+
puts
|
|
50
|
+
|
|
51
|
+
display_messages_example
|
|
52
|
+
puts
|
|
53
|
+
|
|
54
|
+
puts presenter.footer
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def display_entry_group_example
|
|
58
|
+
puts apply_theme('`dsu list` example', theme_color: color_theme.subheader)
|
|
59
|
+
puts
|
|
60
|
+
|
|
61
|
+
options = custom_options
|
|
62
|
+
entry_group = mock_entry_group(options)
|
|
63
|
+
EntryGroup::Show.new(entry_group: entry_group, options: options).render
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def display_configuration_example
|
|
67
|
+
puts apply_theme('`dsu config info` example', theme_color: color_theme.subheader)
|
|
68
|
+
puts
|
|
69
|
+
|
|
70
|
+
Views::Configuration::Show.new(config: Models::Configuration.new(options: custom_options)).render
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def display_messages_example
|
|
74
|
+
options = custom_options
|
|
75
|
+
messages = ['Example 1', 'Example 2', 'Example 3']
|
|
76
|
+
|
|
77
|
+
puts apply_theme('Message examples', theme_color: color_theme.subheader)
|
|
78
|
+
puts
|
|
79
|
+
|
|
80
|
+
Shared::Error.new(messages: messages, header: 'Errors example', options: options).render
|
|
81
|
+
puts
|
|
82
|
+
|
|
83
|
+
Shared::Info.new(messages: messages, header: 'Info example', options: options).render
|
|
84
|
+
puts
|
|
85
|
+
|
|
86
|
+
Shared::Success.new(messages: messages, header: 'Success example', options: options).render
|
|
87
|
+
puts
|
|
88
|
+
|
|
89
|
+
Shared::Warning.new(messages: messages, header: 'Warning example', options: options).render
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def mock_entry_group(options)
|
|
93
|
+
@mock_entry_group ||= Dsu::Models::EntryGroup.new(time: Time.now, entries: [
|
|
94
|
+
Dsu::Models::Entry.new(description: 'Dsu entry 1', options: options),
|
|
95
|
+
Dsu::Models::Entry.new(description: 'Dsu entry 2', options: options),
|
|
96
|
+
Dsu::Models::Entry.new(description: 'Dsu entry 3', options: options)
|
|
97
|
+
], options: options)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def custom_options
|
|
101
|
+
options.merge(theme_name: color_theme.theme_name, output_stream: $stdout)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'active_support/core_ext/numeric/time'
|
|
5
|
+
require_relative '../../models/configuration'
|
|
6
|
+
require_relative '../../support/color_themable'
|
|
7
|
+
|
|
8
|
+
module Dsu
|
|
9
|
+
module Views
|
|
10
|
+
module Configuration
|
|
11
|
+
class Show
|
|
12
|
+
include Support::ColorThemable
|
|
13
|
+
|
|
14
|
+
def initialize(config:, options: {})
|
|
15
|
+
raise ArgumentError, 'config is nil' if config.nil?
|
|
16
|
+
raise ArgumentError, 'config is the wrong object type' unless config.is_a?(Models::Configuration)
|
|
17
|
+
raise ArgumentError, 'options is nil' if options.nil?
|
|
18
|
+
raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
|
|
19
|
+
|
|
20
|
+
@config = config
|
|
21
|
+
@options = options || {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def call
|
|
25
|
+
render!
|
|
26
|
+
end
|
|
27
|
+
alias render call
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_reader :config, :options
|
|
32
|
+
|
|
33
|
+
def render!
|
|
34
|
+
presenter = config.presenter
|
|
35
|
+
puts presenter.configuration_header
|
|
36
|
+
puts presenter.configuration_details
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative '../../models/configuration'
|
|
3
4
|
require_relative '../../models/entry_group'
|
|
4
|
-
require_relative '../../support/entry_group_fileable'
|
|
5
5
|
|
|
6
6
|
module Dsu
|
|
7
7
|
module Views
|
|
8
8
|
module EntryGroup
|
|
9
9
|
class Edit
|
|
10
|
-
include Support::EntryGroupFileable
|
|
11
|
-
|
|
12
10
|
def initialize(entry_group:, options: {})
|
|
13
11
|
raise ArgumentError, 'entry_group is nil' if entry_group.nil?
|
|
14
12
|
raise ArgumentError, 'entry_group is the wrong object type' unless entry_group.is_a?(Models::EntryGroup)
|
|
@@ -108,13 +106,13 @@ module Dsu
|
|
|
108
106
|
# TODO: Make this configurable or accept an option?
|
|
109
107
|
@previous_entry_group ||= (1..7).each do |days|
|
|
110
108
|
t = time.days_ago(days)
|
|
111
|
-
return Models::EntryGroup.
|
|
109
|
+
return Models::EntryGroup.find(time: t) if Models::EntryGroup.exist?(time: t)
|
|
112
110
|
end
|
|
113
111
|
nil
|
|
114
112
|
end
|
|
115
113
|
|
|
116
114
|
def carry_over_entries_to_today?
|
|
117
|
-
|
|
115
|
+
Models::Configuration.new.merge(options).carry_over_entries_to_today?
|
|
118
116
|
end
|
|
119
117
|
end
|
|
120
118
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../../models/color_theme'
|
|
4
|
+
require_relative '../../../support/color_themable'
|
|
5
|
+
require_relative '../../../support/time_formatable'
|
|
6
|
+
|
|
7
|
+
module Dsu
|
|
8
|
+
module Views
|
|
9
|
+
module EntryGroup
|
|
10
|
+
module Shared
|
|
11
|
+
class NoEntriesToDisplay
|
|
12
|
+
include Support::ColorThemable
|
|
13
|
+
include Support::TimeFormatable
|
|
14
|
+
|
|
15
|
+
def initialize(times:, options: {})
|
|
16
|
+
raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
|
|
17
|
+
raise ArgumentError, 'times must contain Time objects' unless times.all?(Time)
|
|
18
|
+
raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash) || options.nil?
|
|
19
|
+
|
|
20
|
+
@times = times
|
|
21
|
+
@options = options || {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def render
|
|
25
|
+
entry_group_times.sort!
|
|
26
|
+
time_range = "#{formatted_time(time: times.first)} " \
|
|
27
|
+
"through #{formatted_time(time: times.last)}"
|
|
28
|
+
message = "(nothing to display for #{time_range})"
|
|
29
|
+
puts apply_theme(message, theme_color: color_theme.info)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def color_theme
|
|
35
|
+
@color_theme ||= Models::ColorTheme.current_or_default
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -3,16 +3,14 @@
|
|
|
3
3
|
require 'time'
|
|
4
4
|
require 'active_support/core_ext/numeric/time'
|
|
5
5
|
require_relative '../../models/entry_group'
|
|
6
|
-
require_relative '../../support/
|
|
7
|
-
require_relative '../../support/say'
|
|
6
|
+
require_relative '../../support/color_themable'
|
|
8
7
|
require_relative '../../support/time_formatable'
|
|
9
8
|
|
|
10
9
|
module Dsu
|
|
11
10
|
module Views
|
|
12
11
|
module EntryGroup
|
|
13
12
|
class Show
|
|
14
|
-
include Support::
|
|
15
|
-
include Support::Say
|
|
13
|
+
include Support::ColorThemable
|
|
16
14
|
include Support::TimeFormatable
|
|
17
15
|
|
|
18
16
|
def initialize(entry_group:, options: {})
|
|
@@ -35,22 +33,25 @@ module Dsu
|
|
|
35
33
|
attr_reader :entry_group, :options
|
|
36
34
|
|
|
37
35
|
def render!
|
|
38
|
-
|
|
39
|
-
|
|
36
|
+
puts presenter.formatted_time
|
|
37
|
+
|
|
38
|
+
entry_group.validate!
|
|
39
|
+
puts presenter.no_entries_available and return if entry_group.entries.empty?
|
|
40
40
|
|
|
41
41
|
entry_group.entries.each_with_index do |entry, index|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
entry_info = "#{prefix} #{description}"
|
|
45
|
-
unless entry.valid?
|
|
46
|
-
entry_info = "#{entry_info} (validation failed: #{entry_errors(entry_group_deleter_service)})"
|
|
47
|
-
end
|
|
48
|
-
say entry_info
|
|
42
|
+
entry_presenter = entry.presenter
|
|
43
|
+
puts entry_presenter.formatted_description_with_index(index: index)
|
|
49
44
|
end
|
|
45
|
+
rescue ActiveModel::ValidationError
|
|
46
|
+
puts apply_theme(errors(entry_group), theme_color: presenter.color_theme.error)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def errors(model)
|
|
50
|
+
model.errors.full_messages.join(', ')
|
|
50
51
|
end
|
|
51
52
|
|
|
52
|
-
def
|
|
53
|
-
|
|
53
|
+
def presenter
|
|
54
|
+
@presenter ||= entry_group.presenter
|
|
54
55
|
end
|
|
55
56
|
end
|
|
56
57
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'message'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class Error < Message
|
|
9
|
+
def initialize(messages:, header: nil, options: {})
|
|
10
|
+
options = { header: header, output_stream: $stderr }.merge(options)
|
|
11
|
+
|
|
12
|
+
super(messages: messages, message_type: :error, options: options)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'message'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class Info < Message
|
|
9
|
+
def initialize(messages:, header: nil, options: {})
|
|
10
|
+
options = { header: header, output_stream: $stdout }.merge(options)
|
|
11
|
+
|
|
12
|
+
super(messages: messages, message_type: :info, options: options)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../../models/color_theme'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class Message
|
|
9
|
+
include Support::ColorThemable
|
|
10
|
+
|
|
11
|
+
MESSAGE_TYPES = %i[error info success warning].freeze
|
|
12
|
+
|
|
13
|
+
def initialize(messages:, message_type:, options: {})
|
|
14
|
+
messages = [messages] unless messages.is_a?(Array)
|
|
15
|
+
messages = messages.select(&:present?)
|
|
16
|
+
|
|
17
|
+
validate_arguments!(messages, message_type, options)
|
|
18
|
+
|
|
19
|
+
@messages = messages
|
|
20
|
+
@message_type = message_type
|
|
21
|
+
@options = options || {}
|
|
22
|
+
@message_color = color_theme.public_send(message_type)
|
|
23
|
+
@header = options[:header]
|
|
24
|
+
@ordered_list = options.fetch(:ordered_list, true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def render
|
|
28
|
+
output_stream.puts to_s
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def to_s
|
|
32
|
+
return if messages.empty?
|
|
33
|
+
|
|
34
|
+
strings = []
|
|
35
|
+
|
|
36
|
+
strings << apply_theme(header, theme_color: color_theme.header) if header.present?
|
|
37
|
+
|
|
38
|
+
strings << if messages.one?
|
|
39
|
+
apply_theme(messages[0], theme_color: message_color)
|
|
40
|
+
else
|
|
41
|
+
messages.each_with_index.map do |message, index|
|
|
42
|
+
message = "#{index + 1}. #{message}" if ordered_list?
|
|
43
|
+
apply_theme(message, theme_color: message_color)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
strings.flatten.join("\n")
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
attr_reader :messages, :message_color, :message_type, :header, :options
|
|
53
|
+
|
|
54
|
+
def color_theme
|
|
55
|
+
@color_theme ||= begin
|
|
56
|
+
theme_name = options.fetch(:theme_name, Models::Configuration.new.theme_name)
|
|
57
|
+
Models::ColorTheme.find(theme_name: theme_name)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ordered_list?
|
|
62
|
+
@ordered_list
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def output_stream
|
|
66
|
+
@output_stream ||= options.fetch(:output_stream, $stdout)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def validate_arguments!(messages, message_type, options)
|
|
70
|
+
raise ArgumentError, 'messages is empty' if messages.empty?
|
|
71
|
+
unless Models::ColorTheme::DEFAULT_THEME_COLORS.key?(message_type)
|
|
72
|
+
raise ArgumentError, 'message_type is not a valid message type'
|
|
73
|
+
end
|
|
74
|
+
raise ArgumentError, 'options is nil' if options.nil?
|
|
75
|
+
|
|
76
|
+
%i[\[\] fetch].each do |method|
|
|
77
|
+
next if options.respond_to?(method)
|
|
78
|
+
|
|
79
|
+
raise ArgumentError, "options does not respond to :#{method}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'error'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class ModelErrors < Error
|
|
9
|
+
def initialize(model:, options: {})
|
|
10
|
+
raise ArgumentError, 'model is nil' if model.nil?
|
|
11
|
+
raise ArgumentError, "model is the wrong object type: \"#{model}\"" unless model.is_a?(ActiveModel::Model)
|
|
12
|
+
|
|
13
|
+
header = options[:header] || 'The following ERRORS were encountered; changes could not be saved:'
|
|
14
|
+
super(messages: model.errors.full_messages, header: header, options: options)
|
|
15
|
+
|
|
16
|
+
@model = model
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render
|
|
20
|
+
return if model.valid?
|
|
21
|
+
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
|
|
27
|
+
attr_reader :model
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'message'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class Success < Message
|
|
9
|
+
def initialize(messages:, header: nil, options: {})
|
|
10
|
+
options = { header: header, output_stream: $stdout }.merge(options)
|
|
11
|
+
|
|
12
|
+
super(messages: messages, message_type: :success, options: options)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'message'
|
|
4
|
+
|
|
5
|
+
module Dsu
|
|
6
|
+
module Views
|
|
7
|
+
module Shared
|
|
8
|
+
class Warning < Message
|
|
9
|
+
def initialize(messages:, header: nil, options: {})
|
|
10
|
+
options = { header: header, output_stream: $stdout }.merge(options)
|
|
11
|
+
|
|
12
|
+
super(messages: messages, message_type: :warning, options: options)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|