piece_of_wax 0.0.2 → 1.0.0.rc5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 683908a54cb7a603677d40b86d375486d3c32f52a0d6630ceb24ac1c91a953eb
4
- data.tar.gz: 6eeb3aef9b284a8086a32ab70248b4ea53bfd9614e417293734fc2f49bd8ccc2
3
+ metadata.gz: 0fdf0186e07e8c9137374525a9befb04f41b30be7dbfe81e65c2d89775a171b2
4
+ data.tar.gz: 475ee015973ef306855f15574fe82334c95ccbd9dd0a4925224d96a0595ca2ee
5
5
  SHA512:
6
- metadata.gz: 4da0542b0d656c49f6278fedfa449966a1a07d0fda7fc3c02d33679b3108605d614237c1ae7c524d498cfc6d3b50ca14439e52dad8a7175baf0bc37a20bf5f3a
7
- data.tar.gz: d610bdae2c2fa0125e1691ba3bc83d7f5051aa5a6455985ac69055d45996a4dcb46ee1a2410f9639c271049513ff91dd485d08a01addccf41f28f6eacf554fe0
6
+ metadata.gz: e18a182873c378b85d385abec297c2041a640432a377e9e91d458f15abe2c2f4b63e03b235bbc343acc61806351f85687d9d762c783f7e2f594005a0669b8646
7
+ data.tar.gz: 6441d89f22d93c54865c1e94f0398db26a4b6856f356699e77b8c599f92ba81dd22e897fd5443c74d307748915baa51f74fb35502376ef051742d00054151141
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require_relative '../lib/piece_of_wax'
4
- PieceOfWax.start(ARGV)
4
+ require "piece_of_wax"
5
+ PieceOfWax::CLI.start(ARGV)
@@ -1,68 +1,66 @@
1
- require "thor"
2
- require "date"
3
-
4
- class PieceOfWax < Thor
5
- desc "activity _activity_", "log activity"
6
- def activity(x)
7
- write_to_file("#{Time.now} - #{x}")
8
- puts "wrote #{x} to file"
9
- end
1
+ # frozen_string_literal: true
10
2
 
11
- desc "read [date]", "print activities on given [date] (yesterday by default)"
12
- def read(date = Date.today.strftime('%Y-%m-%d'))
13
- output =
14
- begin
15
- generate_output(File.open(filename(date)).readlines)
16
- rescue Errno::ENOENT
17
- "Sorry, I couldn't find any logs for #{print_date(date)}."
18
- end
19
- puts output
20
- end
21
-
22
- private
3
+ require "json"
4
+ require "piece_of_wax/cli_options"
5
+ require "piece_of_wax/date_time_helper"
6
+ require "piece_of_wax/date_time_decorator/error_message_display"
7
+ require "piece_of_wax/logfile"
8
+ require "thor"
23
9
 
24
- def write_to_file(str)
25
- today = Time.now.strftime("%y%m%d")
26
- File.open("#{today}.txt", "a+") do |f|
27
- f.puts(str)
10
+ module PieceOfWax
11
+ #
12
+ # Implements command line interface. Each public method corresponds to a `piece_of_wax`
13
+ # subcommand.
14
+ #
15
+ class CLI < Thor
16
+ desc 'activity "_activity_"', "log activity"
17
+ option :time, aliases: :t, desc: "time when activity was started"
18
+ def activity(*str)
19
+ CLIOptions.instance.set(options)
20
+ time = DateTimeHelper.safe_parse(CLIOptions.instance[:time]) || DateTimeHelper.current_time
21
+ description = str.join(" ")
22
+ Logfile.new(DateTimeHelper.current_date).add_activity(time, description)
23
+ $stdout.puts "wrote #{description} to file"
28
24
  end
29
- end
30
-
31
- def filename(date)
32
- "%s.txt" % Date.parse(date).strftime("%y%m%d")
33
- end
34
25
 
35
- def print_date(date)
36
- if date == Date.today.prev_day
37
- 'yesterday'
38
- else
39
- date
26
+ desc 'edit "_activity_"', "edit activity"
27
+ option :n, default: "1", desc: "the activity to edit, 1 being the most recently-logged"
28
+ def edit(*str)
29
+ CLIOptions.instance.set(options)
30
+ description = str.join(" ")
31
+ index = (0 - CLIOptions.instance[:n].to_i)
32
+ logfile = Logfile.new(DateTimeHelper.current_date)
33
+ if logfile.contents[index]
34
+ logfile.edit_activity(index, description)
35
+ $stdout.puts "wrote #{description} to file"
36
+ else
37
+ warn 'activity referenced by "n" option does not exist'
38
+ end
40
39
  end
41
- end
42
40
 
43
- def generate_output(lines)
44
- date = date_format(lines[0].split(' ')[0])
45
- result =
46
- lines.each_cons(2).reduce([date]) do |output, (line1, line2)|
47
- output << output_line(line1, line2)
41
+ desc "read [date]", "print activities on given [date] (today by default)"
42
+ option :military, aliases: :m, desc: "render time in military (24-hour) format"
43
+ option(
44
+ :'quitting-time',
45
+ default: "17:45:00 -0500",
46
+ aliases: :q,
47
+ desc: "last activity period is capped off at this time"
48
+ )
49
+ def read(date_str = "")
50
+ CLIOptions.instance.set(options)
51
+ date = DateTimeHelper.safe_parse(date_str) || DateTimeHelper.current_date
52
+ logfile = Logfile.new(date)
53
+ if logfile.any_contents?
54
+ $stdout.puts logfile
55
+ else
56
+ warn "Sorry, I couldn't find any logs for #{DateTimeDecorator::ErrorMessageDisplay.new(date)}."
48
57
  end
49
- result << output_line(lines.last, "#{date} 05:45:00 -0500 - #{lines.last.split(' - ')[1]}")
50
- end
51
-
52
- def output_line(line1, line2)
53
- date_info1, activity1 = line1.split(' - ')
54
- date_info2, _activity2 = line2.split(' - ')
55
- _date1, time1, _time_delta1 = date_info1.split(' ')
56
- _date2, time2, _time_delta2 = date_info2.split(' ')
57
- "#{time_format(time1)}-#{time_format(time2)} - #{activity1}"
58
- end
58
+ end
59
59
 
60
- def date_format(str)
61
- year, month, day = str.split('-').map(&:to_i)
62
- Date.new(year, month, day).strftime('%-m/%-d/%y')
63
- end
60
+ private
64
61
 
65
- def time_format(str)
66
- str.split(':').first(2).join(':')
62
+ def exit_on_failure?
63
+ true
64
+ end
67
65
  end
68
66
  end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require "singleton"
5
+
6
+ module PieceOfWax
7
+ #
8
+ # Singleton class that stores options/flags passed from the command line. Purpose
9
+ # of the class is to make these options globally available.
10
+ #
11
+ class CLIOptions
12
+ include Singleton
13
+ extend Forwardable
14
+
15
+ def [](key)
16
+ options[key]
17
+ end
18
+
19
+ def set(opts)
20
+ raise "CLIOptions is an immutable singleton; #set can only be called once per program execution" if options.any?
21
+
22
+ @options = opts
23
+ end
24
+
25
+ private
26
+
27
+ def options
28
+ @options || {}
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "piece_of_wax/date_time"
4
+
5
+ module PieceOfWax
6
+ module DateTimeDecorator
7
+ #
8
+ # Decorator class designed to wrap DateTime instances. Implements form of to_s used
9
+ # to render error messages.
10
+ #
11
+ class ErrorMessageDisplay
12
+ def initialize(date_time)
13
+ @date_time = date_time
14
+ end
15
+
16
+ def to_s
17
+ case @date_time.to_date
18
+ when DateTimeHelper.current_date
19
+ "today"
20
+ when DateTimeHelper.current_date.prev_day
21
+ "yesterday"
22
+ else
23
+ @date_time.strftime("%Y-%-m-%-d")
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PieceOfWax
4
+ module DateTimeDecorator
5
+ #
6
+ # Decorator class designed to wrap DateTime instances. Implements form of to_s used
7
+ # to render times in output of `read` subcommand.
8
+ #
9
+ class ReadDisplay
10
+ def initialize(date_time)
11
+ @date_time = date_time
12
+ end
13
+
14
+ def to_s
15
+ if CLIOptions.instance[:military]
16
+ @date_time.strftime("%H:%M")
17
+ else
18
+ @date_time.strftime("%I:%M %p")
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "piece_of_wax/cli_options"
4
+ require "time"
5
+
6
+ module PieceOfWax
7
+ #
8
+ # Implements helper methods related to Date/Time
9
+ #
10
+ module DateTimeHelper
11
+ def self.safe_parse(arg)
12
+ # NOTE: calling to_s so method is safe for non-string args
13
+ str = arg.to_s
14
+ begin
15
+ Time.parse(str)
16
+ rescue ArgumentError
17
+ nil
18
+ end
19
+ end
20
+
21
+ def self.current_date
22
+ # NOTE: Possible that current_date returns two different values over course of
23
+ # same piece_of_wax invocation (if run at midnight.) I consider this
24
+ # to be a major edge case with minimal consequences, so I'm not
25
+ # going to the trouble of memoizing at this point. - Ben S.
26
+ Time.now.to_date
27
+ end
28
+
29
+ def self.current_time
30
+ Time.now
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PieceOfWax
4
+ #
5
+ # A FileIO instance represents a file. Abstracts reading from and writing to the file system.
6
+ #
7
+ class FileIO
8
+ def initialize(filename)
9
+ @filename = filename
10
+ end
11
+
12
+ def read
13
+ File.read(@filename)
14
+ rescue Errno::ENOENT
15
+ nil
16
+ end
17
+
18
+ def overwrite_with(str)
19
+ File.open(@filename, "w") do |f|
20
+ f.write(str)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "erb"
4
+ require "json"
5
+ require "piece_of_wax/file_io"
6
+ require "piece_of_wax/output_line/activity"
7
+ require "piece_of_wax/output_line/date"
8
+
9
+ module PieceOfWax
10
+ #
11
+ # A Logfile instance represents a specific type of file used by piece_of_wax to
12
+ # store logged time (a "time log.") A time log stores information for a single day,
13
+ # which is why a date must be passed to the Logfile constructor.
14
+ #
15
+ class Logfile
16
+ def initialize(date)
17
+ filename = format("%s.txt", date.strftime("%y%m%d"))
18
+ @file_io = FileIO.new(filename)
19
+ end
20
+
21
+ def contents
22
+ return [] unless (raw_content = @file_io.read)
23
+
24
+ JSON.parse(raw_content)
25
+ end
26
+
27
+ def any_contents?
28
+ contents.any?
29
+ end
30
+
31
+ def add_activity(time, description)
32
+ data = { datetime: time, description: description }
33
+ index = proper_index(time)
34
+ json_str = contents.insert(index, data).to_json
35
+ @file_io.overwrite_with(json_str)
36
+ end
37
+
38
+ def edit_activity(index, description)
39
+ updated_contents = contents.tap { |c| c[index]["description"] = description }
40
+ json_str = updated_contents.to_json
41
+ @file_io.overwrite_with(json_str)
42
+ end
43
+
44
+ def to_s
45
+ output =
46
+ ERB.new(<<~ERB).result(binding)
47
+ <%= OutputLine::Date.new(first_entry['datetime']) %>
48
+ <% contents.each_cons(2).map do |line1, line2| %>
49
+ <%= OutputLine::Activity.new(line1['datetime'], line2['datetime'], line1['description']) %>
50
+ <% end.join("\n") %>
51
+ <%= OutputLine::Activity.new(last_entry['datetime'], quitting_time, last_entry['description']) %>
52
+ ERB
53
+ # Unsure why ERB is inserting the double linebreaks after each
54
+ # iteration of `contents.each_cons(2)`. For now, subbing single linebreak
55
+ # to eliminate doubles.
56
+ output.gsub("\n\n", "\n")
57
+ end
58
+
59
+ private
60
+
61
+ def proper_index(time)
62
+ contents.each_with_index do |line, i|
63
+ return i if Time.parse(line["datetime"]) > time
64
+ end
65
+ contents.length
66
+ end
67
+
68
+ def first_entry
69
+ contents.first
70
+ end
71
+
72
+ def last_entry
73
+ contents.last
74
+ end
75
+
76
+ def quitting_time
77
+ CLIOptions.instance[:'quitting-time']
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "piece_of_wax/date_time_helper"
4
+ require "piece_of_wax/date_time_decorator/read_display"
5
+
6
+ module PieceOfWax
7
+ module OutputLine
8
+ #
9
+ # An Activity instance represents a single logged activity, rendered as a line of
10
+ # `read` subcommand output.
11
+ #
12
+ class Activity
13
+ def initialize(start_time_str, end_time_str, description)
14
+ @start_time = DateTimeDecorator::ReadDisplay.new(DateTimeHelper.safe_parse(start_time_str))
15
+ @end_time = DateTimeDecorator::ReadDisplay.new(DateTimeHelper.safe_parse(end_time_str))
16
+ @description = description
17
+ end
18
+
19
+ def to_s
20
+ "#{@start_time}-#{@end_time} - #{@description}"
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "piece_of_wax/date_time_helper"
4
+
5
+ module PieceOfWax
6
+ module OutputLine
7
+ #
8
+ # A Date instance represents the date of a time log (see Logfile,) rendered as a line of
9
+ # `read` subcommand output.
10
+ #
11
+ class Date
12
+ def initialize(datetime_str)
13
+ @date = DateTimeHelper.safe_parse(datetime_str)
14
+ end
15
+
16
+ def to_s
17
+ @date.strftime("%-m/%-d/%y")
18
+ end
19
+ end
20
+ end
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: piece_of_wax
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 1.0.0.rc5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Stegeman
@@ -14,30 +14,114 @@ dependencies:
14
14
  name: thor
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '='
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.20.0
19
+ version: '1.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '='
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.20.0
26
+ version: '1.0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: timecop
28
+ name: aruba
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - '='
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 0.9.1
33
+ version: '1.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '='
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 0.9.1
40
+ version: '1.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: overcommit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.55.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.55.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.11.3
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.11.3
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-nav
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.3'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-remote
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 0.1.7
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 0.1.7
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.7'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.7'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: 0.88.0
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: 0.88.0
41
125
  description: ''
42
126
  email: bstegeman17@gmail.com
43
127
  executables:
@@ -47,6 +131,14 @@ extra_rdoc_files: []
47
131
  files:
48
132
  - bin/piece_of_wax
49
133
  - lib/piece_of_wax.rb
134
+ - lib/piece_of_wax/cli_options.rb
135
+ - lib/piece_of_wax/date_time_decorator/error_message_display.rb
136
+ - lib/piece_of_wax/date_time_decorator/read_display.rb
137
+ - lib/piece_of_wax/date_time_helper.rb
138
+ - lib/piece_of_wax/file_io.rb
139
+ - lib/piece_of_wax/logfile.rb
140
+ - lib/piece_of_wax/output_line/activity.rb
141
+ - lib/piece_of_wax/output_line/date.rb
50
142
  homepage:
51
143
  licenses:
52
144
  - MIT
@@ -62,9 +154,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
62
154
  version: '0'
63
155
  required_rubygems_version: !ruby/object:Gem::Requirement
64
156
  requirements:
65
- - - ">="
157
+ - - ">"
66
158
  - !ruby/object:Gem::Version
67
- version: '0'
159
+ version: 1.3.1
68
160
  requirements: []
69
161
  rubyforge_project:
70
162
  rubygems_version: 2.7.6