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
@@ -1,3 +1,12 @@
1
+ require 'yaml'
2
+
3
+ # The Base model class, from which all other models are derived.
4
+ # Models are given a unique id, which is stored in the class index.
5
+ # FileRecord handles all file storage and retrieval. Options are passed
6
+ # through to FileRecord for the purpose of sending in an alternative directory.
7
+ # example: options = {directory: "my_directory"} will save to Users/username/my_directory.
8
+ #
9
+
1
10
  module Tempo
2
11
  module Model
3
12
 
@@ -29,18 +38,20 @@ module Tempo
29
38
 
30
39
  # example: Tempo::Model::Animal -> tempo_animals.yaml
31
40
  def file
32
- FileRecord::Record.model_filename self
41
+ FileRecord::FileUtility.new(self).filename
33
42
  end
34
43
 
35
- def save_to_file
36
- FileRecord::Record.save_model self
44
+ # pass custom directory through in options
45
+ def save_to_file(options={})
46
+ FileRecord::Record.save_model self, options
37
47
  end
38
48
 
39
- def read_from_file
40
- FileRecord::Record.read_model self
49
+ # pass custom directory through in options
50
+ def read_from_file(options={})
51
+ FileRecord::Record.read_model self, options
41
52
  end
42
53
 
43
- def method_missing meth, *args, &block
54
+ def method_missing(meth, *args, &block)
44
55
 
45
56
  if meth.to_s =~ /^find_by_(.+)$/
46
57
  run_find_by_method($1, *args, &block)
@@ -52,14 +63,14 @@ module Tempo
52
63
  end
53
64
  end
54
65
 
55
- def run_sort_by_method attribute, args=@index.clone, &block
66
+ def run_sort_by_method(attribute, args=@index.clone, &block)
56
67
  attr = "@#{attribute}".to_sym
57
68
  args.sort! { |a,b| a.instance_variable_get( attr ) <=> b.instance_variable_get( attr ) }
58
69
  return args unless block
59
70
  block.call args
60
71
  end
61
72
 
62
- def run_find_by_method attrs, *args, &block
73
+ def run_find_by_method(attrs, *args, &block)
63
74
  # Make an array of attribute names
64
75
  attrs = attrs.split('_and_')
65
76
 
@@ -79,14 +90,14 @@ module Tempo
79
90
  end
80
91
 
81
92
  # find by id should be exact, so we remove the array wrapper
82
- def find_by_id id
93
+ def find_by_id(id)
83
94
  matches = find "id", id
84
95
  match = matches[0]
85
96
  end
86
97
 
87
98
  # example: Tempo::Model.find("id", 1)
88
99
  #
89
- def find key, value
100
+ def find(key, value)
90
101
  key = "@#{key}".to_sym
91
102
  matches = []
92
103
  index.each do |i|
@@ -106,14 +117,14 @@ module Tempo
106
117
  matches
107
118
  end
108
119
 
109
- def delete instance
120
+ def delete(instance)
110
121
  id = instance.id
111
122
  index.delete( instance )
112
123
  ids.delete( id )
113
124
  end
114
- end
125
+ end # class methods
115
126
 
116
- def initialize options={}
127
+ def initialize(options={})
117
128
  id_candidate = options[:id]
118
129
  if !id_candidate
119
130
  @id = self.class.next_id
@@ -133,9 +144,6 @@ module Tempo
133
144
  state.each do |attr|
134
145
  key = attr[1..-1].to_sym
135
146
  val = instance_variable_get attr
136
-
137
- #val = val.to_s if val.kind_of? Time
138
-
139
147
  record[key] = val
140
148
  end
141
149
  record
@@ -147,13 +155,13 @@ module Tempo
147
155
 
148
156
  protected
149
157
 
150
- def self.add_to_index member
158
+ def self.add_to_index(member)
151
159
  @index ||= []
152
160
  @index << member
153
161
  @index.sort! { |a,b| a.id <=> b.id }
154
162
  end
155
163
 
156
- def self.add_id id
164
+ def self.add_id(id)
157
165
  @ids ||=[]
158
166
  @ids << id
159
167
  @ids.sort!
@@ -1,8 +1,10 @@
1
+ require 'yaml'
2
+
1
3
  # Composite Model extends base to accomodate tree structures
2
4
  # Each instance can be a root instance, or a child of another
3
5
  # instance, and each instance can have any number of children.
4
6
  # report_trees is a utility method for testing the validity of the
5
- # model, and cam be used as a template for creating tree reports.
7
+ # model, and can be used as a template for creating tree reports.
6
8
 
7
9
  module Tempo
8
10
  module Model
@@ -26,7 +28,7 @@ module Tempo
26
28
  report_array += "]"
27
29
  end
28
30
 
29
- def delete instance
31
+ def delete(instance)
30
32
  instance.children.each do |child_id|
31
33
  child = find_by_id child_id
32
34
  instance.remove_child child
@@ -47,7 +49,7 @@ module Tempo
47
49
  child.parent = self.id
48
50
  end
49
51
 
50
- def remove_child( child )
52
+ def remove_child(child)
51
53
  @children.delete child.id
52
54
  child.parent = :root
53
55
  end
@@ -1,3 +1,9 @@
1
+ require 'yaml'
2
+
3
+ # Log extends base by allowing models to be associated with a time instance.
4
+ # Ids are only unique by day, and each model also has a day id. Together these
5
+ # two ids assure uniquness. When saved, each day is recorded as it's own file.
6
+
1
7
  module Tempo
2
8
  module Model
3
9
  class Log < Tempo::Model::Base
@@ -12,13 +18,14 @@ module Tempo
12
18
  # id counter is managed through the private methods
13
19
  # increase_id_counter and next_id below
14
20
  #
15
- def id_counter time
21
+ def id_counter(time)
16
22
  dsym = date_symbol time
17
23
  @id_counter = {} unless @id_counter.kind_of? Hash
18
24
  @id_counter[ dsym ] ||= 1
19
25
  end
20
26
 
21
- def ids time
27
+ # Returns an array of ids for the given day
28
+ def ids(time)
22
29
  dsym = date_symbol time
23
30
  @ids = {} unless @ids.kind_of? Hash
24
31
  @ids[dsym] ||= []
@@ -33,68 +40,85 @@ module Tempo
33
40
  @days_index
34
41
  end
35
42
 
36
- def file time
37
- FileRecord::Record.log_filename( self, time )
43
+ # Passthrough function, returns the log filename for a given date
44
+ #
45
+ def file(time)
46
+ FileRecord::FileUtility.new(self, {time: time}).filename
38
47
  end
39
48
 
49
+ # Returns the immediate directory for the log
50
+ # Tempo::Model::MessageLog => tempo_message_logs
40
51
  def dir
41
- FileRecord::Record.log_dirname( self )
52
+ FileRecord::FileUtility.new(self).log_directory
42
53
  end
43
54
 
44
- def records
45
- path = FileRecord::Record.log_dir( self )
46
- Dir[path + "/*.yaml"]
55
+ # Load all records from a directory into an array
56
+ # send alternate directory through options
57
+ def records(options={})
58
+ FileRecord::FileUtility.new(self, options).log_records
47
59
  end
48
60
 
49
- def save_to_file
50
- FileRecord::Record.save_log( self )
61
+ # send alternate directory through options
62
+ def save_to_file(options={})
63
+ FileRecord::Record.save_log(self, options)
51
64
  end
52
65
 
53
-
54
- def read_from_file time
66
+ # send alternate directory through options
67
+ def read_from_file(time, options={})
55
68
  dsym = date_symbol time
56
69
  @days_index[ dsym ] = [] if not days_index.has_key? dsym
57
- FileRecord::Record.read_log( self, time )
70
+ FileRecord::Record.read_log(self, time, options)
58
71
  end
59
72
 
60
73
  # load all the records for a single day
61
74
  #
62
- def load_day_record time
75
+ def load_day_record(time, options={})
63
76
  dsym = date_symbol time
64
77
  if not days_index.has_key? dsym
65
78
  @days_index[ dsym ] = []
66
- read_from_file time
79
+ read_from_file time, options
67
80
  end
68
81
  end
69
82
 
70
83
  # load the records for each day from time 1 to time 2
71
84
  #
72
- def load_days_records time_1, time_2
85
+ def load_days_records(time_1, time_2, options={})
73
86
 
74
87
  return if time_1.nil? || time_2.nil?
75
88
 
76
- days = ( time_2.to_date - time_1.to_date ).to_i
89
+ days = (time_2.to_date - time_1.to_date).to_i
77
90
  return if days < 0
78
91
 
79
- (days + 1).times { |i| load_day_record( time_1.add_days( i ))}
92
+ (days + 1).times { |i| load_day_record(time_1.add_days(i), options)}
80
93
  end
81
94
 
82
95
  # load the records for the most recently recorded day
83
96
  #
84
- def load_last_day
97
+ def load_last_day(options={})
85
98
  reg = /(\d+)\.yaml/
86
- if records.last
87
- d_id = reg.match(records.last)[1] if records.last
99
+ recs = records(options)
100
+ if recs.last
101
+ d_id = reg.match(recs.last)[1]
88
102
  time = day_id_to_time d_id if d_id
89
- load_day_record time
103
+ load_day_record time, options
90
104
  return time
91
105
  end
92
106
  end
93
107
 
108
+ # delete the file for a single day
109
+ # this is necessary for removing a single entry on a day
110
+ # since updates will skip over days with no entries
111
+ #
112
+ def delete_day_record(time, options={})
113
+ options[:time] = time
114
+ options[:destroy] = true
115
+ FileRecord::FileUtility.new(self, options).file_path
116
+ end
117
+
94
118
  # takes and integer, and time or day_id
95
119
  # and returns the instance that matches both
96
120
  # the id and d_id
97
- def find_by_id id, time
121
+ def find_by_id(id, time)
98
122
  time = day_id time
99
123
  ids = find "id", id
100
124
  d_ids = find "d_id", time
@@ -107,19 +131,18 @@ module Tempo
107
131
  # day_ids can be run through without change
108
132
  # Time will be converted into "YYYYmmdd"
109
133
  # ex: 1-1-2014 => "20140101"
110
- def day_id time
111
- if time.kind_of? String
112
- return time if time =~ /^\d{8}$/
113
- end
134
+ def day_id(time)
135
+ return time if time.to_s =~ /^\d{8}$/
136
+
114
137
  raise ArgumentError, "Invalid Time" if not time.kind_of? Time
115
138
  time.strftime("%Y%m%d")
116
139
  end
117
140
 
118
- def day_id_to_time d_id
141
+ def day_id_to_time(d_id)
119
142
  time = Time.new(d_id[0..3].to_i, d_id[4..5].to_i, d_id[6..7].to_i)
120
143
  end
121
144
 
122
- def delete instance
145
+ def delete(instance)
123
146
  id = instance.id
124
147
  dsym = date_symbol instance.d_id
125
148
 
@@ -129,8 +152,8 @@ module Tempo
129
152
  end
130
153
  end
131
154
 
132
- def initialize( options={} )
133
- @start_time = options.fetch(:start_time, Time.now )
155
+ def initialize(options={})
156
+ @start_time = options.fetch(:start_time, Time.now)
134
157
  @start_time = Time.new(@start_time) if @start_time.kind_of? String
135
158
 
136
159
  self.class.load_day_record(@start_time)
@@ -139,7 +162,7 @@ module Tempo
139
162
  id_candidate = options[:id]
140
163
  if !id_candidate
141
164
  @id = self.class.next_id @start_time
142
- elsif self.class.ids( @start_time ).include? id_candidate
165
+ elsif self.class.ids(@start_time).include? id_candidate
143
166
  raise IdentityConflictError, "Id #{id_candidate} already exists"
144
167
  else
145
168
  @id = id_candidate
@@ -160,7 +183,7 @@ module Tempo
160
183
 
161
184
  class << self
162
185
 
163
- def add_to_days_index member
186
+ def add_to_days_index(member)
164
187
  @days_index = {} unless @days_index.kind_of? Hash
165
188
  dsym = date_symbol member.start_time
166
189
  @days_index[dsym] ||= []
@@ -168,7 +191,7 @@ module Tempo
168
191
  @days_index[dsym].sort! { |a,b| a.start_time <=> b.start_time }
169
192
  end
170
193
 
171
- def add_id time, id
194
+ def add_id(time, id)
172
195
  dsym = date_symbol time
173
196
  @ids = {} unless @ids.kind_of? Hash
174
197
  @ids[dsym] ||= []
@@ -176,18 +199,18 @@ module Tempo
176
199
  @ids[dsym].sort!
177
200
  end
178
201
 
179
- def date_symbol time
180
- day_id( time ).to_sym
202
+ def date_symbol(time)
203
+ day_id(time).to_sym
181
204
  end
182
205
 
183
- def increase_id_counter time
206
+ def increase_id_counter(time)
184
207
  dsym = date_symbol time
185
208
  @id_counter = {} unless @id_counter.kind_of? Hash
186
209
  @id_counter[ dsym ] ||= 0
187
210
  @id_counter[ dsym ] = @id_counter[ dsym ].next
188
211
  end
189
212
 
190
- def next_id time
213
+ def next_id(time)
191
214
  while ids(time).include? id_counter time
192
215
  increase_id_counter time
193
216
  end
@@ -1,3 +1,5 @@
1
+ require 'yaml'
2
+
1
3
  module Tempo
2
4
  module Model
3
5
  class Project < Tempo::Model::Composite
@@ -7,11 +9,11 @@ module Tempo
7
9
 
8
10
  class << self
9
11
 
10
- def current( instance=nil )
12
+ def current(instance=nil)
11
13
  @current
12
14
  end
13
15
 
14
- def current=( instance )
16
+ def current=(instance)
15
17
  if instance.class == self
16
18
  @current = instance
17
19
  else
@@ -19,8 +21,8 @@ module Tempo
19
21
  end
20
22
  end
21
23
 
22
- def include?( title )
23
- matches = find_by_title( title )
24
+ def include?(title)
25
+ matches = find_by_title(title)
24
26
  return false if matches.empty?
25
27
  matches.each do |match|
26
28
  return true if match.title == title
@@ -42,6 +44,7 @@ module Tempo
42
44
  self.class.current == self
43
45
  end
44
46
 
47
+ # record the active project by adding current = true
45
48
  def freeze_dry
46
49
  record = super
47
50
  if self.class.current == self
@@ -50,7 +53,7 @@ module Tempo
50
53
  record
51
54
  end
52
55
 
53
- def tag( tags )
56
+ def tag(tags)
54
57
  return unless tags and tags.kind_of? Array
55
58
  tags.each do |tag|
56
59
  tag.split.each {|t| @tags << t if ! @tags.include? t }
@@ -58,7 +61,7 @@ module Tempo
58
61
  @tags.sort!
59
62
  end
60
63
 
61
- def untag( tags )
64
+ def untag(tags)
62
65
  return unless tags and tags.kind_of? Array
63
66
  tags.each do |tag|
64
67
  tag.split.each {|t| @tags.delete t }
@@ -30,7 +30,7 @@ module Tempo
30
30
  end
31
31
  end
32
32
 
33
- def initialize options={}
33
+ def initialize(options={})
34
34
 
35
35
  # declare these first for model organization when sent to YAML
36
36
  @project_title = nil
@@ -54,7 +54,7 @@ module Tempo
54
54
  leave_only_one_running
55
55
  end
56
56
 
57
- def start_time= time
57
+ def start_time=time
58
58
  raise ArgumentError if !time.kind_of? Time
59
59
  if @end_time != :running
60
60
  @start_time = time if verify_times time, @end_time
@@ -66,7 +66,7 @@ module Tempo
66
66
  # end time cannot be set to :running, only to a
67
67
  # valid Time object
68
68
  #
69
- def end_time= time
69
+ def end_time=time
70
70
  raise ArgumentError if !time.kind_of? Time
71
71
  @end_time = time if verify_times self.start_time, time
72
72
  end
@@ -74,7 +74,7 @@ module Tempo
74
74
  # method for updating both times at once, necessary if it would
75
75
  # cause a conflict to do them individually
76
76
  #
77
- def update_times start_time, end_time
77
+ def update_times(start_time, end_time)
78
78
  raise ArgumentError if !start_time.kind_of? Time
79
79
  raise ArgumentError if !end_time.kind_of? Time
80
80
  verify_times start_time, end_time
@@ -86,7 +86,7 @@ module Tempo
86
86
  # Public method to access verify start time,
87
87
  # determine if an error will be raised
88
88
  #
89
- def valid_start_time? time
89
+ def valid_start_time?(time)
90
90
  return false if !time.kind_of? Time
91
91
  begin
92
92
  if @end_time != :running
@@ -103,7 +103,7 @@ module Tempo
103
103
  # Public method to access verify end time,
104
104
  # determine if an error will be raised
105
105
  #
106
- def valid_end_time? time
106
+ def valid_end_time?(time)
107
107
  return false if !time.kind_of? Time
108
108
  begin
109
109
  verify_times self.start_time, time
@@ -160,7 +160,7 @@ module Tempo
160
160
  record
161
161
  end
162
162
 
163
- def tag tags
163
+ def tag(tags)
164
164
  return unless tags and tags.kind_of? Array
165
165
  tags.each do |tag|
166
166
  tag.split.each {|t| @tags << t if ! @tags.include? t }
@@ -168,7 +168,7 @@ module Tempo
168
168
  @tags.sort!
169
169
  end
170
170
 
171
- def untag tags
171
+ def untag(tags)
172
172
  return unless tags and tags.kind_of? Array
173
173
  tags.each do |tag|
174
174
  tag.split.each {|t| @tags.delete t }
@@ -184,7 +184,7 @@ module Tempo
184
184
  #close current at the end time, or on the last minute
185
185
  # of the day if end time is another day
186
186
  #
187
- def self.close_current end_time
187
+ def self.close_current(end_time)
188
188
  if end_time.day > current.start_time.day
189
189
  out = end_of_day current.start_time
190
190
  current.end_time = out
@@ -254,7 +254,7 @@ module Tempo
254
254
  # check a time against all loaded instances, verify that it doesn't
255
255
  # fall in the middle of any closed time records
256
256
  #
257
- def verify_start_time time
257
+ def verify_start_time(time)
258
258
 
259
259
  # Check that there are currently
260
260
  # records on the day to iterate through
@@ -278,7 +278,7 @@ module Tempo
278
278
  # end time of :running. This condition, (currently only possible from init)
279
279
  # requires a second check to close out all but the most recent time entry.
280
280
  #
281
- def verify_times start_time, end_time
281
+ def verify_times(start_time, end_time)
282
282
 
283
283
  verify_start_time start_time
284
284
 
@@ -307,7 +307,7 @@ module Tempo
307
307
  #
308
308
  # It will return true if it is exactly the record start or end time
309
309
  #
310
- def time_in_record? time, record
310
+ def time_in_record?(time, record)
311
311
  return false if record.end_time == :running
312
312
  time >= record.start_time && time <= record.end_time
313
313
  end
@@ -318,7 +318,7 @@ module Tempo
318
318
  # this condition must be accounted for separately.
319
319
  # It assumes a valid start and end time.
320
320
  #
321
- def time_span_intersects_record? start_time, end_time, record
321
+ def time_span_intersects_record?(start_time, end_time, record)
322
322
  if record.end_time == :running
323
323
  return true if start_time <= record.start_time && end_time > record.start_time
324
324
  return false
@@ -332,7 +332,7 @@ module Tempo
332
332
 
333
333
  # returns the last minute of the day
334
334
  #
335
- def self.end_of_day time
335
+ def self.end_of_day(time)
336
336
  Time.new(time.year, time.month, time.day, 23, 59)
337
337
  end
338
338
  end
data/lib/tempo/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Tempo
2
- VERSION = '0.1.6'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -2,19 +2,19 @@ module Tempo
2
2
  module Views
3
3
  class << self
4
4
 
5
- def arrange_parent_child parent, child
5
+ def arrange_parent_child(parent, child)
6
6
  ViewRecords::Message.new "parent project:"
7
7
  ViewRecords::Project.new parent
8
8
  ViewRecords::Message.new "child project:"
9
9
  ViewRecords::Project.new child
10
10
  end
11
11
 
12
- def arrange_root project
12
+ def arrange_root(project)
13
13
  ViewRecords::Message.new "root project:"
14
14
  ViewRecords::Project.new project
15
15
  end
16
16
 
17
- def arrange_already_root project
17
+ def arrange_already_root(project)
18
18
  ViewRecords::Message.new "already a root project:"
19
19
  ViewRecords::Project.new project
20
20
  end
@@ -24,4 +24,4 @@ module Tempo
24
24
  end
25
25
  end
26
26
  end
27
- end
27
+ end
@@ -3,7 +3,7 @@ module Tempo
3
3
  class << self
4
4
 
5
5
  # called in the pre block, pushes relavent options to the reporter
6
- def initialize_view_options command, global_options, options
6
+ def initialize_view_options(command, global_options, options)
7
7
  view_opts = {}
8
8
  view_opts[:verbose] = global_options[:verbose]
9
9
  view_opts[:id] = global_options[:id]
@@ -26,24 +26,7 @@ module Tempo
26
26
  Tempo::Views::Reporter.add_options view_opts
27
27
  end
28
28
 
29
- # DEPRACATE- View is returned in post
30
- #
31
- # puts each line if output=true
32
- # else returns an array of view lines
33
- def return_view( view, options={} )
34
- output = options.fetch( :output, true )
35
-
36
- if output
37
- if view.is_a? String
38
- puts view
39
- else
40
- view.each { |line| puts line }
41
- end
42
- end
43
- view
44
- end
45
-
46
- def options_report( command, global_options, options, args )
29
+ def options_report(command, global_options, options, args)
47
30
  globals_list = "global options: "
48
31
  global_options.each {|k,v| globals_list += "#{k} = #{v}, " if k.kind_of? String and k.length > 1 and !v.nil? }
49
32
  ViewRecords::Message.new globals_list[0..-2], category: :debug
@@ -57,20 +40,24 @@ module Tempo
57
40
  ViewRecords::Message.new "args: #{args}", category: :debug
58
41
  end
59
42
 
60
- def no_items( items, category=:info )
43
+ def no_items(items, category=:info)
61
44
  ViewRecords::Message.new "no #{items} exist", category: category
45
+ if items == "projects"
46
+ ViewRecords::Message.new "You must at least one project before you can begin tracking time"
47
+ ViewRecords::Message.new "run `tempo project --help` for more information"
48
+ end
62
49
  end
63
50
 
64
- def no_match_error( items, request, plural=true )
51
+ def no_match_error(items, request, plural=true)
65
52
  match = plural ? "match" : "matches"
66
53
  ViewRecords::Message.new "no #{items} #{match} the request: #{request}", category: :error
67
54
  end
68
55
 
69
- def already_exists_error( item, request )
56
+ def already_exists_error(item, request)
70
57
  ViewRecords::Message.new "#{item} '#{request}' already exists", category: :error
71
58
  end
72
59
 
73
- def checkout_assistance( options={} )
60
+ def checkout_assistance(options={})
74
61
  ViewRecords::Message.new "checkout command run with no arguments"
75
62
  ViewRecords::Message.new "perhaps you meant one of these?"
76
63
  ViewRecords::Message.new " tempo checkout --add <new project name>"
@@ -17,7 +17,7 @@ module Tempo
17
17
  # record type. See View Records for all possible record types. See screen formatter
18
18
  # for examples of proc blocks.
19
19
  #
20
- def format_records records, options={}
20
+ def format_records(records, options={})
21
21
  @options = options
22
22
  records.each do |record|
23
23
  class_block = "#{record.type}_block"
@@ -27,4 +27,4 @@ module Tempo
27
27
  end
28
28
  end
29
29
  end
30
- end
30
+ end