dsu 2.4.2 → 2.4.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: daab696fe0b9d10062e74dedfba4605ac8d8917d9213188011250def3925a7d7
4
- data.tar.gz: 50b7e48562ef821a547d3ff7468cd92fe2cc6a6af2090fa27b4d3c3f9a853b47
3
+ metadata.gz: 2c8163f812eaded6f88a8fd25e501ebca51e820f7d93b06ab349a9fb4facecde
4
+ data.tar.gz: e18ed74fa803102a3cc957d0b9ce420831ed4054d23d431338af2681a012d236
5
5
  SHA512:
6
- metadata.gz: cd0028f5f8004b5078961725ca5297d0a962fb9d1d40f45bf452e5af0c3feb76992bad84a54c47bf22b0b3b3662946df5354e5c7fe781ad097c38b95bdf82887
7
- data.tar.gz: 97f7f616664a2b406f196c08bfdd283ad5f184d6d9b357e12a59cdb1b4138ca2562859166618b443a73da5e276ae6229a70c6a4114fa8e023cadde8161fb8d25
6
+ metadata.gz: b69dce6d25a7bba32473a8cfbe1c82130ffd19f37a34b8a2c1514f9397add35611dbd8d08da642b03f2eb0c5eab42a2f0b35d20335ed0f9c3f98f19ceeaef9be
7
+ data.tar.gz: 353119cd18d19d9d4f77afcf437a35f1ca262aa9da5cdc91d0477a7d7d373490bac75a2d91d21ebd7dca76930bb3fbde53e4e8fa53693e4364239c8942020cd9
data/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## [2.4.3] 2024-01-07
2
+
3
+ Changes
4
+
5
+ - Code refactors, nothing discernable to the end user.
6
+
7
+ Bug fixes
8
+
9
+ - Fixed bug that failed to display the correct editor if the editor session failed to open. For example, if the editor session failed to open and the EDITOR environment variable was used, the editor in the configuration file would be displayed instead of the EDITOR environment variable value.
10
+
1
11
  ## [2.4.2] 2024-01-06
2
12
 
3
13
  Changes
data/Gemfile.lock CHANGED
@@ -1,10 +1,10 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dsu (2.4.2)
4
+ dsu (2.4.3)
5
5
  activemodel (>= 7.0.8, < 8.0)
6
6
  activesupport (>= 7.0.8, < 8.0)
7
- colorize (>= 0.8.1, < 1.0)
7
+ colorize (>= 1.1, < 2.0)
8
8
  os (>= 1.1, < 2.0)
9
9
  thor (>= 1.2, < 2.0)
10
10
  thor_nested_subcommand (>= 1.0, < 2.0)
@@ -29,7 +29,7 @@ GEM
29
29
  bigdecimal (3.1.5)
30
30
  byebug (11.1.3)
31
31
  coderay (1.1.3)
32
- colorize (0.8.1)
32
+ colorize (1.1.0)
33
33
  concurrent-ruby (1.2.2)
34
34
  connection_pool (2.4.1)
35
35
  diff-lcs (1.5.0)
@@ -93,7 +93,7 @@ GEM
93
93
  parser (~> 3.2.0)
94
94
  rainbow (>= 2.0, < 4.0)
95
95
  rexml (~> 3.1)
96
- regexp_parser (2.8.3)
96
+ regexp_parser (2.9.0)
97
97
  rexml (3.2.6)
98
98
  rspec (3.12.0)
99
99
  rspec-core (~> 3.12.0)
@@ -123,9 +123,9 @@ GEM
123
123
  parser (>= 3.2.1.0)
124
124
  rubocop-capybara (2.20.0)
125
125
  rubocop (~> 1.41)
126
- rubocop-factory_bot (2.25.0)
127
- rubocop (~> 1.33)
128
- rubocop-performance (1.20.1)
126
+ rubocop-factory_bot (2.25.1)
127
+ rubocop (~> 1.41)
128
+ rubocop-performance (1.20.2)
129
129
  rubocop (>= 1.48.1, < 2.0)
130
130
  rubocop-ast (>= 1.30.0, < 2.0)
131
131
  rubocop-rspec (2.26.1)
@@ -141,7 +141,7 @@ GEM
141
141
  simplecov-html (0.12.3)
142
142
  simplecov_json_formatter (0.1.4)
143
143
  thor (1.3.0)
144
- thor_nested_subcommand (1.0.5)
144
+ thor_nested_subcommand (1.0.6)
145
145
  tzinfo (2.0.6)
146
146
  concurrent-ruby (~> 1.0)
147
147
  unicode-display_width (2.5.0)
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # `dsu`
2
2
 
3
3
  [![Ruby](https://github.com/gangelo/dsu/actions/workflows/ruby.yml/badge.svg)](https://github.com/gangelo/dsu/actions/workflows/ruby.yml)
4
- [![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=6)](https://badge.fury.io/gh/gangelo%2Fdsu)
5
- [![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=6)](https://badge.fury.io/rb/dsu)
4
+ [![GitHub version](http://badge.fury.io/gh/gangelo%2Fdsu.svg?refresh=9)](https://badge.fury.io/gh/gangelo%2Fdsu)
5
+ [![Gem Version](https://badge.fury.io/rb/dsu.svg?refresh=9)](https://badge.fury.io/rb/dsu)
6
6
  [![Documentation](http://img.shields.io/badge/docs-rdoc.info-blue.svg)](http://www.rubydoc.info/gems/dsu/)
7
7
  [![Report Issues](https://img.shields.io/badge/report-issues-red.svg)](https://github.com/gangelo/dsu/issues)
8
8
  [![License](http://img.shields.io/badge/license-MIT-yellowgreen.svg)](#license)
@@ -12,6 +12,9 @@
12
12
  - `dsu` uses _no_ network connections whatsoever.
13
13
  - `dsu` stores all of its data _locally_, in .json files.
14
14
  - `dsu` is a simple (but powerful) command-line tool for users who _love_ to work within the terminal.
15
+ - `dsu` versioning follows the [semantic versioning standard](https://semver.org/) (MAJOR.MINOR.PATCH).
16
+ - See the [CHANGELOG.md](https://github.com/gangelo/dsu/blob/main/CHANGELOG.md) before upgrading to a MAJOR `dsu` version.
17
+ - See the [Exporting DSU entries](https://github.com/gangelo/dsu/wiki/Exporting-DSU-entries) wiki on how to export (backup) your data.
15
18
 
16
19
  # Installation
17
20
  ```shell
@@ -22,9 +25,15 @@ gem install dsu
22
25
  [https://github.com/gangelo/dsu/wiki](https://github.com/gangelo/dsu/wiki)
23
26
 
24
27
  # Examples
25
- TODO
28
+ The [dsu wiki](https://github.com/gangelo/dsu/wiki) is repleat with practical examples on how to use `dsu`.
29
+
30
+ It you're interested in how _I personally_ use `dsu` every day, I blog about it [here](https://genemangelojr.blogspot.com/2024/01/the-dsu-ruby-gem-workflow-how-to-use-it.html).
26
31
 
27
32
  # Supported ruby versions
28
- TODO
33
+ `dsu` _should_ work with any ruby version `['>= 3.0.1', '< 4.0']`; however, `dsu` is currently tested against the following ruby versions:
34
+ - 3.0.1
35
+ - 3.0.6
36
+ - 3.1.4
37
+ - 3.2.2
29
38
 
30
- Copyright (c) 2023-2024 Erik Gene Angelo. See [LICENSE](https://github.com/gangelo/dsu/LICENSE.txt) for details.
39
+ Copyright (c) 2023-2024 Erik Gene Angelo. See [LICENSE](https://github.com/gangelo/dsu/blob/main/LICENSE.txt) for details.
data/lib/dsu/base_cli.rb CHANGED
@@ -10,7 +10,6 @@ require_relative 'services/stdout_redirector_service'
10
10
  require_relative 'support/color_themable'
11
11
  require_relative 'support/command_help_colorizeable'
12
12
  require_relative 'support/command_hookable'
13
- require_relative 'support/entry_group_viewable'
14
13
  require_relative 'support/times_sortable'
15
14
  require_relative 'version'
16
15
  require_relative 'views/entry_group/show'
@@ -20,7 +19,6 @@ module Dsu
20
19
  include Support::ColorThemable
21
20
  include Support::CommandHelpColorizable
22
21
  include Support::CommandHookable
23
- include Support::EntryGroupViewable
24
22
  include Support::TimesSortable
25
23
 
26
24
  class_option :debug, type: :boolean, default: false
data/lib/dsu/cli.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require 'fileutils'
4
4
  require 'time'
5
5
  require_relative 'base_cli'
6
+ require_relative 'presenters/entry_group/list/date_presenter'
6
7
  require_relative 'subcommands/browse'
7
8
  require_relative 'subcommands/config'
8
9
  require_relative 'subcommands/delete'
@@ -11,6 +12,7 @@ require_relative 'subcommands/export'
11
12
  require_relative 'subcommands/import'
12
13
  require_relative 'subcommands/list'
13
14
  require_relative 'subcommands/theme'
15
+ require_relative 'views/entry_group/list'
14
16
 
15
17
  module Dsu
16
18
  # The `dsu` command.
@@ -46,7 +48,9 @@ module Dsu
46
48
  end
47
49
  entry = Models::Entry.new(description: description)
48
50
  CommandServices::AddEntryService.new(entry: entry, time: time).call
49
- view_entry_group(time: time)
51
+ presenter = Presenters::EntryGroup::List::DatePresenter.new(times: [time], options: options)
52
+ # TODO: Refactor View::EntryGroup::Show to accept a presenter and use it here
53
+ Views::EntryGroup::List.new(presenter: presenter).render
50
54
  end
51
55
 
52
56
  desc I18n.t('commands.browse.desc'), I18n.t('commands.browse.usage')
@@ -2,7 +2,6 @@
2
2
 
3
3
  require 'active_model'
4
4
  require 'json'
5
- require_relative 'raw_json_file'
6
5
 
7
6
  module Dsu
8
7
  module Crud
@@ -23,12 +22,12 @@ module Dsu
23
22
  self.class.delete!(file_path: file_path)
24
23
  end
25
24
 
26
- def exist?
27
- self.class.exist?(file_path: file_path)
25
+ def file_exist?
26
+ self.class.file_exist?(file_path: file_path)
28
27
  end
29
28
 
30
29
  def persisted?
31
- exist?
30
+ file_exist?
32
31
  end
33
32
 
34
33
  # Override this method to reload data from the file
@@ -67,16 +66,20 @@ module Dsu
67
66
  alias save! write!
68
67
 
69
68
  class << self
70
- def exist?(file_path:)
71
- RawJsonFile.exist?(file_path: file_path)
69
+ def file_exist?(file_path:)
70
+ File.exist?(file_path)
72
71
  end
73
72
 
74
73
  def delete(file_path:)
75
- RawJsonFile.delete(file_path: file_path)
74
+ return false unless file_exist?(file_path: file_path)
75
+
76
+ File.delete(file_path)
77
+
78
+ true
76
79
  end
77
80
 
78
81
  def delete!(file_path:)
79
- RawJsonFile.delete!(file_path: file_path)
82
+ raise file_does_not_exist_message(file_path: file_path) unless delete(file_path: file_path)
80
83
  end
81
84
 
82
85
  def parse(json)
@@ -86,54 +89,65 @@ module Dsu
86
89
  end
87
90
 
88
91
  def read(file_path:)
89
- hash = parse(RawJsonFile.read(file_path: file_path))
92
+ json = File.read(file_path) if file_exist?(file_path: file_path)
93
+ hash = parse(json)
90
94
  return yield hash if hash && block_given?
91
95
 
92
96
  hash
93
97
  end
94
98
 
95
99
  def read!(file_path:)
96
- hash = parse(RawJsonFile.read!(file_path: file_path))
100
+ raise file_does_not_exist_message(file_path: file_path) unless file_exist?(file_path: file_path)
101
+
102
+ hash = read(file_path: file_path)
97
103
  return yield hash if hash && block_given?
98
104
 
99
105
  hash
100
106
  end
101
107
 
102
108
  def write(file_data:, file_path:)
103
- Crud::RawJsonFile.write(file_data: file_data, file_path: file_path)
109
+ raise ArgumentError, 'file_data is nil' if file_data.nil?
110
+ raise ArgumentError, "file_data is the wrong object type:\"#{file_data}\"" unless file_data.is_a?(Hash)
111
+
112
+ file_data = JSON.pretty_generate(file_data)
113
+ File.write(file_path, file_data)
104
114
  end
105
115
 
106
116
  def write!(file_data:, file_path:)
107
117
  write(file_data: file_data, file_path: file_path)
108
118
  end
119
+
120
+ def file_does_not_exist_message(file_path:)
121
+ "File \"#{file_path}\" does not exist"
122
+ end
109
123
  end
110
124
 
111
125
  private
112
126
 
127
+ attr_writer :file_path, :version
128
+
113
129
  def read
114
- hash = self.class.parse(RawJsonFile.read(file_path: file_path))
130
+ hash = self.class.read(file_path: file_path)
115
131
  return yield hash if block_given?
116
132
 
117
133
  hash
118
134
  end
119
135
 
120
136
  def read!
121
- hash = self.class.parse(RawJsonFile.read!(file_path: file_path))
137
+ hash = self.class.read!(file_path: file_path)
122
138
  return yield hash if hash && block_given?
123
139
 
124
140
  hash
125
141
  end
126
142
 
127
143
  def read_version
128
- return 0 unless exist?
144
+ return 0 unless file_exist?
129
145
 
130
146
  hash = read
131
147
  return 0 if hash.nil?
132
148
 
133
149
  hash.fetch(:version, 0).to_i
134
150
  end
135
-
136
- attr_writer :file_path, :version
137
151
  end
138
152
  end
139
153
  end
data/lib/dsu/env.rb CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Dsu
4
4
  class << self
5
- def env
5
+ def env # rubocop:disable Metrics/MethodLength
6
6
  @env ||= Struct.new(:env) do
7
7
  def test?
8
8
  env.fetch('DSU_ENV', nil) == 'test'
@@ -19,6 +19,25 @@ module Dsu
19
19
  def production?
20
20
  env.fetch('DSU_ENV', 'production') == 'production'
21
21
  end
22
+
23
+ def screen_shot_mode?
24
+ development? && (env.fetch('SCREEN_SHOT_USERNAME', '').present? ||
25
+ env.fetch('SCREEN_SHOT_HOSTNAME', '').present?)
26
+ end
27
+
28
+ def screen_shot_prompt
29
+ username = screen_shot_username
30
+ hostname = screen_shot_hostname
31
+ "#{username}@#{hostname}:~ $"
32
+ end
33
+
34
+ def screen_shot_username
35
+ env.fetch('SCREEN_SHOT_USERNAME', 'username')
36
+ end
37
+
38
+ def screen_shot_hostname
39
+ env.fetch('SCREEN_SHOT_HOSTNAME', 'hostname')
40
+ end
22
41
  end.new(ENV)
23
42
  end
24
43
  end
@@ -160,7 +160,7 @@ module Dsu
160
160
  end
161
161
 
162
162
  def exist?(theme_name:)
163
- superclass.exist?(file_path: themes_path_for(theme_name: theme_name))
163
+ superclass.file_exist?(file_path: themes_path_for(theme_name: theme_name))
164
164
  end
165
165
 
166
166
  def find(theme_name:)
@@ -63,6 +63,8 @@ module Dsu
63
63
 
64
64
  attr_reader :options
65
65
 
66
+ alias exist? file_exist?
67
+
66
68
  def initialize(options: {})
67
69
  super(config_path)
68
70
 
@@ -150,7 +150,7 @@ module Dsu
150
150
  end
151
151
 
152
152
  def exist?(time:)
153
- superclass.exist?(file_path: entries_path_for(time: time))
153
+ superclass.file_exist?(file_path: entries_path_for(time: time))
154
154
  end
155
155
 
156
156
  def entry_group_times(between: nil)
@@ -13,6 +13,8 @@ module Dsu
13
13
 
14
14
  attr_reader :options
15
15
 
16
+ alias exist? file_exist?
17
+
16
18
  def initialize(version: nil, options: {})
17
19
  super(migration_version_path)
18
20
 
@@ -10,7 +10,7 @@ module Dsu
10
10
  include Support::ColorThemable
11
11
 
12
12
  def initialize(options: {})
13
- @options = options || {}
13
+ @options = options&.dup || {}
14
14
  @color_theme = Models::ColorTheme.find(theme_name: theme_name)
15
15
  end
16
16
 
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../env'
3
4
  require_relative '../support/color_themable'
4
5
  require_relative 'base_presenter'
5
6
 
@@ -28,7 +29,11 @@ module Dsu
28
29
  private
29
30
 
30
31
  def config_path
31
- @config_path ||= config.file_path
32
+ @config_path ||= if Dsu.env.screen_shot_mode?
33
+ "/Users/#{Dsu.env.screen_shot_username}/.dsu"
34
+ else
35
+ config.file_path
36
+ end
32
37
  end
33
38
 
34
39
  def formatted_config_entry_with_index(config_entry, index:, theme_color:)
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../models/configuration'
4
+ require_relative '../../../models/entry_group'
5
+ require_relative '../../../views/entry_group/shared/no_entries_to_display'
6
+ require_relative '../../base_presenter_ex'
7
+ require_relative 'messages'
8
+ require_relative 'nothing_to_list'
9
+
10
+ module Dsu
11
+ module Presenters
12
+ module EntryGroup
13
+ module List
14
+ class DatePresenter < BasePresenterEx
15
+ include Messages
16
+ include NothingToList
17
+
18
+ def initialize(times:, options: {})
19
+ raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
20
+ raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
21
+
22
+ super(options: options)
23
+
24
+ @times = times
25
+ end
26
+
27
+ def render
28
+ return if nothing_to_list?
29
+
30
+ entry_groups.each do |entry_group|
31
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
32
+ puts
33
+ end
34
+ end
35
+
36
+ def display_nothing_to_list_message
37
+ # This presenter will ALWAYS have something to list (display) since the first
38
+ # and last (if different) entry groups will always be displayed.
39
+ raise 'display_nothing_to_list_message called when there are entries to display'
40
+ end
41
+
42
+ private
43
+
44
+ attr_reader :times
45
+
46
+ def entry_groups
47
+ @entry_groups ||= begin
48
+ options = configuration.to_h.merge(self.options).with_indifferent_access
49
+
50
+ times.filter_map do |time|
51
+ view_options = options.dup
52
+ # Always show the first and last entry groups.
53
+ view_options[:include_all] = true if times_min_max.include?(time)
54
+
55
+ next unless show_entry_group?(time: time, options: view_options)
56
+
57
+ Models::EntryGroup.find_or_initialize(time: time)
58
+ end
59
+ end
60
+ end
61
+
62
+ def times_min_max
63
+ @times_min_max ||= times.minmax
64
+ end
65
+
66
+ def configuration
67
+ @configuration ||= Models::Configuration.new
68
+ end
69
+
70
+ def show_entry_group?(time:, options:)
71
+ Models::EntryGroup.exist?(time: time) || options[:include_all]
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../../models/entry_group'
4
+ require_relative '../../../views/entry_group/shared/no_entries_to_display'
5
+ require_relative '../../base_presenter_ex'
6
+ require_relative 'messages'
7
+ require_relative 'nothing_to_list'
8
+
9
+ module Dsu
10
+ module Presenters
11
+ module EntryGroup
12
+ module List
13
+ class DatesPresenter < BasePresenterEx
14
+ include Messages
15
+ include NothingToList
16
+
17
+ def initialize(times:, options: {})
18
+ raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
19
+ raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
20
+
21
+ super(options: options)
22
+
23
+ @times = times
24
+ end
25
+
26
+ def render
27
+ return if nothing_to_list?
28
+
29
+ entry_groups.each do |entry_group|
30
+ Views::EntryGroup::Show.new(entry_group: entry_group).render
31
+ puts
32
+ end
33
+ end
34
+
35
+ def display_nothing_to_list_message
36
+ raise 'display_nothing_to_list_message called when there are entries to display' unless nothing_to_list?
37
+
38
+ Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :times
44
+
45
+ def entry_groups
46
+ @entry_groups ||= times.filter_map do |time|
47
+ next unless show_entry_group?(time: time, options: options)
48
+
49
+ Models::EntryGroup.find_or_initialize(time: time)
50
+ end
51
+ end
52
+
53
+ def show_entry_group?(time:, options:)
54
+ Models::EntryGroup.exist?(time: time) || options[:include_all]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Presenters
5
+ module EntryGroup
6
+ module List
7
+ module Messages
8
+ def display_nothing_to_list_message
9
+ raise NotImplementedError
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Presenters
5
+ module EntryGroup
6
+ module List
7
+ module NothingToList
8
+ def nothing_to_list?
9
+ entry_groups.none?
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -65,7 +65,7 @@ module Dsu
65
65
  process_entry_group!(entry_group_with_edits)
66
66
  else
67
67
  message_array = I18n.t('services.editor_service.errors.temp_file_error',
68
- editor: configuration.editor,
68
+ editor: ENV.fetch('EDITOR', configuration.editor),
69
69
  status: $CHILD_STATUS).split("\n")
70
70
  puts apply_theme(message_array, theme_color: color_theme.error)
71
71
  end
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../presenters/entry_group/list/date_presenter'
4
+ require_relative '../presenters/entry_group/list/dates_presenter'
3
5
  require_relative '../services/entry_group/counter_service'
4
6
  require_relative '../support/command_options/dsu_times'
5
7
  require_relative '../support/command_options/time_mnemonic'
6
8
  require_relative '../support/time_formatable'
7
- require_relative '../views/entry_group/shared/no_entries_to_display'
9
+ require_relative '../views/entry_group/list'
8
10
  require_relative '../views/shared/error'
9
11
  require_relative 'base_subcommand'
10
12
 
@@ -25,7 +27,8 @@ module Dsu
25
27
  def today
26
28
  time = Time.now
27
29
  times = sorted_dsu_times_for(times: [time.yesterday, time])
28
- view_list_for(times: times, options: options)
30
+ presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
31
+ Views::EntryGroup::List.new(presenter: presenter).render
29
32
  end
30
33
 
31
34
  desc I18n.t('subcommands.list.tomorrow.desc'), I18n.t('subcommands.list.tomorrow.usage')
@@ -33,7 +36,8 @@ module Dsu
33
36
  def tomorrow
34
37
  time = Time.now
35
38
  times = sorted_dsu_times_for(times: [time, time.tomorrow])
36
- view_list_for(times: times, options: options)
39
+ presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
40
+ Views::EntryGroup::List.new(presenter: presenter).render
37
41
  end
38
42
 
39
43
  desc I18n.t('subcommands.list.yesterday.desc'), I18n.t('subcommands.list.yesterday.usage')
@@ -41,7 +45,8 @@ module Dsu
41
45
  def yesterday
42
46
  time = Time.now
43
47
  times = sorted_dsu_times_for(times: [time.yesterday, time.yesterday.yesterday])
44
- view_list_for(times: times, options: options)
48
+ presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
49
+ Views::EntryGroup::List.new(presenter: presenter).render
45
50
  end
46
51
 
47
52
  desc I18n.t('subcommands.list.date.desc'), I18n.t('subcommands.list.date.usage')
@@ -55,7 +60,8 @@ module Dsu
55
60
  Time.parse(date_or_mnemonic)
56
61
  end
57
62
  times = sorted_dsu_times_for(times: [time, time.yesterday])
58
- view_list_for(times: times, options: options)
63
+ presenter = Presenters::EntryGroup::List::DatePresenter.new(times: times, options: options)
64
+ Views::EntryGroup::List.new(presenter: presenter).render
59
65
  rescue ArgumentError => e
60
66
  Views::Shared::Error.new(messages: e.message).render
61
67
  end
@@ -79,11 +85,8 @@ module Dsu
79
85
  # NOTE: special sort here, unlike the other commands where rules for
80
86
  # displaying DSU entries are applied; this is more of a list command.
81
87
  times = times_sort(times: times, entries_display_order: options[:entries_display_order])
82
- view_entry_groups(times: times, options: options) do
83
- if Services::EntryGroup::CounterService.new(times: times).call.zero?
84
- Views::EntryGroup::Shared::NoEntriesToDisplay.new(times: times, options: options).render
85
- end
86
- end
88
+ presenter = Presenters::EntryGroup::List::DatesPresenter.new(times: times, options: options)
89
+ Views::EntryGroup::List.new(presenter: presenter).render
87
90
  rescue ArgumentError => e
88
91
  Views::Shared::Error.new(messages: e.message).render
89
92
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative '../env'
3
4
  require_relative '../models/color_theme'
4
5
  require_relative '../services/stderr_redirector_service'
5
6
  require_relative '../views/shared/error'
@@ -26,6 +27,10 @@ module Dsu
26
27
  end
27
28
 
28
29
  def display_dsu_header
30
+ if Dsu.env.screen_shot_mode?
31
+ puts apply_theme('Running screen shot mode!', theme_color: color_theme.warning)
32
+ puts "#{Dsu.env.screen_shot_prompt} dsu #{ARGV.join(' ')}"
33
+ end
29
34
  end
30
35
 
31
36
  def display_dsu_footer
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../models/configuration'
4
+ require_relative '../presenters/entry_group/list/dates_presenter'
4
5
  require_relative '../services/entry_group/browse_service'
5
6
  require_relative '../services/entry_group/counter_service'
7
+ require_relative '../views/entry_group/list'
6
8
 
7
9
  module Dsu
8
10
  module Support
@@ -23,7 +25,8 @@ module Dsu
23
25
  header = browse_header_for(time: time, options: options)
24
26
  Views::Shared::Info.new(messages: header).render
25
27
  puts
26
- view_entry_groups(times: times, options: options)
28
+ presenter = Presenters::EntryGroup::List::DatesPresenter.new(times: times, options: options)
29
+ Views::EntryGroup::List.new(presenter: presenter).render
27
30
  self.class.display_dsu_footer
28
31
  end
29
32
  output_with_pager output: output, options: options
data/lib/dsu/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Dsu
4
4
  VERSION_REGEX = /\A\d+\.\d+\.\d+(\.(alpha|rc)\.\d+)?\z/
5
- VERSION = '2.4.2'
5
+ VERSION = '2.4.3'
6
6
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Dsu
4
+ module Views
5
+ module EntryGroup
6
+ class List
7
+ def initialize(presenter:)
8
+ @presenter = presenter
9
+ end
10
+
11
+ def render
12
+ return presenter.display_nothing_to_list_message if presenter.nothing_to_list?
13
+
14
+ presenter.render
15
+ end
16
+
17
+ private
18
+
19
+ attr_reader :presenter
20
+ end
21
+ end
22
+ end
23
+ end
@@ -36,7 +36,11 @@ module Dsu
36
36
  puts presenter.formatted_time
37
37
 
38
38
  entry_group.validate!
39
- puts presenter.no_entries_available and return if entry_group.entries.empty?
39
+ if entry_group.entries.empty?
40
+ puts presenter.no_entries_available
41
+
42
+ return
43
+ end
40
44
 
41
45
  entry_group.entries.each_with_index do |entry, index|
42
46
  entry_presenter = entry.presenter
@@ -4,7 +4,7 @@ en:
4
4
  errors:
5
5
  temp_file_error: |
6
6
  Failed to open temporary file in editor '%{editor}'; the system error returned was: '%{status}'.
7
- Either set the EDITOR environment variable or set the dsu editor configuration option (`$ dsu config info`).
8
- Run `$ dsu help config` for more information.
7
+ Check your EDITOR environment variable and the dsu editor configuration option (run `dsu config info`).
8
+ Run `dsu help config` for more information.
9
9
  messages:
10
10
  editing: "Editing entry group %{formatted_time}..."
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dsu
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.2
4
+ version: 2.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gene M. Angelo, Jr.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-06 00:00:00.000000000 Z
11
+ date: 2024-01-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -56,20 +56,20 @@ dependencies:
56
56
  requirements:
57
57
  - - ">="
58
58
  - !ruby/object:Gem::Version
59
- version: 0.8.1
59
+ version: '1.1'
60
60
  - - "<"
61
61
  - !ruby/object:Gem::Version
62
- version: '1.0'
62
+ version: '2.0'
63
63
  type: :runtime
64
64
  prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
67
  - - ">="
68
68
  - !ruby/object:Gem::Version
69
- version: 0.8.1
69
+ version: '1.1'
70
70
  - - "<"
71
71
  - !ruby/object:Gem::Version
72
- version: '1.0'
72
+ version: '2.0'
73
73
  - !ruby/object:Gem::Dependency
74
74
  name: os
75
75
  requirement: !ruby/object:Gem::Requirement
@@ -130,14 +130,13 @@ dependencies:
130
130
  - - "<"
131
131
  - !ruby/object:Gem::Version
132
132
  version: '2.0'
133
- description: " Get ready to jazz and snazz up your daily stand-ups with dsu, the
134
- agile developer's new best friend! This handy command-line gem is all about making
135
- your Daily Stand-Up (DSU) participation smooth, fun, and super efficient. Effortlessly
136
- create, update, and organize your DSU entries, turning the task of tracking and
137
- sharing your daily activities into a breeze. With its intuitive interface and smart
138
- date management, dsu ensures you’re always ready to inform your team about your
139
- recent progress and upcoming plans. Perfect for command-line tool enthusiasts, dsu
140
- brings a dash of simplicity and fun, fun, fun to your daily agile routine!\n"
133
+ description: |2
134
+ dsu is ruby gem that enables anyone practicing Agile methodology to record, keep track of, and manage their daily standup (DSU) activities.
135
+
136
+ * dsu uses no network connections whatsoever.
137
+ * dsu stores all of its data locally, in .json files.
138
+ * dsu is a simple (but powerful) command-line tool for users who love to work within the terminal.
139
+ * dsu versioning follows the semantic versioning standard (MAJOR.MINOR.PATCH).
141
140
  email:
142
141
  - public.gma@gmail.com
143
142
  executables:
@@ -169,7 +168,6 @@ files:
169
168
  - lib/dsu/cli.rb
170
169
  - lib/dsu/command_services/add_entry_service.rb
171
170
  - lib/dsu/crud/json_file.rb
172
- - lib/dsu/crud/raw_json_file.rb
173
171
  - lib/dsu/env.rb
174
172
  - lib/dsu/migration/service.rb
175
173
  - lib/dsu/migration/version.rb
@@ -183,6 +181,10 @@ files:
183
181
  - lib/dsu/presenters/color_theme_presenter.rb
184
182
  - lib/dsu/presenters/color_theme_show_presenter.rb
185
183
  - lib/dsu/presenters/configuration_presenter.rb
184
+ - lib/dsu/presenters/entry_group/list/date_presenter.rb
185
+ - lib/dsu/presenters/entry_group/list/dates_presenter.rb
186
+ - lib/dsu/presenters/entry_group/list/messages.rb
187
+ - lib/dsu/presenters/entry_group/list/nothing_to_list.rb
186
188
  - lib/dsu/presenters/entry_group_presenter.rb
187
189
  - lib/dsu/presenters/entry_presenter.rb
188
190
  - lib/dsu/presenters/export/all_presenter.rb
@@ -229,7 +231,6 @@ files:
229
231
  - lib/dsu/support/command_options/time_mnemonics.rb
230
232
  - lib/dsu/support/descriptable.rb
231
233
  - lib/dsu/support/entry_group_browsable.rb
232
- - lib/dsu/support/entry_group_viewable.rb
233
234
  - lib/dsu/support/field_errors.rb
234
235
  - lib/dsu/support/fileable.rb
235
236
  - lib/dsu/support/presentable.rb
@@ -247,6 +248,7 @@ files:
247
248
  - lib/dsu/views/color_theme/show.rb
248
249
  - lib/dsu/views/configuration/show.rb
249
250
  - lib/dsu/views/entry_group/edit.rb
251
+ - lib/dsu/views/entry_group/list.rb
250
252
  - lib/dsu/views/entry_group/shared/no_entries_to_display.rb
251
253
  - lib/dsu/views/entry_group/shared/no_entries_to_display_for_month_of.rb
252
254
  - lib/dsu/views/entry_group/shared/no_entries_to_display_for_week_of.rb
@@ -290,25 +292,28 @@ post_install_message: |
290
292
  View the dsu README.md here: https://github.com/gangelo/dsu
291
293
  View the dsu CHANGELOG.md: https://github.com/gangelo/dsu/blob/main/CHANGELOG.md
292
294
 
293
- Dsu now has a import command! Try it out by running `dsu import help`.
294
- Dsu now has a export command! Try it out by running `dsu export help`.
295
- Dsu now has a browse command! Try it out by running `dsu browse help`.
296
- Dsu now has a "light" theme for light background terminals! Try it out by running `dsu theme use light`.
297
- Dsu now has a delete command! Try it out by running `dsu delete help`.
295
+ *
296
+ ***
297
+ *******
298
+ *********
299
+ ***********************
300
+ *****************
301
+ *************
302
+ ******* *******
303
+ ***** *****
304
+ *** ***
305
+ ** **
298
306
 
299
- Try a dsu theme by running `dsu theme list` and then `dsu theme use THEME_NAME` where THEME_NAME is the name of the theme you want to try.
307
+ Using dsu? dsu is made available free of charge. Please consider giving dsu a STAR on GitHub as well as sharing dsu with your fellow developers on social media.
300
308
 
301
- :)
309
+ Knowing that dsu is being used and appreciated is a great motivator to continue developing and improving dsu.
302
310
 
303
- Happy New Year 2024 from dsu!
311
+ >>> Star it on github: https://github.com/gangelo/dsu
312
+ >>> Share on social media: https://rubygems.org/gems/dsu
304
313
 
305
- * * *
306
- * * . * . *
307
- * * * * * *
308
- * * * * *
309
- * * *
314
+ Thank you!
310
315
 
311
- May your year be filled with sparks of joy and innovation!
316
+ <3 Gene
312
317
  rdoc_options: []
313
318
  require_paths:
314
319
  - lib
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'fileutils'
4
-
5
- module Dsu
6
- module Crud
7
- module RawJsonFile
8
- class << self
9
- def delete(file_path:)
10
- return false unless exist?(file_path: file_path)
11
-
12
- File.delete(file_path)
13
-
14
- true
15
- end
16
-
17
- def delete!(file_path:)
18
- raise file_does_not_exist_message(file_path: file_path) unless delete(file_path: file_path)
19
- end
20
-
21
- def exist?(file_path:)
22
- File.exist?(file_path)
23
- end
24
-
25
- def read(file_path:)
26
- File.read(file_path) if exist?(file_path: file_path)
27
- end
28
-
29
- def read!(file_path:)
30
- raise file_does_not_exist_message(file_path: file_path) unless exist?(file_path: file_path)
31
-
32
- read(file_path: file_path)
33
- end
34
-
35
- def write(file_data:, file_path:)
36
- raise ArgumentError, 'file_data is nil' if file_data.nil?
37
- raise ArgumentError, "file_data is the wrong object type:\"#{file_data}\"" unless file_data.is_a?(Hash)
38
-
39
- file_data = JSON.pretty_generate(file_data)
40
- File.write(file_path, file_data)
41
- end
42
-
43
- private
44
-
45
- def file_does_not_exist_message(file_path:)
46
- "File \"#{file_path}\" does not exist"
47
- end
48
- end
49
- end
50
- end
51
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Dsu
4
- module Support
5
- module EntryGroupViewable
6
- def view_entry_groups(times:, options: {})
7
- raise ArgumentError, 'times must be an Array' unless times.is_a?(Array)
8
- raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
9
-
10
- total_viewable_entry_groups = 0
11
-
12
- times.each do |time|
13
- view_entry_group(time: time, options: options) do
14
- total_viewable_entry_groups += 1
15
- puts
16
- end
17
- end
18
-
19
- total_unviewable_entry_groups = times.size - total_viewable_entry_groups
20
- yield total_viewable_entry_groups, total_unviewable_entry_groups if block_given?
21
- end
22
-
23
- def view_entry_group(time:, options: {})
24
- raise ArgumentError, 'time must be a Time object' unless time.is_a?(Time)
25
- raise ArgumentError, 'options must be a Hash' unless options.is_a?(Hash)
26
-
27
- return unless show_entry_group?(time: time, options: options)
28
-
29
- entry_group = Models::EntryGroup.find_or_initialize(time: time)
30
- Views::EntryGroup::Show.new(entry_group: entry_group).render
31
-
32
- yield if block_given?
33
- end
34
-
35
- # This method will unconditionally display the FIRST and LAST entry groups
36
- # associated with the times provided by the <times> argument. All other
37
- # entry groups will be conditionally displayed based on the :include_all
38
- # value in the <options> argument.
39
- def view_list_for(times:, options:)
40
- configuration = Models::Configuration.new unless defined?(configuration) && configuration
41
- options = configuration.to_h.merge(options).with_indifferent_access
42
- times_first_and_last = [times.first, times.last]
43
- times.each do |time|
44
- view_options = options.dup
45
- view_options[:include_all] = true if times_first_and_last.include?(time)
46
- view_entry_group(time: time, options: view_options) do
47
- puts
48
- end
49
- end
50
- end
51
-
52
- private
53
-
54
- def show_entry_group?(time:, options:)
55
- Models::EntryGroup.exist?(time: time) || options[:include_all]
56
- end
57
-
58
- module_function :view_entry_group, :view_entry_groups, :view_list_for, :show_entry_group?
59
- end
60
- end
61
- end