dsu 0.1.0.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/.reek.yml +20 -0
- data/.rspec +3 -0
- data/.rubocop.yml +192 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +2 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +19 -0
- data/Gemfile.lock +133 -0
- data/LICENSE.txt +21 -0
- data/README.md +128 -0
- data/Rakefile +12 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/exe/dsu +11 -0
- data/lib/dsu/cli.rb +178 -0
- data/lib/dsu/command_services/add_entry_service.rb +61 -0
- data/lib/dsu/models/entry.rb +49 -0
- data/lib/dsu/models/entry_group.rb +70 -0
- data/lib/dsu/services/configuration_loader_service.rb +34 -0
- data/lib/dsu/services/entry_group_deleter_service.rb +31 -0
- data/lib/dsu/services/entry_group_hydrator_service.rb +43 -0
- data/lib/dsu/services/entry_group_reader_service.rb +36 -0
- data/lib/dsu/services/entry_group_writer_service.rb +45 -0
- data/lib/dsu/services/entry_hydrator_service.rb +35 -0
- data/lib/dsu/subcommands/config.rb +49 -0
- data/lib/dsu/support/ask.rb +38 -0
- data/lib/dsu/support/colorable.rb +13 -0
- data/lib/dsu/support/commander/command.rb +130 -0
- data/lib/dsu/support/commander/command_help.rb +62 -0
- data/lib/dsu/support/commander/subcommand.rb +45 -0
- data/lib/dsu/support/configuration.rb +89 -0
- data/lib/dsu/support/entry_group_fileable.rb +41 -0
- data/lib/dsu/support/entry_group_loadable.rb +52 -0
- data/lib/dsu/support/field_errors.rb +11 -0
- data/lib/dsu/support/folder_locations.rb +21 -0
- data/lib/dsu/support/interactive/cli.rb +161 -0
- data/lib/dsu/support/say.rb +40 -0
- data/lib/dsu/support/time_formatable.rb +42 -0
- data/lib/dsu/validators/entries_validator.rb +64 -0
- data/lib/dsu/validators/time_validator.rb +34 -0
- data/lib/dsu/version.rb +5 -0
- data/lib/dsu/views/entry_group/show.rb +60 -0
- data/lib/dsu.rb +38 -0
- data/sig/dsu.rbs +4 -0
- metadata +199 -0
data/lib/dsu/cli.rb
ADDED
@@ -0,0 +1,178 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
require 'thor'
|
5
|
+
require_relative 'command_services/add_entry_service'
|
6
|
+
require_relative 'models/entry_group'
|
7
|
+
require_relative 'services/configuration_loader_service'
|
8
|
+
require_relative 'services/entry_group_hydrator_service'
|
9
|
+
require_relative 'services/entry_group_reader_service'
|
10
|
+
require_relative 'subcommands/config'
|
11
|
+
require_relative 'version'
|
12
|
+
require_relative 'views/entry_group/show'
|
13
|
+
|
14
|
+
module Dsu
|
15
|
+
#
|
16
|
+
# The `dsu` command.
|
17
|
+
#
|
18
|
+
class CLI < ::Thor
|
19
|
+
class_option :debug, type: :boolean, default: false
|
20
|
+
|
21
|
+
map %w[--version -v] => :version
|
22
|
+
# map %w[--interactive -i] => :interactive
|
23
|
+
|
24
|
+
default_command :help
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def exit_on_failure?
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def initialize(*args)
|
33
|
+
super
|
34
|
+
|
35
|
+
@configuration = Services::ConfigurationLoaderService.new.call
|
36
|
+
end
|
37
|
+
|
38
|
+
desc 'add [OPTIONS] DESCRIPTION [LONG-DESCRIPTION]',
|
39
|
+
'Adds a dsu entry for the date associated with the given option.'
|
40
|
+
long_desc <<-LONG_DESC
|
41
|
+
TBD
|
42
|
+
LONG_DESC
|
43
|
+
option :date, type: :string, aliases: '-d'
|
44
|
+
option :next_day, type: :boolean, aliases: '-n'
|
45
|
+
option :previous_day, type: :boolean, aliases: '-p'
|
46
|
+
option :today, type: :boolean, aliases: '-t', default: true
|
47
|
+
|
48
|
+
def add(description, long_description = nil)
|
49
|
+
entry = Models::Entry.new(description: description, long_description: long_description)
|
50
|
+
time = if options[:date].present?
|
51
|
+
Time.parse(options[:date])
|
52
|
+
elsif options[:next_day].present?
|
53
|
+
1.day.from_now
|
54
|
+
elsif options[:previous_day].present?
|
55
|
+
1.day.ago
|
56
|
+
elsif options[:today].present?
|
57
|
+
Time.now
|
58
|
+
else
|
59
|
+
raise 'No date option specified.'
|
60
|
+
end
|
61
|
+
display_entry_group(time: 1.day.ago(time))
|
62
|
+
puts
|
63
|
+
CommandServices::AddEntryService.new(entry: entry, time: time).call
|
64
|
+
display_entry_group(time: time)
|
65
|
+
end
|
66
|
+
|
67
|
+
desc 'today',
|
68
|
+
'Displays the dsu entries for today.'
|
69
|
+
long_desc <<-LONG_DESC
|
70
|
+
Displays the dsu entries for today. This command has no options.
|
71
|
+
LONG_DESC
|
72
|
+
def today
|
73
|
+
time = Time.now
|
74
|
+
sort_times(times: [1.day.ago(time), time]).each do |time| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
75
|
+
display_entry_group(time: time)
|
76
|
+
puts
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
desc 'tomorrow',
|
81
|
+
'Displays the dsu entries for tomorrow.'
|
82
|
+
long_desc <<-LONG_DESC
|
83
|
+
Displays the dsu entries for tomorrow. This command has no options.
|
84
|
+
LONG_DESC
|
85
|
+
def tomorrow
|
86
|
+
time = Time.now
|
87
|
+
sort_times(times: [1.day.from_now(time), time]).each do |time| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
88
|
+
display_entry_group(time: time)
|
89
|
+
puts
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'yesterday',
|
94
|
+
'Displays the dsu entries for yesterday.'
|
95
|
+
long_desc <<-LONG_DESC
|
96
|
+
Displays the dsu entries for yesterday. This command has no options.
|
97
|
+
LONG_DESC
|
98
|
+
def yesterday
|
99
|
+
time = Time.now
|
100
|
+
sort_times(times: [1.day.ago(time), 2.days.ago(time)]).each do |time| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
101
|
+
display_entry_group(time: time)
|
102
|
+
puts
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
desc 'date',
|
107
|
+
'Displays the dsu entries for DATE.'
|
108
|
+
long_desc <<-LONG_DESC
|
109
|
+
Displays the dsu entries for DATE, where DATE is any date string that can be parsed using `Time.parse`. For example: `require 'time'; Time.parse("2023-01-01")`.
|
110
|
+
LONG_DESC
|
111
|
+
def date(date)
|
112
|
+
time = Time.parse(date)
|
113
|
+
sort_times(times: [1.day.ago(time), time]).each do |time| # rubocop:disable Lint/ShadowingOuterLocalVariable
|
114
|
+
display_entry_group(time: time)
|
115
|
+
puts
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# TODO: Implement this.
|
120
|
+
# desc 'interactive', 'Opens a dsu interactive session'
|
121
|
+
# long_desc ''
|
122
|
+
# option :next_day, type: :boolean, aliases: '-n'
|
123
|
+
# option :previous_day, type: :boolean, aliases: '-p'
|
124
|
+
# option :today, type: :boolean, aliases: '-t'
|
125
|
+
|
126
|
+
# # https://stackoverflow.com/questions/4604905/interactive-prompt-with-thor
|
127
|
+
# def interactive
|
128
|
+
# exit_commands = %w[x q exit quit]
|
129
|
+
# display_interactive_help
|
130
|
+
# loop do
|
131
|
+
# command = ask('dsu > ')
|
132
|
+
# display_interactive_help if command == 'h'
|
133
|
+
# break if exit_commands.include? command
|
134
|
+
# end
|
135
|
+
# say 'Done.'
|
136
|
+
# end
|
137
|
+
|
138
|
+
desc 'config SUBCOMMAND', 'Manage configuration file for this gem'
|
139
|
+
subcommand :config, Subcommands::Config
|
140
|
+
|
141
|
+
desc '--version, -v', 'Displays this gem version'
|
142
|
+
def version
|
143
|
+
say VERSION
|
144
|
+
end
|
145
|
+
|
146
|
+
private
|
147
|
+
|
148
|
+
attr_reader :configuration
|
149
|
+
|
150
|
+
def display_interactive_help
|
151
|
+
say 'Interactive Mode Commands:'
|
152
|
+
say '---'
|
153
|
+
say '[h]: show this help screen'
|
154
|
+
say '[n]: next day'
|
155
|
+
say '[p]: previous day'
|
156
|
+
say '[t]: today'
|
157
|
+
say '[x|q|exit|quit]: Exit interactive mode'
|
158
|
+
end
|
159
|
+
|
160
|
+
def display_entry_group(time:)
|
161
|
+
entry_group = if Models::EntryGroup.exists?(time: time)
|
162
|
+
entry_group_json = Services::EntryGroupReaderService.new(time: time).call
|
163
|
+
Services::EntryGroupHydratorService.new(entry_group_json: entry_group_json).call
|
164
|
+
else
|
165
|
+
Models::EntryGroup.new(time: time)
|
166
|
+
end
|
167
|
+
Views::EntryGroup::Show.new(entry_group: entry_group).display
|
168
|
+
end
|
169
|
+
|
170
|
+
def sort_times(times:)
|
171
|
+
if configuration[:entries_display_order] == 'asc'
|
172
|
+
times.sort # sort ascending
|
173
|
+
elsif configuration[:entries_display_order] == 'desc'
|
174
|
+
times.sort.reverse # sort descending
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../services/entry_group_writer_service'
|
4
|
+
require_relative '../models/entry'
|
5
|
+
require_relative '../support/entry_group_loadable'
|
6
|
+
require_relative '../support/folder_locations'
|
7
|
+
|
8
|
+
module Dsu
|
9
|
+
module CommandServices
|
10
|
+
# This class adds (does NOT update) an entry to an entry group.
|
11
|
+
class AddEntryService
|
12
|
+
include Dsu::Support::EntryGroupLoadable
|
13
|
+
include Dsu::Support::FolderLocations
|
14
|
+
|
15
|
+
attr_reader :entry, :entry_group, :time
|
16
|
+
|
17
|
+
# :entry is an Entry object
|
18
|
+
# :time is a Time object; the time of the entry group.
|
19
|
+
def initialize(entry:, time:)
|
20
|
+
raise ArgumentError, 'entry is nil' if entry.nil?
|
21
|
+
raise ArgumentError, 'entry is the wrong object type' unless entry.is_a?(Dsu::Models::Entry)
|
22
|
+
raise ArgumentError, 'time is nil' if time.nil?
|
23
|
+
raise ArgumentError, 'time is the wrong object type' unless time.is_a?(Time)
|
24
|
+
|
25
|
+
@entry = entry
|
26
|
+
@time = time
|
27
|
+
@entry_group = Dsu::Models::EntryGroup.load(time: time)
|
28
|
+
end
|
29
|
+
|
30
|
+
def call
|
31
|
+
entry.validate!
|
32
|
+
save_entry_group!
|
33
|
+
entry.uuid
|
34
|
+
rescue ActiveModel::ValidationError
|
35
|
+
puts "Error(s) encountered: #{entry.errors.full_messages}"
|
36
|
+
raise
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
attr_writer :entry, :entry_group, :time
|
42
|
+
|
43
|
+
def entry_exists?
|
44
|
+
@entry_exists ||= entry_group.entries.include? entry.uuid
|
45
|
+
end
|
46
|
+
|
47
|
+
def entry_group_hash
|
48
|
+
@entry_group_hash ||= entry_group_hash_for time: time
|
49
|
+
end
|
50
|
+
|
51
|
+
def save_entry_group!
|
52
|
+
raise "Entry #{entry.uuid} already exists in entry group #{time}" if entry_exists?
|
53
|
+
|
54
|
+
entry_group.entries << entry
|
55
|
+
entry_group.validate!
|
56
|
+
|
57
|
+
Dsu::Services::EntryGroupWriterService.new(entry_group: entry_group).call
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'deco_lite'
|
4
|
+
require 'securerandom'
|
5
|
+
|
6
|
+
module Dsu
|
7
|
+
module Models
|
8
|
+
class Entry < DecoLite::Model
|
9
|
+
validates :uuid, presence: true, format: {
|
10
|
+
with: /\A[0-9a-f]{8}\z/i,
|
11
|
+
message: 'is the wrong format. ' \
|
12
|
+
'0-9, a-f, and 8 characters were expected.' \
|
13
|
+
}
|
14
|
+
validates :description, presence: true, length: { minimum: 2, maximum: 80 }
|
15
|
+
validates :long_description, length: { minimum: 2, maximum: 256 }, allow_nil: true
|
16
|
+
|
17
|
+
def initialize(description:, uuid: nil, long_description: nil)
|
18
|
+
raise ArgumentError, 'description is nil' if description.nil?
|
19
|
+
raise ArgumentError, 'description is the wrong object type' unless description.is_a?(String)
|
20
|
+
raise ArgumentError, 'uuid is the wrong object type' unless uuid.is_a?(String) || uuid.nil?
|
21
|
+
raise ArgumentError, 'long_description is the wrong object type' unless long_description.is_a?(String) || long_description.nil?
|
22
|
+
|
23
|
+
uuid ||= SecureRandom.uuid[0..7]
|
24
|
+
|
25
|
+
super(hash: {
|
26
|
+
uuid: uuid,
|
27
|
+
description: description,
|
28
|
+
long_description: long_description
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
def required_fields
|
33
|
+
%i[uuid description]
|
34
|
+
end
|
35
|
+
|
36
|
+
def long_description?
|
37
|
+
long_description.present?
|
38
|
+
end
|
39
|
+
|
40
|
+
def ==(other)
|
41
|
+
return false unless other.is_a?(Entry)
|
42
|
+
|
43
|
+
uuid == other.uuid &&
|
44
|
+
description == other.description &&
|
45
|
+
long_description == other.long_description
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'deco_lite'
|
4
|
+
require_relative '../support/entry_group_loadable'
|
5
|
+
require_relative '../services/entry_group_reader_service'
|
6
|
+
require_relative '../validators/entries_validator'
|
7
|
+
require_relative '../validators/time_validator'
|
8
|
+
|
9
|
+
module Dsu
|
10
|
+
module Models
|
11
|
+
class EntryGroup < DecoLite::Model
|
12
|
+
extend Support::EntryGroupLoadable
|
13
|
+
|
14
|
+
validates_with Validators::EntriesValidator, fields: [:entries]
|
15
|
+
validates_with Validators::TimeValidator, fields: [:time]
|
16
|
+
|
17
|
+
def initialize(time: nil, entries: [])
|
18
|
+
raise ArgumentError, 'time is the wrong object type' unless time.is_a?(Time) || time.nil?
|
19
|
+
raise ArgumentError, 'entries is the wrong object type' unless entries.is_a?(Array) || entries.nil?
|
20
|
+
|
21
|
+
time ||= Time.now
|
22
|
+
time = time.localtime if time.utc?
|
23
|
+
|
24
|
+
entries ||= []
|
25
|
+
|
26
|
+
super(hash: {
|
27
|
+
time: time,
|
28
|
+
entries: entries
|
29
|
+
})
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
def exists?(time:)
|
34
|
+
Dsu::Services::EntryGroupReaderService.entry_group_file_exists?(time: time)
|
35
|
+
end
|
36
|
+
|
37
|
+
def load(time: nil)
|
38
|
+
new(**hydrated_entry_group_hash_for(time: time))
|
39
|
+
end
|
40
|
+
|
41
|
+
# This function returns a hash whose :time and :entries
|
42
|
+
# key values are hydrated with instantiated Time and Entry
|
43
|
+
# objects.
|
44
|
+
def hydrated_entry_group_hash_for(time:)
|
45
|
+
entry_group_hash = entry_group_hash_for(time: time)
|
46
|
+
hydrate_entry_group_hash(entry_group_hash: entry_group_hash, time: time)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def required_fields
|
51
|
+
%i[time entries]
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_h
|
55
|
+
super.tap do |hash|
|
56
|
+
hash[:entries] = hash[:entries].dup
|
57
|
+
hash[:entries].each_with_index do |entry, index|
|
58
|
+
hash[:entries][index] = entry.to_h
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_h_localized
|
64
|
+
to_h.tap do |hash|
|
65
|
+
hash[:time] = hash[:time].localtime
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'erb'
|
4
|
+
require 'yaml'
|
5
|
+
require_relative '../support/configuration'
|
6
|
+
|
7
|
+
module Dsu
|
8
|
+
module Services
|
9
|
+
class ConfigurationLoaderService
|
10
|
+
include Dsu::Support::Configuration
|
11
|
+
|
12
|
+
attr_reader :default_options
|
13
|
+
|
14
|
+
def initialize(default_options: Dsu::Support::Configuration::DEFAULT_DSU_OPTIONS)
|
15
|
+
@default_options = default_options
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
load_config.merge(default_options || {}).presence&.with_indifferent_access || raise('No configuration options found')
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_writer :default_options
|
25
|
+
|
26
|
+
def load_config
|
27
|
+
return {} unless config_file?
|
28
|
+
|
29
|
+
yaml_options = File.read(config_file)
|
30
|
+
YAML.safe_load ERB.new(yaml_options).result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../support/entry_group_fileable'
|
4
|
+
|
5
|
+
# This class is responsible for deleting an entry group file.
|
6
|
+
module Dsu
|
7
|
+
module Services
|
8
|
+
class EntryGroupDeleterService
|
9
|
+
include Dsu::Support::EntryGroupFileable
|
10
|
+
|
11
|
+
def initialize(time:, options: {})
|
12
|
+
@time = time
|
13
|
+
@options = options || {}
|
14
|
+
end
|
15
|
+
|
16
|
+
def call
|
17
|
+
delete_entry_group_file!
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
attr_reader :time, :options
|
23
|
+
|
24
|
+
def delete_entry_group_file!
|
25
|
+
return unless entry_group_file_exists?
|
26
|
+
|
27
|
+
File.delete(entry_group_file_path)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'entry_hydrator_service'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Services
|
7
|
+
class EntryGroupHydratorService
|
8
|
+
def initialize(entry_group_json:, options: {})
|
9
|
+
raise ArgumentError, 'entry_group_json is nil' if entry_group_json.nil?
|
10
|
+
raise ArgumentError, 'entry_group_json is the wrong object type' unless entry_group_json.is_a?(String)
|
11
|
+
raise ArgumentError, 'options is nil' if options.nil?
|
12
|
+
raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
|
13
|
+
|
14
|
+
@entry_group_json = entry_group_json
|
15
|
+
@options = options || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
entry_group_hash = to_h
|
20
|
+
Dsu::Models::EntryGroup.new(**entry_group_hash)
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# Returns a Hash with :time and :entries values hydrated
|
25
|
+
# (i.e. Time and Entry objects respectively).
|
26
|
+
def to_h(entry_group_json:, options: {})
|
27
|
+
JSON.parse(entry_group_json, symbolize_names: true).tap do |hash|
|
28
|
+
hash[:time] = Time.parse(hash[:time])
|
29
|
+
hash[:entries] = EntryHydratorService.hydrate(entries_array: hash[:entries], options: options)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :entry_group_json, :options
|
37
|
+
|
38
|
+
def to_h
|
39
|
+
self.class.to_h(entry_group_json: entry_group_json, options: options)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../support/entry_group_fileable'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Services
|
7
|
+
class EntryGroupReaderService
|
8
|
+
include Dsu::Support::EntryGroupFileable
|
9
|
+
|
10
|
+
def initialize(time:, options: {})
|
11
|
+
@time = time
|
12
|
+
@options = options || {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def call
|
16
|
+
read_entry_group_file
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def entry_group_file_exists?(time:, options: {})
|
21
|
+
new(time: time, options: options).send(:entry_group_file_exists?)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
attr_reader :time, :options
|
28
|
+
|
29
|
+
def read_entry_group_file
|
30
|
+
return {} unless entry_group_file_exists?
|
31
|
+
|
32
|
+
File.read(entry_group_file_path)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
require 'active_support/core_ext/module/delegation'
|
5
|
+
require_relative '../models/entry_group'
|
6
|
+
require_relative '../support/entry_group_fileable'
|
7
|
+
|
8
|
+
module Dsu
|
9
|
+
module Services
|
10
|
+
class EntryGroupWriterService
|
11
|
+
include Dsu::Support::EntryGroupFileable
|
12
|
+
|
13
|
+
delegate :time, to: :entry_group
|
14
|
+
|
15
|
+
def initialize(entry_group:, options: {})
|
16
|
+
raise ArgumentError, 'entry_group is nil' if entry_group.nil?
|
17
|
+
raise ArgumentError, 'entry_group is the wrong object type' unless entry_group.is_a?(Dsu::Models::EntryGroup)
|
18
|
+
raise ArgumentError, 'options is nil' if options.nil?
|
19
|
+
raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
|
20
|
+
|
21
|
+
@entry_group = entry_group
|
22
|
+
@options = options || {}
|
23
|
+
end
|
24
|
+
|
25
|
+
def call
|
26
|
+
entry_group.validate!
|
27
|
+
create_entry_group_path_if!
|
28
|
+
write_entry_group_to_file!
|
29
|
+
rescue ActiveModel::ValidationError
|
30
|
+
puts "Error(s) encountered: #{entry_group.errors.full_messages}"
|
31
|
+
raise
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
attr_reader :entry_group, :options
|
37
|
+
|
38
|
+
def write_entry_group_to_file!
|
39
|
+
create_entry_group_path_if!
|
40
|
+
File.write(entry_group_file_path, JSON.pretty_generate(entry_group.to_h))
|
41
|
+
puts "Wrote group entry file: #{entry_group_file_path}" if ENV['ENV_DEV']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../models/entry'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Services
|
7
|
+
class EntryHydratorService
|
8
|
+
def initialize(entry_hash:, options: {})
|
9
|
+
raise ArgumentError, 'entry_hash is nil' if entry_hash.nil?
|
10
|
+
raise ArgumentError, 'entry_hash is the wrong object type' unless entry_hash.is_a?(Hash)
|
11
|
+
raise ArgumentError, 'options is nil' if options.nil?
|
12
|
+
raise ArgumentError, 'options is the wrong object type' unless options.is_a?(Hash)
|
13
|
+
|
14
|
+
@entry_hash = entry_hash
|
15
|
+
@options = options || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
Dsu::Models::Entry.new(**entry_hash)
|
20
|
+
end
|
21
|
+
|
22
|
+
class << self
|
23
|
+
def hydrate(entries_array:, options: {})
|
24
|
+
entries_array.map do |entry_hash|
|
25
|
+
new(entry_hash: entry_hash, options: options).call
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
attr_reader :entry_hash, :options
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
require_relative '../support/configuration'
|
5
|
+
|
6
|
+
module Dsu
|
7
|
+
module Subcommands
|
8
|
+
class Config < ::Thor
|
9
|
+
include Dsu::Support::Configuration
|
10
|
+
|
11
|
+
default_command :help
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def exit_on_failure?
|
15
|
+
false
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'info', 'Displays information about this gem configuration'
|
20
|
+
long_desc <<-LONG_DESC
|
21
|
+
NAME
|
22
|
+
\x5
|
23
|
+
`dsu config info` -- Displays information about this gem configuration.
|
24
|
+
|
25
|
+
SYNOPSIS
|
26
|
+
\x5
|
27
|
+
dsu config info
|
28
|
+
LONG_DESC
|
29
|
+
def info
|
30
|
+
print_config_file
|
31
|
+
end
|
32
|
+
|
33
|
+
desc 'init', 'Creates and initializes a .dsu file in your home folder'
|
34
|
+
long_desc <<-LONG_DESC
|
35
|
+
NAME
|
36
|
+
\x5
|
37
|
+
`dsu config init` -- will create and initialize a .dsu file
|
38
|
+
in the "#{Dsu::Support::FolderLocations.root_folder}" folder.
|
39
|
+
|
40
|
+
SYNOPSIS
|
41
|
+
\x5
|
42
|
+
dsu config init
|
43
|
+
LONG_DESC
|
44
|
+
def init
|
45
|
+
create_config_file!
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'thor'
|
4
|
+
|
5
|
+
module Dsu
|
6
|
+
module Support
|
7
|
+
module Ask
|
8
|
+
ASK_YES = %w[y yes].freeze
|
9
|
+
# ASK_NO = %w[n no].freeze
|
10
|
+
# ASK_CANCEL = %w[c cancel].freeze
|
11
|
+
# ASK_YES_NO_CANCEL = ASK_YES.concat(ASK_NO).concat(ASK_CANCEL).freeze
|
12
|
+
|
13
|
+
def ask(prompt)
|
14
|
+
options = {}
|
15
|
+
Thor::LineEditor.readline(prompt, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def yes?(prompt, color = nil)
|
19
|
+
Thor::Base.shell.new.yes?(prompt, color)
|
20
|
+
end
|
21
|
+
|
22
|
+
# def no?(prompt)
|
23
|
+
# ask_with(prompt: prompt, values: ASK_NO)
|
24
|
+
# end
|
25
|
+
|
26
|
+
# def yes_no_cancel(prompt)
|
27
|
+
# ask_with(prompt: prompt, values: ASK_YES_NO_CANCEL)
|
28
|
+
# end
|
29
|
+
|
30
|
+
# private
|
31
|
+
|
32
|
+
# def ask_with(prompt:, values:)
|
33
|
+
# p "#{prompt}"
|
34
|
+
# values.include? STDIN.gets.chomp
|
35
|
+
# end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|