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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile.lock +3 -3
  3. data/README.md +9 -3
  4. data/bin/tempo +20 -2
  5. data/features/arrange.feature +1 -1
  6. data/features/directory.feature +108 -0
  7. data/features/project.feature +2 -2
  8. data/features/report.feature +3 -2
  9. data/features/start.feature +0 -1
  10. data/features/step_definitions/tempo_steps.rb +99 -20
  11. data/features/update.feature +7 -1
  12. data/lib/file_record/directory.rb +19 -3
  13. data/lib/file_record/file_utility.rb +122 -0
  14. data/lib/file_record/record.rb +36 -83
  15. data/lib/tempo/controllers/arrange_controller.rb +5 -5
  16. data/lib/tempo/controllers/base.rb +8 -8
  17. data/lib/tempo/controllers/checkout_controller.rb +4 -4
  18. data/lib/tempo/controllers/end_controller.rb +4 -5
  19. data/lib/tempo/controllers/projects_controller.rb +13 -10
  20. data/lib/tempo/controllers/records_controller.rb +8 -5
  21. data/lib/tempo/controllers/report_controller.rb +4 -4
  22. data/lib/tempo/controllers/start_controller.rb +4 -3
  23. data/lib/tempo/controllers/update_controller.rb +22 -8
  24. data/lib/tempo/exceptions.rb +2 -2
  25. data/lib/tempo/models/base.rb +26 -18
  26. data/lib/tempo/models/composite.rb +5 -3
  27. data/lib/tempo/models/log.rb +61 -38
  28. data/lib/tempo/models/project.rb +9 -6
  29. data/lib/tempo/models/time_record.rb +14 -14
  30. data/lib/tempo/version.rb +1 -1
  31. data/lib/tempo/views/arrange_view.rb +4 -4
  32. data/lib/tempo/views/base.rb +10 -23
  33. data/lib/tempo/views/formatters/base.rb +2 -2
  34. data/lib/tempo/views/formatters/screen.rb +7 -7
  35. data/lib/tempo/views/projects_view.rb +8 -8
  36. data/lib/tempo/views/report_view.rb +5 -5
  37. data/lib/tempo/views/reporter.rb +4 -4
  38. data/lib/tempo/views/time_record_view.rb +5 -5
  39. data/lib/tempo/views/view_records/base.rb +8 -8
  40. data/lib/tempo/views/view_records/composite.rb +4 -4
  41. data/lib/tempo/views/view_records/log.rb +3 -3
  42. data/lib/tempo/views/view_records/project.rb +3 -3
  43. data/lib/tempo/views/view_records/time_record.rb +5 -5
  44. data/lib/tempo.rb +3 -0
  45. data/lib/time_utilities.rb +4 -4
  46. data/tempo-cli.gemspec +8 -7
  47. data/test/lib/file_record/directory_test.rb +14 -1
  48. data/test/lib/file_record/record_test.rb +40 -75
  49. data/test/lib/tempo/models/base_test.rb +2 -2
  50. data/test/lib/tempo/models/composite_test.rb +9 -9
  51. data/test/lib/tempo/models/log_test.rb +31 -16
  52. data/test/lib/tempo/models/time_record_test.rb +29 -19
  53. data/test/support/factories.rb +5 -0
  54. data/test/support/helpers.rb +7 -7
  55. 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.3)
4
+ tempo-cli (0.1.6)
5
5
  chronic (~> 0.10.2)
6
- gli (= 2.6.1)
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.6.1)
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, at the root user directory. 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.
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 focus`
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
 
@@ -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
+
@@ -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 projects before any projects exist
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"
@@ -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 /\d{2}:\d{2} - \d{2}:\d{2} \[\d{1,2}:\d{2}\] horticulture: my new project/
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
@@ -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( ENV['HOME'], 'tempo' )
8
- FileUtils.rm_r( @testing_env ) if File.exists?( @testing_env )
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( ENV['HOME'], 'tempo' )
13
- FileUtils.rm_r( @testing_env ) if File.exists?( @testing_env )
14
- Dir.mkdir( @testing_env, 0700 )
15
- projects_file = File.join( @testing_env, 'tempo_projects.yaml' )
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
- File.open( projects_file,'w' ) do |f|
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( ENV['HOME'], 'tempo/tempo_time_records' )
33
- FileUtils.rm_r( @records_directory ) if File.exists?( @records_directory )
34
- Dir.mkdir( @records_directory, 0700 )
35
- projects_file = File.join( @records_directory, '20140101.yaml' )
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( projects_file,'w' ) do |f|
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( ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
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( ENV['HOME'], 'tempo/tempo_time_records', "#{arg1}.yaml")
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 (.*?) file should contain "(.*?)" at line (\d+)$/ do |arg1, arg2, arg3|
108
- file = File.join( ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
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 (.*?) file should contain "(.*?)"$/ do |arg1, arg2|
119
- file = File.join( ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
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 not contain "(.*?)"$/ do |arg1, arg2|
130
- file = File.join( ENV['HOME'], 'tempo', "tempo_#{arg1}s.yaml")
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
+
@@ -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
- def create_new
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
- FileUtils.cp_r( source, Dir.home )
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