punched 1.2.0 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1904b731e6d56b8dc58e49fd74f61d274da5a56342cb97b81a8a9c165f67dcc3
4
- data.tar.gz: d923efb27d5f2f6dea68ae9591a1a2d8ad602ba773a1155eb18642b61bbca434
3
+ metadata.gz: e95894483cb39658bf8b2df9eddece40e114f5afc4c62e8705c5de00345ee3cd
4
+ data.tar.gz: 32431041a7583350ff2f4dd4e1f90c104cae6f100838caf708560a4f9fac5e8a
5
5
  SHA512:
6
- metadata.gz: a98ac0bc3bb08062c85a97098bd5b5f9db1f5adb6f6dac97f02a4838dcde3f99feb91a90da7f45c4b2cf447a36e4cc70c832b1db3c2f0db40ae2d7c8f73a6df6
7
- data.tar.gz: 7f097ad0f236ff27c7eb3dccde336aca01724a04f5613ff4a7eb3dcd9ca3d18438d4ec41cd391a769348ea482fc128971c1ed696ddb2d1d9b3d026c9624a1f02
6
+ metadata.gz: 6d56bd82d7e3e745a0d6c712e3f68af52e65a8df0e0430e68bea264138d36d60a0cef92fcf039ba183307e85e3aab59793525d030c1441fa2709982b742373f7
7
+ data.tar.gz: c99e60f4b12a4848feec37f5454ce1c6b343e5d083821276aa7cb12f050406533b560c02250788736c956e401f05f604afc2ab0c63272a542016623c17f5f0fb
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  Gemfile.lock
2
2
  punchcard_test_data/
3
+ /*.gem
data/.travis.yml CHANGED
@@ -1,11 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.1
4
- - 2.2
5
- - 2.3
6
- - 2.4
7
- - 2.5
8
3
  - 2.6
4
+ - 2.7
9
5
  - ruby-head
10
6
  script: bundle exec rspec
11
7
  matrix:
data/README.md CHANGED
@@ -17,49 +17,45 @@
17
17
 
18
18
  ### Usage
19
19
 
20
- #### Start Project
20
+ #### Start and stop a Project
21
21
 
22
22
  ```sh
23
- $ punched start "Punchcard (programming)"
23
+ $ punched toggle punchcard_programming
24
+ 'punchcard_programming' started (00:00:00 total)
24
25
  ```
25
26
 
26
- #### Wildcard
27
-
28
- Save keystrokes by using wildcard. The first last active project, which matches the pattern (case insensitive) will be selected:
27
+ To stop:
29
28
 
30
29
  ```sh
31
- $ punched start "Punch*"
30
+ $ punched toggle punchcard_programming
31
+ 'punchcard_programming' stopped (00:01:25 total)
32
32
  ```
33
33
 
34
- #### Stop Project
34
+ To be more explicit, you can also use `start` and `stop` instead of `toggle`.
35
35
 
36
- ```sh
37
- $ punched stop "Punch*"
38
- ```
39
-
40
- #### Toggle
36
+ #### Wildcard
41
37
 
42
- Toggle between start and stop:
38
+ Save keystrokes by using wildcard. The first last active project, which matches the (case insensitive) pattern will be selected:
43
39
 
44
40
  ```sh
45
- $ punched toggle "Punch*"
41
+ $ punched toggle 'punched*'
46
42
  ```
47
43
 
48
44
  #### Status
49
45
 
50
46
  ```sh
51
- $ punched status "Punch*"
47
+ $ punched status punched_programming
52
48
 
53
- Punchcard (programming)
49
+ punched_programming
54
50
  01:10:09
55
51
  ```
56
52
 
57
53
  #### List details
58
54
 
59
55
  ```sh
60
- $ punched details "Punch*"
56
+ $ punched details punched_programming
61
57
 
62
- Punchcard (programming) (stopped)
58
+ punched_programming (stopped)
63
59
 
64
60
  00:00:08 2017-05-07 08:16:06 - 2017-05-07 08:16:14
65
61
  00:04:35 2017-05-07 08:22:02 - 2017-05-07 08:26:37
@@ -68,26 +64,44 @@ Toggle between start and stop:
68
64
  01:10:04 (total)
69
65
  ```
70
66
 
67
+ #### Filtering
68
+
69
+ You can filter your spend time with `startAt` and `endAt`:
70
+
71
+ ```sh
72
+ $ punched totalsum myproject --startAt=15.05.2020
73
+ $ punched totalsum myproject --startAt=15.05.2020 --endAt=30.05.2020
74
+ $ punched all plain --startAt=15.05.2020 --endAt=30.05.2020
75
+ ```
76
+
71
77
  #### Set Hourly Rate
72
78
 
73
79
  ```sh
74
- $ punched set "Punch*" hourlyRate 250€
80
+ $ punched set punched_programming hourlyRate 250€
81
+ {"hourlyRate":"250€"}
75
82
  ```
76
83
 
77
- #### Total time in seconds
84
+ #### Sum spended time on project(s)
85
+
86
+ `total` returns the total spend time in seconds:
78
87
 
79
88
  ```sh
80
- $ punched total "Punch*"
89
+ $ punched total punched_programming
90
+ 13505
81
91
  ```
82
92
 
83
- #### Rename and delete Project
93
+ `totalsum` calculates human readable spended time on project(s) (wildcard is used by default):
84
94
 
85
95
  ```sh
86
- $ punched rename "Old Title" "New Title"
96
+ $ punched totalsum punched_programming
97
+ 02:05:06
87
98
  ```
88
99
 
100
+ Use `startAt` and/or `endAt` to set a time range:
101
+
89
102
  ```sh
90
- $ punched remove "Punchcard (programming)"
103
+ $ punched totalsum punched_programming --startAt=2020-05-01 --endAt=2020-05-03
104
+ 01:02:36
91
105
  ```
92
106
 
93
107
  #### Help
@@ -107,14 +121,14 @@ List all available actions:
107
121
  |========================================|=========|=====================|================|=============|==========|
108
122
  | project | status | last active on | total duration | hourly rate | earnings |
109
123
  |========================================|=========|=====================|================|=============|==========|
110
- | Website | stopped | 2017-05-07 15:50:00 | 00:04:40 | 95.0 € | 380.00 € |
124
+ | website | stopped | 2017-05-07 15:50:00 | 00:04:40 | 95.0 € | 380.00 € |
111
125
  |----------------------------------------|---------|---------------------|----------------|-------------|----------|
112
- | Punchcard (programming) | stopped | 2017-07-11 12:47:42 | 01:10:04 | | |
126
+ | punchcard_programming | stopped | 2017-07-11 12:47:42 | 01:10:04 | | |
113
127
  |========================================|=========|=====================|================|=============|==========|
114
128
 
115
129
  ```
116
130
 
117
- To use `md` or `csv` as output format:
131
+ To use `plain`, `md` or `csv` as output format:
118
132
 
119
133
  ```sh
120
134
  $ punched all csv
@@ -126,6 +140,8 @@ To use `md` or `csv` as output format:
126
140
 
127
141
  You can use `all` with any other action as well, e.g. `punched all stop` to stop all running projects.
128
142
 
143
+ Here you can also filter your spend time with `startAt` and `endAt`, respectively.
144
+
129
145
  ### Store projects files in a custom folder and sync them between computers
130
146
 
131
147
  By default, PunchCard will store the data in `~/.punchcard/`. Define your custom destination with:
data/bin/punched CHANGED
@@ -2,9 +2,8 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  THIS_FILE = File.symlink?(__FILE__) ? File.readlink(__FILE__) : __FILE__
5
-
6
5
  begin
7
- require File.expand_path(File.dirname(THIS_FILE) + '/../lib/punchcard')
6
+ require File.expand_path(File.dirname(THIS_FILE) + '/../lib/punchcard.rb')
8
7
  rescue LoadError
9
8
  require 'punchcard'
10
9
  end
@@ -15,19 +14,19 @@ require 'date'
15
14
  require 'json'
16
15
 
17
16
  class UnknownActionError < StandardError; end
18
-
17
+ class InvalidAllArgument < StandardError; end
19
18
  #
20
19
  # CLI Wrapper
21
20
  #
22
21
 
23
22
  def available_actions
24
- PunchCard.new(nil).public_methods(false).reject { |item|
23
+ PunchCard.new(nil).public_methods(false).reject do |item|
25
24
  item.to_s.end_with?('=') || item.to_s == 'project'
26
- }.concat(global_available_actions).sort
25
+ end.concat(global_available_actions).sort
27
26
  end
28
27
 
29
28
  def global_available_actions
30
- [:all, :totalsum]
29
+ %i[all totalsum]
31
30
  end
32
31
 
33
32
  def action_available?(action)
@@ -43,48 +42,58 @@ def usage
43
42
  "Usage: #{File.basename(__FILE__)} #{available_actions.join('|')} 'Name of my project'"
44
43
  end
45
44
 
46
- def all(action)
47
- available_formats = %w[csv plain md]
48
- unless available_formats.include?(action)
49
- raise "Format #{action} is not supported. Possible formats are: #{available_formats.join(',')}"
50
- end
51
-
52
- labels = ['project', 'status', 'last active on', 'total duration', 'hourly rate', 'earnings']
45
+ def list_all_projects(format)
53
46
  data = call_punchcards_by_pattern('*', 'csv').map do |csv_string|
54
47
  csv_data = CSV.parse(csv_string)[0]
55
- last_activity = !csv_data[2].empty? ? DateTime.parse(csv_data[2]).to_time.to_i : 0
48
+ last_activity = !csv_data[2].empty? ? Time.parse(csv_data[2]).to_i : 0
56
49
  csv_data.push(last_activity)
57
50
  csv_data
58
51
  end.sort_by(&:last).reverse.map do |row|
59
52
  row[0...-1]
60
53
  end
61
-
62
54
  return puts('No record(s) so far') if data.empty?
63
55
 
64
- case action
56
+ puts convert_project_data(format, data)
57
+ end
58
+
59
+ def convert_project_data(format, data)
60
+ labels = ['project', 'status', 'last active on', 'total duration', 'hourly rate', 'earnings']
61
+ case format
65
62
  when 'md'
66
- puts MarkdownTables.make_table(labels, data, is_rows: true, align: ['l'])
63
+ MarkdownTables.make_table(labels, data, is_rows: true, align: ['l'])
67
64
  when 'csv'
68
- puts labels.to_csv
69
- puts data.map(&:to_csv).join
65
+ "#{labels.to_csv}\n#{data.map(&:to_csv).join}"
70
66
  when 'plain'
71
67
  table = MarkdownTables.make_table(labels, data, is_rows: true, align: ['l'])
72
- puts MarkdownTables.plain_text(table)
68
+ MarkdownTables.plain_text(table)
69
+ end
70
+ end
71
+
72
+ def all(action)
73
+ available_formats = %w[csv plain md]
74
+ if available_formats.include?(action)
75
+ list_all_projects(action)
76
+ elsif action_available?(action)
77
+ puts call_punchcards_by_pattern('*', action).to_a.join("\n")
78
+ else
79
+ suppurted_arguments = (available_formats + available_actions).reject {|n| n.to_s == 'all' }.uniq
80
+ raise InvalidAllArgument, "'#{action}' is not supported\nSupported formats and actions: #{suppurted_arguments.join(',')}"
73
81
  end
74
82
  end
75
83
 
76
- def call_punchcard(selected_action, project_name)
84
+ def call_punchcard(selected_action:, project_name:, arguments:)
77
85
  if action_available?(selected_action)
78
86
  if !project_name && selected_action != 'list'
79
87
  exit_with_error!("2nd argument has to be the project name, e.g.:\n#{usage}")
80
88
  end
81
89
  punch_card = PunchCard.new project_name
82
90
  begin
83
- arguments = ARGV.drop(2)
84
- if !arguments.empty?
85
- return punch_card.send(selected_action.to_s, *arguments)
91
+ if arguments.nil? || arguments.empty?
92
+ punch_card.public_send(selected_action.to_s)
93
+ elsif arguments.is_a?(Hash)
94
+ punch_card.public_send(selected_action.to_s, **arguments)
86
95
  else
87
- return punch_card.send(selected_action.to_s)
96
+ punch_card.public_send(selected_action.to_s, *arguments)
88
97
  end
89
98
  rescue PunchCardError => e
90
99
  exit_with_error! "Error: #{e.message}"
@@ -94,10 +103,47 @@ def call_punchcard(selected_action, project_name)
94
103
  end
95
104
  end
96
105
 
106
+ def cli_argument_by_name(name)
107
+ value = ARGV.filter { |arg| arg.start_with?("--#{name}=") }&.first
108
+ value = value.split('=')[1] unless value.nil?
109
+ if block_given? && !value.nil? && !value.empty?
110
+ yield value
111
+ else
112
+ value
113
+ end
114
+ end
115
+
97
116
  def call_punchcards_by_pattern(pattern, action)
98
- Dir["#{PunchCard::SETTINGS_DIR}/#{pattern}"].map do |file|
99
- project_name = File.basename(file)
100
- call_punchcard(action, project_name)
117
+ path_patterns = [
118
+ "#{PunchCard::SETTINGS_DIR}/#{pattern}*",
119
+ "#{PunchCard::SETTINGS_DIR}/*#{pattern}*"
120
+ ]
121
+ arguments = {}
122
+ if %w(csv plain md total).include?(action)
123
+ arguments = {
124
+ start_at: cli_argument_by_name('startAt') { |v| Date.parse(v) },
125
+ end_at: cli_argument_by_name('endAt') { |v| Date.parse(v) }
126
+ }
127
+ end
128
+ found = []
129
+ path_patterns.each do |path_pattern|
130
+ found = Dir[path_pattern].map do |file|
131
+ project_name = File.basename(file)
132
+ call_punchcard(
133
+ selected_action: action,
134
+ project_name: project_name,
135
+ arguments: arguments
136
+ )
137
+ end
138
+ break if found.any?
139
+ end
140
+ found
141
+ end
142
+
143
+ def validate_project_name_and_stop_if_invalid(project_name)
144
+ if project_name.strip.strip.start_with?('.')
145
+ STDERR.puts "Error: project name's are not allowed to start with '.'"
146
+ exit 1
101
147
  end
102
148
  end
103
149
 
@@ -114,18 +160,26 @@ if selected_action
114
160
  if selected_action == 'all'
115
161
  all(ARGV[1] || 'plain')
116
162
  elsif selected_action == 'totalsum'
117
- pattern = ARGV[1] || '*'
163
+ pattern_argument = ARGV.slice(1)
164
+ pattern = !pattern_argument || pattern_argument.empty? || pattern_argument.start_with?('--') ? '*' : pattern_argument
118
165
  puts PunchCard.humanize_duration(
119
166
  call_punchcards_by_pattern(pattern, 'total').reduce(&:+)
120
167
  )
121
168
  else
122
- result = call_punchcard(selected_action, project_name)
169
+ validate_project_name_and_stop_if_invalid(project_name)
170
+ result = call_punchcard(
171
+ selected_action: selected_action,
172
+ project_name: project_name,
173
+ arguments: ARGV.drop(2)
174
+ )
123
175
  if result.is_a?(Hash)
124
176
  puts result.to_json
125
177
  else
126
178
  puts result
127
179
  end
128
180
  end
181
+ rescue InvalidAllArgument => e
182
+ exit_with_error! e.message
129
183
  rescue PunchCardError => e
130
184
  exit_with_error! "Error: #{e.message}"
131
185
  rescue UnknownActionError => e
data/lib/punchcard.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # (c) 2017-2019 by Philipp Staender
2
4
 
3
5
  require 'date'
@@ -11,7 +13,7 @@ class PunchCard
11
13
  HOURLY_RATE_PATTERN = /^\s*(\d+)([^\d]+)*\s*/i.freeze
12
14
  TIME_POINT_PATTERN = /^((\d+|.+?\s[\+\-]\d{4}?\s*)(\-)*(\d+|\s.+\d?)*)$/.freeze
13
15
  META_KEY_PATTERN = /^([a-zA-Z0-9]+)\:\s*(.*)$/.freeze
14
- VERSION = '1.2.0'.freeze
16
+ VERSION = '1.3.1'
15
17
 
16
18
  attr_accessor :title
17
19
 
@@ -101,7 +103,7 @@ class PunchCard
101
103
  output.join("\n")
102
104
  end
103
105
 
104
- def csv
106
+ def csv(start_at: nil, end_at: nil)
105
107
  project_exists_or_stop!
106
108
  find_or_make_file
107
109
  durations = []
@@ -110,16 +112,27 @@ class PunchCard
110
112
  points = line_to_time_points(line)
111
113
  next unless points
112
114
 
113
- starttime = points[0]
114
- endtime = points[1] || timestamp
115
+ start_time = points[0]
116
+ end_time = points[1] || timestamp
117
+
118
+ next if time_range_is_excluded_by_filter?(
119
+ start_at: start_at,
120
+ end_at: end_at,
121
+ start_time: start_time,
122
+ end_time: end_time
123
+ )
124
+
115
125
  last_activity = points[1] || points[0]
116
- durations.push duration(starttime, endtime)
126
+ durations.push end_time - start_time
117
127
  end
128
+ total_duration = self.class.humanize_duration(
129
+ durations.reduce(&:+) || 0
130
+ )
118
131
  '"' + [
119
132
  title_and_project,
120
133
  running_status,
121
134
  last_activity ? self.class.format_time(Time.at(last_activity).to_datetime) : '',
122
- humanized_total,
135
+ total_duration,
123
136
  hourly_rate ? hourly_rate[:hourlyRate].to_s + " #{hourly_rate[:currency]}" : '',
124
137
  hourly_rate ? (hourly_rate[:hourlyRate] * total / 3600.0).round(2).to_s + " #{hourly_rate[:currency]}" : ''
125
138
  ].join('","') + '"'
@@ -155,22 +168,32 @@ class PunchCard
155
168
  end
156
169
 
157
170
  def set(key, value)
158
- raise PunchCardError, "Key '#{key}' can only be alphanumeric" unless key =~ /^[a-zA-Z0-9]+$/
171
+ unless key =~ /^[a-zA-Z0-9]+$/
172
+ raise PunchCardError, "Key '#{key}' can only be alphanumeric"
173
+ end
159
174
 
160
175
  @meta_data[key.to_sym] = value
161
176
  write_to_project_file!
162
177
  @meta_data
163
178
  end
164
179
 
165
- def total
180
+ def total(start_at: nil, end_at: nil)
166
181
  total = 0
167
182
  project_data.map do |line|
168
183
  points = line_to_time_points(line)
169
184
  next unless points
170
185
 
171
- starttime = points[0]
172
- endtime = points[1] || timestamp
173
- total += endtime - starttime
186
+ start_time = points[0]
187
+ end_time = points[1] || timestamp
188
+
189
+ next if time_range_is_excluded_by_filter?(
190
+ start_at: start_at,
191
+ end_at: end_at,
192
+ start_time: start_time,
193
+ end_time: end_time
194
+ )
195
+
196
+ total += end_time - start_time
174
197
  end
175
198
  total
176
199
  end
@@ -180,6 +203,8 @@ class PunchCard
180
203
  end
181
204
 
182
205
  def self.humanize_duration(duration)
206
+ return nil unless duration
207
+
183
208
  hours = duration / (60 * 60)
184
209
  minutes = (duration / 60) % 60
185
210
  seconds = duration % 60
@@ -197,7 +222,7 @@ class PunchCard
197
222
  private
198
223
 
199
224
  def hourly_rate
200
- hourly_rate_found = @meta_data[:hourlyRate] && @meta_data[:hourlyRate].match(HOURLY_RATE_PATTERN)
225
+ hourly_rate_found = @meta_data[:hourlyRate]&.match(HOURLY_RATE_PATTERN)
201
226
  return unless hourly_rate_found
202
227
 
203
228
  {
@@ -222,9 +247,9 @@ class PunchCard
222
247
  self.class.humanize_duration total
223
248
  end
224
249
 
225
- def duration(starttime, endtime)
226
- if starttime
227
- self.class.humanize_duration endtime - starttime
250
+ def duration(start_time, end_time)
251
+ if start_time
252
+ self.class.humanize_duration end_time - start_time
228
253
  else
229
254
  self.class.humanize_duration 0
230
255
  end
@@ -254,7 +279,7 @@ class PunchCard
254
279
  matches = line.match(TIME_POINT_PATTERN)
255
280
 
256
281
  time_points = matches ? [string_to_timestamp(matches[2]), string_to_timestamp(matches[4])] : nil
257
- if time_points && time_points.reject(&:nil?).empty?
282
+ if time_points&.reject(&:nil?)&.empty?
258
283
  nil
259
284
  else
260
285
  time_points
@@ -351,4 +376,8 @@ class PunchCard
351
376
  def sanitize_filename(name)
352
377
  name.downcase.gsub(%r{(\\|/)}, '').gsub(/[^0-9a-z.\-]/, '_')
353
378
  end
379
+
380
+ def time_range_is_excluded_by_filter?(start_time:, end_time:, start_at: nil, end_at: nil)
381
+ start_at && start_at.to_time.to_i >= start_time || end_at && end_at.to_time.to_i <= end_time
382
+ end
354
383
  end
data/punched.gemspec CHANGED
@@ -11,9 +11,8 @@ Gem::Specification.new do |s|
11
11
  s.description = 'Minimal time tracking tool for cli'
12
12
  s.license = 'GPL-3.0'
13
13
  s.executables = ['punched']
14
- s.rubyforge_project = 'punched'
15
14
  s.require_paths = ['lib']
16
- s.required_ruby_version = '>= 2.1'
15
+ s.required_ruby_version = '>= 2.6'
17
16
  s.files = `git ls-files`.split("\n")
18
17
 
19
18
  s.add_dependency 'markdown-tables', '~> 1.0.2'
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  $LOAD_PATH.push File.expand_path('lib', __dir__)
2
3
 
3
4
  require 'punchcard'
@@ -13,6 +14,10 @@ def setup_example_settings_dir
13
14
  PunchCard.const_set(:SETTINGS_DIR, example_settings_dir)
14
15
  end
15
16
 
17
+ def punched_bin
18
+ "PUNCHCARD_DIR=#{example_settings_dir} #{Dir.pwd}/bin/punched"
19
+ end
20
+
16
21
  describe PunchCard do
17
22
  before do
18
23
  setup_example_settings_dir
@@ -86,6 +91,13 @@ describe PunchCard do
86
91
  expect(project.project).to eq('playing_mot_rhead')
87
92
  end
88
93
 
94
+ xit 'should read and write utf8 names' do
95
+ PunchCard.new 'Playing Motörhead'
96
+ expect(my_project_file('playing_mot_rhead').strip).to eq('Playing Motörhead')
97
+ project = PunchCard.new 'Playing*'
98
+ expect(project.project).to eq('Playing Motörhead')
99
+ end
100
+
89
101
  it 'should set hourlyRate' do
90
102
  project = start_and_stop
91
103
  project.set 'hourlyRate', '1000 €'
@@ -150,4 +162,10 @@ describe PunchCard do
150
162
  project.remove
151
163
  expect(File.exist?("#{example_settings_dir}/my_project")).to be_falsey
152
164
  end
165
+
166
+ it 'should call punched all' do
167
+ two_seconds_tracking
168
+ result = `#{punched_bin} all`
169
+ expect(result).to match('My Project')
170
+ end
153
171
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: punched
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philipp Staender
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-08-08 00:00:00.000000000 Z
11
+ date: 2020-08-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: markdown-tables
@@ -45,7 +45,7 @@ homepage: https://github.com/pstaender/punchcard
45
45
  licenses:
46
46
  - GPL-3.0
47
47
  metadata: {}
48
- post_install_message:
48
+ post_install_message:
49
49
  rdoc_options: []
50
50
  require_paths:
51
51
  - lib
@@ -53,15 +53,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
53
53
  requirements:
54
54
  - - ">="
55
55
  - !ruby/object:Gem::Version
56
- version: '2.1'
56
+ version: '2.6'
57
57
  required_rubygems_version: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
62
  requirements: []
63
- rubygems_version: 3.0.3
64
- signing_key:
63
+ rubygems_version: 3.0.6
64
+ signing_key:
65
65
  specification_version: 4
66
66
  summary: Punchcard Timetracker
67
67
  test_files: []