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,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