time_tap 0.2.0 → 0.4.0.pre

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 (42) hide show
  1. data/.gitignore +5 -37
  2. data/.rspec +1 -0
  3. data/.ruby-version +1 -0
  4. data/Gemfile +3 -21
  5. data/README.md +104 -41
  6. data/Rakefile +1 -30
  7. data/bin/timetap +25 -26
  8. data/lib/time_tap.rb +126 -52
  9. data/lib/time_tap/backend.rb +9 -0
  10. data/lib/time_tap/backend/file_system.rb +36 -0
  11. data/lib/time_tap/config.yml.example +32 -0
  12. data/lib/time_tap/editor.rb +9 -0
  13. data/lib/time_tap/editor/sublime_text2.rb +15 -0
  14. data/lib/time_tap/editor/text_mate.rb +22 -0
  15. data/lib/time_tap/editor/text_mate2.rb +15 -0
  16. data/lib/time_tap/editor/xcode.rb +24 -0
  17. data/lib/time_tap/project.rb +136 -116
  18. data/lib/time_tap/server.rb +20 -20
  19. data/lib/time_tap/version.rb +3 -0
  20. data/lib/time_tap/views/project.haml +5 -3
  21. data/lib/time_tap/views/project_day.haml +1 -0
  22. data/lib/time_tap/watcher.rb +46 -43
  23. data/log/.git-keep +1 -0
  24. data/spec/lib/time_tap/project_spec.rb +12 -0
  25. data/spec/spec_helper.rb +3 -1
  26. data/spec/time_tap/backend_spec.rb +12 -0
  27. data/spec/time_tap/project_spec.rb +13 -0
  28. data/spec/time_tap/watcher_spec.rb +14 -0
  29. data/spec/time_tap_spec.rb +6 -4
  30. data/time_tap.gemspec +26 -108
  31. data/vendor/SublimeText2/.gitignore +1 -0
  32. data/vendor/SublimeText2/README.md +27 -0
  33. data/vendor/SublimeText2/TimeTap.py +10 -0
  34. data/vendor/TextMate2/TimeTap.tmbundle/Commands/Record current file.tmCommand +33 -0
  35. data/vendor/TextMate2/TimeTap.tmbundle/info.plist +16 -0
  36. data/vendor/TimeTap.tmbundle/Commands/Record current file.tmCommand +36 -0
  37. data/vendor/TimeTap.tmbundle/info.plist +18 -0
  38. metadata +161 -260
  39. data/Gemfile.lock +0 -48
  40. data/VERSION +0 -1
  41. data/config.yaml +0 -8
  42. data/lib/time_tap/editors.rb +0 -23
@@ -5,27 +5,27 @@ require 'action_view'
5
5
 
6
6
  module TimeTap
7
7
  class Server < Sinatra::Application
8
-
8
+
9
9
  include ActionView::Helpers::DateHelper
10
10
  set :haml, { :format => :html5,
11
- :attr_wrapper => '"' ,
11
+ :attr_wrapper => '"' ,
12
12
  :encoding => RUBY19 ? 'UTF-8' : nil}
13
13
  set :root, File.dirname(__FILE__)
14
14
  set :views, Proc.new { File.expand_path("../views", __FILE__) }
15
-
16
-
15
+
16
+
17
17
  before do
18
18
  content_type "text/html", :charset => "utf-8"
19
- Project.load_file('~/.tap_history')
19
+ # Project.load_file('~/.tap_history')
20
20
  end
21
-
22
-
23
-
24
-
25
-
21
+
22
+
23
+
24
+
25
+
26
26
  get '/' do
27
27
  sort = (params[:sort] || :last).to_sym
28
-
28
+
29
29
  @projects = Project.all.sort_by do |project|
30
30
  case sort
31
31
  when :name; project.name
@@ -36,15 +36,15 @@ module TimeTap
36
36
 
37
37
  haml :index
38
38
  end
39
-
39
+
40
40
  get '/project/:name' do
41
41
  @project = Project.find(params[:name])
42
42
  redirect '/' if @project.nil?
43
-
43
+
44
44
  haml :project
45
45
  end
46
-
47
-
46
+
47
+
48
48
  get "/project/:name/:day" do
49
49
  @project = Project.find(params[:name])
50
50
  redirect '/' if @project.nil?
@@ -61,9 +61,9 @@ module TimeTap
61
61
  content_type "text/css", :charset => "utf-8"
62
62
  sass :stylesheet
63
63
  end
64
-
64
+
65
65
  get '/mate' do
66
- tm_project = File.expand_path("#{TimeTap.config[:textmate][:projects] || 'Development/Current Projects'}/#{File.basename(params[:path])}.tmproj")
66
+ tm_project = Dir[ params[:path]+'/*.tmproj' ].first
67
67
  if File.exist?(tm_project)
68
68
  `open "#{tm_project}"`
69
69
  else
@@ -71,11 +71,11 @@ module TimeTap
71
71
  end
72
72
  redirect '/'
73
73
  end
74
-
74
+
75
75
  get '/stop' do
76
76
  $stop = true
77
77
  Process.exit
78
78
  end
79
-
79
+
80
80
  end
81
- end
81
+ end
@@ -0,0 +1,3 @@
1
+ module TimeTap
2
+ VERSION = '0.4.0.pre'
3
+ end
@@ -13,12 +13,14 @@
13
13
  %table.days
14
14
  - current = 0
15
15
  - @project.days.to_a.sort_by(&:first).reverse.each do |day, pinches|
16
- - time = pinches.map(&:duration).inject(0.seconds, &:+)
16
+ - time = pinches.sum(&:duration)
17
17
  - if time >= 1.minute
18
18
  %tr.day
19
19
  %td
20
- %a.day{ :href=> "/project/#{@project.name}/#{current}" } #{( this_year != day.year ? day.strftime("%Y %b %d, %a") : day.strftime("%b %d, %a") )}
20
+ %a.day{:href => "/project/#{@project.name}/#{current}"} #{( this_year != day.year ? day.strftime("%Y %b %d, %a") : day.strftime("%b %d, %a") )}
21
21
  %td
22
22
  %span.work{:style => "background-color:green;color:#ccc;overflow:visible;height:1em"}= '&nbsp;' * (time / 5.minutes.to_f)
23
- %span.tip= time_ago_in_words Time.now - time
23
+ %b= time_ago_in_words Time.now - time
24
+ %td
25
+ %span.tip= "#{'%.2f' % (time / 1.hour.to_f)} hours"
24
26
  - current = current + 1
@@ -10,6 +10,7 @@
10
10
  %h2
11
11
  Detailed View For:
12
12
  %b=( this_year != @current_day.year ? @current_day.strftime("%Y %b %d, %a") : @current_day.strftime("%b %d, %a") )
13
+ %span.tip (#{"%.2f" % (@pinches.sum(&:duration).to_i / 1.hours.to_f).to_s} hours)
13
14
 
14
15
  %table.pinches
15
16
  %tr
@@ -1,49 +1,52 @@
1
- module TimeTap
2
- module Watcher
3
- extend self
4
-
5
- def keep_watching editor
6
- last = nil
7
-
8
- # TODO: pick which application the user wants...
9
- editor = editor.new
10
-
11
- File.open(File.join(TimeTap.config[:root], ".tap_history"), 'a') do |history|
12
- history.sync = true
13
- loop do
14
- exit if $stop
15
- begin
16
- if editor.is_running? && !(path = editor.current_path).blank?
17
- mtime = File.stat(path).mtime
18
- current = [path, mtime]
19
-
20
- unless current == last
21
- # The following equals to this shell code:
22
- # `echo \`date +%s\`: \`pwd\``
23
- history << "#{mtime.to_i}: #{path}\n"
24
- last = [path, mtime]
25
- end
26
- end
27
- rescue Editors::EditorError
28
- # do nothing
29
- rescue
30
- puts Time.now.to_s
31
- puts $!.to_s
32
- puts $!.backtrace.join("\n")
1
+ require 'time_tap/project'
33
2
 
34
- File.open(File.join(TimeTap.config[:root], ".tap_errors"), "w") do |file|
35
- file.puts Time.now.to_s
36
- file.puts $!.to_s
37
- file.puts $!.backtrace.join("\n")
38
- end
3
+ class TimeTap::Watcher
4
+ attr_reader :editor, :backend
5
+ def initialize editor, backend
6
+ @editor, @backend = editor, backend
7
+ logger.info "new watcher #{[editor, backend].inspect}"
8
+ end
9
+
10
+ def logger
11
+ TimeTap.logger
12
+ end
13
+
14
+ def keep_watching
15
+ last = nil
39
16
 
40
- raise if $!.kind_of?(SignalException)
41
- end
42
- sleep 30
43
- end
17
+ loop do
18
+ break if $stop
19
+ logger.info "[TimeTap] checking current_path..."
20
+
21
+ begin
22
+ last = watch!(last)
23
+ rescue
24
+ logger.error "#{$!}\n#{$!.backtrace.join("\n")}"
25
+ raise if $!.kind_of?(SignalException)
44
26
  end
27
+
28
+ break if $stop
29
+ sleep 30
45
30
  end
31
+ end
32
+
33
+
34
+ def watch! last = nil
35
+ path = editor.current_path
46
36
 
47
-
37
+ if path and File.exist? path
38
+ logger.info "[TimeTap::Watcher] path: #{path.inspect}"
39
+ mtime = File.mtime(path).to_i
40
+ current = [mtime, path]
41
+ logger.info "[TimeTap::Watcher] checking entry (#{current == last}): \n#{current.inspect} == \n#{last.inspect}"
42
+
43
+ if current != last
44
+ backend.register *current
45
+ TimeTap::Project.register *current
46
+ TimeTap.logger.info "[TimeTap::Watcher] registered: #{current.inspect}"
47
+ last = current
48
+ end
49
+ end
48
50
  end
49
- end
51
+
52
+ end
data/log/.git-keep ADDED
@@ -0,0 +1 @@
1
+
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'time_tap/project'
3
+
4
+ describe TimeTap::Project do
5
+ before do
6
+ TimeTap::Project.reload!
7
+ end
8
+
9
+ it 'has a 30m puse limit' do
10
+ TimeTap::Project.pause_limit.should eq(30.minutes)
11
+ end
12
+ end
data/spec/spec_helper.rb CHANGED
@@ -17,5 +17,7 @@ require 'rspec/autorun'
17
17
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
18
18
 
19
19
  RSpec.configure do |config|
20
-
20
+ config.before do
21
+ TimeTap.config[:file_name] = './.tap_history-test'
22
+ end
21
23
  end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'time_tap/backend'
3
+
4
+ describe 'Backend' do
5
+ let(:file_name) { './test-file_system-backend' }
6
+
7
+ it 'loads a backend' do
8
+ backend = TimeTap::Backend.load :file_system, :file_name => file_name
9
+ backend.file_name.should eq(File.expand_path(file_name))
10
+ backend.should be_a(TimeTap::Backend::FileSystem)
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+ require 'time_tap/project'
3
+
4
+ describe TimeTap::Project do
5
+ let(:backend) { double('backend') }
6
+ it 'has a configuration' do
7
+ TimeTap::Project.pause_limit.should eq(30.minutes)
8
+ end
9
+ it 'reads from the backend' do
10
+ # backend.should_receive(:load_data).and_return([[Time.now, File.expand_path(__FILE__)]])
11
+ # TimeTap::Project.all.size.should eq(1)
12
+ end
13
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+ require 'time_tap/watcher'
3
+
4
+ describe 'Watcher' do
5
+ let(:file) { __FILE__ }
6
+ let(:editor) { double('editor', :current_path => file) }
7
+ let(:backend) { double('backend') }
8
+
9
+ it 'asks the editor the name of the currently opened file' do
10
+ backend.should_receive(:register).with(File.mtime(file).to_i, file)
11
+ watcher = TimeTap::Watcher.new(editor, backend)
12
+ watcher.watch!
13
+ end
14
+ end
@@ -1,7 +1,9 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
+ require 'time_tap'
2
3
 
3
- describe "TimeTap" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
4
+ describe TimeTap do
5
+ it 'prepares the backend from the config' do
6
+ TimeTap.config[:backend] = :file_system
7
+ TimeTap.backend.should be_a(TimeTap::Backend::FileSystem)
6
8
  end
7
9
  end
data/time_tap.gemspec CHANGED
@@ -1,115 +1,33 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path('../lib', __FILE__)
3
+ require 'time_tap/version'
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{time_tap}
8
- s.version = "0.2.0"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Elia Schito"]
12
- s.date = %q{2010-10-17}
13
- s.default_executable = %q{timetap}
6
+ s.name = 'time_tap'
7
+ s.version = TimeTap::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ['Elia Schito']
10
+ s.email = ['perlelia@gmail']
11
+ s.homepage = 'http://github.com/elia/timetap'
12
+ s.summary = %q{Unobtrusive time-tracking for TextMate.}
14
13
  s.description = %q{TimeTap helps you track the time you spend coding on each project while in TextMate.}
15
- s.email = %q{perlelia@gmail.com}
16
- s.executables = ["timetap"]
17
- s.extra_rdoc_files = [
18
- "LICENSE",
19
- "README.md"
20
- ]
21
- s.files = [
22
- ".gitignore",
23
- "Gemfile",
24
- "Gemfile.lock",
25
- "LICENSE",
26
- "README.md",
27
- "Rakefile",
28
- "VERSION",
29
- "bin/timetap",
30
- "config.yaml",
31
- "lib/time_tap.rb",
32
- "lib/time_tap/daemon.rb",
33
- "lib/time_tap/editors.rb",
34
- "lib/time_tap/project.rb",
35
- "lib/time_tap/server.rb",
36
- "lib/time_tap/tasks.rb",
37
- "lib/time_tap/views/index.haml",
38
- "lib/time_tap/views/layout.haml",
39
- "lib/time_tap/views/project.haml",
40
- "lib/time_tap/views/project_day.haml",
41
- "lib/time_tap/views/stylesheet.sass",
42
- "lib/time_tap/watcher.rb",
43
- "spec/.rspec",
44
- "spec/spec_helper.rb",
45
- "spec/time_tap_spec.rb",
46
- "time_tap.gemspec"
47
- ]
48
- s.homepage = %q{http://github.com/elia/timetap}
49
- s.require_paths = ["lib"]
50
- s.rubygems_version = %q{1.3.7}
51
- s.summary = %q{Unobtrusive time tracking for TextMate}
52
- s.test_files = [
53
- "spec/spec_helper.rb",
54
- "spec/time_tap_spec.rb"
55
- ]
56
14
 
57
- if s.respond_to? :specification_version then
58
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
59
- s.specification_version = 3
15
+ s.rubyforge_project = 'time_tap'
60
16
 
61
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
- s.add_runtime_dependency(%q<activesupport>, ["~> 2.3.8"])
63
- s.add_runtime_dependency(%q<actionpack>, ["~> 2.3.8"])
64
- s.add_runtime_dependency(%q<i18n>, ["~> 0.3.5"])
65
- s.add_runtime_dependency(%q<haml>, [">= 0"])
66
- s.add_runtime_dependency(%q<rb-appscript>, [">= 0"])
67
- s.add_runtime_dependency(%q<sinatra>, [">= 0"])
68
- s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
69
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
70
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
71
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
72
- s.add_development_dependency(%q<rcov>, [">= 0"])
73
- s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
74
- s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
75
- s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
76
- s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
77
- s.add_development_dependency(%q<rcov>, [">= 0"])
78
- else
79
- s.add_dependency(%q<activesupport>, ["~> 2.3.8"])
80
- s.add_dependency(%q<actionpack>, ["~> 2.3.8"])
81
- s.add_dependency(%q<i18n>, ["~> 0.3.5"])
82
- s.add_dependency(%q<haml>, [">= 0"])
83
- s.add_dependency(%q<rb-appscript>, [">= 0"])
84
- s.add_dependency(%q<sinatra>, [">= 0"])
85
- s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
86
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
87
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
88
- s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
89
- s.add_dependency(%q<rcov>, [">= 0"])
90
- s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
91
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
92
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
93
- s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
94
- s.add_dependency(%q<rcov>, [">= 0"])
95
- end
96
- else
97
- s.add_dependency(%q<activesupport>, ["~> 2.3.8"])
98
- s.add_dependency(%q<actionpack>, ["~> 2.3.8"])
99
- s.add_dependency(%q<i18n>, ["~> 0.3.5"])
100
- s.add_dependency(%q<haml>, [">= 0"])
101
- s.add_dependency(%q<rb-appscript>, [">= 0"])
102
- s.add_dependency(%q<sinatra>, [">= 0"])
103
- s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
104
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
105
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
106
- s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
107
- s.add_dependency(%q<rcov>, [">= 0"])
108
- s.add_dependency(%q<rspec>, [">= 2.0.0.beta.19"])
109
- s.add_dependency(%q<yard>, ["~> 0.6.0"])
110
- s.add_dependency(%q<bundler>, ["~> 1.0.0"])
111
- s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
112
- s.add_dependency(%q<rcov>, [">= 0"])
113
- end
114
- end
17
+ s.files = `git ls-files`.split("\n")
18
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
19
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
20
+ s.require_paths = ['lib']
21
+ s.extra_rdoc_files = %w[LICENSE README.md]
115
22
 
23
+ s.add_runtime_dependency 'sinatra', '~> 1.0'
24
+ s.add_runtime_dependency 'actionpack', '~> 3.1.0'
25
+ s.add_runtime_dependency 'activesupport', '~> 3.1.0'
26
+ s.add_runtime_dependency 'haml'
27
+ s.add_runtime_dependency 'sass'
28
+ s.add_runtime_dependency 'rb-appscript'
29
+ s.add_runtime_dependency 'bundler', '~> 1.0'
30
+
31
+ s.add_development_dependency 'rspec', '~> 2.0'
32
+ s.add_development_dependency 'spectator', '~> 1.1'
33
+ end
@@ -0,0 +1 @@
1
+ *.pyc