watch_tower 0.0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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,363 @@
1
+ /**
2
+ * Unobtrusive scripting adapter for jQuery
3
+ *
4
+ * Requires jQuery 1.6.0 or later.
5
+ * https://github.com/rails/jquery-ujs
6
+
7
+ * Uploading file using rails.js
8
+ * =============================
9
+ *
10
+ * By default, browsers do not allow files to be uploaded via AJAX. As a result, if there are any non-blank file fields
11
+ * in the remote form, this adapter aborts the AJAX submission and allows the form to submit through standard means.
12
+ *
13
+ * The `ajax:aborted:file` event allows you to bind your own handler to process the form submission however you wish.
14
+ *
15
+ * Ex:
16
+ * $('form').live('ajax:aborted:file', function(event, elements){
17
+ * // Implement own remote file-transfer handler here for non-blank file inputs passed in `elements`.
18
+ * // Returning false in this handler tells rails.js to disallow standard form submission
19
+ * return false;
20
+ * });
21
+ *
22
+ * The `ajax:aborted:file` event is fired when a file-type input is detected with a non-blank value.
23
+ *
24
+ * Third-party tools can use this hook to detect when an AJAX file upload is attempted, and then use
25
+ * techniques like the iframe method to upload the file instead.
26
+ *
27
+ * Required fields in rails.js
28
+ * ===========================
29
+ *
30
+ * If any blank required inputs (required="required") are detected in the remote form, the whole form submission
31
+ * is canceled. Note that this is unlike file inputs, which still allow standard (non-AJAX) form submission.
32
+ *
33
+ * The `ajax:aborted:required` event allows you to bind your own handler to inform the user of blank required inputs.
34
+ *
35
+ * !! Note that Opera does not fire the form's submit event if there are blank required inputs, so this event may never
36
+ * get fired in Opera. This event is what causes other browsers to exhibit the same submit-aborting behavior.
37
+ *
38
+ * Ex:
39
+ * $('form').live('ajax:aborted:required', function(event, elements){
40
+ * // Returning false in this handler tells rails.js to submit the form anyway.
41
+ * // The blank required inputs are passed to this function in `elements`.
42
+ * return ! confirm("Would you like to submit the form with missing info?");
43
+ * });
44
+ */
45
+
46
+ (function($, undefined) {
47
+ // Shorthand to make it a little easier to call public rails functions from within rails.js
48
+ var rails;
49
+
50
+ $.rails = rails = {
51
+ // Link elements bound by jquery-ujs
52
+ linkClickSelector: 'a[data-confirm], a[data-method], a[data-remote], a[data-disable-with]',
53
+
54
+ // Select elements bound by jquery-ujs
55
+ inputChangeSelector: 'select[data-remote], input[data-remote], textarea[data-remote]',
56
+
57
+ // Form elements bound by jquery-ujs
58
+ formSubmitSelector: 'form',
59
+
60
+ // Form input elements bound by jquery-ujs
61
+ formInputClickSelector: 'form input[type=submit], form input[type=image], form button[type=submit], form button:not([type])',
62
+
63
+ // Form input elements disabled during form submission
64
+ disableSelector: 'input[data-disable-with], button[data-disable-with], textarea[data-disable-with]',
65
+
66
+ // Form input elements re-enabled after form submission
67
+ enableSelector: 'input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled',
68
+
69
+ // Form required input elements
70
+ requiredInputSelector: 'input[name][required]:not([disabled]),textarea[name][required]:not([disabled])',
71
+
72
+ // Form file input elements
73
+ fileInputSelector: 'input:file',
74
+
75
+ // Link onClick disable selector with possible reenable after remote submission
76
+ linkDisableSelector: 'a[data-disable-with]',
77
+
78
+ // Make sure that every Ajax request sends the CSRF token
79
+ CSRFProtection: function(xhr) {
80
+ var token = $('meta[name="csrf-token"]').attr('content');
81
+ if (token) xhr.setRequestHeader('X-CSRF-Token', token);
82
+ },
83
+
84
+ // Triggers an event on an element and returns false if the event result is false
85
+ fire: function(obj, name, data) {
86
+ var event = $.Event(name);
87
+ obj.trigger(event, data);
88
+ return event.result !== false;
89
+ },
90
+
91
+ // Default confirm dialog, may be overridden with custom confirm dialog in $.rails.confirm
92
+ confirm: function(message) {
93
+ return confirm(message);
94
+ },
95
+
96
+ // Default ajax function, may be overridden with custom function in $.rails.ajax
97
+ ajax: function(options) {
98
+ return $.ajax(options);
99
+ },
100
+
101
+ // Submits "remote" forms and links with ajax
102
+ handleRemote: function(element) {
103
+ var method, url, data,
104
+ crossDomain = element.data('cross-domain') || null,
105
+ dataType = element.data('type') || ($.ajaxSettings && $.ajaxSettings.dataType),
106
+ options;
107
+
108
+ if (rails.fire(element, 'ajax:before')) {
109
+
110
+ if (element.is('form')) {
111
+ method = element.attr('method');
112
+ url = element.attr('action');
113
+ data = element.serializeArray();
114
+ // memoized value from clicked submit button
115
+ var button = element.data('ujs:submit-button');
116
+ if (button) {
117
+ data.push(button);
118
+ element.data('ujs:submit-button', null);
119
+ }
120
+ } else if (element.is(rails.inputChangeSelector)) {
121
+ method = element.data('method');
122
+ url = element.data('url');
123
+ data = element.serialize();
124
+ if (element.data('params')) data = data + "&" + element.data('params');
125
+ } else {
126
+ method = element.data('method');
127
+ url = element.attr('href');
128
+ data = element.data('params') || null;
129
+ }
130
+
131
+ options = {
132
+ type: method || 'GET', data: data, dataType: dataType, crossDomain: crossDomain,
133
+ // stopping the "ajax:beforeSend" event will cancel the ajax request
134
+ beforeSend: function(xhr, settings) {
135
+ if (settings.dataType === undefined) {
136
+ xhr.setRequestHeader('accept', '*/*;q=0.5, ' + settings.accepts.script);
137
+ }
138
+ return rails.fire(element, 'ajax:beforeSend', [xhr, settings]);
139
+ },
140
+ success: function(data, status, xhr) {
141
+ element.trigger('ajax:success', [data, status, xhr]);
142
+ },
143
+ complete: function(xhr, status) {
144
+ element.trigger('ajax:complete', [xhr, status]);
145
+ },
146
+ error: function(xhr, status, error) {
147
+ element.trigger('ajax:error', [xhr, status, error]);
148
+ }
149
+ };
150
+ // Only pass url to `ajax` options if not blank
151
+ if (url) { options.url = url; }
152
+
153
+ rails.ajax(options);
154
+ }
155
+ },
156
+
157
+ // Handles "data-method" on links such as:
158
+ // <a href="/users/5" data-method="delete" rel="nofollow" data-confirm="Are you sure?">Delete</a>
159
+ handleMethod: function(link) {
160
+ var href = link.attr('href'),
161
+ method = link.data('method'),
162
+ csrf_token = $('meta[name=csrf-token]').attr('content'),
163
+ csrf_param = $('meta[name=csrf-param]').attr('content'),
164
+ form = $('<form method="post" action="' + href + '"></form>'),
165
+ metadata_input = '<input name="_method" value="' + method + '" type="hidden" />';
166
+
167
+ if (csrf_param !== undefined && csrf_token !== undefined) {
168
+ metadata_input += '<input name="' + csrf_param + '" value="' + csrf_token + '" type="hidden" />';
169
+ }
170
+
171
+ form.hide().append(metadata_input).appendTo('body');
172
+ form.submit();
173
+ },
174
+
175
+ /* Disables form elements:
176
+ - Caches element value in 'ujs:enable-with' data store
177
+ - Replaces element text with value of 'data-disable-with' attribute
178
+ - Adds disabled=disabled attribute
179
+ */
180
+ disableFormElements: function(form) {
181
+ form.find(rails.disableSelector).each(function() {
182
+ var element = $(this), method = element.is('button') ? 'html' : 'val';
183
+ element.data('ujs:enable-with', element[method]());
184
+ element[method](element.data('disable-with'));
185
+ element.attr('disabled', 'disabled');
186
+ });
187
+ },
188
+
189
+ /* Re-enables disabled form elements:
190
+ - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
191
+ - Removes disabled attribute
192
+ */
193
+ enableFormElements: function(form) {
194
+ form.find(rails.enableSelector).each(function() {
195
+ var element = $(this), method = element.is('button') ? 'html' : 'val';
196
+ if (element.data('ujs:enable-with')) element[method](element.data('ujs:enable-with'));
197
+ element.removeAttr('disabled');
198
+ });
199
+ },
200
+
201
+ /* For 'data-confirm' attribute:
202
+ - Fires `confirm` event
203
+ - Shows the confirmation dialog
204
+ - Fires the `confirm:complete` event
205
+
206
+ Returns `true` if no function stops the chain and user chose yes; `false` otherwise.
207
+ Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
208
+ Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
209
+ return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
210
+ */
211
+ allowAction: function(element) {
212
+ var message = element.data('confirm'),
213
+ answer = false, callback;
214
+ if (!message) { return true; }
215
+
216
+ if (rails.fire(element, 'confirm')) {
217
+ answer = rails.confirm(message);
218
+ callback = rails.fire(element, 'confirm:complete', [answer]);
219
+ }
220
+ return answer && callback;
221
+ },
222
+
223
+ // Helper function which checks for blank inputs in a form that match the specified CSS selector
224
+ blankInputs: function(form, specifiedSelector, nonBlank) {
225
+ var inputs = $(), input,
226
+ selector = specifiedSelector || 'input,textarea';
227
+ form.find(selector).each(function() {
228
+ input = $(this);
229
+ // Collect non-blank inputs if nonBlank option is true, otherwise, collect blank inputs
230
+ if (nonBlank ? input.val() : !input.val()) {
231
+ inputs = inputs.add(input);
232
+ }
233
+ });
234
+ return inputs.length ? inputs : false;
235
+ },
236
+
237
+ // Helper function which checks for non-blank inputs in a form that match the specified CSS selector
238
+ nonBlankInputs: function(form, specifiedSelector) {
239
+ return rails.blankInputs(form, specifiedSelector, true); // true specifies nonBlank
240
+ },
241
+
242
+ // Helper function, needed to provide consistent behavior in IE
243
+ stopEverything: function(e) {
244
+ $(e.target).trigger('ujs:everythingStopped');
245
+ e.stopImmediatePropagation();
246
+ return false;
247
+ },
248
+
249
+ // find all the submit events directly bound to the form and
250
+ // manually invoke them. If anyone returns false then stop the loop
251
+ callFormSubmitBindings: function(form) {
252
+ var events = form.data('events'), continuePropagation = true;
253
+ if (events !== undefined && events['submit'] !== undefined) {
254
+ $.each(events['submit'], function(i, obj){
255
+ if (typeof obj.handler === 'function') return continuePropagation = obj.handler(obj.data);
256
+ });
257
+ }
258
+ return continuePropagation;
259
+ },
260
+
261
+ // replace element's html with the 'data-disable-with' after storing original html
262
+ // and prevent clicking on it
263
+ disableElement: function(element) {
264
+ element.data('ujs:enable-with', element.html()); // store enabled state
265
+ element.html(element.data('disable-with')); // set to disabled state
266
+ element.bind('click.railsDisable', function(e) { // prevent further clicking
267
+ return rails.stopEverything(e)
268
+ });
269
+ },
270
+
271
+ // restore element to its original state which was disabled by 'disableElement' above
272
+ enableElement: function(element) {
273
+ if (element.data('ujs:enable-with') !== undefined) {
274
+ element.html(element.data('ujs:enable-with')); // set to old enabled state
275
+ // this should be element.removeData('ujs:enable-with')
276
+ // but, there is currently a bug in jquery which makes hyphenated data attributes not get removed
277
+ element.data('ujs:enable-with', false); // clean up cache
278
+ }
279
+ element.unbind('click.railsDisable'); // enable element
280
+ }
281
+
282
+ };
283
+
284
+ $.ajaxPrefilter(function(options, originalOptions, xhr){ if ( !options.crossDomain ) { rails.CSRFProtection(xhr); }});
285
+
286
+ $(rails.linkDisableSelector).live('ajax:complete', function() {
287
+ rails.enableElement($(this));
288
+ });
289
+
290
+ $(rails.linkClickSelector).live('click.rails', function(e) {
291
+ var link = $(this);
292
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
293
+
294
+ if (link.is(rails.linkDisableSelector)) rails.disableElement(link);
295
+
296
+ if (link.data('remote') !== undefined) {
297
+ rails.handleRemote(link);
298
+ return false;
299
+ } else if (link.data('method')) {
300
+ rails.handleMethod(link);
301
+ return false;
302
+ }
303
+ });
304
+
305
+ $(rails.inputChangeSelector).live('change.rails', function(e) {
306
+ var link = $(this);
307
+ if (!rails.allowAction(link)) return rails.stopEverything(e);
308
+
309
+ rails.handleRemote(link);
310
+ return false;
311
+ });
312
+
313
+ $(rails.formSubmitSelector).live('submit.rails', function(e) {
314
+ var form = $(this),
315
+ remote = form.data('remote') !== undefined,
316
+ blankRequiredInputs = rails.blankInputs(form, rails.requiredInputSelector),
317
+ nonBlankFileInputs = rails.nonBlankInputs(form, rails.fileInputSelector);
318
+
319
+ if (!rails.allowAction(form)) return rails.stopEverything(e);
320
+
321
+ // skip other logic when required values are missing or file upload is present
322
+ if (blankRequiredInputs && form.attr("novalidate") == undefined && rails.fire(form, 'ajax:aborted:required', [blankRequiredInputs])) {
323
+ return rails.stopEverything(e);
324
+ }
325
+
326
+ if (remote) {
327
+ if (nonBlankFileInputs) {
328
+ return rails.fire(form, 'ajax:aborted:file', [nonBlankFileInputs]);
329
+ }
330
+
331
+ // If browser does not support submit bubbling, then this live-binding will be called before direct
332
+ // bindings. Therefore, we should directly call any direct bindings before remotely submitting form.
333
+ if (!$.support.submitBubbles && rails.callFormSubmitBindings(form) === false) return rails.stopEverything(e);
334
+
335
+ rails.handleRemote(form);
336
+ return false;
337
+ } else {
338
+ // slight timeout so that the submit button gets properly serialized
339
+ setTimeout(function(){ rails.disableFormElements(form); }, 13);
340
+ }
341
+ });
342
+
343
+ $(rails.formInputClickSelector).live('click.rails', function(event) {
344
+ var button = $(this);
345
+
346
+ if (!rails.allowAction(button)) return rails.stopEverything(event);
347
+
348
+ // register the pressed submit button
349
+ var name = button.attr('name'),
350
+ data = name ? {name:name, value:button.val()} : null;
351
+
352
+ button.closest('form').data('ujs:submit-button', data);
353
+ });
354
+
355
+ $(rails.formSubmitSelector).live('ajax:beforeSend.rails', function(event) {
356
+ if (this == event.target) rails.disableFormElements($(this));
357
+ });
358
+
359
+ $(rails.formSubmitSelector).live('ajax:complete.rails', function(event) {
360
+ if (this == event.target) rails.enableFormElements($(this));
361
+ });
362
+
363
+ })( jQuery );
File without changes
@@ -0,0 +1,9 @@
1
+ %article.file[file]
2
+ %div
3
+ .path= file.path
4
+ .elapsed= file.elapsed
5
+ .clearfix
6
+ .percentage_img_container
7
+ .percentage
8
+ %img.percentage_img{src: asset_path('percentage.png'), alt: "#{file.percent}% of total time", 'data-width' => file.percent}/
9
+ .clearfix
@@ -0,0 +1,13 @@
1
+ %article.project[project]
2
+ .name
3
+ %a{href: path_to(:project).with(project.id), alt: project.path}
4
+ = project.name.camelcase
5
+ .percentage_img_container
6
+ .percentage
7
+ -# TODO: Move this to the presenter after having resolved the presenter
8
+ -# issues, in Draper I can't access helpers for some reason
9
+ - max_elapsed = ::WatchTower::Server::Project.order('elapsed_time DESC').first.elapsed_time
10
+ - percentage = (project.elapsed_time * 100 / max_elapsed).to_i rescue 100
11
+ %img.percentage_img{src: asset_path('percentage.png'), 'data-width' => percentage}/
12
+ .elapsed= project.elapsed
13
+ .clearfix
@@ -0,0 +1,7 @@
1
+ %section#projects
2
+ %header
3
+ .name Project
4
+ .percentage Percentage
5
+ .elapsed Total time
6
+ .clearfix
7
+ = partial('project', collection: @projects)
@@ -0,0 +1,32 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ / Title
5
+ %title= "Watch Tower - #{@title}"
6
+
7
+ / Meta tags
8
+ %meta{:charset => "utf-8"}/
9
+ %meta{:content => "IE=edge,chrome=1", "http-equiv" => "X-UA-Compatible"}/
10
+ %meta{:content => "width=device-width,initial-scale=1", :name => "viewport"}/
11
+
12
+ / Link tags
13
+ %link{:href => asset_path('application.css'), :rel => "stylesheet"}/
14
+
15
+ / Script tahs
16
+ %script{:src => asset_path('application.js')}
17
+ %body
18
+ %section#wrapper
19
+
20
+ %header
21
+ #logo
22
+ %a{href: "/"}
23
+ %h1 Watch Tower
24
+
25
+ %section#main
26
+ = yield
27
+
28
+ %section#footer
29
+ WatchTower
30
+ -
31
+ %a{href: 'http://www.technogate.fr'}
32
+ TechnoGate
@@ -0,0 +1,12 @@
1
+ %article#project
2
+ %header
3
+ %h1.project_name= @project.name.camelcase
4
+ %h2.project_path= @project.path
5
+ %section#files
6
+ %header
7
+ .path
8
+ File path
9
+ .elapsed
10
+ Total Time
11
+ .clearfix
12
+ = partial('file', collection: FileDecorator.decorate(@project.files.worked_on))
@@ -0,0 +1,146 @@
1
+ ---
2
+ watch_tower:
3
+ # Change this to true once you have reviewed the file
4
+ enabled: false
5
+
6
+ # Start WatchTower on boot?
7
+ launch_on_boot: true
8
+
9
+ # Where do you store your code?
10
+ # This is not needed if all your projects are using Git for versioning control
11
+ # as the project's name and path would be determined from the folder name that
12
+ # contains the .git folder, if that's the case, wherever you store your code,
13
+ # the project name and path can be retrived very easily.
14
+ code_path: ~/Code
15
+
16
+ # How do you nest your code folder?
17
+ # Again this setting is only effective if not all your projects are using Git
18
+ # For example consider this layout of the code folder
19
+ #
20
+ # ~/Code/
21
+ # Clients/
22
+ # AcmeCorp/
23
+ # website/
24
+ # intranet
25
+ # BetaCorp/
26
+ # skunkworks/
27
+ # OpenSource/
28
+ # project_one/
29
+ # timetap/
30
+ #
31
+ # A nested_project_layers setting of 2 would mean we track "AcmeCorp", "BetaCorp", and everything
32
+ # under OpenSource, as their own projects
33
+ #
34
+ nested_project_layers: 2
35
+
36
+ # Pause time, default: 30 minutes
37
+ # The pause time is the time to be considered as a pause time, if you save
38
+ # a file 31 minutes after your last save, the time passed won't be considered
39
+ # as a work time and thus won't be counted in the elapsed time of the project
40
+ pause_time: 30.minutes
41
+
42
+ # Database configuration
43
+ # Uncomment the section best fit for your adapter and edit it
44
+ database:
45
+ # MySQL adapter
46
+ #
47
+ # Ruby:
48
+ # Make sure you have the mysql2 gem
49
+ # gem install mysql2
50
+ #
51
+ # Jruby:
52
+ # Make sure you have the activerecord-jdbcmysql-adapter gem
53
+ # gem install activerecord-jdbcmysql-adapter
54
+ # And replace mysql2 with jdbcmysql
55
+ #
56
+ # development:
57
+ # adapter: mysql2
58
+ # encoding: utf8
59
+ # reconnect: false
60
+ # database: watch_tower_development
61
+ # pool: 5
62
+ # username: root
63
+ # password:
64
+ # socket: /tmp/mysql.sock
65
+ # test:
66
+ # adapter: mysql2
67
+ # encoding: utf8
68
+ # reconnect: false
69
+ # database: watch_tower_test
70
+ # pool: 5
71
+ # username: root
72
+ # password:
73
+ # socket: /tmp/mysql.sock
74
+ # production:
75
+ # adapter: mysql2
76
+ # encoding: utf8
77
+ # reconnect: false
78
+ # database: watch_tower_production
79
+ # pool: 5
80
+ # username: root
81
+ # password:
82
+ # socket: /tmp/mysql.sock
83
+ #
84
+ #
85
+ # PostgresSQL adapter
86
+ #
87
+ # Ruby:
88
+ # Make sure you have the pg adapter installed
89
+ # gem install pg
90
+ #
91
+ # Jruby:
92
+ # Make sure you have the activerecord-jdbcpostgresql-adapter gem
93
+ # gem install activerecord-jdbcpostgresql-adapter
94
+ # And replace postgresql with jdbcpostgresql
95
+ #
96
+ # development:
97
+ # adapter: postgresql
98
+ # encoding: unicode
99
+ # database: watch_tower_development
100
+ # pool: 5
101
+ # username: watch_tower
102
+ # password:
103
+ # test:
104
+ # adapter: postgresql
105
+ # encoding: unicode
106
+ # database: watch_tower_test
107
+ # pool: 5
108
+ # username: watch_tower
109
+ # password:
110
+ #
111
+ # production:
112
+ # adapter: postgresql
113
+ # encoding: unicode
114
+ # database: watch_tower_production
115
+ # pool: 5
116
+ # username: watch_tower
117
+ # password:
118
+ #
119
+ #
120
+ # Sqlite3 Adapter
121
+ #
122
+ # Ruby:
123
+ # Make sure you have the sqlite3
124
+ # gem install sqlite3
125
+ #
126
+ # Jruby:
127
+ # Make sure you have the activerecord-jdbcsqlite3-adapter gem
128
+ # gem install activerecord-jdbcsqlite3-adapter
129
+ # And replace sqlite3 with jdbcsqlite3
130
+ #
131
+ # development:
132
+ # adapter: sqlite3
133
+ # database: ~/.watch_tower/databases/development.sqlite3
134
+ # pool: 5
135
+ # timeout: 5000
136
+ # test:
137
+ # adapter: sqlite3
138
+ # database: ~/.watch_tower/databases/test.sqlite3
139
+ # pool: 5
140
+ # timeout: 5000
141
+ #
142
+ # production:
143
+ # adapter: sqlite3
144
+ # database: ~/.watch_tower/databases/production.sqlite3
145
+ # pool: 5
146
+ # timeout: 5000