watch_tower 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (115) hide show
  1. data/.gitignore +10 -0
  2. data/.todo +33 -0
  3. data/.travis.yml +14 -0
  4. data/Gemfile +43 -0
  5. data/Guardfile +25 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.md +38 -0
  8. data/Rakefile +8 -0
  9. data/TODO +17 -0
  10. data/bin/watchtower +10 -0
  11. data/ci/adapters/jruby-mysql.yml +8 -0
  12. data/ci/adapters/jruby-postgresql.yml +6 -0
  13. data/ci/adapters/jruby-sqlite.yml +6 -0
  14. data/ci/adapters/ruby-mysql.yml +8 -0
  15. data/ci/adapters/ruby-postgresql.yml +6 -0
  16. data/ci/adapters/ruby-sqlite.yml +6 -0
  17. data/ci/travis.rb +102 -0
  18. data/lib/watch_tower.rb +60 -0
  19. data/lib/watch_tower/appscript.rb +22 -0
  20. data/lib/watch_tower/cli.rb +15 -0
  21. data/lib/watch_tower/cli/.gitkeep +0 -0
  22. data/lib/watch_tower/cli/install.rb +63 -0
  23. data/lib/watch_tower/cli/open.rb +24 -0
  24. data/lib/watch_tower/cli/start.rb +140 -0
  25. data/lib/watch_tower/config.rb +38 -0
  26. data/lib/watch_tower/core_ext.rb +4 -0
  27. data/lib/watch_tower/core_ext/.gitkeep +0 -0
  28. data/lib/watch_tower/editor.rb +17 -0
  29. data/lib/watch_tower/editor/.gitkeep +0 -0
  30. data/lib/watch_tower/editor/base_appscript.rb +34 -0
  31. data/lib/watch_tower/editor/base_ps.rb +6 -0
  32. data/lib/watch_tower/editor/textmate.rb +17 -0
  33. data/lib/watch_tower/editor/xcode.rb +22 -0
  34. data/lib/watch_tower/errors.rb +25 -0
  35. data/lib/watch_tower/eye.rb +79 -0
  36. data/lib/watch_tower/project.rb +14 -0
  37. data/lib/watch_tower/project/.gitkeep +0 -0
  38. data/lib/watch_tower/project/any_based.rb +22 -0
  39. data/lib/watch_tower/project/git_based.rb +86 -0
  40. data/lib/watch_tower/project/init.rb +38 -0
  41. data/lib/watch_tower/project/path_based.rb +144 -0
  42. data/lib/watch_tower/server.rb +62 -0
  43. data/lib/watch_tower/server/.gitkeep +0 -0
  44. data/lib/watch_tower/server/app.rb +37 -0
  45. data/lib/watch_tower/server/assets/images/WatchTower.jpg +0 -0
  46. data/lib/watch_tower/server/assets/images/percentage.png +0 -0
  47. data/lib/watch_tower/server/assets/javascripts/application.js +3 -0
  48. data/lib/watch_tower/server/assets/javascripts/percentage.coffee +8 -0
  49. data/lib/watch_tower/server/assets/stylesheets/application.css +7 -0
  50. data/lib/watch_tower/server/assets/stylesheets/global.sass +71 -0
  51. data/lib/watch_tower/server/assets/stylesheets/project.sass +59 -0
  52. data/lib/watch_tower/server/configurations.rb +10 -0
  53. data/lib/watch_tower/server/configurations/asset.rb +43 -0
  54. data/lib/watch_tower/server/database.rb +105 -0
  55. data/lib/watch_tower/server/db/migrate/001_create_projects.rb +15 -0
  56. data/lib/watch_tower/server/db/migrate/002_create_files.rb +16 -0
  57. data/lib/watch_tower/server/db/migrate/003_create_time_entries.rb +12 -0
  58. data/lib/watch_tower/server/db/migrate/004_create_durations.rb +14 -0
  59. data/lib/watch_tower/server/db/migrate/005_add_hash_to_time_entries.rb +6 -0
  60. data/lib/watch_tower/server/db/migrate/006_add_hash_to_files.rb +6 -0
  61. data/lib/watch_tower/server/decorator.rb +21 -0
  62. data/lib/watch_tower/server/decorator/application_decorator.rb +91 -0
  63. data/lib/watch_tower/server/decorator/file_decorator.rb +38 -0
  64. data/lib/watch_tower/server/decorator/project_decorator.rb +51 -0
  65. data/lib/watch_tower/server/helpers.rb +13 -0
  66. data/lib/watch_tower/server/helpers/asset.rb +29 -0
  67. data/lib/watch_tower/server/helpers/improved_partials.rb +41 -0
  68. data/lib/watch_tower/server/models/duration.rb +11 -0
  69. data/lib/watch_tower/server/models/file.rb +31 -0
  70. data/lib/watch_tower/server/models/project.rb +17 -0
  71. data/lib/watch_tower/server/models/time_entry.rb +64 -0
  72. data/lib/watch_tower/server/public/assets/WatchTower-4d6de11e1bd34165ad91ac46fb711bf3.jpg +0 -0
  73. data/lib/watch_tower/server/public/assets/application-7829b53b5ece1a16d22dc3d00f329023.css +107 -0
  74. data/lib/watch_tower/server/public/assets/application-e0e6b7731aade460f680331e65cf0682.js +9359 -0
  75. data/lib/watch_tower/server/public/assets/percentage-d8589e21a5fc85d32a445f531ff8ab95.png +0 -0
  76. data/lib/watch_tower/server/vendor/assets/javascripts/jquery-ui.js +11729 -0
  77. data/lib/watch_tower/server/vendor/assets/javascripts/jquery.js +8981 -0
  78. data/lib/watch_tower/server/vendor/assets/javascripts/jquery_ujs.js +363 -0
  79. data/lib/watch_tower/server/views/.gitkeep +0 -0
  80. data/lib/watch_tower/server/views/_file.haml +9 -0
  81. data/lib/watch_tower/server/views/_project.haml +13 -0
  82. data/lib/watch_tower/server/views/index.haml +7 -0
  83. data/lib/watch_tower/server/views/layout.haml +32 -0
  84. data/lib/watch_tower/server/views/project.haml +12 -0
  85. data/lib/watch_tower/templates/config.yml +146 -0
  86. data/lib/watch_tower/templates/watchtower.plist +23 -0
  87. data/lib/watch_tower/version.rb +8 -0
  88. data/spec/factories.rb +45 -0
  89. data/spec/spec_helper.rb +26 -0
  90. data/spec/support/active_record.rb +44 -0
  91. data/spec/support/factory_girl.rb +6 -0
  92. data/spec/support/launchy.rb +3 -0
  93. data/spec/support/sinatra.rb +10 -0
  94. data/spec/support/timecop.rb +7 -0
  95. data/spec/watch_tower/appscript_spec.rb +6 -0
  96. data/spec/watch_tower/cli/install_spec.rb +16 -0
  97. data/spec/watch_tower/cli/open_spec.rb +14 -0
  98. data/spec/watch_tower/cli/start_spec.rb +17 -0
  99. data/spec/watch_tower/cli_spec.rb +15 -0
  100. data/spec/watch_tower/config_spec.rb +25 -0
  101. data/spec/watch_tower/editor/textmate_spec.rb +43 -0
  102. data/spec/watch_tower/editor/xcode_spec.rb +43 -0
  103. data/spec/watch_tower/editor_spec.rb +19 -0
  104. data/spec/watch_tower/eye_spec.rb +130 -0
  105. data/spec/watch_tower/project/git_based_spec.rb +131 -0
  106. data/spec/watch_tower/project/path_based_spec.rb +111 -0
  107. data/spec/watch_tower/project_spec.rb +82 -0
  108. data/spec/watch_tower/server/app_spec.rb +186 -0
  109. data/spec/watch_tower/server/decorator/project_decorator_spec.rb +60 -0
  110. data/spec/watch_tower/server/models/file_spec.rb +284 -0
  111. data/spec/watch_tower/server/models/project_spec.rb +165 -0
  112. data/spec/watch_tower/server/models/time_entry_spec.rb +37 -0
  113. data/spec/watch_tower/server_spec.rb +4 -0
  114. data/watch_tower.gemspec +80 -0
  115. metadata +450 -0
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ module Server
4
+ module Decorator
5
+ describe ProjectDecorator do
6
+ before(:each) do
7
+ @project = FactoryGirl.create :project
8
+ end
9
+
10
+ subject { ProjectDecorator.find(@project.id) }
11
+
12
+ it "should return a formatted elapsed time" do
13
+ time = 1.day + 2.hours + 3.minutes + 34.seconds
14
+ subject.stubs(:elapsed_time).returns(time)
15
+ subject.elapsed.should == '1 day, 2 hours, 3 minutes and 34 seconds'
16
+ end
17
+
18
+ describe "Application Decorator" do
19
+ describe "#humanize_time" do
20
+ it "should return 1 second" do
21
+ subject.send(:humanize_time, 1).should == '1 second'
22
+ end
23
+
24
+ it "should return 2 seconds" do
25
+ subject.send(:humanize_time, 2).should == '2 seconds'
26
+ end
27
+
28
+ it "should return 1 minute" do
29
+ subject.send(:humanize_time, 1.minute).should == '1 minute'
30
+ end
31
+
32
+ it "should return 2 minutes" do
33
+ subject.send(:humanize_time, 2.minutes).should == '2 minutes'
34
+ end
35
+
36
+ it "should return 1 hour" do
37
+ subject.send(:humanize_time, 1.hour).should == '1 hour'
38
+ end
39
+
40
+ it "should return 2 hours" do
41
+ subject.send(:humanize_time, 2.hours).should == '2 hours'
42
+ end
43
+
44
+ it "should return 1 day" do
45
+ subject.send(:humanize_time, 1.day).should == '1 day'
46
+ end
47
+
48
+ it "should return 2 days" do
49
+ subject.send(:humanize_time, 2.days).should == '2 days'
50
+ end
51
+
52
+ it "should return 1 day, 2 hours, 3 minutes and 34 seconds" do
53
+ time = 1.day + 2.hours + 3.minutes + 34.seconds
54
+ subject.send(:humanize_time, time).should == '1 day, 2 hours, 3 minutes and 34 seconds'
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,284 @@
1
+ require 'spec_helper'
2
+
3
+ module Server
4
+ describe File do
5
+ describe "Attributes" do
6
+ it { should respond_to :path }
7
+ it { should respond_to :elapsed_time }
8
+ it { should respond_to :time_entries_count }
9
+ end
10
+
11
+ describe "Validations" do
12
+ it { should_not be_valid }
13
+
14
+ it "should require an path" do
15
+ m = FactoryGirl.build :file, path: nil
16
+ m.should_not be_valid
17
+ end
18
+
19
+ it "should require a project" do
20
+ m = FactoryGirl.build :file, project: nil
21
+ m.should_not be_valid
22
+ end
23
+
24
+ it "should not require a hash" do
25
+ m = FactoryGirl.build :file, file_hash: nil
26
+ m.should be_valid
27
+ end
28
+
29
+ it "should have a unique path for each project" do
30
+ p = FactoryGirl.create :project
31
+ FactoryGirl.create :file, path: '/path/to/file.rb', project: p
32
+ m = FactoryGirl.build :file, path: '/path/to/file.rb', project: p
33
+ m.should_not be_valid
34
+ end
35
+
36
+ it "should be valid if attributes requirements are met" do
37
+ m = FactoryGirl.build :file
38
+ m.should be_valid
39
+ end
40
+ end
41
+
42
+ describe "Associations" do
43
+ before(:each) do
44
+ @file = FactoryGirl.create :file
45
+ end
46
+
47
+ it { should respond_to :project }
48
+ it { should respond_to :time_entries }
49
+
50
+ it "should increment the counter cache" do
51
+ 10.times do
52
+ FactoryGirl.create :time_entry, file: @file
53
+ end
54
+
55
+ @file.reload.time_entries_count.should == 10
56
+ end
57
+
58
+ it "should belong to a project" do
59
+ project = FactoryGirl.create :project
60
+ file = FactoryGirl.create :file, project: project
61
+
62
+ file.project.should == project
63
+ end
64
+ end
65
+
66
+ describe "TimeEntries count" do
67
+ before(:each) do
68
+ @file = FactoryGirl.create :file
69
+ end
70
+
71
+ it "should calculate elapsed time" do
72
+ 3.times do
73
+ Timecop.freeze(Time.now + 1)
74
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
75
+ end
76
+
77
+ @file.reload
78
+ @file.elapsed_time.should == 2
79
+ end
80
+
81
+ it "should skip the one with pause time" do
82
+ 3.times do
83
+ Timecop.freeze(Time.now + 1)
84
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
85
+ end
86
+
87
+ Timecop.freeze(Time.now + @file.time_entries.first.send(:pause_time) + 1)
88
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
89
+
90
+ 3.times do
91
+ Timecop.freeze(Time.now + 1)
92
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
93
+ end
94
+
95
+ @file.reload
96
+ @file.elapsed_time.should == 5
97
+ end
98
+
99
+ it "should not count the time entries of another file" do
100
+ 3.times do
101
+ Timecop.freeze(Time.now + 1)
102
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
103
+ end
104
+
105
+ 3.times do
106
+ Timecop.freeze(Time.now + 1)
107
+ FactoryGirl.create :time_entry, mtime: Time.now
108
+ end
109
+
110
+ @file.reload
111
+ @file.elapsed_time.should == 2
112
+ end
113
+
114
+ it "should add duration to a different day (2 days)." do
115
+ # Create initial time entries
116
+ 3.times do
117
+ Timecop.freeze(Time.now + 1)
118
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
119
+ end
120
+
121
+ # Go ahead one day
122
+ Timecop.freeze(Time.now + 1.day)
123
+
124
+ # Add 3 more time entries
125
+ 3.times do
126
+ Timecop.freeze(Time.now + 1)
127
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
128
+ end
129
+
130
+ @file.reload
131
+ @file.elapsed_time.should == 4
132
+ @file.durations.size.should == 2
133
+ end
134
+
135
+ it "should correctly record dates" do
136
+ # Record the date of the first day
137
+ Timecop.freeze(Time.now)
138
+ current_day = Time.now.to_date
139
+
140
+ # Create initial time entries
141
+ 3.times do
142
+ Timecop.freeze(Time.now + 1)
143
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
144
+ end
145
+
146
+ # Go ahead one day
147
+ Timecop.freeze(Time.now + 1.day)
148
+ next_day = Time.now.to_date
149
+
150
+ # Add 3 more time entries
151
+ 3.times do
152
+ Timecop.freeze(Time.now + 1)
153
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
154
+ end
155
+
156
+ @file.reload
157
+ @file.durations.order('id ASC').first.date.should == current_day
158
+ @file.durations.order('id ASC').last.date.should == next_day
159
+ end
160
+
161
+ it "should not add elapsed time to the count if the file's has has not changed" do
162
+ # Freeze the time
163
+ Timecop.freeze(Time.now)
164
+ # Create the first time entry
165
+ t1 = FactoryGirl.create :time_entry, file: @file, file_hash: @file.file_hash
166
+ # Go ahead 10 minutes
167
+ Timecop.freeze(Time.now + 10.minutes)
168
+ # Create the second time entry with the same file and hash
169
+ FactoryGirl.create :time_entry, file: @file, file_hash: @file.file_hash
170
+
171
+ @file.elapsed_time.should == 0
172
+ end
173
+
174
+ it "should update the hash of the file with the last time entry" do
175
+ # Freeze the time
176
+ Timecop.freeze(Time.now)
177
+ # Create the first time entry
178
+ t1 = FactoryGirl.create :time_entry, file: @file
179
+ # Go ahead 10 minutes
180
+ Timecop.freeze(Time.now + 10.minutes)
181
+ # Create the second time entry with the same file and hash
182
+ FactoryGirl.create :time_entry, file: @file, file_hash: t1.file_hash
183
+
184
+ @file.file_hash.should == t1.file_hash
185
+ end
186
+
187
+ end
188
+
189
+ describe "scopes" do
190
+ before(:each) do
191
+ @files = 3.times.collect{ FactoryGirl.create :file }
192
+ end
193
+
194
+ it "should have the default scope to" do
195
+ 10.times do |n|
196
+ file = @files.first
197
+ Timecop.freeze(Time.now + n * 2)
198
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
199
+ end
200
+
201
+ 10.times do |n|
202
+ file = @files[1]
203
+ Timecop.freeze(Time.now + n * 10)
204
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
205
+ end
206
+
207
+ 10.times do |n|
208
+ file = @files.last
209
+ Timecop.freeze(Time.now + n * 5)
210
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
211
+ end
212
+
213
+ File.all.first.should == @files[1]
214
+ end
215
+
216
+ it "should have a scope worked_on" do
217
+ File.should respond_to(:worked_on)
218
+ end
219
+
220
+ it "should have a scope worked_on that returns all projects that do not have empty time_entries" do
221
+ 5.times do
222
+ FactoryGirl.create :time_entry, file: @files[0]
223
+ end
224
+
225
+ 5.times do
226
+ FactoryGirl.create :time_entry, file: @files[1]
227
+ end
228
+
229
+ File.worked_on.should_not include(@files.last)
230
+ end
231
+ end
232
+ end
233
+
234
+ describe "Methods" do
235
+ before(:each) do
236
+ @project = FactoryGirl.create :project
237
+ @files = []
238
+ 2.times do
239
+ @files << FactoryGirl.create(:file, project: @project)
240
+ 2.times do
241
+ FactoryGirl.create :time_entry, file: @files.last
242
+ end
243
+ end
244
+ end
245
+
246
+ describe "one project" do
247
+ describe "#sum_elapsed_time" do
248
+ it "should return the sum of all elapsed times of all the files for the same project" do
249
+ @project.files.sum_elapsed_time.should == @files.inject(0) {|s, f| s += f.elapsed_time }
250
+ end
251
+ end
252
+
253
+ describe "#percent" do
254
+ it "should return 50" do
255
+ @project.files.first.percent.should == 50
256
+ end
257
+ end
258
+ end
259
+
260
+ describe "two projects" do
261
+ before(:each) do
262
+ @other_files = []
263
+ 2.times do
264
+ @other_files << FactoryGirl.create(:file)
265
+ 2.times do
266
+ FactoryGirl.create :time_entry, file: @other_files.last
267
+ end
268
+ end
269
+
270
+ describe "#sum_elapsed_time" do
271
+ it "should return the sum of all elapsed times of all the files for the same project" do
272
+ @project.files.sum_elapsed_time.should == @files.inject(0) {|s, f| s += f.elapsed_time }
273
+ end
274
+ end
275
+
276
+ describe "#percent" do
277
+ it "should return 50" do
278
+ @project.files.first.percent.should == 50
279
+ end
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
@@ -0,0 +1,165 @@
1
+ require 'spec_helper'
2
+
3
+ module Server
4
+ describe Project do
5
+ describe "Attributes" do
6
+ it { should respond_to :name }
7
+ it { should respond_to :path }
8
+ it { should respond_to :files_count }
9
+ end
10
+
11
+ describe "Methods" do
12
+ it { should respond_to :elapsed_time }
13
+ end
14
+
15
+ describe "Validations" do
16
+ it { should_not be_valid }
17
+
18
+ it "should require a name" do
19
+ m = FactoryGirl.build :project, name: nil
20
+ m.should_not be_valid
21
+ end
22
+
23
+ it "should require a path" do
24
+ m = FactoryGirl.build :project, path: nil
25
+ m.should_not be_valid
26
+ end
27
+
28
+ it "should be valid if attributes requirements are met" do
29
+ m = FactoryGirl.build :project
30
+ m.should be_valid
31
+ end
32
+ end
33
+
34
+ describe "Associations" do
35
+ before(:each) do
36
+ @project = FactoryGirl.create :project
37
+ end
38
+ it { should respond_to :files }
39
+ it { should respond_to :time_entries }
40
+
41
+ it "should increment the counter cache" do
42
+ 10.times do
43
+ FactoryGirl.create :file, project: @project
44
+ end
45
+
46
+ @project.reload.files_count.should == 10
47
+ end
48
+
49
+ it "should have time_entries through files" # do
50
+ # file1 = FactoryGirl.create :file, project: @project
51
+ # file2 = FactoryGirl.create :file, project: @project
52
+ #
53
+ # 10.times do
54
+ # FactoryGirl.create :time_entry, file: file1
55
+ # end
56
+ #
57
+ # 10.times do
58
+ # FactoryGirl.create :time_entry, file: file2
59
+ # end
60
+ #
61
+ # @project.reload.time_entries.size.should == 20
62
+ # end
63
+ end
64
+
65
+ describe "TimeEntries count" do
66
+ before(:each) do
67
+ @project = FactoryGirl.create :project
68
+ @file = FactoryGirl.create :file, project: @project
69
+ end
70
+
71
+ it "should calculate elapsed time" do
72
+ 3.times do
73
+ Timecop.freeze(Time.now + 1)
74
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
75
+ end
76
+
77
+ @project.reload
78
+ @project.elapsed_time.should == 2
79
+ end
80
+
81
+ it "should skip the one with pause time" do
82
+ 3.times do
83
+ Timecop.freeze(Time.now + 1)
84
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
85
+ end
86
+
87
+ Timecop.freeze(Time.now + @file.time_entries.first.send(:pause_time) + 1)
88
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
89
+
90
+ 3.times do
91
+ Timecop.freeze(Time.now + 1)
92
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
93
+ end
94
+
95
+ @project.reload
96
+ @project.elapsed_time.should == 5
97
+ end
98
+
99
+ it "should not count the time entries of another file" do
100
+ 3.times do
101
+ Timecop.freeze(Time.now + 1)
102
+ FactoryGirl.create :time_entry, file: @file, mtime: Time.now
103
+ end
104
+
105
+ 3.times do
106
+ Timecop.freeze(Time.now + 1)
107
+ FactoryGirl.create :time_entry, mtime: Time.now
108
+ end
109
+
110
+ @project.reload
111
+ @project.elapsed_time.should == 2
112
+ end
113
+ end
114
+
115
+ describe "scopes" do
116
+ before(:each) do
117
+ @projects = 3.times.collect do
118
+ project = FactoryGirl.create :project
119
+ FactoryGirl.create :file, project: project
120
+ project.reload
121
+ end
122
+ end
123
+
124
+ it "should have the default scope to" do
125
+ file = @projects.first.files.first
126
+ 10.times do |n|
127
+ Timecop.freeze(Time.now + n * 2)
128
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
129
+ end
130
+
131
+ file = @projects[1].files.first
132
+ 10.times do |n|
133
+ Timecop.freeze(Time.now + n * 10)
134
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
135
+ end
136
+
137
+ file = @projects.last.files.first
138
+ 10.times do |n|
139
+ Timecop.freeze(Time.now + n * 5)
140
+ FactoryGirl.create :time_entry, file: file, mtime: Time.now
141
+ end
142
+
143
+ Project.all.first.should == @projects[1]
144
+ end
145
+
146
+ it "should have a scope worked_on" do
147
+ Project.should respond_to(:worked_on)
148
+ end
149
+
150
+ it "should have a scope worked_on that returns all projects that do not have empty time_entries" do
151
+ file = @projects[0].files.first
152
+ 5.times do
153
+ FactoryGirl.create :time_entry, file: file
154
+ end
155
+
156
+ file = @projects[1].files.first
157
+ 5.times do
158
+ FactoryGirl.create :time_entry, file: file
159
+ end
160
+
161
+ Project.worked_on.should_not include(@projects.last)
162
+ end
163
+ end
164
+ end
165
+ end