doto 0.0.1.pre.alpha.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.env.test +1 -0
- data/.reek.yml +20 -0
- data/.rspec +3 -0
- data/.rubocop.yml +206 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +7 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +30 -0
- data/Gemfile.lock +179 -0
- data/LICENSE.txt +21 -0
- data/README.md +38 -0
- data/Rakefile +16 -0
- data/bin/console +36 -0
- data/bin/doto +3 -0
- data/bin/setup +18 -0
- data/exe/doto +33 -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/wrap_and_join.rb +31 -0
- data/lib/doto/base_cli.rb +56 -0
- data/lib/doto/cli.rb +131 -0
- data/lib/doto/command_services/add_entry_service.rb +50 -0
- data/lib/doto/crud/json_file.rb +161 -0
- data/lib/doto/env.rb +44 -0
- data/lib/doto/migration/base_service.rb +118 -0
- data/lib/doto/migration/migrator.rb +24 -0
- data/lib/doto/migration/raw_helpers/color_theme_hash.rb +13 -0
- data/lib/doto/migration/raw_helpers/configuration_hash.rb +15 -0
- data/lib/doto/migration/raw_helpers/entry_group_hash.rb +13 -0
- data/lib/doto/migration/raw_json_file.rb +15 -0
- data/lib/doto/migration/raw_json_files.rb +56 -0
- data/lib/doto/migration/v20230613121411/service.rb +94 -0
- data/lib/doto/migration/v20240210161248/service.rb +148 -0
- data/lib/doto/migration/version.rb +7 -0
- data/lib/doto/models/color_theme.rb +224 -0
- data/lib/doto/models/configuration.rb +185 -0
- data/lib/doto/models/entry.rb +63 -0
- data/lib/doto/models/entry_group.rb +223 -0
- data/lib/doto/models/migration_version.rb +49 -0
- data/lib/doto/models/project.rb +295 -0
- data/lib/doto/presenters/base_presenter.rb +32 -0
- data/lib/doto/presenters/base_presenter_ex.rb +15 -0
- data/lib/doto/presenters/color_theme_presenter.rb +52 -0
- data/lib/doto/presenters/color_theme_show_presenter.rb +55 -0
- data/lib/doto/presenters/configuration_presenter.rb +50 -0
- data/lib/doto/presenters/entry_group/list/date_presenter.rb +77 -0
- data/lib/doto/presenters/entry_group/list/dates_presenter.rb +60 -0
- data/lib/doto/presenters/entry_group/list/messages.rb +15 -0
- data/lib/doto/presenters/entry_group/list/nothing_to_list.rb +15 -0
- data/lib/doto/presenters/entry_group_presenter.rb +35 -0
- data/lib/doto/presenters/entry_presenter.rb +25 -0
- data/lib/doto/presenters/export/all_presenter.rb +44 -0
- data/lib/doto/presenters/export/dates_presenter.rb +55 -0
- data/lib/doto/presenters/import/all_presenter.rb +57 -0
- data/lib/doto/presenters/import/dates_presenter.rb +70 -0
- data/lib/doto/presenters/import/import_entry.rb +22 -0
- data/lib/doto/presenters/import/import_file.rb +33 -0
- data/lib/doto/presenters/project/create_presenter.rb +44 -0
- data/lib/doto/presenters/project/defaultable.rb +15 -0
- data/lib/doto/presenters/project/delete_by_number_presenter.rb +54 -0
- data/lib/doto/presenters/project/delete_presenter.rb +53 -0
- data/lib/doto/presenters/project/list_presenter.rb +24 -0
- data/lib/doto/presenters/project/rename_by_number_presenter.rb +63 -0
- data/lib/doto/presenters/project/rename_presenter.rb +57 -0
- data/lib/doto/presenters/project/use_by_number_presenter.rb +57 -0
- data/lib/doto/presenters/project/use_presenter.rb +56 -0
- data/lib/doto/services/color_theme/hydrator_service.rb +42 -0
- data/lib/doto/services/configuration/hydrator_service.rb +42 -0
- data/lib/doto/services/entry/hydrator_service.rb +33 -0
- data/lib/doto/services/entry_group/browse_service.rb +100 -0
- data/lib/doto/services/entry_group/counter_service.rb +32 -0
- data/lib/doto/services/entry_group/deleter_service.rb +35 -0
- data/lib/doto/services/entry_group/editor_service.rb +103 -0
- data/lib/doto/services/entry_group/exporter_service.rb +98 -0
- data/lib/doto/services/entry_group/hydrator_service.rb +37 -0
- data/lib/doto/services/entry_group/importer_service.rb +117 -0
- data/lib/doto/services/migration_version/hydrator_service.rb +36 -0
- data/lib/doto/services/project/hydrator_service.rb +40 -0
- data/lib/doto/services/project/rename_service.rb +70 -0
- data/lib/doto/services/stderr_redirector_service.rb +27 -0
- data/lib/doto/services/stdout_redirector_service.rb +27 -0
- data/lib/doto/services/temp_file/reader_service.rb +33 -0
- data/lib/doto/services/temp_file/writer_service.rb +35 -0
- data/lib/doto/subcommands/base_subcommand.rb +12 -0
- data/lib/doto/subcommands/browse.rb +49 -0
- data/lib/doto/subcommands/config.rb +81 -0
- data/lib/doto/subcommands/delete.rb +108 -0
- data/lib/doto/subcommands/edit.rb +48 -0
- data/lib/doto/subcommands/export.rb +62 -0
- data/lib/doto/subcommands/import.rb +72 -0
- data/lib/doto/subcommands/list.rb +95 -0
- data/lib/doto/subcommands/project.rb +146 -0
- data/lib/doto/subcommands/theme.rb +131 -0
- data/lib/doto/support/ask.rb +44 -0
- data/lib/doto/support/color_themable.rb +36 -0
- data/lib/doto/support/command_help_colorizeable.rb +34 -0
- data/lib/doto/support/command_hookable.rb +71 -0
- data/lib/doto/support/command_options/doto_times.rb +48 -0
- data/lib/doto/support/command_options/time.rb +84 -0
- data/lib/doto/support/command_options/time_mnemonic.rb +108 -0
- data/lib/doto/support/command_options/time_mnemonics.rb +16 -0
- data/lib/doto/support/descriptable.rb +29 -0
- data/lib/doto/support/entry_group_browsable.rb +104 -0
- data/lib/doto/support/field_errors.rb +11 -0
- data/lib/doto/support/fileable.rb +136 -0
- data/lib/doto/support/presentable.rb +11 -0
- data/lib/doto/support/project_file_system.rb +118 -0
- data/lib/doto/support/short_string.rb +24 -0
- data/lib/doto/support/time_comparable.rb +21 -0
- data/lib/doto/support/time_formatable.rb +65 -0
- data/lib/doto/support/times_sortable.rb +71 -0
- data/lib/doto/support/transform_project_name.rb +24 -0
- data/lib/doto/support/utils.rb +11 -0
- data/lib/doto/validators/color_theme_validator.rb +74 -0
- data/lib/doto/validators/description_validator.rb +51 -0
- data/lib/doto/validators/entries_validator.rb +77 -0
- data/lib/doto/validators/project_name_validator.rb +58 -0
- data/lib/doto/validators/time_validator.rb +25 -0
- data/lib/doto/validators/version_validator.rb +29 -0
- data/lib/doto/version.rb +6 -0
- data/lib/doto/views/base_list_view.rb +41 -0
- data/lib/doto/views/color_theme/index.rb +62 -0
- data/lib/doto/views/color_theme/show.rb +107 -0
- data/lib/doto/views/configuration/show.rb +41 -0
- data/lib/doto/views/entry_group/edit.rb +121 -0
- data/lib/doto/views/entry_group/list.rb +23 -0
- data/lib/doto/views/entry_group/shared/no_entries_to_display.rb +53 -0
- data/lib/doto/views/entry_group/shared/no_entries_to_display_for_month_of.rb +32 -0
- data/lib/doto/views/entry_group/shared/no_entries_to_display_for_week_of.rb +33 -0
- data/lib/doto/views/entry_group/shared/no_entries_to_display_for_year_of.rb +33 -0
- data/lib/doto/views/entry_group/show.rb +63 -0
- data/lib/doto/views/export.rb +82 -0
- data/lib/doto/views/import.rb +105 -0
- data/lib/doto/views/import_dates.rb +17 -0
- data/lib/doto/views/project/create.rb +87 -0
- data/lib/doto/views/project/delete.rb +96 -0
- data/lib/doto/views/project/delete_by_number.rb +19 -0
- data/lib/doto/views/project/list.rb +115 -0
- data/lib/doto/views/project/rename.rb +98 -0
- data/lib/doto/views/project/rename_by_number.rb +21 -0
- data/lib/doto/views/project/use.rb +97 -0
- data/lib/doto/views/project/use_by_number.rb +19 -0
- data/lib/doto/views/shared/error.rb +17 -0
- data/lib/doto/views/shared/info.rb +17 -0
- data/lib/doto/views/shared/message.rb +85 -0
- data/lib/doto/views/shared/model_errors.rb +32 -0
- data/lib/doto/views/shared/success.rb +17 -0
- data/lib/doto/views/shared/warning.rb +17 -0
- data/lib/doto.rb +33 -0
- data/lib/locales/en/active_record.yml +17 -0
- data/lib/locales/en/commands.yml +165 -0
- data/lib/locales/en/miscellaneous.yml +29 -0
- data/lib/locales/en/presenters.yml +19 -0
- data/lib/locales/en/services.yml +14 -0
- data/lib/locales/en/subcommands.yml +786 -0
- data/lib/seed_data/0/.todo +5 -0
- data/lib/seed_data/20230613121411/.doto +8 -0
- data/lib/seed_data/20230613121411/doto/migration_version.json +3 -0
- data/lib/seed_data/20230613121411/doto/themes/cherry.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/christmas.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/default.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/lemon.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/light.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/matrix.json +79 -0
- data/lib/seed_data/20230613121411/doto/themes/whiteout.json +79 -0
- data/lib/seed_data/20240210161248/.doto +9 -0
- data/lib/seed_data/20240210161248/doto/current_project.json +4 -0
- data/lib/seed_data/20240210161248/doto/migration_version.json +3 -0
- data/lib/seed_data/20240210161248/doto/projects/default/project.json +5 -0
- data/lib/seed_data/20240210161248/doto/themes/cherry.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/christmas.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/default.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/lemon.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/light.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/matrix.json +79 -0
- data/lib/seed_data/20240210161248/doto/themes/whiteout.json +79 -0
- data/sig/dsu.rbs +4 -0
- metadata +406 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../env'
|
4
|
+
require_relative 'version'
|
5
|
+
|
6
|
+
module Doto
|
7
|
+
module Migration
|
8
|
+
class Migrator
|
9
|
+
class << self
|
10
|
+
def migrate_if!(migration_services: [])
|
11
|
+
return if migration_services.any? do |migration_service|
|
12
|
+
migration_service.migrate_if!
|
13
|
+
migration_service.class.migrates_to_latest_migration_version?
|
14
|
+
end
|
15
|
+
|
16
|
+
raise I18n.t('migrations.error.missing_current_migration_service', migration_version: Migration::VERSION)
|
17
|
+
rescue StandardError => e
|
18
|
+
puts I18n.t('migrations.error.failed', message: e.message)
|
19
|
+
exit 1 unless Doto.env.test?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Doto
|
4
|
+
module Migration
|
5
|
+
module RawHelpers
|
6
|
+
module ConfigurationHash
|
7
|
+
attr_accessor :default_project
|
8
|
+
|
9
|
+
def to_h
|
10
|
+
read.merge(version: version, default_project: default_project)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require_relative 'raw_json_file'
|
7
|
+
|
8
|
+
module Doto
|
9
|
+
module Migration
|
10
|
+
class RawJsonFiles
|
11
|
+
attr_reader :folder
|
12
|
+
|
13
|
+
def initialize(folder)
|
14
|
+
@folder = folder
|
15
|
+
end
|
16
|
+
|
17
|
+
def each_file(regex: //)
|
18
|
+
return unless folder_exist?
|
19
|
+
|
20
|
+
Pathname.new(folder).children.each do |child|
|
21
|
+
next unless child.file? && child.to_s.match?(regex)
|
22
|
+
|
23
|
+
yield RawJsonFile.new(child)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def folder_exist?
|
28
|
+
self.class.folder_exist?(folder: folder)
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def folder_exist?(folder:)
|
33
|
+
Dir.exist?(folder)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_writer :folder
|
40
|
+
|
41
|
+
def safe_cp_r(source, destination)
|
42
|
+
Pathname.new(source).find do |source_path|
|
43
|
+
next if source_path.directory?
|
44
|
+
|
45
|
+
relative_path = source_path.relative_path_from(Pathname.new(source))
|
46
|
+
target_path = Pathname.new(destination).join(relative_path)
|
47
|
+
|
48
|
+
next if target_path.exist?
|
49
|
+
|
50
|
+
FileUtils.mkdir_p(target_path.dirname)
|
51
|
+
FileUtils.cp(source_path, target_path)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_service'
|
4
|
+
require_relative '../raw_helpers/entry_group_hash'
|
5
|
+
require_relative '../version'
|
6
|
+
|
7
|
+
module Doto
|
8
|
+
module Migration
|
9
|
+
module V20230613121411
|
10
|
+
class Service < BaseService
|
11
|
+
class << self
|
12
|
+
def from_migration_version
|
13
|
+
0
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_migration_version
|
17
|
+
20230613121411 # rubocop:disable Style/NumericLiterals
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def run_migration!
|
24
|
+
super
|
25
|
+
|
26
|
+
raise_backup_folder_does_not_exist_error_if!
|
27
|
+
|
28
|
+
delete_old_config_file
|
29
|
+
delete_old_themes_folder
|
30
|
+
|
31
|
+
copy_new_doto_folders
|
32
|
+
copy_new_doto_configuration
|
33
|
+
migrate_entry_groups
|
34
|
+
end
|
35
|
+
|
36
|
+
def config_file_from
|
37
|
+
File.join(root_folder, '.doto')
|
38
|
+
end
|
39
|
+
|
40
|
+
def doto_folder_from
|
41
|
+
File.join(root_folder, 'doto')
|
42
|
+
end
|
43
|
+
|
44
|
+
def entries_folder_from
|
45
|
+
File.join(doto_folder_from, 'entries')
|
46
|
+
end
|
47
|
+
|
48
|
+
def themes_folder_from
|
49
|
+
File.join(doto_folder_from, 'themes')
|
50
|
+
end
|
51
|
+
|
52
|
+
def copy_new_doto_folders
|
53
|
+
puts 'Copying new doto folders...'
|
54
|
+
|
55
|
+
FileUtils.cp_r(File.join(seed_data_folder, '.'), File.join(root_folder, 'doto')) unless pretend?
|
56
|
+
end
|
57
|
+
|
58
|
+
def copy_new_doto_configuration
|
59
|
+
puts 'Copying new doto configuration...'
|
60
|
+
|
61
|
+
FileUtils.cp(seed_data_configuration, config_folder) unless pretend?
|
62
|
+
end
|
63
|
+
|
64
|
+
def migrate_entry_groups
|
65
|
+
puts 'Migrating entry groups...'
|
66
|
+
|
67
|
+
return if pretend?
|
68
|
+
|
69
|
+
puts "\tUpdating entry group version..."
|
70
|
+
|
71
|
+
RawJsonFiles.new(entries_folder_from).each_file(regex: /\d{4}-\d{2}-\d{2}.json/) do |raw_entry_group|
|
72
|
+
raw_entry_group.extend(RawHelpers::EntryGroupHash)
|
73
|
+
raw_entry_group.version = to_migration_version
|
74
|
+
raw_entry_group.save!
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def delete_old_config_file
|
79
|
+
puts 'Deleting old configuration file...'
|
80
|
+
|
81
|
+
return if pretend?
|
82
|
+
|
83
|
+
File.delete(config_file_from) if File.file?(config_file_from)
|
84
|
+
end
|
85
|
+
|
86
|
+
def delete_old_themes_folder
|
87
|
+
puts 'Deleting old themes folder...'
|
88
|
+
|
89
|
+
FileUtils.rm_rf(themes_folder_from) unless pretend?
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,148 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../base_service'
|
4
|
+
require_relative '../raw_helpers/color_theme_hash'
|
5
|
+
require_relative '../raw_helpers/configuration_hash'
|
6
|
+
require_relative '../raw_helpers/entry_group_hash'
|
7
|
+
require_relative '../raw_json_file'
|
8
|
+
require_relative '../version'
|
9
|
+
|
10
|
+
module Doto
|
11
|
+
module Migration
|
12
|
+
module V20240210161248
|
13
|
+
class Service < BaseService
|
14
|
+
class << self
|
15
|
+
def from_migration_version
|
16
|
+
20230613121411 # rubocop:disable Style/NumericLiterals
|
17
|
+
end
|
18
|
+
|
19
|
+
def to_migration_version
|
20
|
+
20240210161248 # rubocop:disable Style/NumericLiterals
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def run_migration!
|
27
|
+
super
|
28
|
+
|
29
|
+
raise_backup_folder_does_not_exist_error_if!
|
30
|
+
|
31
|
+
add_new_color_themes
|
32
|
+
create_default_project
|
33
|
+
create_current_project_file
|
34
|
+
update_configuration
|
35
|
+
update_entry_groups
|
36
|
+
update_color_themes
|
37
|
+
delete_old_entry_folder
|
38
|
+
|
39
|
+
puts 'Migration completed successfully.'
|
40
|
+
end
|
41
|
+
|
42
|
+
def doto_folder_from
|
43
|
+
File.join(root_folder, 'doto')
|
44
|
+
end
|
45
|
+
|
46
|
+
def entries_folder_from
|
47
|
+
File.join(doto_folder_from, 'entries')
|
48
|
+
end
|
49
|
+
|
50
|
+
def themes_folder_from
|
51
|
+
File.join(doto_folder_from, 'themes')
|
52
|
+
end
|
53
|
+
|
54
|
+
def add_new_color_themes
|
55
|
+
puts 'Copying new color themes...'
|
56
|
+
|
57
|
+
FileUtils.mkdir_p(themes_folder_from) unless pretend?
|
58
|
+
|
59
|
+
%w[light.json christmas.json].each do |theme_file|
|
60
|
+
destination_theme_file_path = File.join(themes_folder_from, theme_file)
|
61
|
+
# Don't skip these theme files because they were deployed in the previous
|
62
|
+
# doto version with bugs. We need to overwrite them with this new version.
|
63
|
+
# next if File.exist?(destination_theme_file_path)
|
64
|
+
|
65
|
+
source_theme_file_path = File.join(seed_data_folder, 'themes', theme_file)
|
66
|
+
FileUtils.cp(source_theme_file_path, destination_theme_file_path) unless pretend?
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def create_default_project
|
71
|
+
puts "Creating default project \"#{default_project_name}\"..."
|
72
|
+
|
73
|
+
return if pretend?
|
74
|
+
|
75
|
+
FileUtils.cp_r(File.join(seed_data_folder, 'projects', '.'),
|
76
|
+
File.join(doto_folder, 'projects'))
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_current_project_file
|
80
|
+
puts 'Creating current project file...'
|
81
|
+
|
82
|
+
return if pretend?
|
83
|
+
|
84
|
+
# NOTE: doto_folder won't change and is safe to use here.
|
85
|
+
FileUtils.cp(File.join(seed_data_folder, 'current_project.json'), doto_folder)
|
86
|
+
end
|
87
|
+
|
88
|
+
def update_configuration
|
89
|
+
puts 'Updating configuration...'
|
90
|
+
|
91
|
+
return if pretend?
|
92
|
+
|
93
|
+
# NOTE: config_path won't change and is safe to use here.
|
94
|
+
RawJsonFile.new(config_path).tap do |configuration_file|
|
95
|
+
configuration_file.extend(RawHelpers::ConfigurationHash)
|
96
|
+
configuration_file.version = to_migration_version
|
97
|
+
configuration_file.default_project = default_project_name
|
98
|
+
end.save!
|
99
|
+
end
|
100
|
+
|
101
|
+
def update_entry_groups
|
102
|
+
puts 'Updating entry groups...'
|
103
|
+
|
104
|
+
return if pretend?
|
105
|
+
|
106
|
+
puts "\tCopying entries to default project \"#{default_project_name}\"..."
|
107
|
+
|
108
|
+
entries_folder_to = File.join(doto_folder, 'projects', default_project_name, 'entries')
|
109
|
+
FileUtils.cp_r(File.join(entries_folder_from, '.'), entries_folder_to)
|
110
|
+
|
111
|
+
puts "\tUpdating entry group version..."
|
112
|
+
|
113
|
+
RawJsonFiles.new(entries_folder_to).each_file(regex: /\d{4}-\d{2}-\d{2}.json/) do |raw_entry_group|
|
114
|
+
raw_entry_group.extend(RawHelpers::EntryGroupHash)
|
115
|
+
raw_entry_group.version = to_migration_version
|
116
|
+
raw_entry_group.save!
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def update_color_themes
|
121
|
+
puts 'Updating color themes...'
|
122
|
+
|
123
|
+
FileUtils.cp_r(File.join(backup_folder, 'themes', '.'), themes_folder) unless pretend?
|
124
|
+
|
125
|
+
puts "\tUpdating color theme version..."
|
126
|
+
|
127
|
+
themes_folder_to = File.join(doto_folder, 'themes')
|
128
|
+
|
129
|
+
RawJsonFiles.new(themes_folder_to).each_file(regex: /.+.json/) do |raw_entry_group|
|
130
|
+
raw_entry_group.extend(RawHelpers::ColorThemeHash)
|
131
|
+
raw_entry_group.version = to_migration_version
|
132
|
+
raw_entry_group.save! unless pretend?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def delete_old_entry_folder
|
137
|
+
puts 'Cleaning up old entries...'
|
138
|
+
|
139
|
+
FileUtils.rm_rf(File.join(entries_folder_from)) unless pretend?
|
140
|
+
end
|
141
|
+
|
142
|
+
def default_project_name
|
143
|
+
'default'
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../crud/json_file'
|
4
|
+
require_relative '../migration/version'
|
5
|
+
require_relative '../support/color_themable'
|
6
|
+
require_relative '../support/descriptable'
|
7
|
+
require_relative '../support/fileable'
|
8
|
+
require_relative '../support/presentable'
|
9
|
+
require_relative '../validators/color_theme_validator'
|
10
|
+
require_relative '../validators/description_validator'
|
11
|
+
require_relative '../validators/version_validator'
|
12
|
+
require_relative 'configuration'
|
13
|
+
|
14
|
+
module Doto
|
15
|
+
module Models
|
16
|
+
# This class represents a doto color theme.
|
17
|
+
class ColorTheme < Crud::JsonFile
|
18
|
+
include Support::ColorThemable
|
19
|
+
include Support::Descriptable
|
20
|
+
include Support::Fileable
|
21
|
+
include Support::Presentable
|
22
|
+
|
23
|
+
THEME_FILE_NAME_REGEX = /.+.json/
|
24
|
+
|
25
|
+
VERSION = Migration::VERSION
|
26
|
+
|
27
|
+
DEFAULT_THEME_NAME = 'default'
|
28
|
+
# Theme colors key/value pair format:
|
29
|
+
# <key>: { color: <color> [, mode: <mode>] [, background: <background>] }
|
30
|
+
# Where <color> (required) == any color represented in the colorize gem `String.colors` array.
|
31
|
+
# <mode> (optional, default is :default) == any mode represented in the colorize gem `String.modes` array.
|
32
|
+
# <background> (optional, default is :default) == any color represented in the colorize gem
|
33
|
+
# `String.colors` array.
|
34
|
+
DEFAULT_THEME_COLORS = {
|
35
|
+
help: { color: :cyan },
|
36
|
+
doto_header: { color: :white, mode: :bold, background: :cyan },
|
37
|
+
doto_footer: { color: :cyan },
|
38
|
+
header: { color: :cyan, mode: :bold },
|
39
|
+
subheader: { color: :cyan, mode: :underline },
|
40
|
+
body: { color: :cyan },
|
41
|
+
footer: { color: :light_cyan },
|
42
|
+
date: { color: :cyan, mode: :bold },
|
43
|
+
index: { color: :light_cyan },
|
44
|
+
# Status colors.
|
45
|
+
info: { color: :cyan },
|
46
|
+
success: { color: :green },
|
47
|
+
warning: { color: :yellow },
|
48
|
+
error: { color: :light_yellow, background: :red },
|
49
|
+
# Prompts
|
50
|
+
prompt: { color: :cyan, mode: :bold },
|
51
|
+
prompt_options: { color: :white, mode: :bold }
|
52
|
+
}.freeze
|
53
|
+
DEFAULT_THEME = {
|
54
|
+
version: VERSION,
|
55
|
+
description: 'Default theme.'
|
56
|
+
}.merge(DEFAULT_THEME_COLORS).freeze
|
57
|
+
|
58
|
+
MIN_DESCRIPTION_LENGTH = 2
|
59
|
+
MAX_DESCRIPTION_LENGTH = 256
|
60
|
+
|
61
|
+
# TODO: Validate other attrs.
|
62
|
+
validates_with Validators::DescriptionValidator
|
63
|
+
validates_with Validators::ColorThemeValidator
|
64
|
+
validates_with Validators::VersionValidator
|
65
|
+
|
66
|
+
attr_reader :theme_name, :options
|
67
|
+
|
68
|
+
def initialize(theme_name:, theme_hash: nil, options: {})
|
69
|
+
raise ArgumentError, 'theme_name is nil.' if theme_name.nil?
|
70
|
+
raise ArgumentError, "theme_name is the wrong object type: \"#{theme_name}\"." unless theme_name.is_a?(String)
|
71
|
+
unless theme_hash.is_a?(Hash) || theme_hash.nil?
|
72
|
+
raise ArgumentError, "theme_hash is the wrong object type: \"#{theme_hash}\"."
|
73
|
+
end
|
74
|
+
|
75
|
+
FileUtils.mkdir_p themes_folder
|
76
|
+
|
77
|
+
@theme_name = theme_name
|
78
|
+
@options = options || {}
|
79
|
+
|
80
|
+
super(self.class.send(:themes_path, theme_name: @theme_name))
|
81
|
+
|
82
|
+
theme_hash ||= DEFAULT_THEME.merge(description: "#{@theme_name.capitalize} theme")
|
83
|
+
|
84
|
+
# Color themes I expect will change a lot, so we're using
|
85
|
+
# a little meta-programming here to dynamically create
|
86
|
+
# public attr_readers and private attr_writers based on the
|
87
|
+
# keys in DEFAULT_THEME, then assign those attributes from
|
88
|
+
# the values in theme_hash. theme_hash will be guaranteed to
|
89
|
+
# have the same keys as DEFAULT_THEME.keys at this point
|
90
|
+
# because we called ensure_theme_hash! above.
|
91
|
+
DEFAULT_THEME.each_key do |attr|
|
92
|
+
self.class.class_eval do
|
93
|
+
attr_reader attr
|
94
|
+
attr_writer attr
|
95
|
+
private :"#{attr}="
|
96
|
+
end
|
97
|
+
attr_value = theme_hash[attr]
|
98
|
+
attr_value = attr_value.merge_default_colors if default_theme_color_keys.include?(attr)
|
99
|
+
send(:"#{attr}=", attr_value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def delete
|
104
|
+
self.class.delete(theme_name: theme_name)
|
105
|
+
end
|
106
|
+
|
107
|
+
def delete!
|
108
|
+
self.class.delete!(theme_name: theme_name)
|
109
|
+
end
|
110
|
+
|
111
|
+
def exist?
|
112
|
+
self.class.exist?(theme_name: theme_name)
|
113
|
+
end
|
114
|
+
|
115
|
+
class << self
|
116
|
+
delegate :themes_folder, :themes_path, to: Support::Fileable
|
117
|
+
|
118
|
+
def all
|
119
|
+
Dir.glob("#{themes_folder}/*").map do |file_path|
|
120
|
+
theme_name = File.basename(file_path, '.*')
|
121
|
+
find(theme_name: theme_name)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def configuration
|
126
|
+
Models::Configuration.new
|
127
|
+
end
|
128
|
+
|
129
|
+
def current
|
130
|
+
theme_name = configuration.theme_name
|
131
|
+
return unless exist?(theme_name: theme_name)
|
132
|
+
|
133
|
+
find(theme_name: theme_name)
|
134
|
+
end
|
135
|
+
|
136
|
+
# Returns the current color theme if it exists; otherwise,
|
137
|
+
# it returns the default color theme.
|
138
|
+
def current_or_default
|
139
|
+
current || default
|
140
|
+
end
|
141
|
+
|
142
|
+
def default
|
143
|
+
new(theme_name: DEFAULT_THEME_NAME, theme_hash: DEFAULT_THEME)
|
144
|
+
end
|
145
|
+
|
146
|
+
def delete(theme_name:)
|
147
|
+
superclass.delete(file_path: themes_path(theme_name: theme_name))
|
148
|
+
end
|
149
|
+
|
150
|
+
def delete!(theme_name:)
|
151
|
+
superclass.delete!(file_path: themes_path(theme_name: theme_name))
|
152
|
+
end
|
153
|
+
|
154
|
+
def ensure_color_theme_color_defaults_for(theme_hash: DEFAULT_THEME)
|
155
|
+
theme_hash = theme_hash.dup
|
156
|
+
|
157
|
+
theme_hash.each_pair do |key, value|
|
158
|
+
next unless default_theme_color_keys.include?(key)
|
159
|
+
|
160
|
+
theme_hash[key] = value.merge_default_colors
|
161
|
+
end
|
162
|
+
theme_hash
|
163
|
+
end
|
164
|
+
|
165
|
+
def exist?(theme_name:)
|
166
|
+
superclass.file_exist?(file_path: themes_path(theme_name: theme_name))
|
167
|
+
end
|
168
|
+
|
169
|
+
def find(theme_name:)
|
170
|
+
theme_hash = read!(file_path: themes_path(theme_name: theme_name))
|
171
|
+
Services::ColorTheme::HydratorService.new(theme_name: theme_name, theme_hash: theme_hash).call
|
172
|
+
end
|
173
|
+
|
174
|
+
def find_or_create(theme_name:)
|
175
|
+
return find(theme_name: theme_name) if exist?(theme_name: theme_name)
|
176
|
+
|
177
|
+
new(theme_name: theme_name).tap(&:write!)
|
178
|
+
end
|
179
|
+
|
180
|
+
def find_or_initialize(theme_name:)
|
181
|
+
return find(theme_name: theme_name) if exist?(theme_name: theme_name)
|
182
|
+
|
183
|
+
new(theme_name: theme_name)
|
184
|
+
end
|
185
|
+
|
186
|
+
private
|
187
|
+
|
188
|
+
def default_theme_color_keys
|
189
|
+
DEFAULT_THEME_COLORS.keys
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def to_h
|
194
|
+
{}.tap do |hash|
|
195
|
+
DEFAULT_THEME.each_key do |key|
|
196
|
+
hash[key] = public_send(key)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def ==(other)
|
202
|
+
return false unless other.is_a?(self.class)
|
203
|
+
return false unless other.theme_name == theme_name
|
204
|
+
|
205
|
+
DEFAULT_THEME.keys.all? { |key| public_send(key) == other.public_send(key) }
|
206
|
+
end
|
207
|
+
alias eql? ==
|
208
|
+
|
209
|
+
def hash
|
210
|
+
DEFAULT_THEME.keys.map { |key| public_send(key) }.tap do |hashes|
|
211
|
+
hashes << theme_name.hash
|
212
|
+
end.hash
|
213
|
+
end
|
214
|
+
|
215
|
+
private
|
216
|
+
|
217
|
+
attr_writer :theme_name, :description
|
218
|
+
|
219
|
+
def default_theme_color_keys
|
220
|
+
@default_theme_color_keys ||= self.class.send(:default_theme_color_keys)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|