time_tap 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+ #
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+ #
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+ #
41
+ # For vim:
42
+ #*.swp
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source "http://rubygems.org"
2
+
3
+ gem 'activesupport', '~> 2.3.8'
4
+ gem 'actionpack', '~> 2.3.8'
5
+ gem 'i18n', '~> 0.3.5'
6
+ gem 'haml'
7
+ gem 'rb-appscript'
8
+ gem 'sinatra'
9
+
10
+ # Add dependencies required to use your gem here.
11
+ # Example:
12
+ # gem "activesupport", ">= 2.3.5"
13
+
14
+ # Add dependencies to develop your gem here.
15
+ # Include everything needed to run rake, tests, features, etc.
16
+ group :development do
17
+ gem "rspec", ">= 2.0.0.beta.19"
18
+ gem "yard", "~> 0.6.0"
19
+ gem "bundler", "~> 1.0.0"
20
+ gem "jeweler", "~> 1.5.0.pre3"
21
+ gem "rcov", ">= 0"
22
+ end
@@ -0,0 +1,48 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ actionpack (2.3.9)
5
+ activesupport (= 2.3.9)
6
+ rack (~> 1.1.0)
7
+ activesupport (2.3.9)
8
+ diff-lcs (1.1.2)
9
+ git (1.2.5)
10
+ haml (3.0.21)
11
+ i18n (0.3.7)
12
+ jeweler (1.5.0.pre3)
13
+ bundler (~> 1.0.0)
14
+ git (>= 1.2.5)
15
+ rake
16
+ rack (1.1.0)
17
+ rake (0.8.7)
18
+ rb-appscript (0.5.3)
19
+ rcov (0.9.9)
20
+ rspec (2.0.0.rc)
21
+ rspec-core (= 2.0.0.rc)
22
+ rspec-expectations (= 2.0.0.rc)
23
+ rspec-mocks (= 2.0.0.rc)
24
+ rspec-core (2.0.0.rc)
25
+ rspec-expectations (2.0.0.rc)
26
+ diff-lcs (>= 1.1.2)
27
+ rspec-mocks (2.0.0.rc)
28
+ rspec-core (= 2.0.0.rc)
29
+ rspec-expectations (= 2.0.0.rc)
30
+ sinatra (1.0)
31
+ rack (>= 1.0)
32
+ yard (0.6.1)
33
+
34
+ PLATFORMS
35
+ ruby
36
+
37
+ DEPENDENCIES
38
+ actionpack (~> 2.3.8)
39
+ activesupport (~> 2.3.8)
40
+ bundler (~> 1.0.0)
41
+ haml
42
+ i18n (~> 0.3.5)
43
+ jeweler (~> 1.5.0.pre3)
44
+ rb-appscript
45
+ rcov
46
+ rspec (>= 2.0.0.beta.19)
47
+ sinatra
48
+ yard (~> 0.6.0)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Elia Schito
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,96 @@
1
+ # TimeTap
2
+
3
+ TimeTap helps you track the time you spend coding on each project while in TextMate.
4
+
5
+ Once it's launched you don't have to bother anymore starting/stopping timers or
6
+ inventing some arbitrary amount of time to fill your fancy time tracker.
7
+
8
+ <img src="http://f.cl.ly/items/17025fecf7189518cf07/timetap-project-list.png"/>
9
+ <img src="http://f.cl.ly/items/7b96ad2f7b49a95fdfd0/timetap-project-page.png"/>
10
+
11
+
12
+ ## How does it work
13
+
14
+ TimeTap keeps an eye on (tracks) the modification time of the frontmost file
15
+ and tells you how much time you spent on each project.
16
+
17
+ If you stop coding for a while while squeezing your brains TimeTap understands.
18
+ TimeTap will consider "coding time" pauses to up to 30 minutes between to saves
19
+ in the same project.
20
+
21
+ Technically it saves a timestamp+path of the frontmost file in TextMate every
22
+ 30 seconds, then it digests all this information in a nice Sinatra webapp.
23
+
24
+ The server will respond on http://0.0.0.0:1111/.
25
+
26
+
27
+ ## Instructions
28
+
29
+ Run `ruby -Ilib bin/timetap` or run
30
+ `rake launcher && launchctl load ~/Library/LaunchAgents`
31
+ to add a plist for OSX's launchd and have it launched automatically at login.
32
+
33
+ ## Setting up config file
34
+
35
+ timetap uses a config file to control where projects are kept, etc. Put this config file in `~/.tap_config`.
36
+
37
+ cp config.yaml ~/.tap_config
38
+
39
+ ### Keys Explained
40
+
41
+ root - where the timetap logs should be saved. Recommended value: ~
42
+ code - where all you project live, in a flat hierachy.
43
+ nested_project_layers - see below, on nested projects. Default is 1
44
+
45
+
46
+ Nested Projects allows you to keep your projects inside a hierarchy, instead of the original assumption of timetap (which is that all projects are flat).
47
+
48
+ For example, you could keep your directory structure might look like:
49
+
50
+ ~/Code/
51
+ Clients/
52
+ AcmeCorp/
53
+ website/
54
+ intranet
55
+ BetaCorp/
56
+ skunkworks/
57
+ OpenSource/
58
+ project_one/
59
+ timetap/
60
+
61
+ A `nested_project_layers` setting of 2 (in your `.tap_config` file) would mean we track "AcmeCorp", "BetaCorp", and everything under OpenSource, as their own projects
62
+
63
+ ## Assumptions
64
+
65
+ * You code on TextMate.
66
+ * You save often (like me), at least every 30 minutes.
67
+ * You keep your code organized (I use ~/Code as main code folder).
68
+
69
+
70
+ ## TODO
71
+
72
+ - support other text editors, or at least make it easy to do so
73
+ - (r)spec it!
74
+ - make it more configurable
75
+ - gemify (with jeweler)
76
+ - flatten encoding quick-fixes with proper solutions (eat and spit only utf8)
77
+ - integration with external (online) time tracking tools
78
+ - export to csv (?)
79
+
80
+
81
+ ## How to Contribute
82
+
83
+ Use it, love it, then...
84
+
85
+ * Fork the project.
86
+ * Make your feature addition or bug fix.
87
+ * Add tests for it. This is important so I don't break it in a
88
+ future version unintentionally.
89
+ * Commit, do not mess with rakefile, version, or history.
90
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
91
+ * Send me a pull request. Bonus points for topic branches.
92
+
93
+
94
+ ## Copyright
95
+
96
+ Copyright (c) 2009 Elia Schito. See LICENSE for details.
@@ -0,0 +1,49 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "time_tap"
16
+ gem.summary = %Q{Unobtrusive time tracking for TextMate}
17
+ gem.description = %Q{TimeTap helps you track the time you spend coding on each project while in TextMate.}
18
+ gem.email = "perlelia@gmail.com"
19
+ gem.homepage = "http://github.com/elia/timetap"
20
+ gem.authors = ["Elia Schito"]
21
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
22
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
23
+ # spec.add_runtime_dependency 'jabber4r', '> 0.1'
24
+ # spec.add_development_dependency 'rspec', '> 1.2.3'
25
+ gem.add_development_dependency "rspec", ">= 2.0.0.beta.19"
26
+ gem.add_development_dependency "yard", "~> 0.6.0"
27
+ gem.add_development_dependency "bundler", "~> 1.0.0"
28
+ gem.add_development_dependency "jeweler", "~> 1.5.0.pre3"
29
+ gem.add_development_dependency "rcov", ">= 0"
30
+ end
31
+ Jeweler::RubygemsDotOrgTasks.new
32
+
33
+ require 'rspec/core'
34
+ require 'rspec/core/rake_task'
35
+ RSpec::Core::RakeTask.new(:spec) do |spec|
36
+ spec.pattern = FileList['spec/**/*_spec.rb']
37
+ end
38
+
39
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
40
+ spec.pattern = 'spec/**/*_spec.rb'
41
+ spec.rcov = true
42
+ end
43
+
44
+ task :default => :spec
45
+
46
+ require 'yard'
47
+ YARD::Rake::YardocTask.new
48
+
49
+ require 'time_tap/tasks'
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.0
@@ -0,0 +1,55 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'time_tap'
5
+
6
+
7
+ require 'yaml'
8
+ user_config = File.expand_path("~/.tap_config")
9
+ config_file = File.expand_path('../../config.yaml', __FILE__)
10
+
11
+ options = YAML.load_file(config_file)
12
+ options.merge! YAML.load_file(user_config) if File.exist?(user_config)
13
+ TimeTap.config = options
14
+
15
+
16
+ require 'optparse'
17
+ OptionParser.new do |opts|
18
+ opts.banner = "Usage: #{$0} [options]"
19
+
20
+ opts.on("-f", "--foreground", "Run in foreground.") do
21
+ TimeTap.config[:foreground] = true
22
+ end
23
+
24
+ opts.on("-p", "--port PORT", "Use specified port for server. (default #{options['port']})") do |value|
25
+ TimeTap.config[:port] = value
26
+ end
27
+
28
+ opts.on("--install", "Launch automatically at login.") do
29
+ TimeTap.install!
30
+ puts "\nInstalled. Now run:\n launchctl load ~/Library/LaunchAgents\n\n"
31
+ exit
32
+ end
33
+
34
+ opts.on("--reload", "Reload login launch agent.") do
35
+ puts 'Reloading...'
36
+ TimeTap.reload!
37
+ exit
38
+ end
39
+ end.parse!
40
+
41
+
42
+ unless TimeTap.config[:foreground]
43
+ require 'time_tap/daemon'
44
+
45
+ pid = fork {
46
+ # Try to replace "ruby" with "TimeTap" in the command string (for "ps -A" & co.)
47
+ $0 = 'TimeTap'
48
+
49
+ Process.daemon(true)
50
+ TimeTap.start
51
+ }
52
+ else
53
+ puts "going foreground"
54
+ TimeTap.start
55
+ end
@@ -0,0 +1,8 @@
1
+ root: "~"
2
+ port: 1111
3
+ code: Code
4
+ nested_project_layers: 1
5
+ ruby: /usr/bin/ruby
6
+ textmate:
7
+ projects: ~/Development/Current Projects
8
+
@@ -0,0 +1,128 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+
5
+ require 'rubygems'
6
+ gem 'activesupport', '~> 2.3.8'
7
+ gem 'actionpack', '~> 2.3.8'
8
+ gem 'i18n', '~> 0.3.5'
9
+ gem 'haml'
10
+ gem 'rb-appscript'
11
+ gem 'sinatra'
12
+
13
+
14
+ module TimeTap
15
+ attr_accessor :config
16
+
17
+ extend self
18
+
19
+
20
+ # CONFIGURATION
21
+
22
+ # Are we on 1.9?
23
+ # FIXME: this is wrong! :)
24
+ RUBY19 = RUBY_VERSION.to_f >= 1.9
25
+
26
+ def config= options = {}
27
+ require 'active_support'
28
+
29
+ # CONFIG
30
+ @config = HashWithIndifferentAccess.new(options)
31
+ @config[:root] = File.expand_path(config[:root])
32
+ @config[:port] = config[:port].to_i
33
+ end
34
+
35
+
36
+ def start options = {}
37
+ # REQUIREMENTS
38
+
39
+ require 'yaml'
40
+ require 'active_support'
41
+ require 'time_tap/project'
42
+ require 'time_tap/editors'
43
+ require 'time_tap/watcher'
44
+ require 'time_tap/server'
45
+
46
+
47
+
48
+ # SIGNAL HANDLING
49
+
50
+ Signal.trap("INT") {exit}
51
+ Signal.trap("TERM") {exit}
52
+
53
+
54
+ # WEB SERVER
55
+
56
+ Thread.abort_on_exception = true
57
+
58
+ @server = Thread.new {
59
+ Signal.trap("INT") {exit}
60
+ Signal.trap("TERM") {exit}
61
+
62
+ Server.run! :host => 'localhost', :port => TimeTap.config[:port]
63
+ exit
64
+ }
65
+
66
+
67
+ # WATCHER
68
+
69
+ include Editors
70
+ Watcher.keep_watching(TextMate)
71
+ end
72
+
73
+ # Add a plist for OSX's launchd and have *TimeTap* launched automatically at login.
74
+ def install!
75
+ load_plist_info!
76
+ ruby = config[:ruby] || "/usr/bin/ruby"
77
+ include_dir = '-I'+File.expand_path('../../lib', __FILE__)
78
+ launcher = File.expand_path('../../bin/timetap', __FILE__)
79
+
80
+ puts "\nCreating launchd plist in\n #{plist_path}"
81
+
82
+ File.open(plist_path, 'w') do |file|
83
+ file << <<-PLIST
84
+ <?xml version="1.0" encoding="UTF-8"?>
85
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
86
+ <plist version="1.0">
87
+ <dict>
88
+ <key>Label</key>
89
+ <string>com.eliaesocietas.TimeTap</string>
90
+
91
+ <key>Program</key>
92
+ <string>#{ruby}</string>
93
+
94
+ <key>ProgramArguments</key>
95
+ <array>
96
+ <string>#{ruby}</string>
97
+ <string>#{include_dir}</string>
98
+ <string>#{launcher}</string>
99
+ <string>-f</string>
100
+ </array>
101
+
102
+ <key>OnDemand</key>
103
+ <false/>
104
+
105
+ <key>RunAtLoad</key>
106
+ <true/>
107
+ </dict>
108
+ </plist>
109
+ PLIST
110
+ end
111
+ end
112
+
113
+ def reload!
114
+ load_plist_info!
115
+ command = "launchctl unload #{plist_path}; launchctl load #{plist_path}"
116
+ exec command
117
+ end
118
+
119
+ private
120
+
121
+ attr_reader :plist_path, :plist_name
122
+
123
+ def load_plist_info!
124
+ @plist_name ||= "com.eliaesocietas.TimeTap.plist"
125
+ @plist_path ||= File.expand_path("~/Library/LaunchAgents/#{plist_name}")
126
+ end
127
+
128
+ end