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.
- 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
|