tempo-cli 0.1.6 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Gemfile.lock +3 -3
- data/README.md +9 -3
- data/bin/tempo +20 -2
- data/features/arrange.feature +1 -1
- data/features/directory.feature +108 -0
- data/features/project.feature +2 -2
- data/features/report.feature +3 -2
- data/features/start.feature +0 -1
- data/features/step_definitions/tempo_steps.rb +99 -20
- data/features/update.feature +7 -1
- data/lib/file_record/directory.rb +19 -3
- data/lib/file_record/file_utility.rb +122 -0
- data/lib/file_record/record.rb +36 -83
- data/lib/tempo/controllers/arrange_controller.rb +5 -5
- data/lib/tempo/controllers/base.rb +8 -8
- data/lib/tempo/controllers/checkout_controller.rb +4 -4
- data/lib/tempo/controllers/end_controller.rb +4 -5
- data/lib/tempo/controllers/projects_controller.rb +13 -10
- data/lib/tempo/controllers/records_controller.rb +8 -5
- data/lib/tempo/controllers/report_controller.rb +4 -4
- data/lib/tempo/controllers/start_controller.rb +4 -3
- data/lib/tempo/controllers/update_controller.rb +22 -8
- data/lib/tempo/exceptions.rb +2 -2
- data/lib/tempo/models/base.rb +26 -18
- data/lib/tempo/models/composite.rb +5 -3
- data/lib/tempo/models/log.rb +61 -38
- data/lib/tempo/models/project.rb +9 -6
- data/lib/tempo/models/time_record.rb +14 -14
- data/lib/tempo/version.rb +1 -1
- data/lib/tempo/views/arrange_view.rb +4 -4
- data/lib/tempo/views/base.rb +10 -23
- data/lib/tempo/views/formatters/base.rb +2 -2
- data/lib/tempo/views/formatters/screen.rb +7 -7
- data/lib/tempo/views/projects_view.rb +8 -8
- data/lib/tempo/views/report_view.rb +5 -5
- data/lib/tempo/views/reporter.rb +4 -4
- data/lib/tempo/views/time_record_view.rb +5 -5
- data/lib/tempo/views/view_records/base.rb +8 -8
- data/lib/tempo/views/view_records/composite.rb +4 -4
- data/lib/tempo/views/view_records/log.rb +3 -3
- data/lib/tempo/views/view_records/project.rb +3 -3
- data/lib/tempo/views/view_records/time_record.rb +5 -5
- data/lib/tempo.rb +3 -0
- data/lib/time_utilities.rb +4 -4
- data/tempo-cli.gemspec +8 -7
- data/test/lib/file_record/directory_test.rb +14 -1
- data/test/lib/file_record/record_test.rb +40 -75
- data/test/lib/tempo/models/base_test.rb +2 -2
- data/test/lib/tempo/models/composite_test.rb +9 -9
- data/test/lib/tempo/models/log_test.rb +31 -16
- data/test/lib/tempo/models/time_record_test.rb +29 -19
- data/test/support/factories.rb +5 -0
- data/test/support/helpers.rb +7 -7
- metadata +40 -53
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ce2d1ab18d47aece23c6034eb43e2c668fcb207c
|
4
|
+
data.tar.gz: 7a6d372bb3e12ef63730bed5cac7a30be19a73f8
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2706c79ae90158751b2059067e887d9ee65e672fcaeacf777b58a698a340dc8c1ecfeba11f730e7d1b56732c5056800c3644afaaa3ef5eeaf06d566cbfd26420
|
7
|
+
data.tar.gz: 3acd0dc7d1ee0d3f7a84439b2eb995d9f31ca81e325e012f493439ba2c4c0f58c489eedd61a508ff96575a478b8805aeae3a2deb83586bf44aedb348f7d782aa
|
data/Gemfile.lock
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
tempo-cli (0.1.
|
4
|
+
tempo-cli (0.1.6)
|
5
5
|
chronic (~> 0.10.2)
|
6
|
-
gli (
|
6
|
+
gli (~> 2.10.0)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
@@ -27,7 +27,7 @@ GEM
|
|
27
27
|
ffi (1.9.0)
|
28
28
|
gherkin (2.12.0)
|
29
29
|
multi_json (~> 1.3)
|
30
|
-
gli (2.
|
30
|
+
gli (2.10.0)
|
31
31
|
json (1.8.0)
|
32
32
|
method_source (0.8.1)
|
33
33
|
multi_json (1.7.7)
|
data/README.md
CHANGED
@@ -22,10 +22,16 @@ Future enhancements will include reports by project and time totals by day or by
|
|
22
22
|
|
23
23
|
### Records
|
24
24
|
|
25
|
-
All records are produced in YAML files
|
25
|
+
All records are produced in YAML files. These reports can be edited by hand, but keep in mind that any invalid data could cause problems when it is read back into the app. Make sure time formatting is valid and that they don't overlap, and that all ids are unique per page.
|
26
26
|
|
27
27
|
Each day's time records are designed to work independently. When adding or updating time records, only the records for the days in question are loaded into the app. The most recent day's records will also always be read in, to assure no running records are created earlier than existing records.
|
28
28
|
|
29
|
+
#### Alternate Directory for Records
|
30
|
+
|
31
|
+
By default tempo will create a directory `tempo` in the root level of the user directory, where all records are stored. You can use the global option --directory to supply an alternate directory within the user directory.
|
32
|
+
|
33
|
+
For example, to rout to a Dropbox folder to manage syncing records across computers, you would run `tempo --directory Dropbox <your command>`. This options can also be permanently set in a configuration file. If you run `tempo --directory Dropbox initconfig` the configuration file .tempo.yaml will be created, and all tempo commands will be routed to `/Users/username/Dropbox`.
|
34
|
+
|
29
35
|
### Features
|
30
36
|
|
31
37
|
#### command line assistance
|
@@ -315,7 +321,7 @@ run `bundle exec rake test` to run unit tests only
|
|
315
321
|
|
316
322
|
#### run only some cucumber features
|
317
323
|
|
318
|
-
add @focus before specific features and run `bundle exec rake features
|
324
|
+
add @focus before specific features and run `bundle exec rake features:focus`
|
319
325
|
|
320
326
|
#### cleanup
|
321
327
|
|
@@ -333,4 +339,4 @@ If you would prefer to keep your data in a database, or run independant time she
|
|
333
339
|
|
334
340
|
Another one worth checking out for a lightweight time tracker:
|
335
341
|
|
336
|
-
[t_time_tracker](https://github.com/christiangenco/t_time_tracker)
|
342
|
+
[t_time_tracker](https://github.com/christiangenco/t_time_tracker)
|
data/bin/tempo
CHANGED
@@ -5,9 +5,13 @@ require 'gli'
|
|
5
5
|
require 'tempo'
|
6
6
|
|
7
7
|
include GLI::App
|
8
|
+
|
8
9
|
# to preserve all the args in ARGV:
|
9
10
|
# preserve_argv
|
10
11
|
|
12
|
+
# used to store an alternate directory path:
|
13
|
+
config_file '.tempo.yaml'
|
14
|
+
|
11
15
|
program_desc 'Command line time tracking by project
|
12
16
|
|
13
17
|
For more help on any command, run: tempo [command] --help
|
@@ -25,6 +29,11 @@ desc 'output id'
|
|
25
29
|
long_desc 'include id reporting in the command output'
|
26
30
|
switch [:i, :id]
|
27
31
|
|
32
|
+
desc 'directory'
|
33
|
+
long_desc 'location of the tempo directory'
|
34
|
+
default_value ENV['HOME']
|
35
|
+
flag [:d,:directory]
|
36
|
+
|
28
37
|
# desc 'Describe some flag here'
|
29
38
|
# default_value 'the default'
|
30
39
|
# arg_name 'The name of the argument'
|
@@ -200,10 +209,14 @@ long_desc "Arrange projects into a parent/child hierarchy
|
|
200
209
|
|
201
210
|
New projects are added as root projects by default.
|
202
211
|
|
203
|
-
Use the --child flag with the project command to add a new project as the child of the current project
|
204
|
-
|
205
212
|
Use arrange to arrange existing projects as child or root projects.
|
206
213
|
|
214
|
+
If you want to add a new project that is a child of the existing project,
|
215
|
+
|
216
|
+
use the --child flag with the project command instead:
|
217
|
+
|
218
|
+
$ `tempo project --child 'my new project'
|
219
|
+
|
207
220
|
Arguments supplied will use fuzzy matching unless you:
|
208
221
|
|
209
222
|
- Use the --exact flag to match the arguments exactly
|
@@ -446,6 +459,11 @@ pre do |global,command,options,args|
|
|
446
459
|
|
447
460
|
Tempo::Views::initialize_view_options command.name, global, options
|
448
461
|
|
462
|
+
# pass a custom directory into options
|
463
|
+
if global[:directory] != ENV['HOME']
|
464
|
+
options[:directory] = File.join(ENV['HOME'], global[:directory])
|
465
|
+
end
|
466
|
+
|
449
467
|
# Load the stored records
|
450
468
|
Tempo::Controllers::Records.initialize_from_records options, args
|
451
469
|
|
data/features/arrange.feature
CHANGED
@@ -40,4 +40,4 @@ Feature: Arrange Command manages the hierarchy of a list of projects
|
|
40
40
|
Then the stdout should contain "parent project:\nreading aquaculture digest"
|
41
41
|
And the stdout should contain "child project:\naquaculture"
|
42
42
|
And the project file should contain ":parent: 6" at line 29
|
43
|
-
And the project file should contain "- 4" at line 47
|
43
|
+
And the project file should contain "- 4" at line 47
|
@@ -0,0 +1,108 @@
|
|
1
|
+
Feature: Global Directory Command allows for an alternate directory location
|
2
|
+
The default directory is located in the home directory
|
3
|
+
Adding a directory arguement will run all commands to that subdirectory within the home directory
|
4
|
+
Features that save to a file will save to the new sub-directory
|
5
|
+
|
6
|
+
Scenario: Adding the first project in an alternate directory creates the subdirectory and file
|
7
|
+
Given a clean installation
|
8
|
+
When I successfully run `tempo --directory alt_dir project horticulture`
|
9
|
+
Then the alternate directory project file should contain ":title: horticulture" at line 5
|
10
|
+
And the alternate directory project file should contain "current" at line 7
|
11
|
+
|
12
|
+
Scenario: Deleting a project by full match in an alternate directory
|
13
|
+
Given an alternate directory and an existing project file
|
14
|
+
When I successfully run `tempo --directory alt_dir project --delete "backyard bonsai"`
|
15
|
+
Then the stdout should contain "deleted project:\nbackyard bonsai"
|
16
|
+
And the alternate directory project file should not contain ":title: backyard bonsai"
|
17
|
+
|
18
|
+
Scenario: Tagging a project with a tag in an alternate directory
|
19
|
+
Given an alternate directory and an existing project file
|
20
|
+
When I successfully run `tempo --directory alt_dir project backyard bonsai -t patience`
|
21
|
+
Then the stdout should contain "backyard bonsai"
|
22
|
+
And the stdout should contain "tags: [miniaturization, outdoors, patience]"
|
23
|
+
And the alternate directory project file should contain "- patience"
|
24
|
+
|
25
|
+
Scenario: Arranging a project as a root projects in an alternate directory
|
26
|
+
Given an alternate directory and an existing project file
|
27
|
+
When I successfully run `tempo --directory alt_dir arrange : basement mushrooms`
|
28
|
+
Then the stdout should contain "root project:\nbasement mushrooms"
|
29
|
+
And the alternate directory project file should contain ":parent: :root" at line 20
|
30
|
+
|
31
|
+
Scenario: Arranging a project as a child of another project in an alternate directory
|
32
|
+
Given an alternate directory and an existing project file
|
33
|
+
When I successfully run `tempo --directory alt_dir arrange horticulture : nano aquarium`
|
34
|
+
Then the stdout should contain "parent project:\nhorticulture"
|
35
|
+
And the stdout should contain "child project:\nnano aquarium"
|
36
|
+
And the alternate directory project file should contain ":parent: 1" at line 39
|
37
|
+
And the alternate directory project file should contain "- 5" at line 7
|
38
|
+
|
39
|
+
Scenario: Checkout an existing project with checkout in an alternate directory
|
40
|
+
Given an alternate directory and an existing project file
|
41
|
+
When I successfully run `tempo --directory alt_dir checkout "backyard bonsai"`
|
42
|
+
Then the stdout should contain "switched to project:\nbackyard bonsai"
|
43
|
+
And the alternate directory project file should contain ":current: true" at line 18
|
44
|
+
|
45
|
+
Scenario: Adding and checking out a new project in an alternate directory
|
46
|
+
Given an alternate directory and an existing project file
|
47
|
+
When I successfully run `tempo --directory alt_dir checkout --add "bathtup scuba diving"`
|
48
|
+
Then the stdout should contain "switched to project:\nbathtup scuba diving"
|
49
|
+
And the alternate directory project file should contain ":title: bathtup scuba diving"
|
50
|
+
And the alternate directory project file should contain ":current: true"
|
51
|
+
|
52
|
+
Scenario: Starting a time record in an alternate directory
|
53
|
+
Given an alternate directory and an existing project file
|
54
|
+
When I run `tempo --directory alt_dir start --at "1-1-2014 7:00" filling the bathtub`
|
55
|
+
Then the stdout should contain "time record started"
|
56
|
+
And the alternate directory time record 20140101 should contain ":description: filling the bathtub" at line 3
|
57
|
+
And the alternate directory time record 20140101 should contain ":start_time: 2014-01-01 07:00:00" at line 4
|
58
|
+
|
59
|
+
Scenario: Ending a time record in an alternate directory
|
60
|
+
Given an alternate directory and an existing project file
|
61
|
+
When I successfully run `tempo --directory alt_dir start --at "1-1-2014 7:00" filling the bathtub`
|
62
|
+
And I successfully run `tempo --directory alt_dir end --at "1-1-2014 8:00"`
|
63
|
+
Then the stdout should contain "time record ended"
|
64
|
+
And the alternate directory time record 20140101 should contain ":end_time: 2014-01-01 08:00:00" at line 5
|
65
|
+
|
66
|
+
Scenario: Reporting the time entries on the current day stored in an alternate directory
|
67
|
+
Given an alternate directory and an existing project file
|
68
|
+
When I run `tempo --directory alt_dir start --at 7 alt directory new entry`
|
69
|
+
And I run `tempo --directory alt_dir end --at 8`
|
70
|
+
And I successfully run `tempo --directory alt_dir report`
|
71
|
+
Then the output should match /Records for/
|
72
|
+
|
73
|
+
Scenario: Reporting the time entries for multipe days stored in an alternate directory
|
74
|
+
Given an alternate directory and an existing project file
|
75
|
+
When I run `tempo --directory alt_dir start --at "2014-01-01" alt directory new entry`
|
76
|
+
When I run `tempo --directory alt_dir start --at "2014-01-02" alt directory newer entry`
|
77
|
+
And I successfully run `tempo --directory alt_dir report --from "2014-01-01" --to "2014-01-02"`
|
78
|
+
Then the output should contain "Records for 01/01/2014:"
|
79
|
+
And the output should contain "Records for 01/02/2014:"
|
80
|
+
|
81
|
+
Scenario: Reporting the time entries on a specific day stored in an alternate directory
|
82
|
+
Given an alternate directory and an existing project file
|
83
|
+
When I run `tempo --directory alt_dir start --at "2014-01-01" alt directory new entry`
|
84
|
+
And I successfully run `tempo --directory alt_dir report "2014-01-01"`
|
85
|
+
Then the output should contain "Records for 01/01/2014:"
|
86
|
+
|
87
|
+
Scenario: Deleting the last record stored in an alternate directory
|
88
|
+
Given an alternate directory and an existing project file
|
89
|
+
When I run `tempo --directory alt_dir start --at "2014-01-01" alt directory new entry`
|
90
|
+
And I successfully run `tempo --directory alt_dir update --delete`
|
91
|
+
Then the output should contain "time record deleted:"
|
92
|
+
And the alternate directory time record 20140101 should not exist
|
93
|
+
|
94
|
+
Scenario: Updating the last time record stored in an alternate directory
|
95
|
+
Given an alternate directory and an existing project file
|
96
|
+
When I run `tempo --directory alt_dir start --at "2014-01-01" alt directory new entry`
|
97
|
+
And I successfully run `tempo --directory alt_dir update anemone feeding`
|
98
|
+
Then the output should contain "time record updated:"
|
99
|
+
And the alternate directory time record 20140101 should contain ":description: anemone feeding" at line 3
|
100
|
+
|
101
|
+
Scenario: Updating a time record on an earlier day in an alternate directory
|
102
|
+
Given an alternate directory and an existing project file
|
103
|
+
When I run `tempo --directory alt_dir start --at "12/1/2014 7:00" --end "12/1/2014 8:00" raking leaves`
|
104
|
+
And I run `tempo --directory alt_dir start --at "12/2/2014 8:00" --end "12/1/2014 9:00" counting rosebuds`
|
105
|
+
When I successfully run `tempo --directory alt_dir update --on "12/1/2014" feeding flytraps`
|
106
|
+
Then the output should contain "time record updated:\n07:00 - 08:00 [1:00] horticulture: feeding flytraps"
|
107
|
+
And the alternate directory time record 20141201 should contain ":description: feeding flytraps" at line 3
|
108
|
+
|
data/features/project.feature
CHANGED
@@ -3,13 +3,13 @@ Feature: Project Command manages a list of projects
|
|
3
3
|
New projects can be added and deleted
|
4
4
|
Projects can also be tagged as inactive or inactive
|
5
5
|
|
6
|
-
Scenario: Listing
|
6
|
+
Scenario: Listing the current project before any projects exist
|
7
7
|
Given a clean installation
|
8
8
|
When I run `tempo project`
|
9
9
|
Then the stdout should contain "no projects exist"
|
10
10
|
And the project file should contain "#" at line 1
|
11
11
|
|
12
|
-
Scenario: Listing projects before any projects exist
|
12
|
+
Scenario: Listing all projects before any projects exist
|
13
13
|
Given a clean installation
|
14
14
|
When I run `tempo project --list`
|
15
15
|
Then the stdout should contain "no projects exist"
|
data/features/report.feature
CHANGED
@@ -16,8 +16,9 @@ Feature: Report Command formats and outputs time records
|
|
16
16
|
Given an existing project file
|
17
17
|
When I run `tempo start -a 7 my new project`
|
18
18
|
And I run `tempo end -a 8`
|
19
|
-
And I run `tempo report`
|
20
|
-
Then the output should match
|
19
|
+
And I successfully run `tempo report`
|
20
|
+
Then the output should match /Records for/
|
21
|
+
And the output should match /\d{2}:\d{2} - \d{2}:\d{2} \[\d{1,2}:\d{2}\] horticulture: my new project/
|
21
22
|
|
22
23
|
Scenario: Reporting the time entries on a specific day
|
23
24
|
Given an existing project file
|
data/features/start.feature
CHANGED
@@ -62,7 +62,6 @@ Feature: Start Command starts a new time record
|
|
62
62
|
Then the stdout should contain "time record started"
|
63
63
|
And the time record 20140101 should contain ":end_time: 2014-01-01 23:59" at line 5
|
64
64
|
|
65
|
-
|
66
65
|
Scenario: Adding an earlier time record should immediately close out
|
67
66
|
Given an existing project file
|
68
67
|
When I run `tempo start --at "1-1-2014 10:00"`
|
@@ -4,17 +4,41 @@ When /^I get help for "([^""]*)"$/ do |app_name|
|
|
4
4
|
end
|
5
5
|
|
6
6
|
Given /^a clean installation$/ do
|
7
|
-
@testing_env = File.join(
|
8
|
-
FileUtils.rm_r(
|
7
|
+
@testing_env = File.join(ENV['HOME'], 'tempo')
|
8
|
+
FileUtils.rm_r(@testing_env) if File.exists?(@testing_env)
|
9
|
+
|
10
|
+
# Testing global --directory argument in /alt_dir
|
11
|
+
@alt_dir = File.join(ENV['HOME'], 'alt_dir')
|
12
|
+
FileUtils.rm_r(@alt_dir) if File.exists?(@alt_dir)
|
9
13
|
end
|
10
14
|
|
11
15
|
Given /^an existing project file$/ do
|
12
|
-
@testing_env = File.join(
|
13
|
-
FileUtils.rm_r(
|
14
|
-
Dir.mkdir(
|
15
|
-
projects_file = File.join(
|
16
|
+
@testing_env = File.join(ENV['HOME'], 'tempo')
|
17
|
+
FileUtils.rm_r(@testing_env) if File.exists?(@testing_env)
|
18
|
+
Dir.mkdir(@testing_env, 0700)
|
19
|
+
projects_file = File.join(@testing_env, 'tempo_projects.yaml')
|
20
|
+
|
21
|
+
File.open(projects_file,'w') do |f|
|
22
|
+
projects = ["---", ":id: 1", ":parent: :root", ":children:", "- 2", "- 3", ":title: horticulture", ":tags:", "- cultivation", ":current: true",
|
23
|
+
"---", ":id: 2", ":parent: 1", ":children: []", ":title: backyard bonsai", ":tags:", "- miniaturization", "- outdoors",
|
24
|
+
"---", ":id: 3", ":parent: 1", ":children: []", ":title: basement mushrooms", ":tags:", "- fungi", "- indoors",
|
25
|
+
"---", ":id: 4", ":parent: :root", ":children:", "- 5", "- 6", ":title: aquaculture", ":tags:", "- cultivation",
|
26
|
+
"---", ":id: 5", ":parent: 4", ":children: []", ":title: nano aquarium", ":tags:", "- miniaturization",
|
27
|
+
"---", ":id: 6", ":parent: 4", ":children: []", ":title: reading aquaculture digest", ":tags: []"]
|
28
|
+
|
29
|
+
projects.each do |p|
|
30
|
+
f.puts p
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
16
34
|
|
17
|
-
|
35
|
+
Given /^an alternate directory and an existing project file$/ do
|
36
|
+
@testing_env = File.join(ENV['HOME'], 'alt_dir', 'tempo')
|
37
|
+
FileUtils.rm_r(@testing_env) if File.exists?(@testing_env)
|
38
|
+
FileUtils.mkdir_p(@testing_env)
|
39
|
+
projects_file = File.join(@testing_env, 'tempo_projects.yaml')
|
40
|
+
|
41
|
+
File.open(projects_file,'w') do |f|
|
18
42
|
projects = ["---", ":id: 1", ":parent: :root", ":children:", "- 2", "- 3", ":title: horticulture", ":tags:", "- cultivation", ":current: true",
|
19
43
|
"---", ":id: 2", ":parent: 1", ":children: []", ":title: backyard bonsai", ":tags:", "- miniaturization", "- outdoors",
|
20
44
|
"---", ":id: 3", ":parent: 1", ":children: []", ":title: basement mushrooms", ":tags:", "- fungi", "- indoors",
|
@@ -29,12 +53,12 @@ Given /^an existing project file$/ do
|
|
29
53
|
end
|
30
54
|
|
31
55
|
Given /^an existing time record file$/ do
|
32
|
-
@records_directory = File.join(
|
33
|
-
FileUtils.rm_r(
|
34
|
-
Dir.mkdir(
|
35
|
-
projects_file = File.join(
|
56
|
+
@records_directory = File.join(ENV['HOME'], 'tempo/tempo_time_records')
|
57
|
+
FileUtils.rm_r(@records_directory) if File.exists?(@records_directory)
|
58
|
+
Dir.mkdir(@records_directory, 0700)
|
59
|
+
projects_file = File.join(@records_directory, '20140101.yaml')
|
36
60
|
|
37
|
-
File.open(
|
61
|
+
File.open(projects_file,'w') do |f|
|
38
62
|
records = [ ":description: putting on overalls and straw hat",
|
39
63
|
":start_time: 2014-01-01 05:00:00.000000000 -05:00",
|
40
64
|
":end_time: 2014-01-01 05:15:00.000000000 -05:00",
|
@@ -83,7 +107,7 @@ end
|
|
83
107
|
|
84
108
|
|
85
109
|
Then /^the time record (.*?) should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
86
|
-
file = File.join(
|
110
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
87
111
|
contents = []
|
88
112
|
File.open(file, "r") do |f|
|
89
113
|
f.readlines.each do |line|
|
@@ -94,7 +118,7 @@ Then /^the time record (.*?) should contain "(.*?)" at line (\d+)$/ do |arg1, ar
|
|
94
118
|
end
|
95
119
|
|
96
120
|
Then /^the time record (.*?) should not contain "(.*?)"$/ do |arg1, arg2|
|
97
|
-
file = File.join(
|
121
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
98
122
|
contents = []
|
99
123
|
File.open(file, "r") do |f|
|
100
124
|
f.readlines.each do |line|
|
@@ -104,8 +128,13 @@ Then /^the time record (.*?) should not contain "(.*?)"$/ do |arg1, arg2|
|
|
104
128
|
contents.should_not include arg2
|
105
129
|
end
|
106
130
|
|
107
|
-
Then /^the
|
108
|
-
file = File.join(
|
131
|
+
Then /^the time record (.*?) should not exist$/ do |arg1|
|
132
|
+
file = File.join(ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
|
133
|
+
File.exists?(file).should == false
|
134
|
+
end
|
135
|
+
|
136
|
+
Then /^the (\S*) file should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
137
|
+
file = File.join(ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
|
109
138
|
contents = []
|
110
139
|
File.open(file, "r") do |f|
|
111
140
|
f.readlines.each do |line|
|
@@ -115,8 +144,30 @@ Then /^the (.*?) file should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg
|
|
115
144
|
contents[arg3.to_i - 1].should include arg2
|
116
145
|
end
|
117
146
|
|
118
|
-
Then /^the (
|
119
|
-
file = File.join(
|
147
|
+
Then /^the (\S*) file should contain "(.*?)"$/ do |arg1, arg2|
|
148
|
+
file = File.join(ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
|
149
|
+
contents = []
|
150
|
+
File.open(file, "r") do |f|
|
151
|
+
f.readlines.each do |line|
|
152
|
+
contents << line.chomp
|
153
|
+
end
|
154
|
+
end
|
155
|
+
contents.should include arg2
|
156
|
+
end
|
157
|
+
|
158
|
+
Then /^the (\S*) file should not contain "(.*?)"$/ do |arg1, arg2|
|
159
|
+
file = File.join(ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
|
160
|
+
contents = []
|
161
|
+
File.open(file, "r") do |f|
|
162
|
+
f.readlines.each do |line|
|
163
|
+
contents << line.chomp
|
164
|
+
end
|
165
|
+
end
|
166
|
+
contents.should_not include arg2
|
167
|
+
end
|
168
|
+
|
169
|
+
Then /^the alternate directory (.*?) file should contain "(.*?)"$/ do |arg1, arg2|
|
170
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', "tempo_#{arg1}s.yaml")
|
120
171
|
contents = []
|
121
172
|
File.open(file, "r") do |f|
|
122
173
|
f.readlines.each do |line|
|
@@ -126,8 +177,19 @@ Then /^the (.*?) file should contain "(.*?)"$/ do |arg1, arg2|
|
|
126
177
|
contents.should include arg2
|
127
178
|
end
|
128
179
|
|
129
|
-
Then /^the (.*?) file should
|
130
|
-
file = File.join(
|
180
|
+
Then /^the alternate directory (.*?) file should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
181
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', "tempo_#{arg1}s.yaml")
|
182
|
+
contents = []
|
183
|
+
File.open(file, "r") do |f|
|
184
|
+
f.readlines.each do |line|
|
185
|
+
contents << line.chomp
|
186
|
+
end
|
187
|
+
end
|
188
|
+
contents[arg3.to_i - 1].should include arg2
|
189
|
+
end
|
190
|
+
|
191
|
+
Then /^the alternate directory (.*?) file should not contain "(.*?)"$/ do |arg1, arg2|
|
192
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', "tempo_#{arg1}s.yaml")
|
131
193
|
contents = []
|
132
194
|
File.open(file, "r") do |f|
|
133
195
|
f.readlines.each do |line|
|
@@ -136,3 +198,20 @@ Then /^the (.*?) file should not contain "(.*?)"$/ do |arg1, arg2|
|
|
136
198
|
end
|
137
199
|
contents.should_not include arg2
|
138
200
|
end
|
201
|
+
|
202
|
+
Then /^the alternate directory time record (\d+) should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
|
203
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo', 'tempo_time_records', "#{arg1}.yaml")
|
204
|
+
contents = []
|
205
|
+
File.open(file, "r") do |f|
|
206
|
+
f.readlines.each do |line|
|
207
|
+
contents << line.chomp
|
208
|
+
end
|
209
|
+
end
|
210
|
+
contents[arg3.to_i - 1].should include arg2
|
211
|
+
end
|
212
|
+
|
213
|
+
Then /^the alternate directory time record (.*?) should not exist$/ do |arg1|
|
214
|
+
file = File.join(ENV['HOME'], 'alt_dir', 'tempo/tempo_time_records', "#{arg1}.yaml")
|
215
|
+
File.exists?(file).should == false
|
216
|
+
end
|
217
|
+
|
data/features/update.feature
CHANGED
@@ -19,7 +19,13 @@ Feature: Update Command manages edits to the time records
|
|
19
19
|
And an existing time record file
|
20
20
|
When I successfully run `tempo update --delete`
|
21
21
|
Then the stdout should contain "time record deleted:\n13:32 - 16:46 [3:14] nano aquarium: trimming the coral"
|
22
|
-
And the time record 20140101 should not contain "nano aquarium"
|
22
|
+
And the time record 20140101 should not contain ":project_title: nano aquarium"
|
23
|
+
|
24
|
+
Scenario: Deleting the only record on a day
|
25
|
+
Given an existing project file
|
26
|
+
When I run `tempo start --at "12/1/2014 7:00" --end "12/1/2014 8:00" raking leaves`
|
27
|
+
And I successfully run `tempo update --on "12/1/2014" --delete`
|
28
|
+
Then the time record 20141201 should not exist
|
23
29
|
|
24
30
|
Scenario: Updating the description for the last time record
|
25
31
|
Given an existing project file
|
@@ -1,11 +1,27 @@
|
|
1
1
|
module FileRecord
|
2
2
|
class Directory
|
3
|
+
|
3
4
|
class << self
|
4
|
-
|
5
|
+
|
6
|
+
|
7
|
+
# Create a new directory structure, copying the contents from
|
8
|
+
# directory_structure/tempo into ~/tempo.
|
9
|
+
# If a directory is passed in through the options, the tempo directory
|
10
|
+
# will be created within that directory instead, in the users home folder
|
11
|
+
#
|
12
|
+
# ex. create new(directory: "custom/directory")
|
13
|
+
# => Users/usrname/custom/directory/tempo
|
14
|
+
|
15
|
+
def create_new(options={})
|
16
|
+
|
17
|
+
directory = options.fetch( :directory, Dir.home )
|
5
18
|
cwd = File.expand_path File.dirname(__FILE__)
|
6
19
|
source = File.join(cwd, "directory_structure/tempo")
|
7
|
-
|
20
|
+
if ! Dir.exists? directory
|
21
|
+
FileUtils.mkdir_p directory
|
22
|
+
end
|
23
|
+
FileUtils.cp_r source, directory
|
8
24
|
end
|
9
25
|
end
|
10
26
|
end
|
11
|
-
end
|
27
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Load these file utility functions before the FileRecord::Record class
|
2
|
+
# These functions manage the creation of storage directory and file names,
|
3
|
+
# and check for existing directories
|
4
|
+
|
5
|
+
# Base models are saved into a directory named after the parent Module
|
6
|
+
# example Tempo::Model::Project class instances are saved into:
|
7
|
+
# Users/usrname/tempo/tempo_projects.yaml
|
8
|
+
# if a directory is passed into the initializer, they will be saved into an
|
9
|
+
# alternate directory:
|
10
|
+
# FileRecord::Utility.new directory: "custom/directory"
|
11
|
+
# project file: Users/usrname/custom/directory/tempo/tempo_projects.yaml
|
12
|
+
|
13
|
+
# Logs are saved into a subdirectory containing time stamped files
|
14
|
+
# pass time into options on init
|
15
|
+
# example Tempo::Model::Logs class instances are saved into:
|
16
|
+
# Users/usrname/tempo/tempo_logs/
|
17
|
+
# The files are given filenames based on the Logs day id (11/12/2014 -> '20141112.yaml')
|
18
|
+
# See Tempo::Model::Logs for more information
|
19
|
+
|
20
|
+
module FileRecord
|
21
|
+
class FileUtility
|
22
|
+
|
23
|
+
def initialize(model, options={})
|
24
|
+
@model = model
|
25
|
+
@time = options.fetch(:time, nil)
|
26
|
+
@directory = options.fetch(:directory, Dir.home)
|
27
|
+
|
28
|
+
# options to allow for file creation and destruction,
|
29
|
+
# default to false so that file path enquiries can't
|
30
|
+
# change the directory structure
|
31
|
+
@create = options.fetch( :create, false )
|
32
|
+
@destroy = options.fetch( :destroy, false )
|
33
|
+
end
|
34
|
+
|
35
|
+
def save_instances_to_file(instances)
|
36
|
+
|
37
|
+
File.open( file_path,'a' ) do |f|
|
38
|
+
instances.each do |i|
|
39
|
+
f.puts YAML::dump( i.freeze_dry )
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# split Tempo::Model::Project into ["tempo", "model", "project"]
|
45
|
+
# split Tempo::Model::TimeRecord into ["tempo", "model", "time_record"]
|
46
|
+
def split_name
|
47
|
+
@model.name.to_s.split("::").each {|n| n.gsub!(/([a-z])([A-Z])/, '\1_\2'); n.downcase!}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Tempo::Model::Project -> "tempo"
|
51
|
+
def module_name
|
52
|
+
split_name[0]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Tempo::Model::Project -> "project"
|
56
|
+
def model_name
|
57
|
+
split_name[-1]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Tempo::Model::Log on 12/1/2015 -> 20151201.yaml
|
61
|
+
# Tempo::Model::Base -> tempo_bases.yaml
|
62
|
+
def filename
|
63
|
+
# return Log file name
|
64
|
+
return "#{@model.day_id( @time )}.yaml" if @time
|
65
|
+
|
66
|
+
sn = split_name
|
67
|
+
file = "#{sn[0]}_#{sn[-1]}s.yaml"
|
68
|
+
end
|
69
|
+
|
70
|
+
# ex. Tempo::Model::Log -> tempo_logs
|
71
|
+
def log_directory
|
72
|
+
sn = split_name
|
73
|
+
"#{sn[0]}_#{sn[-1]}s"
|
74
|
+
end
|
75
|
+
|
76
|
+
# Tempo::Model::Log -> Users/usrname/(alternate_directory/)tempo/tempo_logs/
|
77
|
+
# Will also create the directory if not found
|
78
|
+
# This method does not require time to be present in options
|
79
|
+
def log_directory_path
|
80
|
+
dir = File.join(@directory, module_name, log_directory)
|
81
|
+
|
82
|
+
if @create and !File.exists?(dir)
|
83
|
+
Dir.mkdir(dir, 0700)
|
84
|
+
end
|
85
|
+
|
86
|
+
dir
|
87
|
+
end
|
88
|
+
|
89
|
+
# returns full path and file for model
|
90
|
+
# Tempo::Model::Log on 11/12/2014 -> Users/usrname/tempo/tempo_logs/20141112.yaml
|
91
|
+
# Tempo::Model::Base -> Users/usrname/tempo/tempo_bases.yaml
|
92
|
+
# Will also create directory if not found and passed create:true in options
|
93
|
+
# Will destroy file if passed destroy:true in options
|
94
|
+
def file_path
|
95
|
+
|
96
|
+
return clean_path(File.join(log_directory_path, filename)) if @time
|
97
|
+
|
98
|
+
dir = File.join(@directory, module_name)
|
99
|
+
|
100
|
+
if @create and !File.exists?(dir)
|
101
|
+
Dir.mkdir(dir, 0700)
|
102
|
+
end
|
103
|
+
|
104
|
+
clean_path File.join(dir, filename)
|
105
|
+
end
|
106
|
+
|
107
|
+
# Returns the list of log records from a log directory
|
108
|
+
def log_records
|
109
|
+
Dir[log_directory_path + "/*.yaml"].sort!
|
110
|
+
end
|
111
|
+
|
112
|
+
# remove existing file when passed destroy:true in options
|
113
|
+
def clean_path(file_path)
|
114
|
+
|
115
|
+
if @destroy and File.exists?(file_path)
|
116
|
+
File.delete(file_path)
|
117
|
+
end
|
118
|
+
|
119
|
+
file_path
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|