piece_of_wax 0.0.2 → 1.0.0.rc5

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