todidnt 0.0.0
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.
- data/Gemfile +7 -0
- data/Gemfile.lock +16 -0
- data/README +9 -0
- data/bin/todidnt +21 -0
- data/lib/todidnt/git_command.rb +26 -0
- data/lib/todidnt/git_repo.rb +24 -0
- data/lib/todidnt/todo_line.rb +52 -0
- data/lib/todidnt.rb +25 -0
- data/test/lib.rb +8 -0
- data/test/todo_line.rb +86 -0
- data/todidnt.gemspec +16 -0
- metadata +90 -0
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/README
ADDED
data/bin/todidnt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
|
5
|
+
require_relative '../lib/todidnt'
|
6
|
+
|
7
|
+
options = {:path => '.'}
|
8
|
+
ARGV.options do |opts|
|
9
|
+
opts.on('-p', '--path PATH', 'Git directory to run Todidnt in (default: current directory)') do |path|
|
10
|
+
options[:path] = path
|
11
|
+
end
|
12
|
+
|
13
|
+
opts.on_tail('-h', '--help', 'Show this message') do
|
14
|
+
puts opts
|
15
|
+
exit
|
16
|
+
end
|
17
|
+
|
18
|
+
opts.parse!
|
19
|
+
end
|
20
|
+
|
21
|
+
Todidnt::Runner.start(options)
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Todidnt
|
2
|
+
class GitCommand
|
3
|
+
def initialize(command, options)
|
4
|
+
@command = command
|
5
|
+
@options = options
|
6
|
+
end
|
7
|
+
|
8
|
+
def output_lines
|
9
|
+
run!.strip.split(/\n/)
|
10
|
+
end
|
11
|
+
|
12
|
+
def run!
|
13
|
+
`git #{command_with_options}`
|
14
|
+
end
|
15
|
+
|
16
|
+
def command_with_options
|
17
|
+
full_command = @command.to_s
|
18
|
+
|
19
|
+
for option in @options
|
20
|
+
full_command << " #{option.join(' ')}"
|
21
|
+
end
|
22
|
+
|
23
|
+
full_command
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Todidnt
|
2
|
+
class GitRepo
|
3
|
+
def initialize(path)
|
4
|
+
expanded_path = File.expand_path(path)
|
5
|
+
|
6
|
+
if File.exist?(File.join(expanded_path, '.git'))
|
7
|
+
@working_dir = expanded_path
|
8
|
+
else
|
9
|
+
$stderr.puts "Whoops, #{expanded_path} is not a git repository!"
|
10
|
+
exit
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def run(&blk)
|
15
|
+
unless Dir.pwd == @working_dir
|
16
|
+
Dir.chdir(@working_dir) do
|
17
|
+
yield @working_dir
|
18
|
+
end
|
19
|
+
else
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Todidnt
|
2
|
+
class TodoLine
|
3
|
+
IGNORE = %r{assets/js|third_?party|node_modules|jquery|Binary}
|
4
|
+
|
5
|
+
attr_reader :filename, :line_number, :content, :author
|
6
|
+
|
7
|
+
def self.all(expressions)
|
8
|
+
options = [['-n']]
|
9
|
+
expressions.each { |e| options << ['-e', e] }
|
10
|
+
|
11
|
+
grep = GitCommand.new(:grep, options)
|
12
|
+
grep.output_lines.map do |line|
|
13
|
+
filename, line_number, content = line.split(/:/, 3)
|
14
|
+
unless filename =~ IGNORE
|
15
|
+
lines = self.new(filename, line_number.to_i, content.strip[0..100])
|
16
|
+
end
|
17
|
+
end.compact
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize(filename, line_number, content)
|
21
|
+
@filename = filename
|
22
|
+
@line_number = line_number
|
23
|
+
@content = content
|
24
|
+
end
|
25
|
+
|
26
|
+
# TODO: This logic should probably be moved out somewhere else
|
27
|
+
def populate_blame
|
28
|
+
options = [
|
29
|
+
['--line-porcelain'],
|
30
|
+
['-L', "#{@line_number},#{@line_number}"],
|
31
|
+
[@filename]
|
32
|
+
]
|
33
|
+
|
34
|
+
blame = GitCommand.new(:blame, options)
|
35
|
+
blame.output_lines.each do |line|
|
36
|
+
if (author = /author (.*)/.match(line))
|
37
|
+
@author = author[1]
|
38
|
+
elsif (author_time = /author-time (.*)/.match(line))
|
39
|
+
@timestamp = author_time[1].to_i
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def pretty_time
|
45
|
+
Time.at(@timestamp).strftime('%F')
|
46
|
+
end
|
47
|
+
|
48
|
+
def pretty
|
49
|
+
"#{pretty_time} (#{author}, #{filename}:#{line_number}): #{content}"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/lib/todidnt.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'todidnt/git_repo'
|
2
|
+
require_relative 'todidnt/git_command'
|
3
|
+
require_relative 'todidnt/todo_line'
|
4
|
+
|
5
|
+
module Todidnt
|
6
|
+
class Runner
|
7
|
+
def self.start(options)
|
8
|
+
GitRepo.new(options[:path]).run do |path|
|
9
|
+
puts "Running in #{path || 'current directory'}..."
|
10
|
+
lines = TodoLine.all(["TODO"])
|
11
|
+
puts "Found #{lines.count} TODOs. Blaming..."
|
12
|
+
|
13
|
+
lines.each_with_index do |todo, i|
|
14
|
+
todo.populate_blame
|
15
|
+
$stdout.write "\rBlamed: #{i}/#{lines.count}"
|
16
|
+
end
|
17
|
+
|
18
|
+
puts "\nResults:"
|
19
|
+
lines.each do |line|
|
20
|
+
puts line.pretty
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/test/lib.rb
ADDED
data/test/todo_line.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative 'lib'
|
2
|
+
|
3
|
+
class TestTodoLine < Test
|
4
|
+
before do
|
5
|
+
GitCommand.any_instance.stubs(:run!)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.all' do
|
9
|
+
describe 'unit' do
|
10
|
+
it 'constructs the correct `git grep` command' do
|
11
|
+
grep = mock()
|
12
|
+
grep.stubs(:output_lines => [])
|
13
|
+
|
14
|
+
GitCommand.expects(:new).with(:grep, [['-n'], ['-e', 'hello']]).returns(grep)
|
15
|
+
TodoLine.all(['hello'])
|
16
|
+
|
17
|
+
GitCommand.expects(:new).with(:grep, [['-n'], ['-e', 'hello'], ['-e', 'goodbye']]).returns(grep)
|
18
|
+
TodoLine.all(['hello', 'goodbye'])
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'creates a TodoLine object for each result line properly' do
|
22
|
+
GitCommand.any_instance.expects(:output_lines).returns(
|
23
|
+
[
|
24
|
+
'filename.rb:12: content',
|
25
|
+
'other_filename.rb:643: TODO'
|
26
|
+
]
|
27
|
+
)
|
28
|
+
|
29
|
+
TodoLine.expects(:new).with('filename.rb', 12, 'content')
|
30
|
+
TodoLine.expects(:new).with('other_filename.rb', 643, 'TODO')
|
31
|
+
|
32
|
+
TodoLine.all(['anything'])
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe 'functional' do
|
37
|
+
it 'returns a list of TodoLine objects as matches' do
|
38
|
+
GitCommand.any_instance.expects(:output_lines).returns(
|
39
|
+
[
|
40
|
+
'filename.rb:12: content',
|
41
|
+
'other_filename.rb:643: TODO'
|
42
|
+
]
|
43
|
+
)
|
44
|
+
|
45
|
+
todos = TodoLine.all(['anything'])
|
46
|
+
assert_equal 2, todos.count
|
47
|
+
|
48
|
+
assert_equal 'filename.rb', todos.first.filename
|
49
|
+
assert_equal 12, todos.first.line_number
|
50
|
+
assert_equal 'content', todos.first.content
|
51
|
+
|
52
|
+
assert_equal 'other_filename.rb', todos.last.filename
|
53
|
+
assert_equal 643, todos.last.line_number
|
54
|
+
assert_equal 'TODO', todos.last.content
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'ignores lines matching IGNORE list' do
|
58
|
+
GitCommand.any_instance.expects(:output_lines).returns(
|
59
|
+
[
|
60
|
+
'filename.rb:12: content',
|
61
|
+
'thirdparty/other_filename.rb:643: TODO'
|
62
|
+
]
|
63
|
+
)
|
64
|
+
|
65
|
+
todos = TodoLine.all(['anything'])
|
66
|
+
assert_equal 1, todos.count
|
67
|
+
|
68
|
+
assert_equal 'filename.rb', todos.first.filename
|
69
|
+
assert_equal 12, todos.first.line_number
|
70
|
+
assert_equal 'content', todos.first.content
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
describe '#populate_blame' do
|
76
|
+
it 'constructs the correct `git blame` command' do
|
77
|
+
# TODO
|
78
|
+
skip
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'sets author, timestamp properties on the TodoLine object' do
|
82
|
+
# TODO
|
83
|
+
skip
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/todidnt.gemspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'todidnt'
|
3
|
+
s.version = '0.0.0'
|
4
|
+
s.summary = 'Todidnt'
|
5
|
+
s.description = "Todidnt finds and dates todos in your git repository."
|
6
|
+
s.authors = ["Amber Feng"]
|
7
|
+
s.email = 'amber.feng@gmail.com'
|
8
|
+
|
9
|
+
s.add_development_dependency('minitest')
|
10
|
+
s.add_development_dependency('mocha')
|
11
|
+
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.test_files = `git ls-files -- test/*`.split("\n")
|
14
|
+
s.executables = ['todidnt']
|
15
|
+
s.require_paths = %w[lib]
|
16
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: todidnt
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Amber Feng
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-11-07 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: minitest
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: mocha
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
description: Todidnt finds and dates todos in your git repository.
|
47
|
+
email: amber.feng@gmail.com
|
48
|
+
executables:
|
49
|
+
- todidnt
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- Gemfile
|
54
|
+
- Gemfile.lock
|
55
|
+
- README
|
56
|
+
- bin/todidnt
|
57
|
+
- lib/todidnt.rb
|
58
|
+
- lib/todidnt/git_command.rb
|
59
|
+
- lib/todidnt/git_repo.rb
|
60
|
+
- lib/todidnt/todo_line.rb
|
61
|
+
- test/lib.rb
|
62
|
+
- test/todo_line.rb
|
63
|
+
- todidnt.gemspec
|
64
|
+
homepage:
|
65
|
+
licenses: []
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options: []
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ! '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ! '>='
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
requirements: []
|
83
|
+
rubyforge_project:
|
84
|
+
rubygems_version: 1.8.23
|
85
|
+
signing_key:
|
86
|
+
specification_version: 3
|
87
|
+
summary: Todidnt
|
88
|
+
test_files:
|
89
|
+
- test/lib.rb
|
90
|
+
- test/todo_line.rb
|