Pratt 1.5.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (283) hide show
  1. data/.exrc +61 -0
  2. data/.gitignore +4 -0
  3. data/History.txt +6 -0
  4. data/Manifest.txt +46 -0
  5. data/Pratt.gemspec +351 -0
  6. data/README.txt +66 -0
  7. data/Rakefile +85 -0
  8. data/TODO +54 -0
  9. data/VERSION +1 -0
  10. data/bin/pratt.rb +13 -0
  11. data/config.rb +34 -0
  12. data/lib/pratt.rb +527 -0
  13. data/lib/pratt/array.rb +11 -0
  14. data/lib/pratt/string.rb +18 -0
  15. data/models/app.rb +40 -0
  16. data/models/customer.rb +24 -0
  17. data/models/payment.rb +22 -0
  18. data/models/pratt.rb +19 -0
  19. data/models/project.rb +82 -0
  20. data/models/whence.rb +70 -0
  21. data/pkgs/tile-0.8.2.tar.gz +0 -0
  22. data/pkgs/tile-0.8.2/ANNOUNCE.txt +95 -0
  23. data/pkgs/tile-0.8.2/ChangeLog +4651 -0
  24. data/pkgs/tile-0.8.2/Makefile +250 -0
  25. data/pkgs/tile-0.8.2/Makefile.in +250 -0
  26. data/pkgs/tile-0.8.2/README.txt +86 -0
  27. data/pkgs/tile-0.8.2/aclocal.m4 +2 -0
  28. data/pkgs/tile-0.8.2/altTheme.o +0 -0
  29. data/pkgs/tile-0.8.2/blink.o +0 -0
  30. data/pkgs/tile-0.8.2/button.o +0 -0
  31. data/pkgs/tile-0.8.2/cache.o +0 -0
  32. data/pkgs/tile-0.8.2/clamTheme.o +0 -0
  33. data/pkgs/tile-0.8.2/classicTheme.o +0 -0
  34. data/pkgs/tile-0.8.2/config.log +1330 -0
  35. data/pkgs/tile-0.8.2/config.status +795 -0
  36. data/pkgs/tile-0.8.2/configure +15248 -0
  37. data/pkgs/tile-0.8.2/configure.in +89 -0
  38. data/pkgs/tile-0.8.2/demos/autocomplete.tcl +59 -0
  39. data/pkgs/tile-0.8.2/demos/demo.tcl +870 -0
  40. data/pkgs/tile-0.8.2/demos/dirbrowser.tcl +167 -0
  41. data/pkgs/tile-0.8.2/demos/dlgtest.tcl +97 -0
  42. data/pkgs/tile-0.8.2/demos/iconlib.tcl +110 -0
  43. data/pkgs/tile-0.8.2/demos/repeater.tcl +117 -0
  44. data/pkgs/tile-0.8.2/demos/toolbutton.tcl +101 -0
  45. data/pkgs/tile-0.8.2/doc/Geometry.3 +230 -0
  46. data/pkgs/tile-0.8.2/doc/INDEX.MAP +153 -0
  47. data/pkgs/tile-0.8.2/doc/Makefile +36 -0
  48. data/pkgs/tile-0.8.2/doc/TILE.XML +45 -0
  49. data/pkgs/tile-0.8.2/doc/Theme.3 +34 -0
  50. data/pkgs/tile-0.8.2/doc/button.n +75 -0
  51. data/pkgs/tile-0.8.2/doc/checkbutton.n +61 -0
  52. data/pkgs/tile-0.8.2/doc/combobox.n +98 -0
  53. data/pkgs/tile-0.8.2/doc/converting.txt +97 -0
  54. data/pkgs/tile-0.8.2/doc/dialog.n +122 -0
  55. data/pkgs/tile-0.8.2/doc/entry.n +438 -0
  56. data/pkgs/tile-0.8.2/doc/frame.n +43 -0
  57. data/pkgs/tile-0.8.2/doc/html/Geometry.html +304 -0
  58. data/pkgs/tile-0.8.2/doc/html/Theme.html +48 -0
  59. data/pkgs/tile-0.8.2/doc/html/button.html +120 -0
  60. data/pkgs/tile-0.8.2/doc/html/category-index.html +18 -0
  61. data/pkgs/tile-0.8.2/doc/html/checkbutton.html +94 -0
  62. data/pkgs/tile-0.8.2/doc/html/combobox.html +164 -0
  63. data/pkgs/tile-0.8.2/doc/html/converting.txt +97 -0
  64. data/pkgs/tile-0.8.2/doc/html/dialog.html +159 -0
  65. data/pkgs/tile-0.8.2/doc/html/entry.html +613 -0
  66. data/pkgs/tile-0.8.2/doc/html/frame.html +76 -0
  67. data/pkgs/tile-0.8.2/doc/html/image.html +100 -0
  68. data/pkgs/tile-0.8.2/doc/html/index.html +25 -0
  69. data/pkgs/tile-0.8.2/doc/html/keyword-index.html +228 -0
  70. data/pkgs/tile-0.8.2/doc/html/label.html +133 -0
  71. data/pkgs/tile-0.8.2/doc/html/labelframe.html +91 -0
  72. data/pkgs/tile-0.8.2/doc/html/manpage.css +212 -0
  73. data/pkgs/tile-0.8.2/doc/html/menubutton.html +63 -0
  74. data/pkgs/tile-0.8.2/doc/html/notebook.html +280 -0
  75. data/pkgs/tile-0.8.2/doc/html/paned.html +149 -0
  76. data/pkgs/tile-0.8.2/doc/html/progressbar.html +138 -0
  77. data/pkgs/tile-0.8.2/doc/html/radiobutton.html +89 -0
  78. data/pkgs/tile-0.8.2/doc/html/scrollbar.html +221 -0
  79. data/pkgs/tile-0.8.2/doc/html/separator.html +48 -0
  80. data/pkgs/tile-0.8.2/doc/html/sizegrip.html +62 -0
  81. data/pkgs/tile-0.8.2/doc/html/style.html +172 -0
  82. data/pkgs/tile-0.8.2/doc/html/tile-intro.html +164 -0
  83. data/pkgs/tile-0.8.2/doc/html/treeview.html +634 -0
  84. data/pkgs/tile-0.8.2/doc/html/widget.html +342 -0
  85. data/pkgs/tile-0.8.2/doc/image.n +81 -0
  86. data/pkgs/tile-0.8.2/doc/internals.txt +409 -0
  87. data/pkgs/tile-0.8.2/doc/label.n +75 -0
  88. data/pkgs/tile-0.8.2/doc/labelframe.n +64 -0
  89. data/pkgs/tile-0.8.2/doc/man.macros +239 -0
  90. data/pkgs/tile-0.8.2/doc/menubutton.n +41 -0
  91. data/pkgs/tile-0.8.2/doc/notebook.n +188 -0
  92. data/pkgs/tile-0.8.2/doc/paned.n +95 -0
  93. data/pkgs/tile-0.8.2/doc/progressbar.n +79 -0
  94. data/pkgs/tile-0.8.2/doc/radiobutton.n +57 -0
  95. data/pkgs/tile-0.8.2/doc/scrollbar.n +160 -0
  96. data/pkgs/tile-0.8.2/doc/separator.n +30 -0
  97. data/pkgs/tile-0.8.2/doc/sizegrip.n +53 -0
  98. data/pkgs/tile-0.8.2/doc/style.n +119 -0
  99. data/pkgs/tile-0.8.2/doc/tile-intro.n +165 -0
  100. data/pkgs/tile-0.8.2/doc/tmml.options +4 -0
  101. data/pkgs/tile-0.8.2/doc/treeview.n +415 -0
  102. data/pkgs/tile-0.8.2/doc/widget.n +227 -0
  103. data/pkgs/tile-0.8.2/doc/xml/Geometry.tmml +379 -0
  104. data/pkgs/tile-0.8.2/doc/xml/INDEX.MAP +153 -0
  105. data/pkgs/tile-0.8.2/doc/xml/Theme.tmml +63 -0
  106. data/pkgs/tile-0.8.2/doc/xml/button.tmml +134 -0
  107. data/pkgs/tile-0.8.2/doc/xml/checkbutton.tmml +119 -0
  108. data/pkgs/tile-0.8.2/doc/xml/combobox.tmml +184 -0
  109. data/pkgs/tile-0.8.2/doc/xml/dialog.tmml +195 -0
  110. data/pkgs/tile-0.8.2/doc/xml/entry.tmml +630 -0
  111. data/pkgs/tile-0.8.2/doc/xml/frame.tmml +98 -0
  112. data/pkgs/tile-0.8.2/doc/xml/image.tmml +101 -0
  113. data/pkgs/tile-0.8.2/doc/xml/label.tmml +154 -0
  114. data/pkgs/tile-0.8.2/doc/xml/labelframe.tmml +116 -0
  115. data/pkgs/tile-0.8.2/doc/xml/menubutton.tmml +80 -0
  116. data/pkgs/tile-0.8.2/doc/xml/notebook.tmml +306 -0
  117. data/pkgs/tile-0.8.2/doc/xml/paned.tmml +154 -0
  118. data/pkgs/tile-0.8.2/doc/xml/progressbar.tmml +151 -0
  119. data/pkgs/tile-0.8.2/doc/xml/radiobutton.tmml +109 -0
  120. data/pkgs/tile-0.8.2/doc/xml/scrollbar.tmml +233 -0
  121. data/pkgs/tile-0.8.2/doc/xml/separator.tmml +59 -0
  122. data/pkgs/tile-0.8.2/doc/xml/sizegrip.tmml +82 -0
  123. data/pkgs/tile-0.8.2/doc/xml/style.tmml +171 -0
  124. data/pkgs/tile-0.8.2/doc/xml/tile-intro.tmml +192 -0
  125. data/pkgs/tile-0.8.2/doc/xml/treeview.tmml +604 -0
  126. data/pkgs/tile-0.8.2/doc/xml/widget.tmml +372 -0
  127. data/pkgs/tile-0.8.2/entry.o +0 -0
  128. data/pkgs/tile-0.8.2/frame.o +0 -0
  129. data/pkgs/tile-0.8.2/generic/Makefile.in +221 -0
  130. data/pkgs/tile-0.8.2/generic/TODO +493 -0
  131. data/pkgs/tile-0.8.2/generic/altTheme.c +1172 -0
  132. data/pkgs/tile-0.8.2/generic/blink.c +168 -0
  133. data/pkgs/tile-0.8.2/generic/button.c +858 -0
  134. data/pkgs/tile-0.8.2/generic/cache.c +354 -0
  135. data/pkgs/tile-0.8.2/generic/clamTheme.c +974 -0
  136. data/pkgs/tile-0.8.2/generic/classicTheme.c +518 -0
  137. data/pkgs/tile-0.8.2/generic/configure +10334 -0
  138. data/pkgs/tile-0.8.2/generic/configure.in +100 -0
  139. data/pkgs/tile-0.8.2/generic/entry.c +1922 -0
  140. data/pkgs/tile-0.8.2/generic/frame.c +648 -0
  141. data/pkgs/tile-0.8.2/generic/gunk.h +44 -0
  142. data/pkgs/tile-0.8.2/generic/image.c +416 -0
  143. data/pkgs/tile-0.8.2/generic/label.c +663 -0
  144. data/pkgs/tile-0.8.2/generic/layout.c +1215 -0
  145. data/pkgs/tile-0.8.2/generic/manager.c +554 -0
  146. data/pkgs/tile-0.8.2/generic/manager.h +91 -0
  147. data/pkgs/tile-0.8.2/generic/notebook.c +1380 -0
  148. data/pkgs/tile-0.8.2/generic/paned.c +958 -0
  149. data/pkgs/tile-0.8.2/generic/pkgIndex.tcl.in +7 -0
  150. data/pkgs/tile-0.8.2/generic/progress.c +549 -0
  151. data/pkgs/tile-0.8.2/generic/scale.c +526 -0
  152. data/pkgs/tile-0.8.2/generic/scroll.c +253 -0
  153. data/pkgs/tile-0.8.2/generic/scrollbar.c +346 -0
  154. data/pkgs/tile-0.8.2/generic/separator.c +132 -0
  155. data/pkgs/tile-0.8.2/generic/square.c +306 -0
  156. data/pkgs/tile-0.8.2/generic/tagset.c +147 -0
  157. data/pkgs/tile-0.8.2/generic/tile.c +296 -0
  158. data/pkgs/tile-0.8.2/generic/tkElements.c +1280 -0
  159. data/pkgs/tile-0.8.2/generic/tkTheme.c +1708 -0
  160. data/pkgs/tile-0.8.2/generic/tkTheme.h +419 -0
  161. data/pkgs/tile-0.8.2/generic/tkThemeInt.h +45 -0
  162. data/pkgs/tile-0.8.2/generic/tkstate.c +268 -0
  163. data/pkgs/tile-0.8.2/generic/trace.c +145 -0
  164. data/pkgs/tile-0.8.2/generic/track.c +174 -0
  165. data/pkgs/tile-0.8.2/generic/treeview.c +3211 -0
  166. data/pkgs/tile-0.8.2/generic/ttk.decls +154 -0
  167. data/pkgs/tile-0.8.2/generic/ttkDecls.h +340 -0
  168. data/pkgs/tile-0.8.2/generic/ttkStubInit.c +61 -0
  169. data/pkgs/tile-0.8.2/generic/ttkStubLib.c +70 -0
  170. data/pkgs/tile-0.8.2/generic/widget.c +785 -0
  171. data/pkgs/tile-0.8.2/generic/widget.h +263 -0
  172. data/pkgs/tile-0.8.2/image.o +0 -0
  173. data/pkgs/tile-0.8.2/label.o +0 -0
  174. data/pkgs/tile-0.8.2/layout.o +0 -0
  175. data/pkgs/tile-0.8.2/library/altTheme.tcl +101 -0
  176. data/pkgs/tile-0.8.2/library/aquaTheme.tcl +62 -0
  177. data/pkgs/tile-0.8.2/library/button.tcl +85 -0
  178. data/pkgs/tile-0.8.2/library/clamTheme.tcl +139 -0
  179. data/pkgs/tile-0.8.2/library/classicTheme.tcl +108 -0
  180. data/pkgs/tile-0.8.2/library/combobox.tcl +439 -0
  181. data/pkgs/tile-0.8.2/library/cursors.tcl +36 -0
  182. data/pkgs/tile-0.8.2/library/defaults.tcl +118 -0
  183. data/pkgs/tile-0.8.2/library/dialog.tcl +274 -0
  184. data/pkgs/tile-0.8.2/library/entry.tcl +580 -0
  185. data/pkgs/tile-0.8.2/library/fonts.tcl +153 -0
  186. data/pkgs/tile-0.8.2/library/icons.tcl +105 -0
  187. data/pkgs/tile-0.8.2/library/keynav.tcl +192 -0
  188. data/pkgs/tile-0.8.2/library/menubutton.tcl +171 -0
  189. data/pkgs/tile-0.8.2/library/notebook.tcl +193 -0
  190. data/pkgs/tile-0.8.2/library/paned.tcl +87 -0
  191. data/pkgs/tile-0.8.2/library/progress.tcl +51 -0
  192. data/pkgs/tile-0.8.2/library/scale.tcl +54 -0
  193. data/pkgs/tile-0.8.2/library/scrollbar.tcl +125 -0
  194. data/pkgs/tile-0.8.2/library/sizegrip.tcl +77 -0
  195. data/pkgs/tile-0.8.2/library/tile.tcl +211 -0
  196. data/pkgs/tile-0.8.2/library/treeview.tcl +382 -0
  197. data/pkgs/tile-0.8.2/library/utils.tcl +254 -0
  198. data/pkgs/tile-0.8.2/library/winTheme.tcl +77 -0
  199. data/pkgs/tile-0.8.2/library/xpTheme.tcl +63 -0
  200. data/pkgs/tile-0.8.2/libtile0.8.2.so +0 -0
  201. data/pkgs/tile-0.8.2/libttkstub.a +0 -0
  202. data/pkgs/tile-0.8.2/license.terms +24 -0
  203. data/pkgs/tile-0.8.2/macosx/aquaTheme.c +1076 -0
  204. data/pkgs/tile-0.8.2/manager.o +0 -0
  205. data/pkgs/tile-0.8.2/notebook.o +0 -0
  206. data/pkgs/tile-0.8.2/paned.o +0 -0
  207. data/pkgs/tile-0.8.2/pkgIndex.tcl +3 -0
  208. data/pkgs/tile-0.8.2/progress.o +0 -0
  209. data/pkgs/tile-0.8.2/scale.o +0 -0
  210. data/pkgs/tile-0.8.2/scroll.o +0 -0
  211. data/pkgs/tile-0.8.2/scrollbar.o +0 -0
  212. data/pkgs/tile-0.8.2/separator.o +0 -0
  213. data/pkgs/tile-0.8.2/tagset.o +0 -0
  214. data/pkgs/tile-0.8.2/tclconfig/install-sh +119 -0
  215. data/pkgs/tile-0.8.2/tclconfig/tcl.m4 +4069 -0
  216. data/pkgs/tile-0.8.2/tclconfig/teax.m4 +109 -0
  217. data/pkgs/tile-0.8.2/tests/all.tcl +18 -0
  218. data/pkgs/tile-0.8.2/tests/bwidget.test +103 -0
  219. data/pkgs/tile-0.8.2/tests/cbtest.tcl +125 -0
  220. data/pkgs/tile-0.8.2/tests/combobox.test +51 -0
  221. data/pkgs/tile-0.8.2/tests/compound.tcl +92 -0
  222. data/pkgs/tile-0.8.2/tests/entry.test +285 -0
  223. data/pkgs/tile-0.8.2/tests/entrytest.tcl +78 -0
  224. data/pkgs/tile-0.8.2/tests/image.test +94 -0
  225. data/pkgs/tile-0.8.2/tests/labelframe.tcl +41 -0
  226. data/pkgs/tile-0.8.2/tests/labelframe.test +137 -0
  227. data/pkgs/tile-0.8.2/tests/layout.test +33 -0
  228. data/pkgs/tile-0.8.2/tests/misc.test +35 -0
  229. data/pkgs/tile-0.8.2/tests/nbtest.tcl +66 -0
  230. data/pkgs/tile-0.8.2/tests/notebook.test +500 -0
  231. data/pkgs/tile-0.8.2/tests/paned.test +298 -0
  232. data/pkgs/tile-0.8.2/tests/progress.test +92 -0
  233. data/pkgs/tile-0.8.2/tests/pwtest.tcl +90 -0
  234. data/pkgs/tile-0.8.2/tests/sbtest.tcl +79 -0
  235. data/pkgs/tile-0.8.2/tests/scrollbar.test +77 -0
  236. data/pkgs/tile-0.8.2/tests/sgtest.tcl +52 -0
  237. data/pkgs/tile-0.8.2/tests/testutils.tcl +20 -0
  238. data/pkgs/tile-0.8.2/tests/tile.test +674 -0
  239. data/pkgs/tile-0.8.2/tests/treetags.test +78 -0
  240. data/pkgs/tile-0.8.2/tests/treeview.test +563 -0
  241. data/pkgs/tile-0.8.2/tests/tvtest.tcl +332 -0
  242. data/pkgs/tile-0.8.2/tests/validate.test +278 -0
  243. data/pkgs/tile-0.8.2/tile.o +0 -0
  244. data/pkgs/tile-0.8.2/tkElements.o +0 -0
  245. data/pkgs/tile-0.8.2/tkTheme.o +0 -0
  246. data/pkgs/tile-0.8.2/tkstate.o +0 -0
  247. data/pkgs/tile-0.8.2/tools/genStubs.tcl +861 -0
  248. data/pkgs/tile-0.8.2/trace.o +0 -0
  249. data/pkgs/tile-0.8.2/track.o +0 -0
  250. data/pkgs/tile-0.8.2/treeview.o +0 -0
  251. data/pkgs/tile-0.8.2/ttkStubInit.o +0 -0
  252. data/pkgs/tile-0.8.2/ttkStubLib.o +0 -0
  253. data/pkgs/tile-0.8.2/widget.o +0 -0
  254. data/pkgs/tile-0.8.2/win/Tile.dsp +261 -0
  255. data/pkgs/tile-0.8.2/win/makefile.vc +527 -0
  256. data/pkgs/tile-0.8.2/win/monitor.c +164 -0
  257. data/pkgs/tile-0.8.2/win/nmakehlp.c +483 -0
  258. data/pkgs/tile-0.8.2/win/rules.vc +512 -0
  259. data/pkgs/tile-0.8.2/win/tile.rc +40 -0
  260. data/pkgs/tile-0.8.2/win/winTheme.c +734 -0
  261. data/pkgs/tile-0.8.2/win/xpTheme.c +1029 -0
  262. data/spec/app_spec.rb +48 -0
  263. data/spec/customer_spec.rb +31 -0
  264. data/spec/fixtures/graph.expectation +18 -0
  265. data/spec/payment_spec.rb +19 -0
  266. data/spec/pratt_spec.rb +148 -0
  267. data/spec/project_spec.rb +163 -0
  268. data/spec/rcov.opts +0 -0
  269. data/spec/spec.opts +1 -0
  270. data/spec/spec_helper.rb +21 -0
  271. data/spec/whence_spec.rb +54 -0
  272. data/tasks/pratt.rb +84 -0
  273. data/templates/model.eruby +12 -0
  274. data/templates/spec.eruby +8 -0
  275. data/views/env.rb +22 -0
  276. data/views/graph.eruby +20 -0
  277. data/views/invoice.eruby +148 -0
  278. data/views/main.rb +92 -0
  279. data/views/pid.eruby +3 -0
  280. data/views/pop.rb +94 -0
  281. data/views/pop2.rb +75 -0
  282. data/views/raw.eruby +11 -0
  283. metadata +390 -0
@@ -0,0 +1,66 @@
1
+ = pratt
2
+
3
+ * Pratt (url)
4
+
5
+ == DESCRIPTION:
6
+
7
+ Pratt (describe your package)
8
+ (Pro/Re)-Active Time Tracker
9
+
10
+ == FEATURES/PROBLEMS:
11
+
12
+ * (list of features or problems)
13
+ Currently you'll need to manually fix the project data since I haven't
14
+ fully integrated the support for Projects to have customer's and my current
15
+ implementation is shoddy.
16
+
17
+ == SYNOPSIS:
18
+
19
+ * pratt (code sample of usage)
20
+
21
+ ** PRATT_ENV=production pratt -D # daemonize (or if already daemonized init the
22
+ gui)
23
+ ** pratt -h (as you'd expect lists available commands)
24
+
25
+ == REQUIREMENTS:
26
+
27
+ * pratt
28
+ debian: libtcltk-ruby tk-tile libsqlite3-dev
29
+ gems: activerecord sqlite3-ruby rspec mocha
30
+ tk: http://sourceforge.net/project/showfiles.php?group_id=11464&package_id=107795&release_id=562098
31
+
32
+ == INSTALL:
33
+
34
+ * sudo gem install
35
+
36
+ Credits
37
+
38
+ Michael Goff
39
+ Thanks for helping me with some of the workflow details
40
+ Michael and Kent Krenrich
41
+ Thanks for the UI input, suggestions, and testing.
42
+
43
+ == LICENSE:
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2009 QualitySmith LLC
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,85 @@
1
+ # -*- ruby -*-
2
+
3
+ require 'rubygems'
4
+ require 'rake'
5
+ require 'lib/pratt'
6
+ require 'tasks/pratt'
7
+
8
+ task :default => :spec
9
+
10
+ begin
11
+ require 'jeweler'
12
+ Jeweler::Tasks.new do |gem|
13
+ gem.name = Pratt::NAME
14
+ gem.summary = Pratt::SUMMARY
15
+ gem.description = Pratt::DESCRIPTION
16
+ gem.email = "frogstarr78@gmail.com"
17
+ gem.homepage = "http://github.com/frogstarr78/pratt"
18
+ gem.authors = Pratt::AUTHORS
19
+ gem.add_development_dependency 'rspec', '>=1.2.6'
20
+ gem.add_development_dependency 'mocha', '>=0.9.5'
21
+ gem.add_runtime_dependency 'activerecord', '>=2.1.1'
22
+ gem.add_runtime_dependency 'sqlite3-ruby', '>=1.2.4'
23
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
24
+ end
25
+ Jeweler::GemcutterTasks.new
26
+ rescue LoadError
27
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
28
+ end
29
+
30
+ require 'spec/rake/spectask'
31
+ Spec::Rake::SpecTask.new(:spec) do |spec|
32
+ spec.libs << 'lib' << 'spec'
33
+ spec.spec_files = FileList['spec/**/*_spec.rb']
34
+ end
35
+
36
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
37
+ spec.libs << 'lib' << 'spec'
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.rcov = true
40
+ end
41
+
42
+ task :spec => :check_dependencies
43
+
44
+ begin
45
+ require 'reek/rake_task'
46
+ Reek::RakeTask.new do |t|
47
+ t.fail_on_error = true
48
+ t.verbose = false
49
+ t.source_files = 'lib/**/*.rb'
50
+ end
51
+ rescue LoadError
52
+ task :reek do
53
+ abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
54
+ end
55
+ end
56
+
57
+ begin
58
+ require 'roodi'
59
+ require 'roodi_task'
60
+ RoodiTask.new do |t|
61
+ t.verbose = false
62
+ end
63
+ rescue LoadError
64
+ task :roodi do
65
+ abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
66
+ end
67
+ end
68
+
69
+ task :default => :spec
70
+
71
+ require 'rake/rdoctask'
72
+ Rake::RDocTask.new do |rdoc|
73
+ if File.exist?('VERSION')
74
+ version = File.read('VERSION')
75
+ else
76
+ version = ""
77
+ end
78
+
79
+ rdoc.rdoc_dir = 'rdoc'
80
+ rdoc.title = "pratt #{version}"
81
+ rdoc.rdoc_files.include('README*')
82
+ rdoc.rdoc_files.include('lib/**/*.rb')
83
+ end
84
+
85
+ # vim: syntax=Ruby
data/TODO ADDED
@@ -0,0 +1,54 @@
1
+ General:
2
+ [ ] Add environment ability
3
+ [ ] Change to use raw command line arguments / Remove optparse lib
4
+ [ ] Update general package setup (It's messy)
5
+ [?] Add file watching like merb
6
+ [?] Move to Observer Pattern for reporting functionality
7
+
8
+ Invoicing:
9
+ [ ] Add shifty week plugin
10
+ [ ] Add support for variable rates.
11
+
12
+ Templating:
13
+ [ ] Invoices
14
+ [ ] Move other output to template based
15
+
16
+ Data:
17
+ [ ] Change to Use DataMapper
18
+ [?] Put everything in the db? (logs, app locks, etc)
19
+
20
+ GUI:
21
+ [ ] Add TrayIcon.
22
+ [ ] Customer CRUD
23
+ [ ] Payment CRUD
24
+ [ ] Re-design prompts
25
+ [ ] Remove fork in favor of ( eval OR (!?!) GUI classes )
26
+
27
+ Test:
28
+ [ ] Add more I'm not testing everything
29
+ [ ] Flog
30
+ [ ] Flay
31
+
32
+
33
+
34
+ Done:
35
+ [x] Whence.last should be last unended.
36
+ [x] Add date range argument to timed report.
37
+ [x] Add validation for start/end at.
38
+ [x] Manual data manipulation date/time.
39
+ [x] Change pop prompt to show helpful time.
40
+ [x] Change Start/Continue interface.
41
+ [x] Add directoryless agnostic runner.
42
+ [x] Log output.
43
+ [x] Add Quiet option??
44
+ [x] Allow only one prompt at a time.
45
+ [x] Handle stop when there isn't a running project w/out errors
46
+ [x] fix gui so if last_unended is detected app knows which gui to display
47
+ [x] Intelligently detect correctly running stored pid.
48
+ [x] Fix pid directory access.
49
+
50
+ [x] Add support for Customer's model
51
+ [x] Add Template command
52
+ [x] Add Invoice command
53
+ [x] Fix colorability
54
+ [x] Add console command
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.5.6
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'fileutils'
4
+ bin_file = __FILE__
5
+ bin_file = File.symlink?(bin_file) ? File.readlink(bin_file) : bin_file
6
+
7
+ bin_path = File.expand_path( '..', bin_file )
8
+ pratt_dir = File.dirname( bin_path )
9
+ FileUtils.chdir pratt_dir
10
+
11
+ require "lib/pratt"
12
+
13
+ Pratt.parse(ARGV)
@@ -0,0 +1,34 @@
1
+ require 'active_record'
2
+ PRATT_ENV = ENV["PRATT_ENV"] || 'development' unless Object.const_defined? :PRATT_ENV
3
+
4
+ class Pratt
5
+ @@connected_to = nil
6
+ module Test
7
+ end
8
+
9
+ include FileUtils
10
+
11
+ class << self
12
+ def connected_to?
13
+ @@connected_to
14
+ end
15
+
16
+ def connect! to_env = :development
17
+ puts "Connecting to: #{to_env}"
18
+ @@connected_to = "#{to_env}.sqlite3"
19
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3', :database => connected_to? )
20
+ unless File.exists? connected_to?
21
+ Pratt.migrate
22
+ end
23
+ end
24
+
25
+ def connected?
26
+ ActiveRecord::Base.connected?
27
+ end
28
+
29
+ def config= env
30
+ include Pratt::Test
31
+ end
32
+ end
33
+
34
+ end
@@ -0,0 +1,527 @@
1
+ # coding: utf-8
2
+ require 'fileutils'
3
+ require 'optparse'
4
+ require 'pathname'
5
+
6
+ require 'rubygems'
7
+ require 'colored'
8
+ require 'chronic'
9
+ require 'active_record'
10
+ require 'erubis'
11
+ require 'hoe'
12
+ require 'mocha'
13
+ require 'config'
14
+
15
+ require 'lib/pratt/array'
16
+ require 'lib/pratt/string'
17
+
18
+ require 'models/app'
19
+ require 'models/customer'
20
+ require 'models/whence'
21
+ require 'models/project'
22
+ require 'models/payment'
23
+
24
+ class Pratt
25
+
26
+ NAME = 'Pratt'
27
+ URL = 'http://www.frogstarr78.com/projects/pratt'
28
+ AUTHORS = ['Scott Noel-Hemming']
29
+ SUMMARY = "Pro/Re-Active Time Tracker. Track time based on what you expect to be working on, with frequent prompts to ensure accuracy."
30
+ DESCRIPTION = %q|
31
+ Need a way to keep track of your time, but get caught up in work? Or constant interruptions?
32
+ Yeah you know who I'm talking about. Those people from the [abc] department always "NEEDING xyz FEATURE NOW!!!".
33
+ Seriously though. I'm usually just loose track of time. I wanted an app that I could start with a task I think
34
+ I'll be working on, but that get's in my face constantly to ensure I'm still working on it. And if I'm not any longer,
35
+ provides an easy way of changing to another task, or if I have changed tasks and not already updated the app, would
36
+ provide an easy way of changing the task of the previously recorded interval. That's what this is intended to do.
37
+
38
+ Time Tracking.
39
+ Proactively set what you expect to work on.
40
+ Reactively modify what you are no longer working on.
41
+ |
42
+ DEPENDENCIES = ["activerecord >=2.1.1", "sqlite3-ruby >=1.2.4", "rspec >=1.2.6", "mocha >=0.9.5"]
43
+ VERSION = File.open( File.join(Dir.pwd, 'VERSION') ).read.strip
44
+
45
+ PID_FILE='pratt.pid'
46
+ FMT = "%a %X %b %d %Y"
47
+ INVOICE_FMT = "%x"
48
+ @@color = true
49
+
50
+ attr_accessor :when_to, :scale, :color, :show_all, :env, :raw_conditions, :template
51
+ attr_reader :project, :todo
52
+ def initialize proj = nil #, when_to
53
+ @when_to = Time.now
54
+ @week = false
55
+ @day = false
56
+ @todo = []
57
+ @scale = nil
58
+ @show_all = false
59
+ @template = nil
60
+ @env = :development
61
+ @raw_conditions = ''
62
+ self.project = proj unless proj.nil?
63
+ end
64
+
65
+ def project= proj
66
+ if proj.is_a?(Project)
67
+ @project = proj
68
+ else
69
+ @project = Project.find_or_create_by_name( { :name => proj } )
70
+ end
71
+ end
72
+
73
+ def << what
74
+ @todo << what
75
+ end
76
+
77
+ def app
78
+ @app ||= App.last
79
+ @app
80
+ end
81
+
82
+ def graph
83
+ @primary = @off_total = @rest_total = 0.0
84
+ self.template = 'graph'
85
+
86
+ if project?
87
+ @projects = [project]
88
+
89
+ @primary = project.time_spent(scale, when_to)
90
+ @scaled_total = project.whences.time_spent(scale, when_to)
91
+ else
92
+ @projects = Project.all
93
+
94
+ @projects.each do |proj|
95
+ @primary = proj.time_spent(scale, when_to) if proj.name == Project.primary.name
96
+ @off_total = proj.time_spent(scale, when_to) if proj.name == Project.off.name
97
+ @rest_total += proj.time_spent(scale, when_to) if Project.rest.collect(&:name).include?(proj.name)
98
+ end
99
+ @scaled_total = Whence.time_spent(scale, when_to)-@off_total
100
+ end
101
+
102
+ if @primary + @off_total + @rest_total > 0.0
103
+ process_template!
104
+ else
105
+ "No data to report"
106
+ end
107
+ end
108
+
109
+ def invoice
110
+ self.template = 'invoice'
111
+
112
+ if project?
113
+ @projects = [project]
114
+
115
+ @total = project.time_spent(scale, when_to)
116
+ else
117
+ @projects = (Project.all - [Project.primary, Project.off])
118
+ @projects.select! {|proj| show_all or ( !show_all and proj.time_spent(scale, when_to) != 0.0 ) }
119
+
120
+ @total = @projects.inject 0.0 do |total, proj|
121
+ total += proj.time_spent(scale, when_to)
122
+ total
123
+ end
124
+ end
125
+
126
+ @projects.each do |project|
127
+ puts "#{project.name} #{project.payment.inspect}"
128
+ end
129
+ if @total > 0.0
130
+ puts process_template!
131
+ else
132
+ puts "No data to report"
133
+ end
134
+ end
135
+
136
+ def console options = []
137
+ options << %w(-r\ irb/completion -r\ lib/pratt --simple-prompt)
138
+ exec "irb #{options.join ' '}"
139
+ end
140
+
141
+ def current
142
+ project_names = ([Project.primary, Project.off] | Project.rest).collect(&:name)
143
+
144
+ if last_whence = Whence.last_unended || Whence.last
145
+ puts " projects: " << (
146
+ project_names.collect {|project_name| "'#{project_name.send(last_whence.end_at.nil? && last_whence.project.name == project_name ? :green : :magenta)}'" }
147
+ ) * ' '
148
+ if last_whence.end_at.nil?
149
+ puts " started: #{last_whence.start_at.strftime(FMT).send(:blue)}"
150
+ time_til = ( app.interval - ( Time.now - last_whence.start_at ) )
151
+ puts "next prompt: %s %s"% [Pratt.send( :fmt_i, time_til / 60.0, 'min', :yellow ), Pratt.send( :fmt_i, time_til % 60, 'sec', :yellow ), ], ''
152
+ end
153
+ else
154
+ puts " projects: " << (
155
+ project_names.collect do |project_name|
156
+ "'#{project_name.magenta}'"
157
+ end
158
+ ) * ' '
159
+ end
160
+ end
161
+
162
+ def begin
163
+ self.project.start! when_to
164
+ end
165
+ def restart
166
+ if project?
167
+ project.restart! when_to
168
+ else
169
+ Whence.last_unended.project.restart! when_to
170
+ end
171
+ end
172
+ def end
173
+ if project?
174
+ project.stop! when_to
175
+ else
176
+ Whence.last_unended.stop! when_to
177
+ end
178
+ end
179
+ def change
180
+ Whence.last.change! project.name
181
+ end
182
+ def destroy
183
+ project.destroy
184
+ end
185
+
186
+ def cpid
187
+ `pgrep -fl 'bin/pratt'`.chomp.split(' ').first
188
+ end
189
+
190
+ def pid
191
+ self.template = 'pid'
192
+ puts process_template!
193
+ end
194
+
195
+ def raw
196
+ self.template = 'raw'
197
+
198
+ if project = Project.find_by_name( raw_conditions )
199
+ @whences = project.whences.all
200
+ else
201
+ case raw_conditions
202
+ when 'all'
203
+ @whences = Whence.find raw_conditions.to_sym
204
+ when /^last$/, 'first'
205
+ @whences = [Whence.find raw_conditions.to_sym]
206
+ when /last[\(\s]?(\d+)[\)\s]?/
207
+ @whences = Whence.all.last($1.to_i)
208
+ else
209
+ @whences = Whence.all :conditions => ["start_at > ?", Chronic.parse("today 00:00")]
210
+ end
211
+ end
212
+ @whences.sort_by(&:id)
213
+ puts process_template!
214
+ end
215
+
216
+ def quit
217
+ project.stop! if project? and project.whences.last_unended
218
+ Whence.last_unended.stop! if Whence.last_unended
219
+ begin
220
+ Process.kill("KILL", app.pid.to_i)
221
+ rescue Errno::ESRCH
222
+ end
223
+ app.pid = ''
224
+ app.gui = ''
225
+ app.save!
226
+ end
227
+
228
+ def gui
229
+ if Whence.last_unended
230
+ pop
231
+ else
232
+ main
233
+ end
234
+ end
235
+
236
+ def show_env
237
+ defork { system("ruby views/env.rb ") }
238
+ end
239
+
240
+ def detect
241
+ if self.daemonized?
242
+ gui
243
+ else
244
+ daemonize!
245
+ end
246
+ end
247
+
248
+ def unlock
249
+ self.app.unlock
250
+ end
251
+
252
+ def run
253
+ self.begin if i_should? :begin
254
+ self.change if i_should? :change
255
+ self.restart if i_should? :restart
256
+ self.end if i_should? :end
257
+
258
+ self.destroy if i_should? :destroy
259
+
260
+ self.pid if i_should? :pid
261
+ self.raw if i_should? :raw
262
+ self.current if i_should? :current
263
+ puts self.graph if i_should? :graph
264
+ self.invoice if i_should? :invoice
265
+ self.console if i_should? :console
266
+ self.gui if i_should? :gui
267
+ self.show_env if i_should? :env
268
+ self.detect if i_should? :detect
269
+ self.unlock if i_should? :unlock
270
+
271
+ self.quit if i_should? :quit
272
+ self.daemonize! if i_should? :daemonize and not self.daemonized?
273
+ end
274
+
275
+ def daemonized?
276
+ !app.pid.blank? and ( cpid.to_i == app.pid )
277
+ end
278
+ def daemonize!
279
+ defork {
280
+ puts "pratt (#{Process.pid.to_s.yellow})"
281
+ app.pid = Process.pid
282
+ app.save!
283
+
284
+ gui
285
+ while(daemonized?)
286
+ sleep(app.interval)
287
+ gui
288
+ end
289
+ quit
290
+ }
291
+ end
292
+
293
+ def max
294
+ self.class.max
295
+ end
296
+
297
+ private
298
+ def main
299
+ self.app.reload
300
+ return if self.app.gui?('main', true)
301
+ self.app.log('main')
302
+ projects = ([Project.primary, Project.off] | Project.rest).collect(&:name)
303
+ if Whence.count == 0
304
+ # first run
305
+ project = Whence.new(:project => Project.primary)
306
+ else
307
+ project = Whence.last_unended || Whence.last
308
+ end
309
+ defork { system("ruby views/main.rb --projects '#{projects*"','"}' --current '#{project.project.name}'") }
310
+ end
311
+ def pop
312
+ self.app.reload
313
+ return if self.app.gui?('pop', true)
314
+ self.app.log('pop')
315
+ self.project = Whence.last_unended.project
316
+ defork { system("ruby views/pop.rb --project '#{project.name}' --start '#{project.whences.last_unended.start_at}' --project_time '#{Pratt.totals(project.time_spent)}'") }
317
+ end
318
+
319
+ def i_should? what
320
+ @todo.include?(what)
321
+ end
322
+
323
+ def project?
324
+ !@project.nil? and @project.name?
325
+ end
326
+
327
+ def process_template!
328
+ input = File.open(Pratt.root("views", "#{template}.eruby").first).read
329
+ erubis = Erubis::Eruby.new(input)
330
+ erubis.evaluate(self)
331
+ end
332
+
333
+ def padded_to_max string
334
+ self.class.padded_to_max string
335
+ end
336
+
337
+ def defork &block
338
+ Process.detach(
339
+ fork &block
340
+ )
341
+ end
342
+
343
+ class << self
344
+
345
+
346
+ def max
347
+ # TODO Fix me
348
+ @max ||= Project.all.inject(0) {|x,p| x = p.name.length if p.name.length > x; x }
349
+ end
350
+
351
+ def color
352
+ @@color
353
+ end
354
+
355
+ def color= c
356
+ @@color = c
357
+ end
358
+
359
+ def color?
360
+ @@color == true
361
+ end
362
+
363
+
364
+ def parse args
365
+ me = Pratt.new
366
+
367
+ opt = OptionParser.new do |opt|
368
+ Pratt.connect! ENV['PRATT_ENV'] || 'development' unless Pratt.connected?
369
+
370
+ opt.on('-b', "--begin PROJECT_NAME", String, "Begin project tracking.") do |proj|
371
+ me.project = proj
372
+ me << :begin
373
+ end
374
+ opt.on('-r', "--restart [PROJECT_NAME]", String, "Restart project log (stop last log and start a new one).
375
+ Applies to last un-ended project, unless a specific project is provided.") do |proj|
376
+ me.project = proj
377
+ me << :restart
378
+ end
379
+ opt.on('-e', "--end [PROJECT_NAME]", String, "Stop tracking interval for last project or supplied project if provided.") do |proj|
380
+ me.project = proj
381
+ me << :end
382
+ end
383
+ opt.on('-c', "--change PROJECT_NAME", String, "Change last time interval to this project.") do |proj|
384
+ me.project = proj
385
+ me << :change
386
+ end
387
+
388
+ opt.on('-g', "--graph [PROJECT_NAME]", String, "Display time spent on supplied project or all projects without argument value.") do |proj|
389
+ me.project = proj
390
+ me << :graph
391
+ end
392
+
393
+ opt.on('-I', "--invoice", "Create an invoice.") do
394
+ me << :invoice
395
+ end
396
+
397
+ templates = []
398
+ Pratt.root("views", "*.eruby") {|view| templates << File.basename(view, '.eruby') }
399
+ opt.on('-t', "--template TEMPLATE", templates, "Template to use for displaying work done.
400
+ Available templates are #{templates.to_sentence('or')}.") do |template|
401
+ me.template = template
402
+ end
403
+ opt.on('--destroy PROJECT_NAME', String, "Remove a project.") do |proj|
404
+ me.project = proj
405
+ me << :destroy
406
+ end
407
+
408
+ opt.on '-w', '--when_to TIME', String, 'When to do something.
409
+ (e.g. log time start|stop, or what time interval to graph)
410
+ If graphing, silently ignored w/out scale argument.' do |when_to|
411
+ me.when_to = Chronic.parse(when_to)
412
+ end
413
+ scales = %w(day week month quarter year)
414
+ opt.on('-l', '--scale SCALE', scales, "Granularity of time argument
415
+ Available scales are #{scales.to_sentence('or')}.
416
+ Only applies to graphing.") do |scale|
417
+ me.scale = scale
418
+ end
419
+
420
+ opt.on('-P', '--pid', "Process id display. (Is it still running)") do
421
+ me << :pid
422
+ end
423
+ opt.on('-R', '--raw [CONDITIONS]', "Dump logs (semi-)raw") do |conditions|
424
+ me << :raw
425
+ me.raw_conditions = conditions
426
+ end
427
+ opt.on('-L', '--log', "Redirect errors") do
428
+ FileUtils.mkdir 'log' unless File.exists? 'log'
429
+ $stderr.reopen('log/pratt.log', 'a')
430
+ $stderr.sync = true
431
+ end
432
+
433
+ opt.on('-N', '--no-color', "Display output without color or special characters.") do
434
+ Pratt.color = false
435
+ end
436
+ opt.on('-A', '--show-all', "Display all project regardless of other options.") do
437
+ me.show_all = true
438
+ end
439
+
440
+ opt.on('-C', "--current", "Show available projects and current project (if there is one)") do
441
+ me << :current
442
+ end
443
+
444
+ opt.on('-i', "--interval INTERVAL", Float, "Set the remind interval/min (Only applies to daemonized process).") do |interval|
445
+ me.app.interval = interval
446
+ me.app.save
447
+ end
448
+ opt.on('-D', '--detect', 'Detect appropriate behavior. (Daemonize or Graphical).') do
449
+ me << :detect
450
+ end
451
+ opt.on('-G', '--gui', 'Show "smart" gui.') do
452
+ me << :gui
453
+ end
454
+ opt.on("-d", "--daemonize", "Start daemon.") do
455
+ me << :daemonize
456
+ end
457
+ opt.on('-q', "--quit", "Stop daemon.") do
458
+ me << :quit
459
+ end
460
+ opt.on('-U', '--unlock', "Manually unlock a gui that has died but left it's lock around.") do
461
+ me << :unlock
462
+ end
463
+
464
+ opt.on '-V', '--version' do
465
+ puts "Pro-Reactive Time Tracker [Pratt] (#{VERSION})"
466
+ end
467
+
468
+ opt.parse!
469
+ end
470
+
471
+ me << :env if args.include? 'env'
472
+ me << :console if args.include? 'console'
473
+
474
+ me.run
475
+ end
476
+
477
+ def totals hr, fmt = false
478
+ "#{fmt_i(hr / 24, 'day', :cyan)} #{fmt_i(hr % 24, 'hour', :yellow)} #{fmt_i((60*(hr -= hr.to_i)), 'min', :green)}"
479
+ end
480
+
481
+ def percent label, off, total, color
482
+ fmt_f((off/total)*100, label, color)
483
+ end
484
+
485
+ def root *globs, &block
486
+ root = Dir.pwd
487
+ if globs.empty?
488
+ subdir = root
489
+ else
490
+ subdir = File.join(root, *globs)
491
+ end
492
+
493
+ if block_given?
494
+ Pathname.glob(subdir) {|dir_files| yield dir_files }
495
+ else
496
+ Pathname.glob(subdir)
497
+ end
498
+ end
499
+
500
+
501
+ def padded_to_max string
502
+ "%#{max}.#{max}s"% string
503
+ end
504
+
505
+ def migrate
506
+ Pratt.root( 'models', '*.rb' ) do |model_file|
507
+ klass = File.basename( model_file, '.rb' ).capitalize.constantize
508
+ begin
509
+ ActiveRecord::Base.connection.table_structure(model_file)
510
+ rescue ActiveRecord::StatementInvalid
511
+ klass.migrate :up if klass.superclass == ActiveRecord::Base
512
+ end
513
+ end
514
+ end
515
+
516
+ private
517
+
518
+ def fmt_f flt, label, color
519
+ padded_to_max(label) << " " << (("%0.2f"% flt) << '%').send(color)
520
+ end
521
+
522
+ def fmt_i int, label, color
523
+ "%s #{label}"% [("%02i"% int).send(color), label]
524
+ end
525
+
526
+ end
527
+ end