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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +55 -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 +70 -25
- 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
|